// source --> https://eternize360.com.br/wp-includes/js/jquery/jquery.min.js?ver=3.7.1 
/*! jQuery v3.7.1 | (c) OpenJS Foundation and other contributors | jquery.org/license */
!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(ie,e){"use strict";var oe=[],r=Object.getPrototypeOf,ae=oe.slice,g=oe.flat?function(e){return oe.flat.call(e)}:function(e){return oe.concat.apply([],e)},s=oe.push,se=oe.indexOf,n={},i=n.toString,ue=n.hasOwnProperty,o=ue.toString,a=o.call(Object),le={},v=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},y=function(e){return null!=e&&e===e.window},C=ie.document,u={type:!0,src:!0,nonce:!0,noModule:!0};function m(e,t,n){var r,i,o=(n=n||C).createElement("script");if(o.text=e,t)for(r in u)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[i.call(e)]||"object":typeof e}var t="3.7.1",l=/HTML$/i,ce=function(e,t){return new ce.fn.init(e,t)};function c(e){var t=!!e&&"length"in e&&e.length,n=x(e);return!v(e)&&!y(e)&&("array"===n||0===t||"number"==typeof t&&0<t&&t-1 in e)}function fe(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}ce.fn=ce.prototype={jquery:t,constructor:ce,length:0,toArray:function(){return ae.call(this)},get:function(e){return null==e?ae.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=ce.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return ce.each(this,e)},map:function(n){return this.pushStack(ce.map(this,function(e,t){return n.call(e,t,e)}))},slice:function(){return this.pushStack(ae.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(ce.grep(this,function(e,t){return(t+1)%2}))},odd:function(){return this.pushStack(ce.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:s,sort:oe.sort,splice:oe.splice},ce.extend=ce.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||v(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&&(ce.isPlainObject(r)||(i=Array.isArray(r)))?(n=a[t],o=i&&!Array.isArray(n)?[]:i||ce.isPlainObject(n)?n:{},i=!1,a[t]=ce.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},ce.extend({expando:"jQuery"+(t+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]"!==i.call(e))&&(!(t=r(e))||"function"==typeof(n=ue.call(t,"constructor")&&t.constructor)&&o.call(n)===a)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e,t,n){m(e,{nonce:t&&t.nonce},n)},each:function(e,t){var n,r=0;if(c(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},text:function(e){var t,n="",r=0,i=e.nodeType;if(!i)while(t=e[r++])n+=ce.text(t);return 1===i||11===i?e.textContent:9===i?e.documentElement.textContent:3===i||4===i?e.nodeValue:n},makeArray:function(e,t){var n=t||[];return null!=e&&(c(Object(e))?ce.merge(n,"string"==typeof e?[e]:e):s.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:se.call(t,e,n)},isXMLDoc:function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!l.test(t||n&&n.nodeName||"HTML")},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(c(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:le}),"function"==typeof Symbol&&(ce.fn[Symbol.iterator]=oe[Symbol.iterator]),ce.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(e,t){n["[object "+t+"]"]=t.toLowerCase()});var pe=oe.pop,de=oe.sort,he=oe.splice,ge="[\\x20\\t\\r\\n\\f]",ve=new RegExp("^"+ge+"+|((?:^|[^\\\\])(?:\\\\.)*)"+ge+"+$","g");ce.contains=function(e,t){var n=t&&t.parentNode;return e===n||!(!n||1!==n.nodeType||!(e.contains?e.contains(n):e.compareDocumentPosition&&16&e.compareDocumentPosition(n)))};var f=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g;function p(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e}ce.escapeSelector=function(e){return(e+"").replace(f,p)};var ye=C,me=s;!function(){var e,b,w,o,a,T,r,C,d,i,k=me,S=ce.expando,E=0,n=0,s=W(),c=W(),u=W(),h=W(),l=function(e,t){return e===t&&(a=!0),0},f="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",t="(?:\\\\[\\da-fA-F]{1,6}"+ge+"?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+",p="\\["+ge+"*("+t+")(?:"+ge+"*([*^$|!~]?=)"+ge+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+t+"))|)"+ge+"*\\]",g=":("+t+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+p+")*)|.*)\\)|)",v=new RegExp(ge+"+","g"),y=new RegExp("^"+ge+"*,"+ge+"*"),m=new RegExp("^"+ge+"*([>+~]|"+ge+")"+ge+"*"),x=new RegExp(ge+"|>"),j=new RegExp(g),A=new RegExp("^"+t+"$"),D={ID:new RegExp("^#("+t+")"),CLASS:new RegExp("^\\.("+t+")"),TAG:new RegExp("^("+t+"|[*])"),ATTR:new RegExp("^"+p),PSEUDO:new RegExp("^"+g),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ge+"*(even|odd|(([+-]|)(\\d*)n|)"+ge+"*(?:([+-]|)"+ge+"*(\\d+)|))"+ge+"*\\)|)","i"),bool:new RegExp("^(?:"+f+")$","i"),needsContext:new RegExp("^"+ge+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ge+"*((?:-\\d)?\\d*)"+ge+"*\\)|)(?=[^-]|$)","i")},N=/^(?:input|select|textarea|button)$/i,q=/^h\d$/i,L=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,H=/[+~]/,O=new RegExp("\\\\[\\da-fA-F]{1,6}"+ge+"?|\\\\([^\\r\\n\\f])","g"),P=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))},M=function(){V()},R=J(function(e){return!0===e.disabled&&fe(e,"fieldset")},{dir:"parentNode",next:"legend"});try{k.apply(oe=ae.call(ye.childNodes),ye.childNodes),oe[ye.childNodes.length].nodeType}catch(e){k={apply:function(e,t){me.apply(e,ae.call(t))},call:function(e){me.apply(e,ae.call(arguments,1))}}}function I(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&&(V(e),e=e||T,C)){if(11!==p&&(u=L.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return k.call(n,a),n}else if(f&&(a=f.getElementById(i))&&I.contains(e,a)&&a.id===i)return k.call(n,a),n}else{if(u[2])return k.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&e.getElementsByClassName)return k.apply(n,e.getElementsByClassName(i)),n}if(!(h[t+" "]||d&&d.test(t))){if(c=t,f=e,1===p&&(x.test(t)||m.test(t))){(f=H.test(t)&&U(e.parentNode)||e)==e&&le.scope||((s=e.getAttribute("id"))?s=ce.escapeSelector(s):e.setAttribute("id",s=S)),o=(l=Y(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+Q(l[o]);c=l.join(",")}try{return k.apply(n,f.querySelectorAll(c)),n}catch(e){h(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return re(t.replace(ve,"$1"),e,n,r)}function W(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function F(e){return e[S]=!0,e}function $(e){var t=T.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function B(t){return function(e){return fe(e,"input")&&e.type===t}}function _(t){return function(e){return(fe(e,"input")||fe(e,"button"))&&e.type===t}}function z(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&&R(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function X(a){return F(function(o){return o=+o,F(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 U(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}function V(e){var t,n=e?e.ownerDocument||e:ye;return n!=T&&9===n.nodeType&&n.documentElement&&(r=(T=n).documentElement,C=!ce.isXMLDoc(T),i=r.matches||r.webkitMatchesSelector||r.msMatchesSelector,r.msMatchesSelector&&ye!=T&&(t=T.defaultView)&&t.top!==t&&t.addEventListener("unload",M),le.getById=$(function(e){return r.appendChild(e).id=ce.expando,!T.getElementsByName||!T.getElementsByName(ce.expando).length}),le.disconnectedMatch=$(function(e){return i.call(e,"*")}),le.scope=$(function(){return T.querySelectorAll(":scope")}),le.cssHas=$(function(){try{return T.querySelector(":has(*,:jqfake)"),!1}catch(e){return!0}}),le.getById?(b.filter.ID=function(e){var t=e.replace(O,P);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&C){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(O,P);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&&C){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=function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):t.querySelectorAll(e)},b.find.CLASS=function(e,t){if("undefined"!=typeof t.getElementsByClassName&&C)return t.getElementsByClassName(e)},d=[],$(function(e){var t;r.appendChild(e).innerHTML="<a id='"+S+"' href='' disabled='disabled'></a><select id='"+S+"-\r\\' disabled='disabled'><option selected=''></option></select>",e.querySelectorAll("[selected]").length||d.push("\\["+ge+"*(?:value|"+f+")"),e.querySelectorAll("[id~="+S+"-]").length||d.push("~="),e.querySelectorAll("a#"+S+"+*").length||d.push(".#.+[+~]"),e.querySelectorAll(":checked").length||d.push(":checked"),(t=T.createElement("input")).setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),r.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&d.push(":enabled",":disabled"),(t=T.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||d.push("\\["+ge+"*name"+ge+"*="+ge+"*(?:''|\"\")")}),le.cssHas||d.push(":has"),d=d.length&&new RegExp(d.join("|")),l=function(e,t){if(e===t)return a=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!le.sortDetached&&t.compareDocumentPosition(e)===n?e===T||e.ownerDocument==ye&&I.contains(ye,e)?-1:t===T||t.ownerDocument==ye&&I.contains(ye,t)?1:o?se.call(o,e)-se.call(o,t):0:4&n?-1:1)}),T}for(e in I.matches=function(e,t){return I(e,null,null,t)},I.matchesSelector=function(e,t){if(V(e),C&&!h[t+" "]&&(!d||!d.test(t)))try{var n=i.call(e,t);if(n||le.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){h(t,!0)}return 0<I(t,T,null,[e]).length},I.contains=function(e,t){return(e.ownerDocument||e)!=T&&V(e),ce.contains(e,t)},I.attr=function(e,t){(e.ownerDocument||e)!=T&&V(e);var n=b.attrHandle[t.toLowerCase()],r=n&&ue.call(b.attrHandle,t.toLowerCase())?n(e,t,!C):void 0;return void 0!==r?r:e.getAttribute(t)},I.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},ce.uniqueSort=function(e){var t,n=[],r=0,i=0;if(a=!le.sortStable,o=!le.sortStable&&ae.call(e,0),de.call(e,l),a){while(t=e[i++])t===e[i]&&(r=n.push(i));while(r--)he.call(e,n[r],1)}return o=null,e},ce.fn.uniqueSort=function(){return this.pushStack(ce.uniqueSort(ae.apply(this)))},(b=ce.expr={cacheLength:50,createPseudo:F,match:D,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(O,P),e[3]=(e[3]||e[4]||e[5]||"").replace(O,P),"~="===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]||I.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]&&I.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return D.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&j.test(n)&&(t=Y(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(O,P).toLowerCase();return"*"===e?function(){return!0}:function(e){return fe(e,t)}},CLASS:function(e){var t=s[e+" "];return t||(t=new RegExp("(^|"+ge+")"+e+"("+ge+"|$)"))&&s(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=I.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(v," ")+" ").indexOf(i):"|="===r&&(t===i||t.slice(0,i.length+1)===i+"-"))}},CHILD:function(d,e,t,h,g){var v="nth"!==d.slice(0,3),y="last"!==d.slice(-4),m="of-type"===e;return 1===h&&0===g?function(e){return!!e.parentNode}:function(e,t,n){var r,i,o,a,s,u=v!==y?"nextSibling":"previousSibling",l=e.parentNode,c=m&&e.nodeName.toLowerCase(),f=!n&&!m,p=!1;if(l){if(v){while(u){o=e;while(o=o[u])if(m?fe(o,c):1===o.nodeType)return!1;s=u="only"===d&&!s&&"nextSibling"}return!0}if(s=[y?l.firstChild:l.lastChild],y&&f){p=(a=(r=(i=l[S]||(l[S]={}))[d]||[])[0]===E&&r[1])&&r[2],o=a&&l.childNodes[a];while(o=++a&&o&&o[u]||(p=a=0)||s.pop())if(1===o.nodeType&&++p&&o===e){i[d]=[E,a,p];break}}else if(f&&(p=a=(r=(i=e[S]||(e[S]={}))[d]||[])[0]===E&&r[1]),!1===p)while(o=++a&&o&&o[u]||(p=a=0)||s.pop())if((m?fe(o,c):1===o.nodeType)&&++p&&(f&&((i=o[S]||(o[S]={}))[d]=[E,p]),o===e))break;return(p-=g)===h||p%h==0&&0<=p/h}}},PSEUDO:function(e,o){var t,a=b.pseudos[e]||b.setFilters[e.toLowerCase()]||I.error("unsupported pseudo: "+e);return a[S]?a(o):1<a.length?(t=[e,e,"",o],b.setFilters.hasOwnProperty(e.toLowerCase())?F(function(e,t){var n,r=a(e,o),i=r.length;while(i--)e[n=se.call(e,r[i])]=!(t[n]=r[i])}):function(e){return a(e,0,t)}):a}},pseudos:{not:F(function(e){var r=[],i=[],s=ne(e.replace(ve,"$1"));return s[S]?F(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:F(function(t){return function(e){return 0<I(t,e).length}}),contains:F(function(t){return t=t.replace(O,P),function(e){return-1<(e.textContent||ce.text(e)).indexOf(t)}}),lang:F(function(n){return A.test(n||"")||I.error("unsupported lang: "+n),n=n.replace(O,P).toLowerCase(),function(e){var t;do{if(t=C?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=ie.location&&ie.location.hash;return t&&t.slice(1)===e.id},root:function(e){return e===r},focus:function(e){return e===function(){try{return T.activeElement}catch(e){}}()&&T.hasFocus()&&!!(e.type||e.href||~e.tabIndex)},enabled:z(!1),disabled:z(!0),checked:function(e){return fe(e,"input")&&!!e.checked||fe(e,"option")&&!!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 q.test(e.nodeName)},input:function(e){return N.test(e.nodeName)},button:function(e){return fe(e,"input")&&"button"===e.type||fe(e,"button")},text:function(e){var t;return fe(e,"input")&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:X(function(){return[0]}),last:X(function(e,t){return[t-1]}),eq:X(function(e,t,n){return[n<0?n+t:n]}),even:X(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:X(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:X(function(e,t,n){var r;for(r=n<0?n+t:t<n?t:n;0<=--r;)e.push(r);return e}),gt:X(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]=B(e);for(e in{submit:!0,reset:!0})b.pseudos[e]=_(e);function G(){}function Y(e,t){var n,r,i,o,a,s,u,l=c[e+" "];if(l)return t?0:l.slice(0);a=e,s=[],u=b.preFilter;while(a){for(o in n&&!(r=y.exec(a))||(r&&(a=a.slice(r[0].length)||a),s.push(i=[])),n=!1,(r=m.exec(a))&&(n=r.shift(),i.push({value:n,type:r[0].replace(ve," ")}),a=a.slice(n.length)),b.filter)!(r=D[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?I.error(e):c(e,s).slice(0)}function Q(e){for(var t=0,n=e.length,r="";t<n;t++)r+=e[t].value;return r}function J(a,e,t){var s=e.dir,u=e.next,l=u||s,c=t&&"parentNode"===l,f=n++;return e.first?function(e,t,n){while(e=e[s])if(1===e.nodeType||c)return a(e,t,n);return!1}:function(e,t,n){var r,i,o=[E,f];if(n){while(e=e[s])if((1===e.nodeType||c)&&a(e,t,n))return!0}else while(e=e[s])if(1===e.nodeType||c)if(i=e[S]||(e[S]={}),u&&fe(e,u))e=e[s]||e;else{if((r=i[l])&&r[0]===E&&r[1]===f)return o[2]=r[2];if((i[l]=o)[2]=a(e,t,n))return!0}return!1}}function K(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 Z(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 ee(d,h,g,v,y,e){return v&&!v[S]&&(v=ee(v)),y&&!y[S]&&(y=ee(y,e)),F(function(e,t,n,r){var i,o,a,s,u=[],l=[],c=t.length,f=e||function(e,t,n){for(var r=0,i=t.length;r<i;r++)I(e,t[r],n);return n}(h||"*",n.nodeType?[n]:n,[]),p=!d||!e&&h?f:Z(f,u,d,n,r);if(g?g(p,s=y||(e?d:c||v)?[]:t,n,r):s=p,v){i=Z(s,l),v(i,[],n,r),o=i.length;while(o--)(a=i[o])&&(s[l[o]]=!(p[l[o]]=a))}if(e){if(y||d){if(y){i=[],o=s.length;while(o--)(a=s[o])&&i.push(p[o]=a);y(null,s=[],i,r)}o=s.length;while(o--)(a=s[o])&&-1<(i=y?se.call(e,a):u[o])&&(e[i]=!(t[i]=a))}}else s=Z(s===t?s.splice(c,s.length):s),y?y(null,t,s,r):k.apply(t,s)})}function te(e){for(var i,t,n,r=e.length,o=b.relative[e[0].type],a=o||b.relative[" "],s=o?1:0,u=J(function(e){return e===i},a,!0),l=J(function(e){return-1<se.call(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=[J(K(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 ee(1<s&&K(c),1<s&&Q(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace(ve,"$1"),t,s<n&&te(e.slice(s,n)),n<r&&te(e=e.slice(n)),n<r&&Q(e))}c.push(t)}return K(c)}function ne(e,t){var n,v,y,m,x,r,i=[],o=[],a=u[e+" "];if(!a){t||(t=Y(e)),n=t.length;while(n--)(a=te(t[n]))[S]?i.push(a):o.push(a);(a=u(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=E+=null==p?1:Math.random()||.1,g=d.length;for(i&&(w=t==T||t||i);l!==g&&null!=(o=d[l]);l++){if(x&&o){a=0,t||o.ownerDocument==T||(V(o),n=!C);while(s=v[a++])if(s(o,t||T,n)){k.call(r,o);break}i&&(E=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]=pe.call(r));f=Z(f)}k.apply(r,f),i&&!e&&0<f.length&&1<u+y.length&&ce.uniqueSort(r)}return i&&(E=h,w=p),c},m?F(r):r))).selector=e}return a}function re(e,t,n,r){var i,o,a,s,u,l="function"==typeof e&&e,c=!r&&Y(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&&C&&b.relative[o[1].type]){if(!(t=(b.find.ID(a.matches[0].replace(O,P),t)||[])[0]))return n;l&&(t=t.parentNode),e=e.slice(o.shift().value.length)}i=D.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(O,P),H.test(o[0].type)&&U(t.parentNode)||t))){if(o.splice(i,1),!(e=r.length&&Q(o)))return k.apply(n,r),n;break}}}return(l||ne(e,c))(r,t,!C,n,!t||H.test(e)&&U(t.parentNode)||t),n}G.prototype=b.filters=b.pseudos,b.setFilters=new G,le.sortStable=S.split("").sort(l).join("")===S,V(),le.sortDetached=$(function(e){return 1&e.compareDocumentPosition(T.createElement("fieldset"))}),ce.find=I,ce.expr[":"]=ce.expr.pseudos,ce.unique=ce.uniqueSort,I.compile=ne,I.select=re,I.setDocument=V,I.tokenize=Y,I.escape=ce.escapeSelector,I.getText=ce.text,I.isXML=ce.isXMLDoc,I.selectors=ce.expr,I.support=ce.support,I.uniqueSort=ce.uniqueSort}();var d=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&ce(e).is(n))break;r.push(e)}return r},h=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},b=ce.expr.match.needsContext,w=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function T(e,n,r){return v(n)?ce.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?ce.grep(e,function(e){return e===n!==r}):"string"!=typeof n?ce.grep(e,function(e){return-1<se.call(n,e)!==r}):ce.filter(n,e,r)}ce.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?ce.find.matchesSelector(r,e)?[r]:[]:ce.find.matches(e,ce.grep(t,function(e){return 1===e.nodeType}))},ce.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(ce(e).filter(function(){for(t=0;t<r;t++)if(ce.contains(i[t],this))return!0}));for(n=this.pushStack([]),t=0;t<r;t++)ce.find(e,i[t],n);return 1<r?ce.uniqueSort(n):n},filter:function(e){return this.pushStack(T(this,e||[],!1))},not:function(e){return this.pushStack(T(this,e||[],!0))},is:function(e){return!!T(this,"string"==typeof e&&b.test(e)?ce(e):e||[],!1).length}});var k,S=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(ce.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||k,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:S.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 ce?t[0]:t,ce.merge(this,ce.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:C,!0)),w.test(r[1])&&ce.isPlainObject(t))for(r in t)v(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=C.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):v(e)?void 0!==n.ready?n.ready(e):e(ce):ce.makeArray(e,this)}).prototype=ce.fn,k=ce(C);var E=/^(?:parents|prev(?:Until|All))/,j={children:!0,contents:!0,next:!0,prev:!0};function A(e,t){while((e=e[t])&&1!==e.nodeType);return e}ce.fn.extend({has:function(e){var t=ce(e,this),n=t.length;return this.filter(function(){for(var e=0;e<n;e++)if(ce.contains(this,t[e]))return!0})},closest:function(e,t){var n,r=0,i=this.length,o=[],a="string"!=typeof e&&ce(e);if(!b.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&&ce.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(1<o.length?ce.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?se.call(ce(e),this[0]):se.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(ce.uniqueSort(ce.merge(this.get(),ce(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),ce.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return d(e,"parentNode")},parentsUntil:function(e,t,n){return d(e,"parentNode",n)},next:function(e){return A(e,"nextSibling")},prev:function(e){return A(e,"previousSibling")},nextAll:function(e){return d(e,"nextSibling")},prevAll:function(e){return d(e,"previousSibling")},nextUntil:function(e,t,n){return d(e,"nextSibling",n)},prevUntil:function(e,t,n){return d(e,"previousSibling",n)},siblings:function(e){return h((e.parentNode||{}).firstChild,e)},children:function(e){return h(e.firstChild)},contents:function(e){return null!=e.contentDocument&&r(e.contentDocument)?e.contentDocument:(fe(e,"template")&&(e=e.content||e),ce.merge([],e.childNodes))}},function(r,i){ce.fn[r]=function(e,t){var n=ce.map(this,i,e);return"Until"!==r.slice(-5)&&(t=e),t&&"string"==typeof t&&(n=ce.filter(t,n)),1<this.length&&(j[r]||ce.uniqueSort(n),E.test(r)&&n.reverse()),this.pushStack(n)}});var D=/[^\x20\t\r\n\f]+/g;function N(e){return e}function q(e){throw e}function L(e,t,n,r){var i;try{e&&v(i=e.promise)?i.call(e).done(t).fail(n):e&&v(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}ce.Callbacks=function(r){var e,n;r="string"==typeof r?(e=r,n={},ce.each(e.match(D)||[],function(e,t){n[t]=!0}),n):ce.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){ce.each(e,function(e,t){v(t)?r.unique&&f.has(t)||s.push(t):t&&t.length&&"string"!==x(t)&&n(t)})}(arguments),t&&!i&&c()),this},remove:function(){return ce.each(arguments,function(e,t){var n;while(-1<(n=ce.inArray(t,s,n)))s.splice(n,1),n<=l&&l--}),this},has:function(e){return e?-1<ce.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},ce.extend({Deferred:function(e){var o=[["notify","progress",ce.Callbacks("memory"),ce.Callbacks("memory"),2],["resolve","done",ce.Callbacks("once memory"),ce.Callbacks("once memory"),0,"resolved"],["reject","fail",ce.Callbacks("once memory"),ce.Callbacks("once memory"),1,"rejected"]],i="pending",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 ce.Deferred(function(r){ce.each(o,function(e,t){var n=v(i[t[4]])&&i[t[4]];s[t[1]](function(){var e=n&&n.apply(this,arguments);e&&v(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,v(t)?s?t.call(e,l(u,o,N,s),l(u,o,q,s)):(u++,t.call(e,l(u,o,N,s),l(u,o,q,s),l(u,o,N,o.notifyWith))):(a!==N&&(n=void 0,r=[e]),(s||o.resolveWith)(n,r))}},t=s?e:function(){try{e()}catch(e){ce.Deferred.exceptionHook&&ce.Deferred.exceptionHook(e,t.error),u<=i+1&&(a!==q&&(n=void 0,r=[e]),o.rejectWith(n,r))}};i?t():(ce.Deferred.getErrorHook?t.error=ce.Deferred.getErrorHook():ce.Deferred.getStackHook&&(t.error=ce.Deferred.getStackHook()),ie.setTimeout(t))}}return ce.Deferred(function(e){o[0][3].add(l(0,e,v(r)?r:N,e.notifyWith)),o[1][3].add(l(0,e,v(t)?t:N)),o[2][3].add(l(0,e,v(n)?n:q))}).promise()},promise:function(e){return null!=e?ce.extend(e,a):a}},s={};return ce.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=ae.call(arguments),o=ce.Deferred(),a=function(t){return function(e){r[t]=this,i[t]=1<arguments.length?ae.call(arguments):e,--n||o.resolveWith(r,i)}};if(n<=1&&(L(e,o.done(a(t)).resolve,o.reject,!n),"pending"===o.state()||v(i[t]&&i[t].then)))return o.then();while(t--)L(i[t],a(t),o.reject);return o.promise()}});var H=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;ce.Deferred.exceptionHook=function(e,t){ie.console&&ie.console.warn&&e&&H.test(e.name)&&ie.console.warn("jQuery.Deferred exception: "+e.message,e.stack,t)},ce.readyException=function(e){ie.setTimeout(function(){throw e})};var O=ce.Deferred();function P(){C.removeEventListener("DOMContentLoaded",P),ie.removeEventListener("load",P),ce.ready()}ce.fn.ready=function(e){return O.then(e)["catch"](function(e){ce.readyException(e)}),this},ce.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--ce.readyWait:ce.isReady)||(ce.isReady=!0)!==e&&0<--ce.readyWait||O.resolveWith(C,[ce])}}),ce.ready.then=O.then,"complete"===C.readyState||"loading"!==C.readyState&&!C.documentElement.doScroll?ie.setTimeout(ce.ready):(C.addEventListener("DOMContentLoaded",P),ie.addEventListener("load",P));var M=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===x(n))for(s in i=!0,n)M(e,t,s,n[s],!0,o,a);else if(void 0!==r&&(i=!0,v(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(ce(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},R=/^-ms-/,I=/-([a-z])/g;function W(e,t){return t.toUpperCase()}function F(e){return e.replace(R,"ms-").replace(I,W)}var $=function(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType};function B(){this.expando=ce.expando+B.uid++}B.uid=1,B.prototype={cache:function(e){var t=e[this.expando];return t||(t={},$(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[F(t)]=n;else for(r in t)i[F(r)]=t[r];return i},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][F(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(F):(t=F(t))in r?[t]:t.match(D)||[]).length;while(n--)delete r[t[n]]}(void 0===t||ce.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&&!ce.isEmptyObject(t)}};var _=new B,z=new B,X=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,U=/[A-Z]/g;function V(e,t,n){var r,i;if(void 0===n&&1===e.nodeType)if(r="data-"+t.replace(U,"-$&").toLowerCase(),"string"==typeof(n=e.getAttribute(r))){try{n="true"===(i=n)||"false"!==i&&("null"===i?null:i===+i+""?+i:X.test(i)?JSON.parse(i):i)}catch(e){}z.set(e,t,n)}else n=void 0;return n}ce.extend({hasData:function(e){return z.hasData(e)||_.hasData(e)},data:function(e,t,n){return z.access(e,t,n)},removeData:function(e,t){z.remove(e,t)},_data:function(e,t,n){return _.access(e,t,n)},_removeData:function(e,t){_.remove(e,t)}}),ce.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=z.get(o),1===o.nodeType&&!_.get(o,"hasDataAttrs"))){t=a.length;while(t--)a[t]&&0===(r=a[t].name).indexOf("data-")&&(r=F(r.slice(5)),V(o,r,i[r]));_.set(o,"hasDataAttrs",!0)}return i}return"object"==typeof n?this.each(function(){z.set(this,n)}):M(this,function(e){var t;if(o&&void 0===e)return void 0!==(t=z.get(o,n))?t:void 0!==(t=V(o,n))?t:void 0;this.each(function(){z.set(this,n,e)})},null,e,1<arguments.length,null,!0)},removeData:function(e){return this.each(function(){z.remove(this,e)})}}),ce.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=_.get(e,t),n&&(!r||Array.isArray(n)?r=_.access(e,t,ce.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=ce.queue(e,t),r=n.length,i=n.shift(),o=ce._queueHooks(e,t);"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,function(){ce.dequeue(e,t)},o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return _.get(e,n)||_.access(e,n,{empty:ce.Callbacks("once memory").add(function(){_.remove(e,[t+"queue",n])})})}}),ce.fn.extend({queue:function(t,n){var e=2;return"string"!=typeof t&&(n=t,t="fx",e--),arguments.length<e?ce.queue(this[0],t):void 0===n?this:this.each(function(){var e=ce.queue(this,t,n);ce._queueHooks(this,t),"fx"===t&&"inprogress"!==e[0]&&ce.dequeue(this,t)})},dequeue:function(e){return this.each(function(){ce.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=ce.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=_.get(o[a],e+"queueHooks"))&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var G=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,Y=new RegExp("^(?:([+-])=|)("+G+")([a-z%]*)$","i"),Q=["Top","Right","Bottom","Left"],J=C.documentElement,K=function(e){return ce.contains(e.ownerDocument,e)},Z={composed:!0};J.getRootNode&&(K=function(e){return ce.contains(e.ownerDocument,e)||e.getRootNode(Z)===e.ownerDocument});var ee=function(e,t){return"none"===(e=t||e).style.display||""===e.style.display&&K(e)&&"none"===ce.css(e,"display")};function te(e,t,n,r){var i,o,a=20,s=r?function(){return r.cur()}:function(){return ce.css(e,t,"")},u=s(),l=n&&n[3]||(ce.cssNumber[t]?"":"px"),c=e.nodeType&&(ce.cssNumber[t]||"px"!==l&&+u)&&Y.exec(ce.css(e,t));if(c&&c[3]!==l){u/=2,l=l||c[3],c=+u||1;while(a--)ce.style(e,t,c+l),(1-o)*(1-(o=s()/u||.5))<=0&&(a=0),c/=o;c*=2,ce.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 ne={};function re(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]=_.get(r,"display")||null,l[c]||(r.style.display="")),""===r.style.display&&ee(r)&&(l[c]=(u=a=o=void 0,a=(i=r).ownerDocument,s=i.nodeName,(u=ne[s])||(o=a.body.appendChild(a.createElement(s)),u=ce.css(o,"display"),o.parentNode.removeChild(o),"none"===u&&(u="block"),ne[s]=u)))):"none"!==n&&(l[c]="none",_.set(r,"display",n)));for(c=0;c<f;c++)null!=l[c]&&(e[c].style.display=l[c]);return e}ce.fn.extend({show:function(){return re(this,!0)},hide:function(){return re(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){ee(this)?ce(this).show():ce(this).hide()})}});var xe,be,we=/^(?:checkbox|radio)$/i,Te=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i,Ce=/^$|^module$|\/(?:java|ecma)script/i;xe=C.createDocumentFragment().appendChild(C.createElement("div")),(be=C.createElement("input")).setAttribute("type","radio"),be.setAttribute("checked","checked"),be.setAttribute("name","t"),xe.appendChild(be),le.checkClone=xe.cloneNode(!0).cloneNode(!0).lastChild.checked,xe.innerHTML="<textarea>x</textarea>",le.noCloneChecked=!!xe.cloneNode(!0).lastChild.defaultValue,xe.innerHTML="<option></option>",le.option=!!xe.lastChild;var ke={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 Se(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&fe(e,t)?ce.merge([e],n):n}function Ee(e,t){for(var n=0,r=e.length;n<r;n++)_.set(e[n],"globalEval",!t||_.get(t[n],"globalEval"))}ke.tbody=ke.tfoot=ke.colgroup=ke.caption=ke.thead,ke.th=ke.td,le.option||(ke.optgroup=ke.option=[1,"<select multiple='multiple'>","</select>"]);var je=/<|&#?\w+;/;function Ae(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"===x(o))ce.merge(p,o.nodeType?[o]:o);else if(je.test(o)){a=a||f.appendChild(t.createElement("div")),s=(Te.exec(o)||["",""])[1].toLowerCase(),u=ke[s]||ke._default,a.innerHTML=u[1]+ce.htmlPrefilter(o)+u[2],c=u[0];while(c--)a=a.lastChild;ce.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<ce.inArray(o,r))i&&i.push(o);else if(l=K(o),a=Se(f.appendChild(o),"script"),l&&Ee(a),n){c=0;while(o=a[c++])Ce.test(o.type||"")&&n.push(o)}return f}var De=/^([^.]*)(?:\.(.+)|)/;function Ne(){return!0}function qe(){return!1}function Le(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)Le(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=qe;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return ce().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=ce.guid++)),e.each(function(){ce.event.add(this,t,i,r,n)})}function He(e,r,t){t?(_.set(e,r,!1),ce.event.add(e,r,{namespace:!1,handler:function(e){var t,n=_.get(this,r);if(1&e.isTrigger&&this[r]){if(n)(ce.event.special[r]||{}).delegateType&&e.stopPropagation();else if(n=ae.call(arguments),_.set(this,r,n),this[r](),t=_.get(this,r),_.set(this,r,!1),n!==t)return e.stopImmediatePropagation(),e.preventDefault(),t}else n&&(_.set(this,r,ce.event.trigger(n[0],n.slice(1),this)),e.stopPropagation(),e.isImmediatePropagationStopped=Ne)}})):void 0===_.get(e,r)&&ce.event.add(e,r,Ne)}ce.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=_.get(t);if($(t)){n.handler&&(n=(o=n).handler,i=o.selector),i&&ce.find.matchesSelector(J,i),n.guid||(n.guid=ce.guid++),(u=v.events)||(u=v.events=Object.create(null)),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof ce&&ce.event.triggered!==e.type?ce.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(D)||[""]).length;while(l--)d=g=(s=De.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=ce.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=ce.event.special[d]||{},c=ce.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&ce.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),ce.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=_.hasData(e)&&_.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(D)||[""]).length;while(l--)if(d=g=(s=De.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=ce.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)||ce.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)ce.event.remove(e,d+t[l],n,r,!0);ce.isEmptyObject(u)&&_.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=new Array(arguments.length),u=ce.event.fix(e),l=(_.get(this,"events")||Object.create(null))[u.type]||[],c=ce.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=ce.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=((ce.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<ce(i,this).index(l):ce.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(ce.Event.prototype,t,{enumerable:!0,configurable:!0,get:v(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[ce.expando]?e:new ce.Event(e)},special:{load:{noBubble:!0},click:{setup:function(e){var t=this||e;return we.test(t.type)&&t.click&&fe(t,"input")&&He(t,"click",!0),!1},trigger:function(e){var t=this||e;return we.test(t.type)&&t.click&&fe(t,"input")&&He(t,"click"),!0},_default:function(e){var t=e.target;return we.test(t.type)&&t.click&&fe(t,"input")&&_.get(t,"click")||fe(t,"a")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}}},ce.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n)},ce.Event=function(e,t){if(!(this instanceof ce.Event))return new ce.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&!1===e.returnValue?Ne:qe,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&&ce.extend(this,t),this.timeStamp=e&&e.timeStamp||Date.now(),this[ce.expando]=!0},ce.Event.prototype={constructor:ce.Event,isDefaultPrevented:qe,isPropagationStopped:qe,isImmediatePropagationStopped:qe,isSimulated:!1,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=Ne,e&&!this.isSimulated&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=Ne,e&&!this.isSimulated&&e.stopPropagation()},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=Ne,e&&!this.isSimulated&&e.stopImmediatePropagation(),this.stopPropagation()}},ce.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:!0},ce.event.addProp),ce.each({focus:"focusin",blur:"focusout"},function(r,i){function o(e){if(C.documentMode){var t=_.get(this,"handle"),n=ce.event.fix(e);n.type="focusin"===e.type?"focus":"blur",n.isSimulated=!0,t(e),n.target===n.currentTarget&&t(n)}else ce.event.simulate(i,e.target,ce.event.fix(e))}ce.event.special[r]={setup:function(){var e;if(He(this,r,!0),!C.documentMode)return!1;(e=_.get(this,i))||this.addEventListener(i,o),_.set(this,i,(e||0)+1)},trigger:function(){return He(this,r),!0},teardown:function(){var e;if(!C.documentMode)return!1;(e=_.get(this,i)-1)?_.set(this,i,e):(this.removeEventListener(i,o),_.remove(this,i))},_default:function(e){return _.get(e.target,r)},delegateType:i},ce.event.special[i]={setup:function(){var e=this.ownerDocument||this.document||this,t=C.documentMode?this:e,n=_.get(t,i);n||(C.documentMode?this.addEventListener(i,o):e.addEventListener(r,o,!0)),_.set(t,i,(n||0)+1)},teardown:function(){var e=this.ownerDocument||this.document||this,t=C.documentMode?this:e,n=_.get(t,i)-1;n?_.set(t,i,n):(C.documentMode?this.removeEventListener(i,o):e.removeEventListener(r,o,!0),_.remove(t,i))}}}),ce.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(e,i){ce.event.special[e]={delegateType:i,bindType:i,handle:function(e){var t,n=e.relatedTarget,r=e.handleObj;return n&&(n===this||ce.contains(this,n))||(e.type=r.origType,t=r.handler.apply(this,arguments),e.type=i),t}}}),ce.fn.extend({on:function(e,t,n,r){return Le(this,e,t,n,r)},one:function(e,t,n,r){return Le(this,e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,ce(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=qe),this.each(function(){ce.event.remove(this,e,n,t)})}});var Oe=/<script|<style|<link/i,Pe=/checked\s*(?:[^=]|=\s*.checked.)/i,Me=/^\s*<!\[CDATA\[|\]\]>\s*$/g;function Re(e,t){return fe(e,"table")&&fe(11!==t.nodeType?t:t.firstChild,"tr")&&ce(e).children("tbody")[0]||e}function Ie(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function We(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Fe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(_.hasData(e)&&(s=_.get(e).events))for(i in _.remove(t,"handle events"),s)for(n=0,r=s[i].length;n<r;n++)ce.event.add(t,i,s[i][n]);z.hasData(e)&&(o=z.access(e),a=ce.extend({},o),z.set(t,a))}}function $e(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=v(d);if(h||1<f&&"string"==typeof d&&!le.checkClone&&Pe.test(d))return n.each(function(e){var t=n.eq(e);h&&(r[0]=d.call(this,e,t.html())),$e(t,r,i,o)});if(f&&(t=(e=Ae(r,n[0].ownerDocument,!1,n,o)).firstChild,1===e.childNodes.length&&(e=t),t||o)){for(s=(a=ce.map(Se(e,"script"),Ie)).length;c<f;c++)u=e,c!==p&&(u=ce.clone(u,!0,!0),s&&ce.merge(a,Se(u,"script"))),i.call(n[c],u,c);if(s)for(l=a[a.length-1].ownerDocument,ce.map(a,We),c=0;c<s;c++)u=a[c],Ce.test(u.type||"")&&!_.access(u,"globalEval")&&ce.contains(l,u)&&(u.src&&"module"!==(u.type||"").toLowerCase()?ce._evalUrl&&!u.noModule&&ce._evalUrl(u.src,{nonce:u.nonce||u.getAttribute("nonce")},l):m(u.textContent.replace(Me,""),u,l))}return n}function Be(e,t,n){for(var r,i=t?ce.filter(t,e):e,o=0;null!=(r=i[o]);o++)n||1!==r.nodeType||ce.cleanData(Se(r)),r.parentNode&&(n&&K(r)&&Ee(Se(r,"script")),r.parentNode.removeChild(r));return e}ce.extend({htmlPrefilter:function(e){return e},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=K(e);if(!(le.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||ce.isXMLDoc(e)))for(a=Se(c),r=0,i=(o=Se(e)).length;r<i;r++)s=o[r],u=a[r],void 0,"input"===(l=u.nodeName.toLowerCase())&&we.test(s.type)?u.checked=s.checked:"input"!==l&&"textarea"!==l||(u.defaultValue=s.defaultValue);if(t)if(n)for(o=o||Se(e),a=a||Se(c),r=0,i=o.length;r<i;r++)Fe(o[r],a[r]);else Fe(e,c);return 0<(a=Se(c,"script")).length&&Ee(a,!f&&Se(e,"script")),c},cleanData:function(e){for(var t,n,r,i=ce.event.special,o=0;void 0!==(n=e[o]);o++)if($(n)){if(t=n[_.expando]){if(t.events)for(r in t.events)i[r]?ce.event.remove(n,r):ce.removeEvent(n,r,t.handle);n[_.expando]=void 0}n[z.expando]&&(n[z.expando]=void 0)}}}),ce.fn.extend({detach:function(e){return Be(this,e,!0)},remove:function(e){return Be(this,e)},text:function(e){return M(this,function(e){return void 0===e?ce.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 $e(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Re(this,e).appendChild(e)})},prepend:function(){return $e(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Re(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return $e(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return $e(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&&(ce.cleanData(Se(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return ce.clone(this,e,t)})},html:function(e){return M(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&&!Oe.test(e)&&!ke[(Te.exec(e)||["",""])[1].toLowerCase()]){e=ce.htmlPrefilter(e);try{for(;n<r;n++)1===(t=this[n]||{}).nodeType&&(ce.cleanData(Se(t,!1)),t.innerHTML=e);t=0}catch(e){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var n=[];return $e(this,arguments,function(e){var t=this.parentNode;ce.inArray(this,n)<0&&(ce.cleanData(Se(this)),t&&t.replaceChild(e,this))},n)}}),ce.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,a){ce.fn[e]=function(e){for(var t,n=[],r=ce(e),i=r.length-1,o=0;o<=i;o++)t=o===i?this:this.clone(!0),ce(r[o])[a](t),s.apply(n,t.get());return this.pushStack(n)}});var _e=new RegExp("^("+G+")(?!px)[a-z%]+$","i"),ze=/^--/,Xe=function(e){var t=e.ownerDocument.defaultView;return t&&t.opener||(t=ie),t.getComputedStyle(e)},Ue=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},Ve=new RegExp(Q.join("|"),"i");function Ge(e,t,n){var r,i,o,a,s=ze.test(t),u=e.style;return(n=n||Xe(e))&&(a=n.getPropertyValue(t)||n[t],s&&a&&(a=a.replace(ve,"$1")||void 0),""!==a||K(e)||(a=ce.style(e,t)),!le.pixelBoxStyles()&&_e.test(a)&&Ve.test(t)&&(r=u.width,i=u.minWidth,o=u.maxWidth,u.minWidth=u.maxWidth=u.width=a,a=n.width,u.width=r,u.minWidth=i,u.maxWidth=o)),void 0!==a?a+"":a}function Ye(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%",J.appendChild(u).appendChild(l);var e=ie.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),J.removeChild(u),l=null}}function t(e){return Math.round(parseFloat(e))}var n,r,i,o,a,s,u=C.createElement("div"),l=C.createElement("div");l.style&&(l.style.backgroundClip="content-box",l.cloneNode(!0).style.backgroundClip="",le.clearCloneStyle="content-box"===l.style.backgroundClip,ce.extend(le,{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=C.createElement("table"),t=C.createElement("tr"),n=C.createElement("div"),e.style.cssText="position:absolute;left:-11111px;border-collapse:separate",t.style.cssText="box-sizing:content-box;border:1px solid",t.style.height="1px",n.style.height="9px",n.style.display="block",J.appendChild(e).appendChild(t).appendChild(n),r=ie.getComputedStyle(t),a=parseInt(r.height,10)+parseInt(r.borderTopWidth,10)+parseInt(r.borderBottomWidth,10)===t.offsetHeight,J.removeChild(e)),a}}))}();var Qe=["Webkit","Moz","ms"],Je=C.createElement("div").style,Ke={};function Ze(e){var t=ce.cssProps[e]||Ke[e];return t||(e in Je?e:Ke[e]=function(e){var t=e[0].toUpperCase()+e.slice(1),n=Qe.length;while(n--)if((e=Qe[n]+t)in Je)return e}(e)||e)}var et=/^(none|table(?!-c[ea]).+)/,tt={position:"absolute",visibility:"hidden",display:"block"},nt={letterSpacing:"0",fontWeight:"400"};function rt(e,t,n){var r=Y.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||"px"):t}function it(e,t,n,r,i,o){var a="width"===t?1:0,s=0,u=0,l=0;if(n===(r?"border":"content"))return 0;for(;a<4;a+=2)"margin"===n&&(l+=ce.css(e,n+Q[a],!0,i)),r?("content"===n&&(u-=ce.css(e,"padding"+Q[a],!0,i)),"margin"!==n&&(u-=ce.css(e,"border"+Q[a]+"Width",!0,i))):(u+=ce.css(e,"padding"+Q[a],!0,i),"padding"!==n?u+=ce.css(e,"border"+Q[a]+"Width",!0,i):s+=ce.css(e,"border"+Q[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+l}function ot(e,t,n){var r=Xe(e),i=(!le.boxSizingReliable()||n)&&"border-box"===ce.css(e,"boxSizing",!1,r),o=i,a=Ge(e,t,r),s="offset"+t[0].toUpperCase()+t.slice(1);if(_e.test(a)){if(!n)return a;a="auto"}return(!le.boxSizingReliable()&&i||!le.reliableTrDimensions()&&fe(e,"tr")||"auto"===a||!parseFloat(a)&&"inline"===ce.css(e,"display",!1,r))&&e.getClientRects().length&&(i="border-box"===ce.css(e,"boxSizing",!1,r),(o=s in e)&&(a=e[s])),(a=parseFloat(a)||0)+it(e,t,n||(i?"border":"content"),o,r,a)+"px"}function at(e,t,n,r,i){return new at.prototype.init(e,t,n,r,i)}ce.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Ge(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,aspectRatio:!0,borderImageSlice:!0,columnCount:!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,scale:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeMiterlimit:!0,strokeOpacity:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=F(t),u=ze.test(t),l=e.style;if(u||(t=Ze(s)),a=ce.cssHooks[t]||ce.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=Y.exec(n))&&i[1]&&(n=te(e,t,i),o="number"),null!=n&&n==n&&("number"!==o||u||(n+=i&&i[3]||(ce.cssNumber[s]?"":"px")),le.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=F(t);return ze.test(t)||(t=Ze(s)),(a=ce.cssHooks[t]||ce.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Ge(e,t,r)),"normal"===i&&t in nt&&(i=nt[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),ce.each(["height","width"],function(e,u){ce.cssHooks[u]={get:function(e,t,n){if(t)return!et.test(ce.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?ot(e,u,n):Ue(e,tt,function(){return ot(e,u,n)})},set:function(e,t,n){var r,i=Xe(e),o=!le.scrollboxSize()&&"absolute"===i.position,a=(o||n)&&"border-box"===ce.css(e,"boxSizing",!1,i),s=n?it(e,u,n,a,i):0;return a&&o&&(s-=Math.ceil(e["offset"+u[0].toUpperCase()+u.slice(1)]-parseFloat(i[u])-it(e,u,"border",!1,i)-.5)),s&&(r=Y.exec(t))&&"px"!==(r[3]||"px")&&(e.style[u]=t,t=ce.css(e,u)),rt(0,t,s)}}}),ce.cssHooks.marginLeft=Ye(le.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Ge(e,"marginLeft"))||e.getBoundingClientRect().left-Ue(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),ce.each({margin:"",padding:"",border:"Width"},function(i,o){ce.cssHooks[i+o]={expand:function(e){for(var t=0,n={},r="string"==typeof e?e.split(" "):[e];t<4;t++)n[i+Q[t]+o]=r[t]||r[t-2]||r[0];return n}},"margin"!==i&&(ce.cssHooks[i+o].set=rt)}),ce.fn.extend({css:function(e,t){return M(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=Xe(e),i=t.length;a<i;a++)o[t[a]]=ce.css(e,t[a],!1,r);return o}return void 0!==n?ce.style(e,t,n):ce.css(e,t)},e,t,1<arguments.length)}}),((ce.Tween=at).prototype={constructor:at,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||ce.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(ce.cssNumber[n]?"":"px")},cur:function(){var e=at.propHooks[this.prop];return e&&e.get?e.get(this):at.propHooks._default.get(this)},run:function(e){var t,n=at.propHooks[this.prop];return this.options.duration?this.pos=t=ce.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):at.propHooks._default.set(this),this}}).init.prototype=at.prototype,(at.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=ce.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){ce.fx.step[e.prop]?ce.fx.step[e.prop](e):1!==e.elem.nodeType||!ce.cssHooks[e.prop]&&null==e.elem.style[Ze(e.prop)]?e.elem[e.prop]=e.now:ce.style(e.elem,e.prop,e.now+e.unit)}}}).scrollTop=at.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},ce.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},ce.fx=at.prototype.init,ce.fx.step={};var st,ut,lt,ct,ft=/^(?:toggle|show|hide)$/,pt=/queueHooks$/;function dt(){ut&&(!1===C.hidden&&ie.requestAnimationFrame?ie.requestAnimationFrame(dt):ie.setTimeout(dt,ce.fx.interval),ce.fx.tick())}function ht(){return ie.setTimeout(function(){st=void 0}),st=Date.now()}function gt(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=Q[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function vt(e,t,n){for(var r,i=(yt.tweeners[t]||[]).concat(yt.tweeners["*"]),o=0,a=i.length;o<a;o++)if(r=i[o].call(n,t,e))return r}function yt(o,e,t){var n,a,r=0,i=yt.prefilters.length,s=ce.Deferred().always(function(){delete u.elem}),u=function(){if(a)return!1;for(var e=st||ht(),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:ce.extend({},e),opts:ce.extend(!0,{specialEasing:{},easing:ce.easing._default},t),originalProperties:e,originalOptions:t,startTime:st||ht(),duration:t.duration,tweens:[],createTween:function(e,t){var n=ce.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=F(n)],o=e[n],Array.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),(a=ce.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=yt.prefilters[r].call(l,o,c,l.opts))return v(n.stop)&&(ce._queueHooks(l.elem,l.opts.queue).stop=n.stop.bind(n)),n;return ce.map(c,vt,l),v(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),ce.fx.timer(ce.extend(u,{elem:o,anim:l,queue:l.opts.queue})),l}ce.Animation=ce.extend(yt,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return te(n.elem,e,Y.exec(t),n),n}]},tweener:function(e,t){v(e)?(t=e,e=["*"]):e=e.match(D);for(var n,r=0,i=e.length;r<i;r++)n=e[r],yt.tweeners[n]=yt.tweeners[n]||[],yt.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&&ee(e),v=_.get(e,"fxshow");for(r in n.queue||(null==(a=ce._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--,ce.queue(e,"fx").length||a.empty.fire()})})),t)if(i=t[r],ft.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]||ce.style(e,r)}if((u=!ce.isEmptyObject(t))||!ce.isEmptyObject(d))for(r in f&&1===e.nodeType&&(n.overflow=[h.overflow,h.overflowX,h.overflowY],null==(l=v&&v.display)&&(l=_.get(e,"display")),"none"===(c=ce.css(e,"display"))&&(l?c=l:(re([e],!0),l=e.style.display||l,c=ce.css(e,"display"),re([e]))),("inline"===c||"inline-block"===c&&null!=l)&&"none"===ce.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=_.access(e,"fxshow",{display:l}),o&&(v.hidden=!g),g&&re([e],!0),p.done(function(){for(r in g||re([e]),_.remove(e,"fxshow"),d)ce.style(e,r,d[r])})),u=vt(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?yt.prefilters.unshift(e):yt.prefilters.push(e)}}),ce.speed=function(e,t,n){var r=e&&"object"==typeof e?ce.extend({},e):{complete:n||!n&&t||v(e)&&e,duration:e,easing:n&&t||t&&!v(t)&&t};return ce.fx.off?r.duration=0:"number"!=typeof r.duration&&(r.duration in ce.fx.speeds?r.duration=ce.fx.speeds[r.duration]:r.duration=ce.fx.speeds._default),null!=r.queue&&!0!==r.queue||(r.queue="fx"),r.old=r.complete,r.complete=function(){v(r.old)&&r.old.call(this),r.queue&&ce.dequeue(this,r.queue)},r},ce.fn.extend({fadeTo:function(e,t,n,r){return this.filter(ee).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(t,e,n,r){var i=ce.isEmptyObject(t),o=ce.speed(e,n,r),a=function(){var e=yt(this,ce.extend({},t),o);(i||_.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=ce.timers,r=_.get(this);if(t)r[t]&&r[t].stop&&a(r[t]);else for(t in r)r[t]&&r[t].stop&&pt.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||ce.dequeue(this,i)})},finish:function(a){return!1!==a&&(a=a||"fx"),this.each(function(){var e,t=_.get(this),n=t[a+"queue"],r=t[a+"queueHooks"],i=ce.timers,o=n?n.length:0;for(t.finish=!0,ce.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})}}),ce.each(["toggle","show","hide"],function(e,r){var i=ce.fn[r];ce.fn[r]=function(e,t,n){return null==e||"boolean"==typeof e?i.apply(this,arguments):this.animate(gt(r,!0),e,t,n)}}),ce.each({slideDown:gt("show"),slideUp:gt("hide"),slideToggle:gt("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,r){ce.fn[e]=function(e,t,n){return this.animate(r,e,t,n)}}),ce.timers=[],ce.fx.tick=function(){var e,t=0,n=ce.timers;for(st=Date.now();t<n.length;t++)(e=n[t])()||n[t]!==e||n.splice(t--,1);n.length||ce.fx.stop(),st=void 0},ce.fx.timer=function(e){ce.timers.push(e),ce.fx.start()},ce.fx.interval=13,ce.fx.start=function(){ut||(ut=!0,dt())},ce.fx.stop=function(){ut=null},ce.fx.speeds={slow:600,fast:200,_default:400},ce.fn.delay=function(r,e){return r=ce.fx&&ce.fx.speeds[r]||r,e=e||"fx",this.queue(e,function(e,t){var n=ie.setTimeout(e,r);t.stop=function(){ie.clearTimeout(n)}})},lt=C.createElement("input"),ct=C.createElement("select").appendChild(C.createElement("option")),lt.type="checkbox",le.checkOn=""!==lt.value,le.optSelected=ct.selected,(lt=C.createElement("input")).value="t",lt.type="radio",le.radioValue="t"===lt.value;var mt,xt=ce.expr.attrHandle;ce.fn.extend({attr:function(e,t){return M(this,ce.attr,e,t,1<arguments.length)},removeAttr:function(e){return this.each(function(){ce.removeAttr(this,e)})}}),ce.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?ce.prop(e,t,n):(1===o&&ce.isXMLDoc(e)||(i=ce.attrHooks[t.toLowerCase()]||(ce.expr.match.bool.test(t)?mt:void 0)),void 0!==n?null===n?void ce.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=ce.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!le.radioValue&&"radio"===t&&fe(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(D);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),mt={set:function(e,t,n){return!1===t?ce.removeAttr(e,n):e.setAttribute(n,n),n}},ce.each(ce.expr.match.bool.source.match(/\w+/g),function(e,t){var a=xt[t]||ce.find.attr;xt[t]=function(e,t,n){var r,i,o=t.toLowerCase();return n||(i=xt[o],xt[o]=r,r=null!=a(e,t,n)?o:null,xt[o]=i),r}});var bt=/^(?:input|select|textarea|button)$/i,wt=/^(?:a|area)$/i;function Tt(e){return(e.match(D)||[]).join(" ")}function Ct(e){return e.getAttribute&&e.getAttribute("class")||""}function kt(e){return Array.isArray(e)?e:"string"==typeof e&&e.match(D)||[]}ce.fn.extend({prop:function(e,t){return M(this,ce.prop,e,t,1<arguments.length)},removeProp:function(e){return this.each(function(){delete this[ce.propFix[e]||e]})}}),ce.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&ce.isXMLDoc(e)||(t=ce.propFix[t]||t,i=ce.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=ce.find.attr(e,"tabindex");return t?parseInt(t,10):bt.test(e.nodeName)||wt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),le.optSelected||(ce.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)}}),ce.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){ce.propFix[this.toLowerCase()]=this}),ce.fn.extend({addClass:function(t){var e,n,r,i,o,a;return v(t)?this.each(function(e){ce(this).addClass(t.call(this,e,Ct(this)))}):(e=kt(t)).length?this.each(function(){if(r=Ct(this),n=1===this.nodeType&&" "+Tt(r)+" "){for(o=0;o<e.length;o++)i=e[o],n.indexOf(" "+i+" ")<0&&(n+=i+" ");a=Tt(n),r!==a&&this.setAttribute("class",a)}}):this},removeClass:function(t){var e,n,r,i,o,a;return v(t)?this.each(function(e){ce(this).removeClass(t.call(this,e,Ct(this)))}):arguments.length?(e=kt(t)).length?this.each(function(){if(r=Ct(this),n=1===this.nodeType&&" "+Tt(r)+" "){for(o=0;o<e.length;o++){i=e[o];while(-1<n.indexOf(" "+i+" "))n=n.replace(" "+i+" "," ")}a=Tt(n),r!==a&&this.setAttribute("class",a)}}):this:this.attr("class","")},toggleClass:function(t,n){var e,r,i,o,a=typeof t,s="string"===a||Array.isArray(t);return v(t)?this.each(function(e){ce(this).toggleClass(t.call(this,e,Ct(this),n),n)}):"boolean"==typeof n&&s?n?this.addClass(t):this.removeClass(t):(e=kt(t),this.each(function(){if(s)for(o=ce(this),i=0;i<e.length;i++)r=e[i],o.hasClass(r)?o.removeClass(r):o.addClass(r);else void 0!==t&&"boolean"!==a||((r=Ct(this))&&_.set(this,"__className__",r),this.setAttribute&&this.setAttribute("class",r||!1===t?"":_.get(this,"__className__")||""))}))},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&-1<(" "+Tt(Ct(n))+" ").indexOf(t))return!0;return!1}});var St=/\r/g;ce.fn.extend({val:function(n){var r,e,i,t=this[0];return arguments.length?(i=v(n),this.each(function(e){var t;1===this.nodeType&&(null==(t=i?n.call(this,e,ce(this).val()):n)?t="":"number"==typeof t?t+="":Array.isArray(t)&&(t=ce.map(t,function(e){return null==e?"":e+""})),(r=ce.valHooks[this.type]||ce.valHooks[this.nodeName.toLowerCase()])&&"set"in r&&void 0!==r.set(this,t,"value")||(this.value=t))})):t?(r=ce.valHooks[t.type]||ce.valHooks[t.nodeName.toLowerCase()])&&"get"in r&&void 0!==(e=r.get(t,"value"))?e:"string"==typeof(e=t.value)?e.replace(St,""):null==e?"":e:void 0}}),ce.extend({valHooks:{option:{get:function(e){var t=ce.find.attr(e,"value");return null!=t?t:Tt(ce.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||!fe(n.parentNode,"optgroup"))){if(t=ce(n).val(),a)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=ce.makeArray(t),a=i.length;while(a--)((r=i[a]).selected=-1<ce.inArray(ce.valHooks.option.get(r),o))&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),ce.each(["radio","checkbox"],function(){ce.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=-1<ce.inArray(ce(e).val(),t)}},le.checkOn||(ce.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Et=ie.location,jt={guid:Date.now()},At=/\?/;ce.parseXML=function(e){var t,n;if(!e||"string"!=typeof e)return null;try{t=(new ie.DOMParser).parseFromString(e,"text/xml")}catch(e){}return n=t&&t.getElementsByTagName("parsererror")[0],t&&!n||ce.error("Invalid XML: "+(n?ce.map(n.childNodes,function(e){return e.textContent}).join("\n"):e)),t};var Dt=/^(?:focusinfocus|focusoutblur)$/,Nt=function(e){e.stopPropagation()};ce.extend(ce.event,{trigger:function(e,t,n,r){var i,o,a,s,u,l,c,f,p=[n||C],d=ue.call(e,"type")?e.type:e,h=ue.call(e,"namespace")?e.namespace.split("."):[];if(o=f=a=n=n||C,3!==n.nodeType&&8!==n.nodeType&&!Dt.test(d+ce.event.triggered)&&(-1<d.indexOf(".")&&(d=(h=d.split(".")).shift(),h.sort()),u=d.indexOf(":")<0&&"on"+d,(e=e[ce.expando]?e:new ce.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]:ce.makeArray(t,[e]),c=ce.event.special[d]||{},r||!c.trigger||!1!==c.trigger.apply(n,t))){if(!r&&!c.noBubble&&!y(n)){for(s=c.delegateType||d,Dt.test(s+d)||(o=o.parentNode);o;o=o.parentNode)p.push(o),a=o;a===(n.ownerDocument||C)&&p.push(a.defaultView||a.parentWindow||ie)}i=0;while((o=p[i++])&&!e.isPropagationStopped())f=o,e.type=1<i?s:c.bindType||d,(l=(_.get(o,"events")||Object.create(null))[e.type]&&_.get(o,"handle"))&&l.apply(o,t),(l=u&&o[u])&&l.apply&&$(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)||!$(n)||u&&v(n[d])&&!y(n)&&((a=n[u])&&(n[u]=null),ce.event.triggered=d,e.isPropagationStopped()&&f.addEventListener(d,Nt),n[d](),e.isPropagationStopped()&&f.removeEventListener(d,Nt),ce.event.triggered=void 0,a&&(n[u]=a)),e.result}},simulate:function(e,t,n){var r=ce.extend(new ce.Event,n,{type:e,isSimulated:!0});ce.event.trigger(r,null,t)}}),ce.fn.extend({trigger:function(e,t){return this.each(function(){ce.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return ce.event.trigger(e,t,n,!0)}});var qt=/\[\]$/,Lt=/\r?\n/g,Ht=/^(?:submit|button|image|reset|file)$/i,Ot=/^(?:input|select|textarea|keygen)/i;function Pt(n,e,r,i){var t;if(Array.isArray(e))ce.each(e,function(e,t){r||qt.test(n)?i(n,t):Pt(n+"["+("object"==typeof t&&null!=t?e:"")+"]",t,r,i)});else if(r||"object"!==x(e))i(n,e);else for(t in e)Pt(n+"["+t+"]",e[t],r,i)}ce.param=function(e,t){var n,r=[],i=function(e,t){var n=v(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(null==e)return"";if(Array.isArray(e)||e.jquery&&!ce.isPlainObject(e))ce.each(e,function(){i(this.name,this.value)});else for(n in e)Pt(n,e[n],t,i);return r.join("&")},ce.fn.extend({serialize:function(){return ce.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=ce.prop(this,"elements");return e?ce.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!ce(this).is(":disabled")&&Ot.test(this.nodeName)&&!Ht.test(e)&&(this.checked||!we.test(e))}).map(function(e,t){var n=ce(this).val();return null==n?null:Array.isArray(n)?ce.map(n,function(e){return{name:t.name,value:e.replace(Lt,"\r\n")}}):{name:t.name,value:n.replace(Lt,"\r\n")}}).get()}});var Mt=/%20/g,Rt=/#.*$/,It=/([?&])_=[^&]*/,Wt=/^(.*?):[ \t]*([^\r\n]*)$/gm,Ft=/^(?:GET|HEAD)$/,$t=/^\/\//,Bt={},_t={},zt="*/".concat("*"),Xt=C.createElement("a");function Ut(o){return function(e,t){"string"!=typeof e&&(t=e,e="*");var n,r=0,i=e.toLowerCase().match(D)||[];if(v(t))while(n=i[r++])"+"===n[0]?(n=n.slice(1)||"*",(o[n]=o[n]||[]).unshift(t)):(o[n]=o[n]||[]).push(t)}}function Vt(t,i,o,a){var s={},u=t===_t;function l(e){var r;return s[e]=!0,ce.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 Gt(e,t){var n,r,i=ce.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&ce.extend(!0,e,r),e}Xt.href=Et.href,ce.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Et.href,type:"GET",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(Et.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":zt,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":ce.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?Gt(Gt(e,ce.ajaxSettings),t):Gt(ce.ajaxSettings,e)},ajaxPrefilter:Ut(Bt),ajaxTransport:Ut(_t),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=ce.ajaxSetup({},t),y=v.context||v,m=v.context&&(y.nodeType||y.jquery)?ce(y):ce.event,x=ce.Deferred(),b=ce.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=Wt.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||Et.href)+"").replace($t,Et.protocol+"//"),v.type=t.method||t.type||v.method||v.type,v.dataTypes=(v.dataType||"*").toLowerCase().match(D)||[""],null==v.crossDomain){r=C.createElement("a");try{r.href=v.url,r.href=r.href,v.crossDomain=Xt.protocol+"//"+Xt.host!=r.protocol+"//"+r.host}catch(e){v.crossDomain=!0}}if(v.data&&v.processData&&"string"!=typeof v.data&&(v.data=ce.param(v.data,v.traditional)),Vt(Bt,v,t,T),h)return T;for(i in(g=ce.event&&v.global)&&0==ce.active++&&ce.event.trigger("ajaxStart"),v.type=v.type.toUpperCase(),v.hasContent=!Ft.test(v.type),f=v.url.replace(Rt,""),v.hasContent?v.data&&v.processData&&0===(v.contentType||"").indexOf("application/x-www-form-urlencoded")&&(v.data=v.data.replace(Mt,"+")):(o=v.url.slice(f.length),v.data&&(v.processData||"string"==typeof v.data)&&(f+=(At.test(f)?"&":"?")+v.data,delete v.data),!1===v.cache&&(f=f.replace(It,"$1"),o=(At.test(f)?"&":"?")+"_="+jt.guid+++o),v.url=f+o),v.ifModified&&(ce.lastModified[f]&&T.setRequestHeader("If-Modified-Since",ce.lastModified[f]),ce.etag[f]&&T.setRequestHeader("If-None-Match",ce.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]?", "+zt+"; 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=Vt(_t,v,t,T)){if(T.readyState=1,g&&m.trigger("ajaxSend",[T,v]),h)return T;v.async&&0<v.timeout&&(d=ie.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&&ie.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<ce.inArray("script",v.dataTypes)&&ce.inArray("json",v.dataTypes)<0&&(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"))&&(ce.lastModified[f]=u),(u=T.getResponseHeader("etag"))&&(ce.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]),--ce.active||ce.event.trigger("ajaxStop")))}return T},getJSON:function(e,t,n){return ce.get(e,t,n,"json")},getScript:function(e,t){return ce.get(e,void 0,t,"script")}}),ce.each(["get","post"],function(e,i){ce[i]=function(e,t,n,r){return v(t)&&(r=r||n,n=t,t=void 0),ce.ajax(ce.extend({url:e,type:i,dataType:r,data:t,success:n},ce.isPlainObject(e)&&e))}}),ce.ajaxPrefilter(function(e){var t;for(t in e.headers)"content-type"===t.toLowerCase()&&(e.contentType=e.headers[t]||"")}),ce._evalUrl=function(e,t,n){return ce.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,converters:{"text script":function(){}},dataFilter:function(e){ce.globalEval(e,t,n)}})},ce.fn.extend({wrapAll:function(e){var t;return this[0]&&(v(e)&&(e=e.call(this[0])),t=ce(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 v(n)?this.each(function(e){ce(this).wrapInner(n.call(this,e))}):this.each(function(){var e=ce(this),t=e.contents();t.length?t.wrapAll(n):e.append(n)})},wrap:function(t){var n=v(t);return this.each(function(e){ce(this).wrapAll(n?t.call(this,e):t)})},unwrap:function(e){return this.parent(e).not("body").each(function(){ce(this).replaceWith(this.childNodes)}),this}}),ce.expr.pseudos.hidden=function(e){return!ce.expr.pseudos.visible(e)},ce.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},ce.ajaxSettings.xhr=function(){try{return new ie.XMLHttpRequest}catch(e){}};var Yt={0:200,1223:204},Qt=ce.ajaxSettings.xhr();le.cors=!!Qt&&"withCredentials"in Qt,le.ajax=Qt=!!Qt,ce.ajaxTransport(function(i){var o,a;if(le.cors||Qt&&!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(Yt[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&&ie.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()}}}),ce.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),ce.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 ce.globalEval(e),e}}}),ce.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),ce.ajaxTransport("script",function(n){var r,i;if(n.crossDomain||n.scriptAttrs)return{send:function(e,t){r=ce("<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)}),C.head.appendChild(r[0])},abort:function(){i&&i()}}});var Jt,Kt=[],Zt=/(=)\?(?=&|$)|\?\?/;ce.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Kt.pop()||ce.expando+"_"+jt.guid++;return this[e]=!0,e}}),ce.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Zt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Zt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=v(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Zt,"$1"+r):!1!==e.jsonp&&(e.url+=(At.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||ce.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=ie[r],ie[r]=function(){o=arguments},n.always(function(){void 0===i?ce(ie).removeProp(r):ie[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Kt.push(r)),o&&v(i)&&i(o[0]),o=i=void 0}),"script"}),le.createHTMLDocument=((Jt=C.implementation.createHTMLDocument("").body).innerHTML="<form></form><form></form>",2===Jt.childNodes.length),ce.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(le.createHTMLDocument?((r=(t=C.implementation.createHTMLDocument("")).createElement("base")).href=C.location.href,t.head.appendChild(r)):t=C),o=!n&&[],(i=w.exec(e))?[t.createElement(i[1])]:(i=Ae([e],t,o),o&&o.length&&ce(o).remove(),ce.merge([],i.childNodes)));var r,i,o},ce.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1<s&&(r=Tt(e.slice(s)),e=e.slice(0,s)),v(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),0<a.length&&ce.ajax({url:e,type:i||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?ce("<div>").append(ce.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},ce.expr.pseudos.animated=function(t){return ce.grep(ce.timers,function(e){return t===e.elem}).length},ce.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=ce.css(e,"position"),c=ce(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=ce.css(e,"top"),u=ce.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),v(t)&&(t=t.call(e,n,ce.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):c.css(f)}},ce.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){ce.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"===ce.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"===ce.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=ce(e).offset()).top+=ce.css(e,"borderTopWidth",!0),i.left+=ce.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-ce.css(r,"marginTop",!0),left:t.left-i.left-ce.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===ce.css(e,"position"))e=e.offsetParent;return e||J})}}),ce.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;ce.fn[t]=function(e){return M(this,function(e,t,n){var r;if(y(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)}}),ce.each(["top","left"],function(e,n){ce.cssHooks[n]=Ye(le.pixelPosition,function(e,t){if(t)return t=Ge(e,n),_e.test(t)?ce(e).position()[n]+"px":t})}),ce.each({Height:"height",Width:"width"},function(a,s){ce.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){ce.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return M(this,function(e,t,n){var r;return y(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?ce.css(e,t,i):ce.style(e,t,n,i)},s,n?e:void 0,n)}})}),ce.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){ce.fn[t]=function(e){return this.on(t,e)}}),ce.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.on("mouseenter",e).on("mouseleave",t||e)}}),ce.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){ce.fn[n]=function(e,t){return 0<arguments.length?this.on(n,null,e,t):this.trigger(n)}});var en=/^[\s\uFEFF\xA0]+|([^\s\uFEFF\xA0])[\s\uFEFF\xA0]+$/g;ce.proxy=function(e,t){var n,r,i;if("string"==typeof t&&(n=e[t],t=e,e=n),v(e))return r=ae.call(arguments,2),(i=function(){return e.apply(t||this,r.concat(ae.call(arguments)))}).guid=e.guid=e.guid||ce.guid++,i},ce.holdReady=function(e){e?ce.readyWait++:ce.ready(!0)},ce.isArray=Array.isArray,ce.parseJSON=JSON.parse,ce.nodeName=fe,ce.isFunction=v,ce.isWindow=y,ce.camelCase=F,ce.type=x,ce.now=Date.now,ce.isNumeric=function(e){var t=ce.type(e);return("number"===t||"string"===t)&&!isNaN(e-parseFloat(e))},ce.trim=function(e){return null==e?"":(e+"").replace(en,"$1")},"function"==typeof define&&define.amd&&define("jquery",[],function(){return ce});var tn=ie.jQuery,nn=ie.$;return ce.noConflict=function(e){return ie.$===ce&&(ie.$=nn),e&&ie.jQuery===ce&&(ie.jQuery=tn),ce},"undefined"==typeof e&&(ie.jQuery=ie.$=ce),ce});
jQuery.noConflict();
// source --> https://eternize360.com.br/wp-includes/js/jquery/jquery-migrate.min.js?ver=3.4.1 
/*! jQuery Migrate v3.4.1 | (c) OpenJS Foundation and other contributors | jquery.org/license */
"undefined"==typeof jQuery.migrateMute&&(jQuery.migrateMute=!0),function(t){"use strict";"function"==typeof define&&define.amd?define(["jquery"],function(e){return t(e,window)}):"object"==typeof module&&module.exports?module.exports=t(require("jquery"),window):t(jQuery,window)}(function(s,n){"use strict";function e(e){return 0<=function(e,t){for(var r=/^(\d+)\.(\d+)\.(\d+)/,n=r.exec(e)||[],o=r.exec(t)||[],a=1;a<=3;a++){if(+o[a]<+n[a])return 1;if(+n[a]<+o[a])return-1}return 0}(s.fn.jquery,e)}s.migrateVersion="3.4.1";var t=Object.create(null);s.migrateDisablePatches=function(){for(var e=0;e<arguments.length;e++)t[arguments[e]]=!0},s.migrateEnablePatches=function(){for(var e=0;e<arguments.length;e++)delete t[arguments[e]]},s.migrateIsPatchEnabled=function(e){return!t[e]},n.console&&n.console.log&&(s&&e("3.0.0")&&!e("5.0.0")||n.console.log("JQMIGRATE: jQuery 3.x-4.x REQUIRED"),s.migrateWarnings&&n.console.log("JQMIGRATE: Migrate plugin loaded multiple times"),n.console.log("JQMIGRATE: Migrate is installed"+(s.migrateMute?"":" with logging active")+", version "+s.migrateVersion));var o={};function u(e,t){var r=n.console;!s.migrateIsPatchEnabled(e)||s.migrateDeduplicateWarnings&&o[t]||(o[t]=!0,s.migrateWarnings.push(t+" ["+e+"]"),r&&r.warn&&!s.migrateMute&&(r.warn("JQMIGRATE: "+t),s.migrateTrace&&r.trace&&r.trace()))}function r(e,t,r,n,o){Object.defineProperty(e,t,{configurable:!0,enumerable:!0,get:function(){return u(n,o),r},set:function(e){u(n,o),r=e}})}function a(e,t,r,n,o){var a=e[t];e[t]=function(){return o&&u(n,o),(s.migrateIsPatchEnabled(n)?r:a||s.noop).apply(this,arguments)}}function c(e,t,r,n,o){if(!o)throw new Error("No warning message provided");return a(e,t,r,n,o),0}function i(e,t,r,n){return a(e,t,r,n),0}s.migrateDeduplicateWarnings=!0,s.migrateWarnings=[],void 0===s.migrateTrace&&(s.migrateTrace=!0),s.migrateReset=function(){o={},s.migrateWarnings.length=0},"BackCompat"===n.document.compatMode&&u("quirks","jQuery is not compatible with Quirks Mode");var d,l,p,f={},m=s.fn.init,y=s.find,h=/\[(\s*[-\w]+\s*)([~|^$*]?=)\s*([-\w#]*?#[-\w#]*)\s*\]/,g=/\[(\s*[-\w]+\s*)([~|^$*]?=)\s*([-\w#]*?#[-\w#]*)\s*\]/g,v=/^[\s\uFEFF\xA0]+|([^\s\uFEFF\xA0])[\s\uFEFF\xA0]+$/g;for(d in i(s.fn,"init",function(e){var t=Array.prototype.slice.call(arguments);return s.migrateIsPatchEnabled("selector-empty-id")&&"string"==typeof e&&"#"===e&&(u("selector-empty-id","jQuery( '#' ) is not a valid selector"),t[0]=[]),m.apply(this,t)},"selector-empty-id"),s.fn.init.prototype=s.fn,i(s,"find",function(t){var r=Array.prototype.slice.call(arguments);if("string"==typeof t&&h.test(t))try{n.document.querySelector(t)}catch(e){t=t.replace(g,function(e,t,r,n){return"["+t+r+'"'+n+'"]'});try{n.document.querySelector(t),u("selector-hash","Attribute selector with '#' must be quoted: "+r[0]),r[0]=t}catch(e){u("selector-hash","Attribute selector with '#' was not fixed: "+r[0])}}return y.apply(this,r)},"selector-hash"),y)Object.prototype.hasOwnProperty.call(y,d)&&(s.find[d]=y[d]);c(s.fn,"size",function(){return this.length},"size","jQuery.fn.size() is deprecated and removed; use the .length property"),c(s,"parseJSON",function(){return JSON.parse.apply(null,arguments)},"parseJSON","jQuery.parseJSON is deprecated; use JSON.parse"),c(s,"holdReady",s.holdReady,"holdReady","jQuery.holdReady is deprecated"),c(s,"unique",s.uniqueSort,"unique","jQuery.unique is deprecated; use jQuery.uniqueSort"),r(s.expr,"filters",s.expr.pseudos,"expr-pre-pseudos","jQuery.expr.filters is deprecated; use jQuery.expr.pseudos"),r(s.expr,":",s.expr.pseudos,"expr-pre-pseudos","jQuery.expr[':'] is deprecated; use jQuery.expr.pseudos"),e("3.1.1")&&c(s,"trim",function(e){return null==e?"":(e+"").replace(v,"$1")},"trim","jQuery.trim is deprecated; use String.prototype.trim"),e("3.2.0")&&(c(s,"nodeName",function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},"nodeName","jQuery.nodeName is deprecated"),c(s,"isArray",Array.isArray,"isArray","jQuery.isArray is deprecated; use Array.isArray")),e("3.3.0")&&(c(s,"isNumeric",function(e){var t=typeof e;return("number"==t||"string"==t)&&!isNaN(e-parseFloat(e))},"isNumeric","jQuery.isNumeric() is deprecated"),s.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(e,t){f["[object "+t+"]"]=t.toLowerCase()}),c(s,"type",function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?f[Object.prototype.toString.call(e)]||"object":typeof e},"type","jQuery.type is deprecated"),c(s,"isFunction",function(e){return"function"==typeof e},"isFunction","jQuery.isFunction() is deprecated"),c(s,"isWindow",function(e){return null!=e&&e===e.window},"isWindow","jQuery.isWindow() is deprecated")),s.ajax&&(l=s.ajax,p=/(=)\?(?=&|$)|\?\?/,i(s,"ajax",function(){var e=l.apply(this,arguments);return e.promise&&(c(e,"success",e.done,"jqXHR-methods","jQXHR.success is deprecated and removed"),c(e,"error",e.fail,"jqXHR-methods","jQXHR.error is deprecated and removed"),c(e,"complete",e.always,"jqXHR-methods","jQXHR.complete is deprecated and removed")),e},"jqXHR-methods"),e("4.0.0")||s.ajaxPrefilter("+json",function(e){!1!==e.jsonp&&(p.test(e.url)||"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&p.test(e.data))&&u("jsonp-promotion","JSON-to-JSONP auto-promotion is deprecated")}));var j=s.fn.removeAttr,b=s.fn.toggleClass,w=/\S+/g;function x(e){return e.replace(/-([a-z])/g,function(e,t){return t.toUpperCase()})}i(s.fn,"removeAttr",function(e){var r=this,n=!1;return s.each(e.match(w),function(e,t){s.expr.match.bool.test(t)&&r.each(function(){if(!1!==s(this).prop(t))return!(n=!0)}),n&&(u("removeAttr-bool","jQuery.fn.removeAttr no longer sets boolean properties: "+t),r.prop(t,!1))}),j.apply(this,arguments)},"removeAttr-bool"),i(s.fn,"toggleClass",function(t){return void 0!==t&&"boolean"!=typeof t?b.apply(this,arguments):(u("toggleClass-bool","jQuery.fn.toggleClass( boolean ) is deprecated"),this.each(function(){var e=this.getAttribute&&this.getAttribute("class")||"";e&&s.data(this,"__className__",e),this.setAttribute&&this.setAttribute("class",!e&&!1!==t&&s.data(this,"__className__")||"")}))},"toggleClass-bool");var Q,A,R=!1,C=/^[a-z]/,N=/^(?:Border(?:Top|Right|Bottom|Left)?(?:Width|)|(?:Margin|Padding)?(?:Top|Right|Bottom|Left)?|(?:Min|Max)?(?:Width|Height))$/;s.swap&&s.each(["height","width","reliableMarginRight"],function(e,t){var r=s.cssHooks[t]&&s.cssHooks[t].get;r&&(s.cssHooks[t].get=function(){var e;return R=!0,e=r.apply(this,arguments),R=!1,e})}),i(s,"swap",function(e,t,r,n){var o,a,i={};for(a in R||u("swap","jQuery.swap() is undocumented and deprecated"),t)i[a]=e.style[a],e.style[a]=t[a];for(a in o=r.apply(e,n||[]),t)e.style[a]=i[a];return o},"swap"),e("3.4.0")&&"undefined"!=typeof Proxy&&(s.cssProps=new Proxy(s.cssProps||{},{set:function(){return u("cssProps","jQuery.cssProps is deprecated"),Reflect.set.apply(this,arguments)}})),e("4.0.0")?(A={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},"undefined"!=typeof Proxy?s.cssNumber=new Proxy(A,{get:function(){return u("css-number","jQuery.cssNumber is deprecated"),Reflect.get.apply(this,arguments)},set:function(){return u("css-number","jQuery.cssNumber is deprecated"),Reflect.set.apply(this,arguments)}}):s.cssNumber=A):A=s.cssNumber,Q=s.fn.css,i(s.fn,"css",function(e,t){var r,n,o=this;return e&&"object"==typeof e&&!Array.isArray(e)?(s.each(e,function(e,t){s.fn.css.call(o,e,t)}),this):("number"==typeof t&&(r=x(e),n=r,C.test(n)&&N.test(n[0].toUpperCase()+n.slice(1))||A[r]||u("css-number",'Number-typed values are deprecated for jQuery.fn.css( "'+e+'", value )')),Q.apply(this,arguments))},"css-number");var S,P,k,H,E=s.data;i(s,"data",function(e,t,r){var n,o,a;if(t&&"object"==typeof t&&2===arguments.length){for(a in n=s.hasData(e)&&E.call(this,e),o={},t)a!==x(a)?(u("data-camelCase","jQuery.data() always sets/gets camelCased names: "+a),n[a]=t[a]):o[a]=t[a];return E.call(this,e,o),t}return t&&"string"==typeof t&&t!==x(t)&&(n=s.hasData(e)&&E.call(this,e))&&t in n?(u("data-camelCase","jQuery.data() always sets/gets camelCased names: "+t),2<arguments.length&&(n[t]=r),n[t]):E.apply(this,arguments)},"data-camelCase"),s.fx&&(k=s.Tween.prototype.run,H=function(e){return e},i(s.Tween.prototype,"run",function(){1<s.easing[this.easing].length&&(u("easing-one-arg","'jQuery.easing."+this.easing.toString()+"' should use only one argument"),s.easing[this.easing]=H),k.apply(this,arguments)},"easing-one-arg"),S=s.fx.interval,P="jQuery.fx.interval is deprecated",n.requestAnimationFrame&&Object.defineProperty(s.fx,"interval",{configurable:!0,enumerable:!0,get:function(){return n.document.hidden||u("fx-interval",P),s.migrateIsPatchEnabled("fx-interval")&&void 0===S?13:S},set:function(e){u("fx-interval",P),S=e}}));var M=s.fn.load,q=s.event.add,O=s.event.fix;s.event.props=[],s.event.fixHooks={},r(s.event.props,"concat",s.event.props.concat,"event-old-patch","jQuery.event.props.concat() is deprecated and removed"),i(s.event,"fix",function(e){var t,r=e.type,n=this.fixHooks[r],o=s.event.props;if(o.length){u("event-old-patch","jQuery.event.props are deprecated and removed: "+o.join());while(o.length)s.event.addProp(o.pop())}if(n&&!n._migrated_&&(n._migrated_=!0,u("event-old-patch","jQuery.event.fixHooks are deprecated and removed: "+r),(o=n.props)&&o.length))while(o.length)s.event.addProp(o.pop());return t=O.call(this,e),n&&n.filter?n.filter(t,e):t},"event-old-patch"),i(s.event,"add",function(e,t){return e===n&&"load"===t&&"complete"===n.document.readyState&&u("load-after-event","jQuery(window).on('load'...) called after load event occurred"),q.apply(this,arguments)},"load-after-event"),s.each(["load","unload","error"],function(e,t){i(s.fn,t,function(){var e=Array.prototype.slice.call(arguments,0);return"load"===t&&"string"==typeof e[0]?M.apply(this,e):(u("shorthand-removed-v3","jQuery.fn."+t+"() is deprecated"),e.splice(0,0,t),arguments.length?this.on.apply(this,e):(this.triggerHandler.apply(this,e),this))},"shorthand-removed-v3")}),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,r){c(s.fn,r,function(e,t){return 0<arguments.length?this.on(r,null,e,t):this.trigger(r)},"shorthand-deprecated-v3","jQuery.fn."+r+"() event shorthand is deprecated")}),s(function(){s(n.document).triggerHandler("ready")}),s.event.special.ready={setup:function(){this===n.document&&u("ready-event","'ready' event is deprecated")}},c(s.fn,"bind",function(e,t,r){return this.on(e,null,t,r)},"pre-on-methods","jQuery.fn.bind() is deprecated"),c(s.fn,"unbind",function(e,t){return this.off(e,null,t)},"pre-on-methods","jQuery.fn.unbind() is deprecated"),c(s.fn,"delegate",function(e,t,r,n){return this.on(t,e,r,n)},"pre-on-methods","jQuery.fn.delegate() is deprecated"),c(s.fn,"undelegate",function(e,t,r){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",r)},"pre-on-methods","jQuery.fn.undelegate() is deprecated"),c(s.fn,"hover",function(e,t){return this.on("mouseenter",e).on("mouseleave",t||e)},"pre-on-methods","jQuery.fn.hover() is deprecated");function T(e){var t=n.document.implementation.createHTMLDocument("");return t.body.innerHTML=e,t.body&&t.body.innerHTML}var F=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi;s.UNSAFE_restoreLegacyHtmlPrefilter=function(){s.migrateEnablePatches("self-closed-tags")},i(s,"htmlPrefilter",function(e){var t,r;return(r=(t=e).replace(F,"<$1></$2>"))!==t&&T(t)!==T(r)&&u("self-closed-tags","HTML tags must be properly nested and closed: "+t),e.replace(F,"<$1></$2>")},"self-closed-tags"),s.migrateDisablePatches("self-closed-tags");var D,W,_,I=s.fn.offset;return i(s.fn,"offset",function(){var e=this[0];return!e||e.nodeType&&e.getBoundingClientRect?I.apply(this,arguments):(u("offset-valid-elem","jQuery.fn.offset() requires a valid DOM element"),arguments.length?this:void 0)},"offset-valid-elem"),s.ajax&&(D=s.param,i(s,"param",function(e,t){var r=s.ajaxSettings&&s.ajaxSettings.traditional;return void 0===t&&r&&(u("param-ajax-traditional","jQuery.param() no longer uses jQuery.ajaxSettings.traditional"),t=r),D.call(this,e,t)},"param-ajax-traditional")),c(s.fn,"andSelf",s.fn.addBack,"andSelf","jQuery.fn.andSelf() is deprecated and removed, use jQuery.fn.addBack()"),s.Deferred&&(W=s.Deferred,_=[["resolve","done",s.Callbacks("once memory"),s.Callbacks("once memory"),"resolved"],["reject","fail",s.Callbacks("once memory"),s.Callbacks("once memory"),"rejected"],["notify","progress",s.Callbacks("memory"),s.Callbacks("memory")]],i(s,"Deferred",function(e){var a=W(),i=a.promise();function t(){var o=arguments;return s.Deferred(function(n){s.each(_,function(e,t){var r="function"==typeof o[e]&&o[e];a[t[1]](function(){var e=r&&r.apply(this,arguments);e&&"function"==typeof e.promise?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[t[0]+"With"](this===i?n.promise():this,r?[e]:arguments)})}),o=null}).promise()}return c(a,"pipe",t,"deferred-pipe","deferred.pipe() is deprecated"),c(i,"pipe",t,"deferred-pipe","deferred.pipe() is deprecated"),e&&e.call(a,a),a},"deferred-pipe"),s.Deferred.exceptionHook=W.exceptionHook),s});
// source --> https://eternize360.com.br/wp-content/plugins/wpvr-embed-addon/public/js/wpvr-embed-addon-public.js?ver=1.2.2 
(function( $ ) {
	'use strict';

	/**
	 * All of the code for your public-facing JavaScript source
	 * should reside in this file.
	 *
	 * Note: It has been assumed you will write jQuery code here, so the
	 * $ function reference has been prepared for usage within the scope
	 * of this function.
	 *
	 * This enables you to define handlers, for when the DOM is ready:
	 *
	 * $(function() {
	 *
	 * });
	 *
	 * When the window is loaded:
	 *
	 * $( window ).load(function() {
	 *
	 * });
	 *
	 * ...and/or other possibilities.
	 *
	 * Ideally, it is not considered best practise to attach more than a
	 * single DOM-ready or window-load handler for a particular page.
	 * Although scripts in the WordPress core, Plugins and Themes may be
	 * practising this, we should strive to set a better example in our own work.
	 */

})( jQuery );
// source --> https://eternize360.com.br/wp-content/plugins/wpvr-pro/public/js/wpvr-pro-public.js?ver=6.7.0 
(function( $ ) {
	'use strict';

	/**
	 * All of the code for your public-facing JavaScript source
	 * should reside in this file.
	 *
	 * Note: It has been assumed you will write jQuery code here, so the
	 * $ function reference has been prepared for usage within the scope
	 * of this function.
	 *
	 * This enables you to define handlers, for when the DOM is ready:
	 *
	 * $(function() {
	 *
	 * });
	 *
	 * When the window is loaded:
	 *
	 * $( window ).load(function() {
	 *
	 * });
	 *
	 * ...and/or other possibilities.
	 *
	 * Ideally, it is not considered best practise to attach more than a
	 * single DOM-ready or window-load handler for a particular page.
	 * Although scripts in the WordPress core, Plugins and Themes may be
	 * practising this, we should strive to set a better example in our own work.
	 */

	jQuery(document).ready(function($) {
		// Handle floorplan pointer positioning for responsive images
		function adjustFloorplanPointers() {
			$('.wpvr-floor-map').each(function() {
				var $floorMap = $(this);
				var $img = $floorMap.find('img');
				var $pointers = $floorMap.find('.floor-plan-pointer');
				
				if ($img.length && $pointers.length) {
					// Get actual image dimensions
					var imgWidth = $img[0].naturalWidth || $img[0].width;
					var imgHeight = $img[0].naturalHeight || $img[0].height;
					var displayWidth = $img.width();
					var displayHeight = $img.height();
					
					// Calculate scaling factors
					var scaleX = displayWidth / imgWidth;
					var scaleY = displayHeight / imgHeight;
					
					// Pointer dimensions (24px based on typical pointer size)
					var pointerWidth = 24;
					var pointerHeight = 24;
					
					// Calculate pointer offset percentages
					var pointerOffsetX = (pointerWidth / 2) / displayWidth * 100;
					var pointerOffsetY = (pointerHeight / 2) / displayHeight * 100;
					
					// Adjust each pointer position
					$pointers.each(function() {
						var $pointer = $(this);
						var originalTop = parseFloat($pointer.attr('data-top'));
						var originalLeft = parseFloat($pointer.attr('data-left'));
						
						// Convert percentage to pixels based on original dimensions
						var originalTopPx = (originalTop / 100) * imgHeight;
						var originalLeftPx = (originalLeft / 100) * imgWidth;
						
						// Scale to display dimensions
						var displayTopPx = originalTopPx * scaleY;
						var displayLeftPx = originalLeftPx * scaleX;
						
						// Convert back to percentage based on display dimensions
						var displayTopPercent = (displayTopPx / displayHeight) * 100;
						var displayLeftPercent = (displayLeftPx / displayWidth) * 100;
						
						// Apply boundary constraints
						displayTopPercent = Math.max(pointerOffsetY, Math.min(100 - pointerOffsetY, displayTopPercent));
						displayLeftPercent = Math.max(pointerOffsetX, Math.min(100 - pointerOffsetX, displayLeftPercent));
						
						// Update pointer position
						$pointer.css({
							'top': displayTopPercent + '%',
							'left': displayLeftPercent + '%'
						});
					});
				}
			});
		}
	
		// Run on image load and window resize
		$(window).on('load resize', function() {
			setTimeout(adjustFloorplanPointers, 100);
		});
	
		// Also run when floorplan is shown
		$(document).on('click', '.flooplan-toggle, [class*="floorplan"]', function() {
			setTimeout(adjustFloorplanPointers, 200);
		});
		
		// Initial adjustment
		setTimeout(adjustFloorplanPointers, 500);
	});

        // =====================================================================
        // Analytics 2.0 — Custom event dispatching hooks (T015)
        // Dispatches CustomEvents so wpvr-analytics-tracker.js can listen.
        // =====================================================================
        $(document).on('wpvr_tour_loaded', function (e, tourId) {
                document.dispatchEvent(new CustomEvent('wpvr:tour_loaded', { detail: { tour_id: tourId } }));
        });

        $(document).on('wpvr_scene_changed', function (e, from, to) {
                document.dispatchEvent(new CustomEvent('wpvr:scene_changed', { detail: { from: from, to: to } }));
        });

        $(document).on('wpvr_hotspot_hovered', function (e, hotspotId, sceneId) {
                document.dispatchEvent(new CustomEvent('wpvr:hotspot_hovered', { detail: { hotspot_id: hotspotId, scene_id: sceneId } }));
        });

        $(document).on('wpvr_hotspot_clicked', function (e, hotspotId, sceneId, hotspotType, url) {
                document.dispatchEvent(new CustomEvent('wpvr:hotspot_clicked', {
                        detail: { hotspot_id: hotspotId, scene_id: sceneId, hotspot_type: hotspotType, url: url || '' }
                }));
        });

        // The free plugin's wpvrhotspot() is a direct Pannellum clickHandlerFunc callback
        // and never fires the wpvr_hotspot_clicked jQuery event above. Wrap the global
        // function at DOM-ready (after the free plugin script has defined it) so that
        // content hotspot clicks (info/URL types) are tracked in Analytics 2.0.
        $(function () {
            if (typeof window.wpvrhotspot === 'function') {
                var _originalWpvrHotspot = window.wpvrhotspot;
                window.wpvrhotspot = function (hotSpotDiv, hotspotData) {
                    document.dispatchEvent(new CustomEvent('wpvr:hotspot_clicked', {
                        detail: {
                            hotspot_id:   (hotspotData && hotspotData.hotspot_id)   || null,
                            scene_id:     (hotspotData && hotspotData.scene_id)     || null,
                            hotspot_type: 'info',
                            url:          (hotspotData && hotspotData.url)          || null
                        }
                    }));
                    return _originalWpvrHotspot(hotSpotDiv, hotspotData);
                };
            }
        });

}(jQuery));
// source --> https://eternize360.com.br/wp-content/plugins/wpvr-pro/public/lib/pannellum/src/js/pannellum.js?ver=1 
/*
 * Pannellum - An HTML5 based Panorama Viewer
 * Copyright (c) 2011-2019 Matthew Petroff
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

window.pannellum = (function(window, document, undefined) {

    'use strict';

    /**
     * Creates a new panorama viewer.
     * @constructor
     * @param {HTMLElement|string} container - The container (div) element for the
     *      viewer, or its ID.
     * @param {Object} initialConfig - Inital configuration for viewer.
     */
    function Viewer(container, initialConfig) {

        var _this = this;

// Declare variables
        var config,
            renderer,
            preview,
            isUserInteracting = false,
            latestInteraction = Date.now(),
            onPointerDownPointerX = 0,
            onPointerDownPointerY = 0,
            onPointerDownPointerDist = -1,
            onPointerDownYaw = 0,
            onPointerDownPitch = 0,
            keysDown = new Array(10),
            fullscreenActive = false,
            loaded,
            error = false,
            isTimedOut = false,
            listenersAdded = false,
            panoImage,
            prevTime,
            speed = {'yaw': 0, 'pitch': 0, 'hfov': 0},
            animating = false,
            orientation = false,
            orientationYawOffset = 0,
            autoRotateStart,
            autoRotateSpeed = 0,
            origHfov,
            origPitch,
            animatedMove = {},
            externalEventListeners = {},
            specifiedPhotoSphereExcludes = [],
            update = false, // Should we update when still to render dynamic content
            eps = 1e-6,
            hotspotsCreated = false,
            destroyed = false;

        var defaultConfig = {
            hfov: 100,
            minHfov: 50,
            multiResMinHfov: false,
            maxHfov: 120,
            pitch: 0,
            minPitch: undefined,
            maxPitch: undefined,
            yaw: 0,
            minYaw: -180,
            maxYaw: 180,
            roll: 0,
            haov: 360,
            vaov: 180,
            vOffset: 0,
            autoRotate: false,
            autoRotateInactivityDelay: -1,
            autoRotateStopDelay: undefined,
            type: 'equirectangular',
            northOffset: 0,
            showFullscreenCtrl: true,
            dynamic: false,
            dynamicUpdate: false,
            doubleClickZoom: true,
            keyboardZoom: true,
            mouseZoom: true,
            showZoomCtrl: true,
            autoLoad: false,
            showControls: true,
            orientationOnByDefault: false,
            hotSpotDebug: false,
            backgroundColor: [0, 0, 0],
            avoidShowingBackground: false,
            animationTimingFunction: timingFunction,
            draggable: true,
            disableKeyboardCtrl: false,
            crossOrigin: 'anonymous',
            touchPanSpeedCoeffFactor: 1,
            capturedKeyNumbers: [16, 17, 27, 37, 38, 39, 40, 61, 107, 109, 173, 187, 189],
            friction: 0.15
        };

// Translatable / configurable strings
// Some strings contain '%s', which is a placeholder for inserted values
// When setting strings in external configuration, `\n` should be used instead of `<br>` to insert line breaks
        defaultConfig.strings = {
            // Labels
            loadButtonLabel: 'Click to<br>Load<br>Panorama',
            loadingLabel: 'Loading...',
            bylineLabel: ' %s',    // One substitution: author

            // Errors
            noPanoramaError: 'No panorama image was specified.',
            fileAccessError: 'The file %s could not be accessed.',  // One substitution: file URL
            malformedURLError: 'There is something wrong with the panorama URL.',
            iOS8WebGLError: "Due to iOS 8's broken WebGL implementation, only " +
                "progressive encoded JPEGs work for your device (this " +
                "panorama uses standard encoding).",
            genericWebGLError: 'Your browser does not have the necessary WebGL support to display this panorama.',
            textureSizeError: 'This panorama is too big for your device! It\'s ' +
                '%spx wide, but your device only supports images up to ' +
                '%spx wide. Try another device.' +
                ' (If you\'re the author, try scaling down the image.)',    // Two substitutions: image width, max image width
            unknownError: 'Unknown error. Check developer console.',
        };

// Initialize container
        container = typeof container === 'string' ? document.getElementById(container) : container;
        container.classList.add('pnlm-container');
        container.tabIndex = 0;

// Create container for ui
        var uiContainer = document.createElement('div');
        uiContainer.className = 'pnlm-ui';
        container.appendChild(uiContainer);

// Create container for renderer
        var renderContainer = document.createElement('div');
        renderContainer.className = 'pnlm-render-container';
        container.appendChild(renderContainer);
        var dragFix = document.createElement('div');
        dragFix.className = 'pnlm-dragfix';
        uiContainer.appendChild(dragFix);

// Display about information on right click off
//         var aboutMsg = document.createElement('span');
//         aboutMsg.className = 'pnlm-about-msg';
//==wpvr custom rextheme link==//
//         aboutMsg.innerHTML = '<a href="https://rextheme.com/docs/wpvr-360-panorama-and-virtual-tour-creator-for-wordpress/" target="_blank">Rextheme</a>';
//==wpvr custom rextheme link end==//
//         uiContainer.appendChild(aboutMsg);
//         dragFix.addEventListener('contextmenu', aboutMessage);

// Create info display
        var infoDisplay = {};

// Hot spot debug indicator
        var hotSpotDebugIndicator = document.createElement('div');
        hotSpotDebugIndicator.className = 'pnlm-sprite pnlm-hot-spot-debug-indicator';
        uiContainer.appendChild(hotSpotDebugIndicator);

// Panorama info
        infoDisplay.container = document.createElement('div');
        infoDisplay.container.className = 'pnlm-panorama-info';
        infoDisplay.title = document.createElement('div');
        infoDisplay.title.className = 'pnlm-title-box';
        infoDisplay.container.appendChild(infoDisplay.title);
        infoDisplay.author = document.createElement('div');
        infoDisplay.author.className = 'pnlm-author-box';
        infoDisplay.container.appendChild(infoDisplay.author);
        uiContainer.appendChild(infoDisplay.container);

// Load box
        infoDisplay.load = {};
        infoDisplay.load.box = document.createElement('div');
        infoDisplay.load.box.className = 'pnlm-load-box';
        infoDisplay.load.boxp = document.createElement('p');
        infoDisplay.load.box.appendChild(infoDisplay.load.boxp);
        infoDisplay.load.lbox = document.createElement('div');
        infoDisplay.load.lbox.className = 'pnlm-lbox';
        infoDisplay.load.lbox.innerHTML = '<div class="pnlm-loading"></div>';
        infoDisplay.load.box.appendChild(infoDisplay.load.lbox);
        infoDisplay.load.lbar = document.createElement('div');
        infoDisplay.load.lbar.className = 'pnlm-lbar';
        infoDisplay.load.lbarFill = document.createElement('div');
        infoDisplay.load.lbarFill.className = 'pnlm-lbar-fill';
        infoDisplay.load.lbar.appendChild(infoDisplay.load.lbarFill);
        infoDisplay.load.box.appendChild(infoDisplay.load.lbar);
        infoDisplay.load.msg = document.createElement('p');
        infoDisplay.load.msg.className = 'pnlm-lmsg';
        infoDisplay.load.box.appendChild(infoDisplay.load.msg);
        uiContainer.appendChild(infoDisplay.load.box);

// Error message
        infoDisplay.errorMsg = document.createElement('div');
        infoDisplay.errorMsg.className = 'pnlm-error-msg pnlm-info-box';
        uiContainer.appendChild(infoDisplay.errorMsg);

// Create controls
        var controls = {};
        controls.container = document.createElement('div');
        controls.container.className = 'pnlm-controls-container';
        uiContainer.appendChild(controls.container);

// Load button
        controls.load = document.createElement('div');
        controls.load.className = 'pnlm-load-button';
        controls.load.addEventListener('click', function() {
            processOptions();
            load();
        });
        uiContainer.appendChild(controls.load);

// Zoom controls
        controls.zoom = document.createElement('div');
        controls.zoom.className = 'pnlm-zoom-controls pnlm-controls';
        controls.zoomIn = document.createElement('div');
        controls.zoomIn.className = 'pnlm-zoom-in pnlm-sprite pnlm-control';
        controls.zoomIn.addEventListener('click', zoomIn);
        controls.zoom.appendChild(controls.zoomIn);
        controls.zoomOut = document.createElement('div');
        controls.zoomOut.className = 'pnlm-zoom-out pnlm-sprite pnlm-control';
        controls.zoomOut.addEventListener('click', zoomOut);
        controls.zoom.appendChild(controls.zoomOut);
        controls.container.appendChild(controls.zoom);

// Fullscreen toggle
        controls.fullscreen = document.createElement('div');
        controls.fullscreen.addEventListener('click', toggleFullscreen);
        controls.fullscreen.className = 'pnlm-fullscreen-toggle-button pnlm-sprite pnlm-fullscreen-toggle-button-inactive pnlm-controls pnlm-control';
        if (document.fullscreenEnabled || document.mozFullScreenEnabled || document.webkitFullscreenEnabled || document.msFullscreenEnabled)
            controls.container.appendChild(controls.fullscreen);

// Device orientation toggle
        controls.orientation = document.createElement('div');
        controls.orientation.addEventListener('click', function(e) {
            if (orientation)
                stopOrientation();
            else
                startOrientation();
        });
        controls.orientation.addEventListener('mousedown', function(e) {e.stopPropagation();});
        controls.orientation.addEventListener('touchstart', function(e) {e.stopPropagation();});
        controls.orientation.addEventListener('pointerdown', function(e) {e.stopPropagation();});
        controls.orientation.className = 'pnlm-orientation-button pnlm-orientation-button-inactive pnlm-sprite pnlm-controls pnlm-control';
        var orientationSupport = false;
        if (window.DeviceOrientationEvent && location.protocol == 'https:' &&
            navigator.userAgent.toLowerCase().indexOf('mobi') >= 0) {
            // This user agent check is here because there's no way to check if a
            // device has an inertia measurement unit. We used to be able to check if a
            // DeviceOrientationEvent had non-null values, but with iOS 13 requiring a
            // permission prompt to access such events, this is no longer possible.
            controls.container.appendChild(controls.orientation);
            orientationSupport = true;
        }

// Compass
        var compass = document.createElement('div');
        compass.className = 'pnlm-compass pnlm-controls pnlm-control';
        uiContainer.appendChild(compass);

// Load and process configuration
        if (initialConfig.firstScene) {
            // Activate first scene if specified in URL
            mergeConfig(initialConfig.firstScene);
        } else if (initialConfig.default && initialConfig.default.firstScene) {
            // Activate first scene if specified in file
            mergeConfig(initialConfig.default.firstScene);
        } else {
            mergeConfig(null);
        }
        processOptions(true);

        /**
         * Initializes viewer.
         * @private
         */
        function init() {
            // Display an error for IE 9 as it doesn't work but also doesn't otherwise
            // show an error (older versions don't work at all)
            // Based on: http://stackoverflow.com/a/10965203
            var div = document.createElement("div");
            div.innerHTML = "<!--[if lte IE 9]><i></i><![endif]-->";
            if (div.getElementsByTagName("i").length == 1) {
                anError();
                return;
            }

            origHfov = config.hfov;
            origPitch = config.pitch;

            var i, p;

            if (config.type == 'cubemap') {
                panoImage = [];
                for (i = 0; i < 6; i++) {
                    panoImage.push(new Image());
                    panoImage[i].crossOrigin = config.crossOrigin;
                }
                infoDisplay.load.lbox.style.display = 'block';
                infoDisplay.load.lbar.style.display = 'none';
            } else if (config.type == 'multires') {
                var c = JSON.parse(JSON.stringify(config.multiRes));    // Deep copy
                // Avoid "undefined" in path, check (optional) multiRes.basePath, too
                // Use only multiRes.basePath if it's an absolute URL
                if (config.basePath && config.multiRes.basePath &&
                    !(/^(?:[a-z]+:)?\/\//i.test(config.multiRes.basePath))) {
                    c.basePath = config.basePath + config.multiRes.basePath;
                } else if (config.multiRes.basePath) {
                    c.basePath = config.multiRes.basePath;
                } else if(config.basePath) {
                    c.basePath = config.basePath;
                }
                panoImage = c;
            } else {
                if (config.dynamic === true) {
                    panoImage = config.panorama;
                } else {
                    if (config.panorama === undefined) {
                        anError(config.strings.noPanoramaError);
                        return;
                    }
                    panoImage = new Image();
                }
            }

            // Configure image loading
            if (config.type == 'cubemap') {
                // Quick loading counter for synchronous loading
                var itemsToLoad = 6;

                var onLoad = function() {
                    itemsToLoad--;
                    if (itemsToLoad === 0) {
                        onImageLoad();
                    }
                };

                var onError = function(e) {
                    var a = document.createElement('a');
                    a.href = e.target.src;
                    a.textContent = a.href;
                    anError(config.strings.fileAccessError.replace('%s', a.outerHTML));
                };

                for (i = 0; i < panoImage.length; i++) {
                    p = config.cubeMap[i];
                    if (p == "null") { // support partial cubemap image with explicitly empty faces
                        console.log('Will use background instead of missing cubemap face ' + i);
                        onLoad();
                    } else {
                        if (config.basePath && !absoluteURL(p)) {
                            p = config.basePath + p;
                        }
                        panoImage[i].onload = onLoad;
                        panoImage[i].onerror = onError;
                        panoImage[i].src = sanitizeURL(p);
                    }
                }
            } else if (config.type == 'multires') {
                onImageLoad();
            } else {
                p = '';
                if (config.basePath) {
                    p = config.basePath;
                }

                if (config.dynamic !== true) {
                    // Still image
                    p = absoluteURL(config.panorama) ? config.panorama : p + config.panorama;

                    panoImage.onload = function() {
                        window.URL.revokeObjectURL(this.src);  // Clean up
                        onImageLoad();
                    };

                    var xhr = new XMLHttpRequest();
                    xhr.onloadend = function() {
                        if (xhr.status != 200) {
                            // Display error if image can't be loaded
                            var a = document.createElement('a');
                            a.href = p;
                            a.textContent = a.href;
                            anError(config.strings.fileAccessError.replace('%s', a.outerHTML));
                        }
                        var img = this.response;
                        parseGPanoXMP(img, p);
                        infoDisplay.load.msg.innerHTML = '';
                    };
                    xhr.onprogress = function(e) {
                        if (e.lengthComputable) {
                            // Display progress
                            var percent = e.loaded / e.total * 100;
                            infoDisplay.load.lbarFill.style.width = percent + '%';
                            var unit, numerator, denominator;
                            if (e.total > 1e6) {
                                unit = 'MB';
                                numerator = (e.loaded / 1e6).toFixed(2);
                                denominator = (e.total / 1e6).toFixed(2);
                            } else if (e.total > 1e3) {
                                unit = 'kB';
                                numerator = (e.loaded / 1e3).toFixed(1);
                                denominator = (e.total / 1e3).toFixed(1);
                            } else {
                                unit = 'B';
                                numerator = e.loaded;
                                denominator = e.total;
                            }
                            infoDisplay.load.msg.innerHTML = numerator + ' / ' + denominator + ' ' + unit;
                        } else {
                            // Display loading spinner
                            infoDisplay.load.lbox.style.display = 'block';
                            infoDisplay.load.lbar.style.display = 'none';
                        }
                    };
                    try {
                        xhr.open('GET', p, true);
                    } catch (e) {
                        // Malformed URL
                        anError(config.strings.malformedURLError);
                    }
                    xhr.responseType = 'blob';
                    xhr.setRequestHeader('Accept', 'image/*,*/*;q=0.9');
                    xhr.withCredentials = config.crossOrigin === 'use-credentials';
                    xhr.send();
                }
            }

            if (config.draggable)
                uiContainer.classList.add('pnlm-grab');
            uiContainer.classList.remove('pnlm-grabbing');

            // Properly handle switching to dynamic scenes
            update = config.dynamicUpdate === true;
            if (config.dynamic && update) {
                panoImage = config.panorama;
                onImageLoad();
            }
        }

        /**
         * Test if URL is absolute or relative.
         * @private
         * @param {string} url - URL to test
         * @returns {boolean} True if absolute, else false
         */
        function absoluteURL(url) {
            // From http://stackoverflow.com/a/19709846
            return new RegExp('^(?:[a-z]+:)?//', 'i').test(url) || url[0] == '/' || url.slice(0, 5) == 'blob:';
        }

        /**
         * Create renderer and initialize event listeners once image is loaded.
         * @private
         */
        function onImageLoad() {
            if (!renderer)
                renderer = new libpannellum.renderer(renderContainer);

            // Only add event listeners once
            if (!listenersAdded) {
                listenersAdded = true;
                dragFix.addEventListener('mousedown', onDocumentMouseDown, false);
                document.addEventListener('mousemove', onDocumentMouseMove, false);
                document.addEventListener('compasschange', onDocumentCompassChange, false);
                document.addEventListener('mouseup', onDocumentMouseUp, false);
                if (config.mouseZoom) {
                    uiContainer.addEventListener('mousewheel', onDocumentMouseWheel, false);
                    uiContainer.addEventListener('DOMMouseScroll', onDocumentMouseWheel, false);
                }
                if (config.doubleClickZoom) {
                    dragFix.addEventListener('dblclick', onDocumentDoubleClick, false);
                }
                container.addEventListener('mozfullscreenchange', onFullScreenChange, false);
                container.addEventListener('webkitfullscreenchange', onFullScreenChange, false);
                container.addEventListener('msfullscreenchange', onFullScreenChange, false);
                container.addEventListener('fullscreenchange', onFullScreenChange, false);
                window.addEventListener('resize', onDocumentResize, false);
                window.addEventListener('orientationchange', onDocumentResize, false);
                if (!config.disableKeyboardCtrl) {
                    container.addEventListener('keydown', onDocumentKeyPress, false);
                    container.addEventListener('keyup', onDocumentKeyUp, false);
                    container.addEventListener('blur', clearKeys, false);
                }
                document.addEventListener('mouseleave', onDocumentMouseUp, false);
                if (document.documentElement.style.pointerAction === '' &&
                    document.documentElement.style.touchAction === '') {
                    dragFix.addEventListener('pointerdown', onDocumentPointerDown, false);
                    dragFix.addEventListener('pointermove', onDocumentPointerMove, false);
                    dragFix.addEventListener('pointerup', onDocumentPointerUp, false);
                    dragFix.addEventListener('pointerleave', onDocumentPointerUp, false);
                } else {
                    dragFix.addEventListener('touchstart', onDocumentTouchStart, false);
                    dragFix.addEventListener('touchmove', onDocumentTouchMove, false);
                    dragFix.addEventListener('touchend', onDocumentTouchEnd, false);
                }

                // Deal with MS pointer events
                if (window.navigator.pointerEnabled)
                    container.style.touchAction = 'none';
            }

            renderInit();
            setHfov(config.hfov); // possibly adapt hfov after configuration and canvas is complete; prevents empty space on top or bottom by zomming out too much
            setTimeout(function(){isTimedOut = true;}, 500);
        }

        /**
         * Parses Google Photo Sphere XMP Metadata.
         * https://developers.google.com/photo-sphere/metadata/
         * @private
         * @param {Image} image - Image to read XMP metadata from.
         */
        function parseGPanoXMP(image, url) {
            var reader = new FileReader();
            reader.addEventListener('loadend', function() {
                var img = reader.result;

                // This awful browser specific test exists because iOS 8 does not work
                // with non-progressive encoded JPEGs.
                if (navigator.userAgent.toLowerCase().match(/(iphone|ipod|ipad).* os 8_/)) {
                    var flagIndex = img.indexOf('\xff\xc2');
                    if (flagIndex < 0 || flagIndex > 65536)
                        anError(config.strings.iOS8WebGLError);
                }

                var start = img.indexOf('<x:xmpmeta');
                if (start > -1 && config.ignoreGPanoXMP !== true) {
                    var xmpData = img.substring(start, img.indexOf('</x:xmpmeta>') + 12);

                    // Extract the requested tag from the XMP data
                    var getTag = function(tag) {
                        var result;
                        if (xmpData.indexOf(tag + '="') >= 0) {
                            result = xmpData.substring(xmpData.indexOf(tag + '="') + tag.length + 2);
                            result = result.substring(0, result.indexOf('"'));
                        } else if (xmpData.indexOf(tag + '>') >= 0) {
                            result = xmpData.substring(xmpData.indexOf(tag + '>') + tag.length + 1);
                            result = result.substring(0, result.indexOf('<'));
                        }
                        if (result !== undefined) {
                            return Number(result);
                        }
                        return null;
                    };

                    // Relevant XMP data
                    var xmp = {
                        fullWidth: getTag('GPano:FullPanoWidthPixels'),
                        croppedWidth: getTag('GPano:CroppedAreaImageWidthPixels'),
                        fullHeight: getTag('GPano:FullPanoHeightPixels'),
                        croppedHeight: getTag('GPano:CroppedAreaImageHeightPixels'),
                        topPixels: getTag('GPano:CroppedAreaTopPixels'),
                        heading: getTag('GPano:PoseHeadingDegrees'),
                        horizonPitch: getTag('GPano:PosePitchDegrees'),
                        horizonRoll: getTag('GPano:PoseRollDegrees')
                    };

                    if (xmp.fullWidth !== null && xmp.croppedWidth !== null &&
                        xmp.fullHeight !== null && xmp.croppedHeight !== null &&
                        xmp.topPixels !== null) {

                        // Set up viewer using GPano XMP data
                        if (specifiedPhotoSphereExcludes.indexOf('haov') < 0)
                            config.haov = xmp.croppedWidth / xmp.fullWidth * 360;
                        if (specifiedPhotoSphereExcludes.indexOf('vaov') < 0)
                            config.vaov = xmp.croppedHeight / xmp.fullHeight * 180;
                        if (specifiedPhotoSphereExcludes.indexOf('vOffset') < 0)
                            config.vOffset = ((xmp.topPixels + xmp.croppedHeight / 2) / xmp.fullHeight - 0.5) * -180;
                        if (xmp.heading !== null && specifiedPhotoSphereExcludes.indexOf('northOffset') < 0) {
                            // TODO: make sure this works correctly for partial panoramas
                            config.northOffset = xmp.heading;
                            if (config.compass !== false) {
                                config.compass = true;
                            }
                        }
                        if (xmp.horizonPitch !== null && xmp.horizonRoll !== null) {
                            if (specifiedPhotoSphereExcludes.indexOf('horizonPitch') < 0)
                                config.horizonPitch = xmp.horizonPitch;
                            if (specifiedPhotoSphereExcludes.indexOf('horizonRoll') < 0)
                                config.horizonRoll = xmp.horizonRoll;
                        }

                        // TODO: add support for initial view settings
                    }
                }

                // Load panorama
                panoImage.src = window.URL.createObjectURL(image);
                panoImage.onerror = function() {
                    // If the image fails to load, we check the Content Security Policy
                    // headers and see if they block loading images as blobs. If they
                    // do, we load the image directly from the URL. While this should
                    // allow the image to load, it does prevent parsing of XMP data.
                    function getCspHeaders() {
                        if (!window.fetch)
                            return null;
                        return window.fetch(document.location.href)
                            .then(function(resp){
                                return resp.headers.get('Content-Security-Policy');
                            });
                    }
                    getCspHeaders().then(function(cspHeaders) {
                        if (cspHeaders) {
                            var invalidImgSource = cspHeaders.split(";").find(function(p) {
                                var matchstring = p.match(/img-src(.*)/);
                                if (matchstring) {
                                    return !matchstring[1].includes("blob");
                                }
                            });
                            if (invalidImgSource) {
                                console.log('CSP blocks blobs; reverting to URL.');
                                panoImage.crossOrigin = config.crossOrigin;
                                panoImage.src = url;
                            }
                        }
                    });
                }
            });
            if (reader.readAsBinaryString !== undefined)
                reader.readAsBinaryString(image);
            else
                reader.readAsText(image);
        }

        /**
         * Displays an error message.
         * @private
         * @param {string} errorMsg - Error message to display. If not specified, a
         *      generic WebGL error is displayed.
         */
        function anError(errorMsg) {
            if (errorMsg === undefined)
                errorMsg = config.strings.genericWebGLError;
            infoDisplay.errorMsg.innerHTML = '<p>' + errorMsg + '</p>';
            controls.load.style.display = 'none';
            infoDisplay.load.box.style.display = 'none';
            infoDisplay.errorMsg.style.display = 'table';
            error = true;
            loaded = undefined;
            renderContainer.style.display = 'none';
            fireEvent('error', errorMsg);
        }

        /**
         * Hides error message display.
         * @private
         */
        function clearError() {
            if (error) {
                infoDisplay.load.box.style.display = 'none';
                infoDisplay.errorMsg.style.display = 'none';
                error = false;
                renderContainer.style.display = 'block';
                fireEvent('errorcleared');
            }
        }

        /**
         * Displays about message.
         * @private
         * @param {MouseEvent} event - Right click location
         */
        function aboutMessage(event) {
            var pos = mousePosition(event);
            aboutMsg.style.left = pos.x + 'px';
            aboutMsg.style.top = pos.y + 'px';
            clearTimeout(aboutMessage.t1);
            clearTimeout(aboutMessage.t2);
            aboutMsg.style.display = 'block';
            aboutMsg.style.opacity = 1;
            aboutMessage.t1 = setTimeout(function() {aboutMsg.style.opacity = 0;}, 2000);
            aboutMessage.t2 = setTimeout(function() {aboutMsg.style.display = 'none';}, 2500);
            event.preventDefault();
        }

        /**
         * Calculate mouse position relative to top left of viewer container.
         * @private
         * @param {MouseEvent} event - Mouse event to use in calculation
         * @returns {Object} Calculated X and Y coordinates
         */
        function mousePosition(event) {
            var bounds = container.getBoundingClientRect();
            var pos = {};
            // pageX / pageY needed for iOS
            pos.x = (event.clientX || event.pageX) - bounds.left;
            pos.y = (event.clientY || event.pageY) - bounds.top;
            return pos;
        }

        /**
         * Event handler for mouse clicks. Initializes panning. Prints center and click
         * location coordinates when hot spot debugging is enabled.
         * @private
         * @param {MouseEvent} event - Document mouse down event.
         */
        function onDocumentMouseDown(event) {
            // Override default action
            event.preventDefault();
            // But not all of it
            container.focus();

            // Only do something if the panorama is loaded
            if (!loaded || !config.draggable) {
                return;
            }

            // Calculate mouse position relative to top left of viewer container
            var pos = mousePosition(event);

            // Log pitch / yaw of mouse click when debugging / placing hot spots
            if (config.hotSpotDebug) {
                var coords = mouseEventToCoords(event);
                console.log('Pitch: ' + coords[0] + ', Yaw: ' + coords[1] + ', Center Pitch: ' +
                    config.pitch + ', Center Yaw: ' + config.yaw + ', HFOV: ' + config.hfov);
            }

            // Turn off auto-rotation if enabled
            stopAnimation();

            stopOrientation();
            config.roll = 0;

            speed.hfov = 0;

            isUserInteracting = true;
            latestInteraction = Date.now();

            onPointerDownPointerX = pos.x;
            onPointerDownPointerY = pos.y;

            onPointerDownYaw = config.yaw;
            onPointerDownPitch = config.pitch;

            uiContainer.classList.add('pnlm-grabbing');
            uiContainer.classList.remove('pnlm-grab');

            fireEvent('mousedown', event);
            animateInit();
        }

        /**
         * Event handler for double clicks. Zooms in at clicked location
         * @private
         * @param {MouseEvent} event - Document mouse down event.
         */
        function onDocumentDoubleClick(event) {
            if (config.minHfov === config.hfov) {
                _this.setHfov(origHfov, 1000);
            } else {
                var coords = mouseEventToCoords(event);
                _this.lookAt(coords[0], coords[1], config.minHfov, 1000);
            }
        }

        /**
         * Calculate panorama pitch and yaw from location of mouse event.
         * @private
         * @param {MouseEvent} event - Document mouse down event.
         * @returns {number[]} [pitch, yaw]
         */
        function mouseEventToCoords(event) {
            var pos = mousePosition(event);
            var canvas = renderer.getCanvas();
            var canvasWidth = canvas.clientWidth,
                canvasHeight = canvas.clientHeight;
            var x = pos.x / canvasWidth * 2 - 1;
            var y = (1 - pos.y / canvasHeight * 2) * canvasHeight / canvasWidth;
            var focal = 1 / Math.tan(config.hfov * Math.PI / 360);
            var s = Math.sin(config.pitch * Math.PI / 180);
            var c = Math.cos(config.pitch * Math.PI / 180);
            var a = focal * c - y * s;
            var root = Math.sqrt(x*x + a*a);
            var pitch = Math.atan((y * c + focal * s) / root) * 180 / Math.PI;
            var yaw = Math.atan2(x / root, a / root) * 180 / Math.PI + config.yaw;
            if (yaw < -180)
                yaw += 360;
            if (yaw > 180)
                yaw -= 360;
            return [pitch, yaw];
        }

        function onDocumentCompassChange(event) {
            
        }

        /**
         * Event handler for mouse moves. Pans center of view.
         * @private
         * @param {MouseEvent} event - Document mouse move event.
         */
        function onDocumentMouseMove(event) {
            if (isUserInteracting && loaded) {
                latestInteraction = Date.now();
                var canvas = renderer.getCanvas();
                var canvasWidth = canvas.clientWidth,
                    canvasHeight = canvas.clientHeight;
                var pos = mousePosition(event);
                //TODO: This still isn't quite right
                var yaw = ((Math.atan(onPointerDownPointerX / canvasWidth * 2 - 1) - Math.atan(pos.x / canvasWidth * 2 - 1)) * 180 / Math.PI * config.hfov / 90) + onPointerDownYaw;
                speed.yaw = (yaw - config.yaw) % 360 * 0.2;
                config.yaw = yaw;

                var vfov = 2 * Math.atan(Math.tan(config.hfov/360*Math.PI) * canvasHeight / canvasWidth) * 180 / Math.PI;

                var pitch = ((Math.atan(pos.y / canvasHeight * 2 - 1) - Math.atan(onPointerDownPointerY / canvasHeight * 2 - 1)) * 180 / Math.PI * vfov / 90) + onPointerDownPitch;
                speed.pitch = (pitch - config.pitch) * 0.2;
                config.pitch = pitch;

                var mirrorData = {
                    pitch: pitch,
                    yaw: yaw
                }
                fireEvent('mousemove', mirrorData);

            }
        }

        /**
         * Event handler for mouse up events. Stops panning.
         * @private
         */
        function onDocumentMouseUp(event) {
            if (!isUserInteracting) {
                return;
            }
            isUserInteracting = false;
            if (Date.now() - latestInteraction > 15) {
                // Prevents jump when user rapidly moves mouse, stops, and then
                // releases the mouse button
                speed.pitch = speed.yaw = 0;
            }
            uiContainer.classList.add('pnlm-grab');
            uiContainer.classList.remove('pnlm-grabbing');
            latestInteraction = Date.now();

            fireEvent('mouseup', event);
        }

        /**
         * Event handler for touches. Initializes panning if one touch or zooming if
         * two touches.
         * @private
         * @param {TouchEvent} event - Document touch start event.
         */
        function onDocumentTouchStart(event) {
            // Only do something if the panorama is loaded
            if (!loaded || !config.draggable) {
                return;
            }

            // Turn off auto-rotation if enabled
            stopAnimation();

            stopOrientation();
            config.roll = 0;

            speed.hfov = 0;

            // Calculate touch position relative to top left of viewer container
            var pos0 = mousePosition(event.targetTouches[0]);

            onPointerDownPointerX = pos0.x;
            onPointerDownPointerY = pos0.y;

            if (event.targetTouches.length == 2) {
                // Down pointer is the center of the two fingers
                var pos1 = mousePosition(event.targetTouches[1]);
                onPointerDownPointerX += (pos1.x - pos0.x) * 0.5;
                onPointerDownPointerY += (pos1.y - pos0.y) * 0.5;
                onPointerDownPointerDist = Math.sqrt((pos0.x - pos1.x) * (pos0.x - pos1.x) +
                    (pos0.y - pos1.y) * (pos0.y - pos1.y));
            }
            isUserInteracting = true;
            latestInteraction = Date.now();

            onPointerDownYaw = config.yaw;
            onPointerDownPitch = config.pitch;

            fireEvent('touchstart', event);
            animateInit();
        }

        /**
         * Event handler for touch movements. Pans center of view if one touch or
         * adjusts zoom if two touches.
         * @private
         * @param {TouchEvent} event - Document touch move event.
         */
        function onDocumentTouchMove(event) {
            if (!config.draggable) {
                return;
            }

            // Override default action
            event.preventDefault();
            if (loaded) {
                latestInteraction = Date.now();
            }
            if (isUserInteracting && loaded) {
                var pos0 = mousePosition(event.targetTouches[0]);
                var clientX = pos0.x;
                var clientY = pos0.y;

                if (event.targetTouches.length == 2 && onPointerDownPointerDist != -1) {
                    var pos1 = mousePosition(event.targetTouches[1]);
                    clientX += (pos1.x - pos0.x) * 0.5;
                    clientY += (pos1.y - pos0.y) * 0.5;
                    var clientDist = Math.sqrt((pos0.x - pos1.x) * (pos0.x - pos1.x) +
                        (pos0.y - pos1.y) * (pos0.y - pos1.y));
                    setHfov(config.hfov + (onPointerDownPointerDist - clientDist) * 0.1);
                    onPointerDownPointerDist = clientDist;
                }

                // The smaller the config.hfov value (the more zoomed-in the user is), the faster
                // yaw/pitch are perceived to change on one-finger touchmove (panning) events and vice versa.
                // To improve usability at both small and large zoom levels (config.hfov values)
                // we introduce a dynamic pan speed coefficient.
                //
                // Currently this seems to *roughly* keep initial drag/pan start position close to
                // the user's finger while panning regardless of zoom level / config.hfov value.
                var touchmovePanSpeedCoeff = (config.hfov / 360) * config.touchPanSpeedCoeffFactor;

                var yaw = (onPointerDownPointerX - clientX) * touchmovePanSpeedCoeff + onPointerDownYaw;
                speed.yaw = (yaw - config.yaw) % 360 * 0.2;
                config.yaw = yaw;

                var pitch = (clientY - onPointerDownPointerY) * touchmovePanSpeedCoeff + onPointerDownPitch;
                speed.pitch = (pitch - config.pitch) * 0.2;
                config.pitch = pitch;
                var mirrorData = {
                    pitch: pitch,
                    yaw: yaw
                }
                fireEvent('touchmove', mirrorData);
            }
        }

        /**
         * Event handler for end of touches. Stops panning and/or zooming.
         * @private
         */
        function onDocumentTouchEnd() {
            isUserInteracting = false;
            if (Date.now() - latestInteraction > 150) {
                speed.pitch = speed.yaw = 0;
            }
            onPointerDownPointerDist = -1;
            latestInteraction = Date.now();

            fireEvent('touchend', event);
        }

        var pointerIDs = [],
            pointerCoordinates = [];
        /**
         * Event handler for touch starts in IE / Edge.
         * @private
         * @param {PointerEvent} event - Document pointer down event.
         */
        function onDocumentPointerDown(event) {
            if (event.pointerType == 'touch') {
                // Only do something if the panorama is loaded
                if (!loaded || !config.draggable)
                    return;
                pointerIDs.push(event.pointerId);
                pointerCoordinates.push({clientX: event.clientX, clientY: event.clientY});
                event.targetTouches = pointerCoordinates;
                onDocumentTouchStart(event);
                event.preventDefault();
            }
        }

        /**
         * Event handler for touch moves in IE / Edge.
         * @private
         * @param {PointerEvent} event - Document pointer move event.
         */
        function onDocumentPointerMove(event) {
            if (event.pointerType == 'touch') {
                if (!config.draggable)
                    return;
                for (var i = 0; i < pointerIDs.length; i++) {
                    if (event.pointerId == pointerIDs[i]) {
                        pointerCoordinates[i].clientX = event.clientX;
                        pointerCoordinates[i].clientY = event.clientY;
                        event.targetTouches = pointerCoordinates;
                        onDocumentTouchMove(event);
                        event.preventDefault();
                        return;
                    }
                }
            }
        }

        /**
         * Event handler for touch ends in IE / Edge.
         * @private
         * @param {PointerEvent} event - Document pointer up event.
         */
        function onDocumentPointerUp(event) {
            if (event.pointerType == 'touch') {
                var defined = false;
                for (var i = 0; i < pointerIDs.length; i++) {
                    if (event.pointerId == pointerIDs[i])
                        pointerIDs[i] = undefined;
                    if (pointerIDs[i])
                        defined = true;
                }
                if (!defined) {
                    pointerIDs = [];
                    pointerCoordinates = [];
                    onDocumentTouchEnd();
                }
                event.preventDefault();
            }
        }

        /**
         * Event handler for mouse wheel. Changes zoom.
         * @private
         * @param {WheelEvent} event - Document mouse wheel event.
         */
        function onDocumentMouseWheel(event) {
            // Only do something if the panorama is loaded and mouse wheel zoom is enabled
            if (!loaded || (config.mouseZoom == 'fullscreenonly' && !fullscreenActive)) {
                return;
            }

            event.preventDefault();

            // Turn off auto-rotation if enabled
            stopAnimation();
            latestInteraction = Date.now();

            if (event.wheelDeltaY) {
                // WebKit
                setHfov(config.hfov - event.wheelDeltaY * 0.05);
                speed.hfov = event.wheelDelta < 0 ? 1 : -1;
            } else if (event.wheelDelta) {
                // Opera / Explorer 9
                setHfov(config.hfov - event.wheelDelta * 0.05);
                speed.hfov = event.wheelDelta < 0 ? 1 : -1;
            } else if (event.detail) {
                // Firefox
                setHfov(config.hfov + event.detail * 1.5);
                speed.hfov = event.detail > 0 ? 1 : -1;
            }
            animateInit();
        }

        /**
         * Event handler for key presses. Updates list of currently pressed keys.
         * @private
         * @param {KeyboardEvent} event - Document key press event.
         */
        function onDocumentKeyPress(event) {
            // Turn off auto-rotation if enabled
            stopAnimation();
            latestInteraction = Date.now();

            stopOrientation();
            config.roll = 0;

            // Record key pressed
            var keynumber = event.which || event.keycode;

            // Override default action for keys that are used
            if (config.capturedKeyNumbers.indexOf(keynumber) < 0)
                return;
            event.preventDefault();

            // If escape key is pressed
            if (keynumber == 27) {
                // If in fullscreen mode
                if (fullscreenActive) {
                    toggleFullscreen();
                }
            } else {
                // Change key
                changeKey(keynumber, true);
            }
        }

        /**
         * Clears list of currently pressed keys.
         * @private
         */
        function clearKeys() {
            for (var i = 0; i < 10; i++) {
                keysDown[i] = false;
            }
        }

        /**
         * Event handler for key releases. Updates list of currently pressed keys.
         * @private
         * @param {KeyboardEvent} event - Document key up event.
         */
        function onDocumentKeyUp(event) {
            // Record key pressed
            var keynumber = event.which || event.keycode;

            // Override default action for keys that are used
            if (config.capturedKeyNumbers.indexOf(keynumber) < 0)
                return;
            event.preventDefault();

            // Change key
            changeKey(keynumber, false);
        }

        /**
         * Updates list of currently pressed keys.
         * @private
         * @param {number} keynumber - Key number.
         * @param {boolean} value - Whether or not key is pressed.
         */
        function changeKey(keynumber, value) {
            var keyChanged = false;
            switch(keynumber) {
                // If minus key is released
                case 109: case 189: case 17: case 173:
                    if (keysDown[0] != value) { keyChanged = true; }
                    keysDown[0] = value; break;

                // If plus key is released
                case 107: case 187: case 16: case 61:
                    if (keysDown[1] != value) { keyChanged = true; }
                    keysDown[1] = value; break;

                // If up arrow is released
                case 38:
                    if (keysDown[2] != value) { keyChanged = true; }
                    keysDown[2] = value; break;

                // If "w" is released
                case 87:
                    if (keysDown[6] != value) { keyChanged = true; }
                    keysDown[6] = value; break;

                // If down arrow is released
                case 40:
                    if (keysDown[3] != value) { keyChanged = true; }
                    keysDown[3] = value; break;

                // If "s" is released
                case 83:
                    if (keysDown[7] != value) { keyChanged = true; }
                    keysDown[7] = value; break;

                // If left arrow is released
                case 37:
                    if (keysDown[4] != value) { keyChanged = true; }
                    keysDown[4] = value; break;

                // If "a" is released
                case 65:
                    if (keysDown[8] != value) { keyChanged = true; }
                    keysDown[8] = value; break;

                // If right arrow is released
                case 39:
                    if (keysDown[5] != value) { keyChanged = true; }
                    keysDown[5] = value; break;

                // If "d" is released
                case 68:
                    if (keysDown[9] != value) { keyChanged = true; }
                    keysDown[9] = value;
            }

            if (keyChanged && value) {
                if (typeof performance !== 'undefined' && performance.now()) {
                    prevTime = performance.now();
                } else {
                    prevTime = Date.now();
                }
                animateInit();
            }
        }

        /**
         * Pans and/or zooms panorama based on currently pressed keys. Also handles
         * panorama "inertia" and auto rotation.
         * @private
         */
        function keyRepeat() {
            // Only do something if the panorama is loaded
            if (!loaded) {
                return;
            }

            var isKeyDown = false;

            var prevPitch = config.pitch;
            var prevYaw = config.yaw;
            var prevZoom = config.hfov;

            var newTime;
            if (typeof performance !== 'undefined' && performance.now()) {
                newTime = performance.now();
            } else {
                newTime = Date.now();
            }
            if (prevTime === undefined) {
                prevTime = newTime;
            }
            var diff = (newTime - prevTime) * config.hfov / 1700;
            diff = Math.min(diff, 1.0);

            // If minus key is down
            if (keysDown[0] && config.keyboardZoom === true) {
                setHfov(config.hfov + (speed.hfov * 0.8 + 0.5) * diff);
                isKeyDown = true;
            }

            // If plus key is down
            if (keysDown[1] && config.keyboardZoom === true) {
                setHfov(config.hfov + (speed.hfov * 0.8 - 0.2) * diff);
                isKeyDown = true;
            }

            // If up arrow or "w" is down
            if (keysDown[2] || keysDown[6]) {
                // Pan up
                config.pitch += (speed.pitch * 0.8 + 0.2) * diff;
                isKeyDown = true;
            }

            // If down arrow or "s" is down
            if (keysDown[3] || keysDown[7]) {
                // Pan down
                config.pitch += (speed.pitch * 0.8 - 0.2) * diff;
                isKeyDown = true;
            }

            // If left arrow or "a" is down
            if (keysDown[4] || keysDown[8]) {
                // Pan left
                config.yaw += (speed.yaw * 0.8 - 0.2) * diff;
                isKeyDown = true;
            }

            // If right arrow or "d" is down
            if (keysDown[5] || keysDown[9]) {
                // Pan right
                config.yaw += (speed.yaw * 0.8 + 0.2) * diff;
                isKeyDown = true;
            }

            if (isKeyDown)
                latestInteraction = Date.now();

            // If auto-rotate
            if (config.autoRotate) {
                // Pan
                if (newTime - prevTime > 0.001) {
                    var timeDiff = (newTime - prevTime) / 1000;
                    var yawDiff = (speed.yaw / timeDiff * diff - config.autoRotate * 0.2) * timeDiff;
                    yawDiff = (-config.autoRotate > 0 ? 1 : -1) * Math.min(Math.abs(config.autoRotate * timeDiff), Math.abs(yawDiff));
                    config.yaw += yawDiff;
                }

                // Deal with stopping auto rotation after a set delay
                if (config.autoRotateStopDelay) {
                    config.autoRotateStopDelay -= newTime - prevTime;
                    if (config.autoRotateStopDelay <= 0) {
                        config.autoRotateStopDelay = false;
                        autoRotateSpeed = config.autoRotate;
                        config.autoRotate = 0;
                    }
                }
            }

            // Animated moves
            if (animatedMove.pitch) {
                animateMove('pitch');
                prevPitch = config.pitch;
            }
            if (animatedMove.yaw) {
                animateMove('yaw');
                prevYaw = config.yaw;
            }
            if (animatedMove.hfov) {
                animateMove('hfov');
                prevZoom = config.hfov;
            }

            // "Inertia"
            if (diff > 0 && !config.autoRotate) {
                // "Friction"
                var slowDownFactor = 1 - config.friction;

                // Yaw
                if (!keysDown[4] && !keysDown[5] && !keysDown[8] && !keysDown[9] && !animatedMove.yaw) {
                    config.yaw += speed.yaw * diff * slowDownFactor;
                }
                // Pitch
                if (!keysDown[2] && !keysDown[3] && !keysDown[6] && !keysDown[7] && !animatedMove.pitch) {
                    config.pitch += speed.pitch * diff * slowDownFactor;
                }
                // Zoom
                if (!keysDown[0] && !keysDown[1] && !animatedMove.hfov) {
                    setHfov(config.hfov + speed.hfov * diff * slowDownFactor);
                }
            }

            prevTime = newTime;
            if (diff > 0) {
                speed.yaw = speed.yaw * 0.8 + (config.yaw - prevYaw) / diff * 0.2;
                speed.pitch = speed.pitch * 0.8 + (config.pitch - prevPitch) / diff * 0.2;
                speed.hfov = speed.hfov * 0.8 + (config.hfov - prevZoom) / diff * 0.2;

                // Limit speed
                var maxSpeed = config.autoRotate ? Math.abs(config.autoRotate) : 5;
                speed.yaw = Math.min(maxSpeed, Math.max(speed.yaw, -maxSpeed));
                speed.pitch = Math.min(maxSpeed, Math.max(speed.pitch, -maxSpeed));
                speed.hfov = Math.min(maxSpeed, Math.max(speed.hfov, -maxSpeed));
            }

            // Stop movement if opposite controls are pressed
            if (keysDown[0] && keysDown[1]) {
                speed.hfov = 0;
            }
            if ((keysDown[2] || keysDown[6]) && (keysDown[3] || keysDown[7])) {
                speed.pitch = 0;
            }
            if ((keysDown[4] || keysDown[8]) && (keysDown[5] || keysDown[9])) {
                speed.yaw = 0;
            }
        }

        /**
         * Animates moves.
         * @param {string} axis - Axis to animate
         * @private
         */
        function animateMove(axis) {
            var t = animatedMove[axis];
            var normTime = Math.min(1, Math.max((Date.now() - t.startTime) / 1000 / (t.duration / 1000), 0));
            var result = t.startPosition + config.animationTimingFunction(normTime) * (t.endPosition - t.startPosition);
            if ((t.endPosition > t.startPosition && result >= t.endPosition) ||
                (t.endPosition < t.startPosition && result <= t.endPosition) ||
                t.endPosition === t.startPosition) {
                result = t.endPosition;
                speed[axis] = 0;
                delete animatedMove[axis];
            }
            config[axis] = result;
        }

        /**
         * @param {number} t - Normalized time in animation
         * @return {number} Position in animation
         * @private
         */
        function timingFunction(t) {
            // easeInOutQuad from https://gist.github.com/gre/1650294
            return t < 0.5 ? 2*t*t : -1+(4-2*t)*t;
        }

        /**
         * Event handler for document resizes. Updates viewer size and rerenders view.
         * @private
         */
        function onDocumentResize() {
            // Resize panorama renderer (moved to onFullScreenChange)
            //renderer.resize();
            //animateInit();

            // Kludge to deal with WebKit regression: https://bugs.webkit.org/show_bug.cgi?id=93525
            onFullScreenChange('resize');
        }

        /**
         * Initializes animation.
         * @private
         */
        function animateInit() {
            if (animating) {
                return;
            }
            animating = true;
            animate();
        }

        /**
         * Animates view, using requestAnimationFrame to trigger rendering.
         * @private
         */
        function animate() {
            if (destroyed) {
                return;
            }

            render();
            if (autoRotateStart)
                clearTimeout(autoRotateStart);
            if (isUserInteracting || orientation === true) {
                requestAnimationFrame(animate);
            } else if (keysDown[0] || keysDown[1] || keysDown[2] || keysDown[3] ||
                keysDown[4] || keysDown[5] || keysDown[6] || keysDown[7] ||
                keysDown[8] || keysDown[9] || config.autoRotate ||
                animatedMove.pitch || animatedMove.yaw || animatedMove.hfov ||
                Math.abs(speed.yaw) > 0.01 || Math.abs(speed.pitch) > 0.01 ||
                Math.abs(speed.hfov) > 0.01) {

                keyRepeat();
                if (config.autoRotateInactivityDelay >= 0 && autoRotateSpeed &&
                    Date.now() - latestInteraction > config.autoRotateInactivityDelay &&
                    !config.autoRotate) {
                    config.autoRotate = autoRotateSpeed;
                    _this.lookAt(origPitch, undefined, origHfov, 3000);
                }
                requestAnimationFrame(animate);
            } else if (renderer && (renderer.isLoading() || (config.dynamic === true && update))) {
                requestAnimationFrame(animate);
            } else {
                fireEvent('animatefinished', {pitch: _this.getPitch(), yaw: _this.getYaw(), hfov: _this.getHfov()});
                animating = false;
                prevTime = undefined;
                var autoRotateStartTime = config.autoRotateInactivityDelay -
                    (Date.now() - latestInteraction);
                if (autoRotateStartTime > 0) {
                    autoRotateStart = setTimeout(function() {
                        config.autoRotate = autoRotateSpeed;
                        _this.lookAt(origPitch, undefined, origHfov, 3000);
                        animateInit();
                    }, autoRotateStartTime);
                } else if (config.autoRotateInactivityDelay >= 0 && autoRotateSpeed) {
                    config.autoRotate = autoRotateSpeed;
                    _this.lookAt(origPitch, undefined, origHfov, 3000);
                    animateInit();
                }
            }
        }

        /**
         * Renders panorama view.
         * @private
         */
        function render() {
            var tmpyaw;

            if (loaded) {
                var canvas = renderer.getCanvas();

                if (config.autoRotate !== false) {
                    // When auto-rotating this check needs to happen first (see issue #764)
                    if (config.yaw > 360) {
                        config.yaw -= 360;
                    } else if (config.yaw < -360) {
                        config.yaw += 360;
                    }
                }

                // Keep a tmp value of yaw for autoRotate comparison later
                tmpyaw = config.yaw;

                // Optionally avoid showing background (empty space) on left or right by adapting min/max yaw
                var hoffcut = 0,
                    voffcut = 0;
                if (config.avoidShowingBackground) {
                    var hfov2 = config.hfov / 2,
                        vfov2 = Math.atan2(Math.tan(hfov2 / 180 * Math.PI), (canvas.width / canvas.height)) * 180 / Math.PI,
                        transposed = config.vaov > config.haov;
                    if (transposed) {
                        voffcut = vfov2 * (1 - Math.min(Math.cos((config.pitch - hfov2) / 180 * Math.PI),
                            Math.cos((config.pitch + hfov2) / 180 * Math.PI)));
                    } else {
                        hoffcut = hfov2 * (1 - Math.min(Math.cos((config.pitch - vfov2) / 180 * Math.PI),
                            Math.cos((config.pitch + vfov2) / 180 * Math.PI)));
                    }
                }

                // Ensure the yaw is within min and max allowed
                var yawRange = config.maxYaw - config.minYaw,
                    minYaw = -180,
                    maxYaw = 180;
                if (yawRange < 360) {
                    minYaw = config.minYaw + config.hfov / 2 + hoffcut;
                    maxYaw = config.maxYaw - config.hfov / 2 - hoffcut;
                    if (yawRange < config.hfov) {
                        // Lock yaw to average of min and max yaw when both can be seen at once
                        minYaw = maxYaw = (minYaw + maxYaw) / 2;
                    }
                    config.yaw = Math.max(minYaw, Math.min(maxYaw, config.yaw));
                }

                if (!(config.autoRotate !== false)) {
                    // When not auto-rotating, this check needs to happen after the
                    // previous check (see issue #698)
                    if (config.yaw > 360) {
                        config.yaw -= 360;
                    } else if (config.yaw < -360) {
                        config.yaw += 360;
                    }
                }

                // Check if we autoRotate in a limited by min and max yaw
                // If so reverse direction
                if (config.autoRotate !== false && tmpyaw != config.yaw &&
                    prevTime !== undefined) { // this condition prevents changing the direction initially
                    config.autoRotate *= -1;
                }

                // Ensure the calculated pitch is within min and max allowed
                var vfov = 2 * Math.atan(Math.tan(config.hfov / 180 * Math.PI * 0.5) /
                    (canvas.width / canvas.height)) / Math.PI * 180;
                var minPitch = config.minPitch + vfov / 2,
                    maxPitch = config.maxPitch - vfov / 2;
                var pitchRange = config.maxPitch - config.minPitch;
                if (pitchRange < vfov) {
                    // Lock pitch to average of min and max pitch when both can be seen at once
                    minPitch = maxPitch = (minPitch + maxPitch) / 2;
                }
                if (isNaN(minPitch))
                    minPitch = -90;
                if (isNaN(maxPitch))
                    maxPitch = 90;
                config.pitch = Math.max(minPitch, Math.min(maxPitch, config.pitch));

                renderer.render(config.pitch * Math.PI / 180, config.yaw * Math.PI / 180, config.hfov * Math.PI / 180, {roll: config.roll * Math.PI / 180});

                renderHotSpots();

                // Update compass
                //compass data fired by sakib
                // const node = document.getElementsByClassName('add-pulse')[0];
                // if(node) {
                //     node.style.transform = 'rotate(' + (-config.yaw - config.northOffset) + 'deg)';
                // }
                if (config.compass) {
                    compass.style.transform = 'rotate(' + (-config.yaw - config.northOffset) + 'deg)';
                    compass.style.webkitTransform = 'rotate(' + (-config.yaw - config.northOffset) + 'deg)';
                }
            }
        }

        /**
         * Creates a new quaternion.
         * @private
         * @constructor
         * @param {Number} w - W value
         * @param {Number} x - X value
         * @param {Number} y - Y value
         * @param {Number} z - Z value
         */
        function Quaternion(w, x, y, z) {
            this.w = w;
            this.x = x;
            this.y = y;
            this.z = z;
        }

        /**
         * Multiplies quaternions.
         * @private
         * @param {Quaternion} q - Quaternion to multiply
         * @returns {Quaternion} Result of multiplication
         */
        Quaternion.prototype.multiply = function(q) {
            return new Quaternion(this.w*q.w - this.x*q.x - this.y*q.y - this.z*q.z,
                this.x*q.w + this.w*q.x + this.y*q.z - this.z*q.y,
                this.y*q.w + this.w*q.y + this.z*q.x - this.x*q.z,
                this.z*q.w + this.w*q.z + this.x*q.y - this.y*q.x);
        };

        /**
         * Converts quaternion to Euler angles.
         * @private
         * @returns {Number[]} [phi angle, theta angle, psi angle]
         */
        Quaternion.prototype.toEulerAngles = function() {
            var phi = Math.atan2(2 * (this.w * this.x + this.y * this.z),
                    1 - 2 * (this.x * this.x + this.y * this.y)),
                theta = Math.asin(2 * (this.w * this.y - this.z * this.x)),
                psi = Math.atan2(2 * (this.w * this.z + this.x * this.y),
                    1 - 2 * (this.y * this.y + this.z * this.z));
            return [phi, theta, psi];
        };

        /**
         * Converts device orientation API Tait-Bryan angles to a quaternion.
         * @private
         * @param {Number} alpha - Alpha angle (in degrees)
         * @param {Number} beta - Beta angle (in degrees)
         * @param {Number} gamma - Gamma angle (in degrees)
         * @returns {Quaternion} Orientation quaternion
         */
        function taitBryanToQuaternion(alpha, beta, gamma) {
            var r = [beta ? beta * Math.PI / 180 / 2 : 0,
                gamma ? gamma * Math.PI / 180 / 2 : 0,
                alpha ? alpha * Math.PI / 180 / 2 : 0];
            var c = [Math.cos(r[0]), Math.cos(r[1]), Math.cos(r[2])],
                s = [Math.sin(r[0]), Math.sin(r[1]), Math.sin(r[2])];

            return new Quaternion(c[0]*c[1]*c[2] - s[0]*s[1]*s[2],
                s[0]*c[1]*c[2] - c[0]*s[1]*s[2],
                c[0]*s[1]*c[2] + s[0]*c[1]*s[2],
                c[0]*c[1]*s[2] + s[0]*s[1]*c[2]);
        }

        /**
         * Computes current device orientation quaternion from device orientation API
         * Tait-Bryan angles.
         * @private
         * @param {Number} alpha - Alpha angle (in degrees)
         * @param {Number} beta - Beta angle (in degrees)
         * @param {Number} gamma - Gamma angle (in degrees)
         * @returns {Quaternion} Orientation quaternion
         */
        function computeQuaternion(alpha, beta, gamma) {
            // Convert Tait-Bryan angles to quaternion
            var quaternion = taitBryanToQuaternion(alpha, beta, gamma);
            // Apply world transform
            quaternion = quaternion.multiply(new Quaternion(Math.sqrt(0.5), -Math.sqrt(0.5), 0, 0));
            // Apply screen transform
            var angle = window.orientation ? -window.orientation * Math.PI / 180 / 2 : 0;
            return quaternion.multiply(new Quaternion(Math.cos(angle), 0, -Math.sin(angle), 0));
        }

        /**
         * Event handler for device orientation API. Controls pointing.
         * @private
         * @param {DeviceOrientationEvent} event - Device orientation event.
         */
        function orientationListener(e) {
            var q = computeQuaternion(e.alpha, e.beta, e.gamma).toEulerAngles();
            if (typeof(orientation) == 'number' && orientation < 10) {
                // This kludge is necessary because iOS sometimes provides a few stale
                // device orientation events when the listener is removed and then
                // readded. Thus, we skip the first 10 events to prevent this from
                // causing problems.
                orientation += 1;
            } else if (orientation === 10) {
                // Record starting yaw to prevent jumping
                orientationYawOffset = q[2] / Math.PI * 180 + config.yaw;
                orientation = true;
                requestAnimationFrame(animate);
            } else {
                config.pitch = q[0] / Math.PI * 180;
                config.roll = -q[1] / Math.PI * 180;
                config.yaw = -q[2] / Math.PI * 180 + orientationYawOffset;
            }
        }

        /**
         * Initializes renderer.
         * @private
         */
        function renderInit() {
            try {
                var params = {};
                if (config.horizonPitch !== undefined)
                    params.horizonPitch = config.horizonPitch * Math.PI / 180;
                if (config.horizonRoll !== undefined)
                    params.horizonRoll = config.horizonRoll * Math.PI / 180;
                if (config.backgroundColor !== undefined)
                    params.backgroundColor = config.backgroundColor;
                renderer.init(panoImage, config.type, config.dynamic, config.haov * Math.PI / 180, config.vaov * Math.PI / 180, config.vOffset * Math.PI / 180, renderInitCallback, params);
                if (config.dynamic !== true) {
                    // Allow image to be garbage collected
                    panoImage = undefined;
                }
            } catch(event) {
                // Panorama not loaded

                // Display error if there is a bad texture
                if (event.type == 'webgl error' || event.type == 'no webgl') {
                    anError();
                } else if (event.type == 'webgl size error') {
                    anError(config.strings.textureSizeError.replace('%s', event.width).replace('%s', event.maxWidth));
                } else {
                    anError(config.strings.unknownError);
                    throw event;
                }
            }
        }

        /**
         * Triggered when render initialization finishes. Handles fading between
         * scenes as well as showing the compass and hotspots and hiding the loading
         * display.
         * @private
         */
        function renderInitCallback() {
            // Fade if specified
            if (config.sceneFadeDuration && renderer.fadeImg !== undefined) {
                renderer.fadeImg.style.opacity = 0;
                // Remove image
                var fadeImg = renderer.fadeImg;
                delete renderer.fadeImg;
                setTimeout(function() {
                    renderContainer.removeChild(fadeImg);
                    fireEvent('scenechangefadedone');
                }, config.sceneFadeDuration);
            }

            // Show compass if applicable
            if (config.compass) {
                compass.style.display = 'inline';
            } else {
                compass.style.display = 'none';
            }

            // Show hotspots
            createHotSpots();

            // Hide loading display
            infoDisplay.load.box.style.display = 'none';
            if (preview !== undefined) {
                renderContainer.removeChild(preview);
                preview = undefined;
            }
            loaded = true;

            animateInit();

            fireEvent('load');
        }

        /**
         * Creates hot spot element for the current scene.
         * @private
         * @param {Object} hs - The configuration for the hotspot
         */
        function createHotSpot(hs) {
            // Make sure hot spot pitch and yaw are numbers
            hs.pitch = Number(hs.pitch) || 0;
            hs.yaw = Number(hs.yaw) || 0;

            var div = document.createElement('div');
            div.className = 'pnlm-hotspot-base';
            if (hs.cssClass)
                div.className += ' ' + hs.cssClass;
            else
                div.className += ' pnlm-hotspot pnlm-sprite pnlm-' + escapeHTML(hs.type);

            //Shahin - customized hotspot dom for Hexagon shape
            if(hs.hotspot_shape === 'hexagon'){
                var hotspotShape = document.createElement('div');
                hotspotShape.className = 'hexagon-wrapper';
                const hexagonShapeIcon = '<svg fill="none" width="30" height="25" viewBox="0 0 40 35" xmlns="http://www.w3.org/2000/svg"><path fill="#00b4ff" d="M0 17.32L10 0h20l10 17.32-10 17.321H10L0 17.321z"/></svg>';
                hotspotShape.innerHTML = hexagonShapeIcon;
                div.appendChild(hotspotShape);
            }
            //Sakib - customized hotspot dom for vr mode
            var title = document.createElement('span');
            title.className = 'vr-mode-title';
            if (hs.sceneId)
                title.innerHTML = escapeHTML(hs.sceneId);
            //Sakib - customized hotspot dom for vr mode end

            var span = document.createElement('span');
            if (hs.text)
                span.innerHTML = escapeHTML(hs.text);

            var a;
            if (hs.video) {
                var video = document.createElement('video'),
                    vidp = hs.video;
                if (config.basePath && !absoluteURL(vidp))
                    vidp = config.basePath + vidp;
                video.src = sanitizeURL(vidp);
                video.controls = true;
                video.style.width = hs.width + 'px';
                renderContainer.appendChild(div);
                span.appendChild(video);
            } else if (hs.image) {
                var imgp = hs.image;
                if (config.basePath && !absoluteURL(imgp))
                    imgp = config.basePath + imgp;
                a = document.createElement('a');
                a.href = sanitizeURL(hs.URL ? hs.URL : imgp, true);
                a.target = '_blank';
                span.appendChild(a);
                var image = document.createElement('img');
                image.src = sanitizeURL(imgp);
                image.style.width = hs.width + 'px';
                image.style.paddingTop = '5px';
                renderContainer.appendChild(div);
                a.appendChild(image);
                span.style.maxWidth = 'initial';
            } else if (hs.URL) {
                a = document.createElement('a');
                a.href = sanitizeURL(hs.URL, true);
                if (hs.attributes) {
                    for (var key in hs.attributes) {
                        a.setAttribute(key, hs.attributes[key]);
                    }
                } else {
                    //wpvr edit by sakib for in tab url open// -- handling from here
                    if (hs.wpvr_url_open == 'on') {
                        a.target = '_self';
                    }
                    else {
                        a.target = '_blank';
                    }
                }
                renderContainer.appendChild(a);
                div.className += ' pnlm-pointer';
                span.className += ' pnlm-pointer';
                a.appendChild(div);
            } else {
                if (hs.sceneId) {
                    div.onclick = div.ontouchend = function() {
                        if (!div.clicked) {
                            div.clicked = true;
                            loadScene(hs.sceneId, hs.targetPitch, hs.targetYaw, hs.targetHfov);
                        }
                        return false;
                    };
                    div.className += ' pnlm-pointer';
                    span.className += ' pnlm-pointer';
                }

                if (hs.sceneId)
                    div.appendChild(title);

                renderContainer.appendChild(div);
            }

            if (hs.createTooltipFunc) {
                hs.createTooltipFunc(div, hs.createTooltipArgs);
            } else if (hs.text || hs.video || hs.image) {
                div.classList.add('pnlm-tooltip');
                div.appendChild(span);
                span.style.width = span.scrollWidth - 20 + 'px';
                span.style.marginLeft = -(span.scrollWidth - div.offsetWidth) / 2 + 'px';
                span.style.marginTop = -span.scrollHeight - 12 + 'px';
            }
            if (hs.clickHandlerFunc) {
                div.addEventListener('click', function(e) {
                    hs.clickHandlerFunc(e, hs.clickHandlerArgs);
                }, 'false');
                div.className += ' pnlm-pointer';
                span.className += ' pnlm-pointer';
            }
            hs.div = div;
        }

        /**
         * Creates hot spot elements for the current scene.
         * @private
         */
        function createHotSpots() {
            if (hotspotsCreated) return;

            if (!config.hotSpots) {
                config.hotSpots = [];
            } else {
                // Sort by pitch so tooltip is never obscured by another hot spot
                config.hotSpots = config.hotSpots.sort(function(a, b) {
                    return a.pitch < b.pitch;
                });
                config.hotSpots.forEach(createHotSpot);
            }
            hotspotsCreated = true;
            renderHotSpots();
        }

        /**
         * Destroys currently created hot spot elements.
         * @private
         */
        function destroyHotSpots() {
            var hs = config.hotSpots;
            hotspotsCreated = false;
            delete config.hotSpots;
            if (hs) {
                for (var i = 0; i < hs.length; i++) {
                    var current = hs[i].div;
                    if (current) {
                        while (current.parentNode && current.parentNode != renderContainer) {
                            current = current.parentNode;
                        }
                        renderContainer.removeChild(current);
                    }
                    delete hs[i].div;
                }
            }
        }

        /**
         * Renders hot spot, updating its position and visibility.
         * @private
         */
        function renderHotSpot(hs) {
            var hsPitchSin = Math.sin(hs.pitch * Math.PI / 180),
                hsPitchCos = Math.cos(hs.pitch * Math.PI / 180),
                configPitchSin = Math.sin(config.pitch * Math.PI / 180),
                configPitchCos = Math.cos(config.pitch * Math.PI / 180),
                yawCos = Math.cos((-hs.yaw + config.yaw) * Math.PI / 180);
            var z = hsPitchSin * configPitchSin + hsPitchCos * yawCos * configPitchCos;
            if ((hs.yaw <= 90 && hs.yaw > -90 && z <= 0) ||
                ((hs.yaw > 90 || hs.yaw <= -90) && z <= 0)) {
                hs.div.style.visibility = 'hidden';
            } else {
                var yawSin = Math.sin((-hs.yaw + config.yaw) * Math.PI / 180),
                    hfovTan = Math.tan(config.hfov * Math.PI / 360);
                hs.div.style.visibility = 'visible';
                // Subpixel rendering doesn't work in Firefox
                // https://bugzilla.mozilla.org/show_bug.cgi?id=739176
                var canvas = renderer.getCanvas(),
                    canvasWidth = canvas.clientWidth,
                    canvasHeight = canvas.clientHeight;
                var coord = [-canvasWidth / hfovTan * yawSin * hsPitchCos / z / 2,
                    -canvasWidth / hfovTan * (hsPitchSin * configPitchCos -
                        hsPitchCos * yawCos * configPitchSin) / z / 2];
                // Apply roll
                var rollSin = Math.sin(config.roll * Math.PI / 180),
                    rollCos = Math.cos(config.roll * Math.PI / 180);
                coord = [coord[0] * rollCos - coord[1] * rollSin,
                    coord[0] * rollSin + coord[1] * rollCos];
                // Apply transform
                coord[0] += (canvasWidth - hs.div.offsetWidth) / 2;
                coord[1] += (canvasHeight - hs.div.offsetHeight) / 2;
                var transform = 'translate(' + coord[0] + 'px, ' + coord[1] +
                    'px) translateZ(9999px) rotate(' + config.roll + 'deg)';
                if (hs.scale) {
                    transform += ' scale(' + (origHfov/config.hfov) / z + ')';
                }
                hs.div.style.webkitTransform = transform;
                hs.div.style.MozTransform = transform;
                hs.div.style.transform = transform;
            }
        }

        /**
         * Renders hot spots, updating their positions and visibility.
         * @private
         */
        function renderHotSpots() {
            config.hotSpots.forEach(renderHotSpot);
        }

        /**
         * Merges a scene configuration into the current configuration.
         * @private
         * @param {string} sceneId - Identifier of scene configuration to merge in.
         */
        function mergeConfig(sceneId) {
            config = {};
            var k, s;
            var photoSphereExcludes = ['haov', 'vaov', 'vOffset', 'northOffset', 'horizonPitch', 'horizonRoll'];
            specifiedPhotoSphereExcludes = [];

            // Merge default config
            for (k in defaultConfig) {
                if (defaultConfig.hasOwnProperty(k)) {
                    config[k] = defaultConfig[k];
                }
            }

            // Merge default scene config
            for (k in initialConfig.default) {
                if (initialConfig.default.hasOwnProperty(k)) {
                    if (k == 'strings') {
                        for (s in initialConfig.default.strings) {
                            if (initialConfig.default.strings.hasOwnProperty(s)) {
                                config.strings[s] = escapeHTML(initialConfig.default.strings[s]);
                            }
                        }
                    } else {
                        config[k] = initialConfig.default[k];
                        if (photoSphereExcludes.indexOf(k) >= 0) {
                            specifiedPhotoSphereExcludes.push(k);
                        }
                    }
                }
            }

            // Merge current scene config
            if ((sceneId !== null) && (sceneId !== '') && (initialConfig.scenes) && (initialConfig.scenes[sceneId])) {
                var scene = initialConfig.scenes[sceneId];
                for (k in scene) {
                    if (scene.hasOwnProperty(k)) {
                        if (k == 'strings') {
                            for (s in scene.strings) {
                                if (scene.strings.hasOwnProperty(s)) {
                                    config.strings[s] = escapeHTML(scene.strings[s]);
                                }
                            }
                        } else {
                            config[k] = scene[k];
                            if (photoSphereExcludes.indexOf(k) >= 0) {
                                specifiedPhotoSphereExcludes.push(k);
                            }
                        }
                    }
                }
                config.scene = sceneId;
            }

            // Merge initial config
            for (k in initialConfig) {
                if (initialConfig.hasOwnProperty(k)) {
                    if (k == 'strings') {
                        for (s in initialConfig.strings) {
                            if (initialConfig.strings.hasOwnProperty(s)) {
                                config.strings[s] = escapeHTML(initialConfig.strings[s]);
                            }
                        }
                    } else {
                        config[k] = initialConfig[k];
                        if (photoSphereExcludes.indexOf(k) >= 0) {
                            specifiedPhotoSphereExcludes.push(k);
                        }
                    }
                }
            }
        }

        /**
         * Processes configuration options.
         * @param {boolean} [isPreview] - Whether or not the preview is being displayed
         * @private
         */
        function processOptions(isPreview) {
            isPreview = isPreview ? isPreview : false;

            // Process preview first so it always loads before the browser hits its
            // maximum number of connections to a server as can happen with cubic
            // panoramas
            if (isPreview && 'preview' in config) {
                var p = config.preview;
                if (config.basePath && !absoluteURL(p))
                    p = config.basePath + p;
                preview = document.createElement('div');
                preview.className = 'pnlm-preview-img';
                preview.style.backgroundImage = "url('" + sanitizeURLForCss(p) + "')";
                renderContainer.appendChild(preview);
            }

            // Handle different preview values
            var title = config.title,
                author = config.author;
            if (isPreview) {
                if ('previewTitle' in config)
                    config.title = config.previewTitle;
                if ('previewAuthor' in config)
                    config.author = config.previewAuthor;
            }

            // Reset title / author display
            if (!config.hasOwnProperty('title'))
                infoDisplay.title.innerHTML = '';
            if (!config.hasOwnProperty('author'))
                infoDisplay.author.innerHTML = '';
            if (!config.hasOwnProperty('title') && !config.hasOwnProperty('author'))
                infoDisplay.container.style.display = 'none';

            // Fill in load button label and loading box text
            controls.load.innerHTML = '<p>' + config.strings.loadButtonLabel + '</p>';
            infoDisplay.load.boxp.innerHTML = config.strings.loadingLabel;

            // Process other options
            for (var key in config) {
                if (config.hasOwnProperty(key)) {
                    switch(key) {
                        case 'title':
                            infoDisplay.title.innerHTML = escapeHTML(config[key]);
                            infoDisplay.container.style.display = 'inline';
                            break;

                        case 'author':
                            var authorText = escapeHTML(config[key]);
                            if (config.authorURL) {
                                var authorLink = document.createElement('a');
                                authorLink.href = sanitizeURL(config['authorURL'], true);
                                authorLink.target = '_blank';
                                authorLink.innerHTML = escapeHTML(config[key]);
                                authorText = authorLink.outerHTML;
                            }
                            infoDisplay.author.innerHTML = config.strings.bylineLabel.replace('%s', authorText);
                            infoDisplay.container.style.display = 'inline';
                            break;

                        case 'fallback':
                            var link = document.createElement('a');
                            link.href = sanitizeURL(config[key], true);
                            link.target = '_blank';
                            link.textContent = 'Click here to view this panorama in an alternative viewer.';
                            var message = document.createElement('p');
                            message.textContent = 'Your browser does not support WebGL.';
                            message.appendChild(document.createElement('br'));
                            message.appendChild(link);
                            infoDisplay.errorMsg.innerHTML = ''; // Removes all children nodes
                            infoDisplay.errorMsg.appendChild(message);
                            break;

                        case 'hfov':
                            setHfov(Number(config[key]));
                            break;

                        case 'autoLoad':
                            if (config[key] === true && renderer === undefined) {
                                // Show loading box
                                infoDisplay.load.box.style.display = 'inline';
                                // Hide load button
                                controls.load.style.display = 'none';
                                // Initialize
                                init();
                            }
                            break;

                        case 'showZoomCtrl':
                            if (config[key] && config.showControls != false) {
                                // Show zoom controls
                                controls.zoom.style.display = 'block';
                            } else {
                                // Hide zoom controls
                                controls.zoom.style.display = 'none';
                            }
                            break;

                        case 'showFullscreenCtrl':
                            if (config[key] && config.showControls != false && ('fullscreen' in document || 'mozFullScreen' in document ||
                                'webkitIsFullScreen' in document || 'msFullscreenElement' in document)) {

                                // Show fullscreen control
                                controls.fullscreen.style.display = 'block';
                            } else {
                                // Hide fullscreen control
                                controls.fullscreen.style.display = 'none';
                            }
                            break;

                        case 'hotSpotDebug':
                            if (config[key])
                                hotSpotDebugIndicator.style.display = 'block';
                            else
                                hotSpotDebugIndicator.style.display = 'none';
                            break;

                        case 'showControls':
                            if (!config[key]) {
                                controls.orientation.style.display = 'none';
                                controls.zoom.style.display = 'none';
                                controls.fullscreen.style.display = 'none';
                            }
                            break;

                        case 'orientationOnByDefault':
                            if (config[key])
                                startOrientation();
                            break;
                    }
                }
            }

            if (isPreview) {
                // Restore original values if changed for preview
                if (title)
                    config.title = title;
                else
                    delete config.title;
                if (author)
                    config.author = author;
                else
                    delete config.author;
            }
        }

        /**
         * Toggles fullscreen mode.
         * @private
         */
        function toggleFullscreen() {
            if (loaded && !error) {
                if (!fullscreenActive) {
                    try {
                        if (container.requestFullscreen) {
                            container.requestFullscreen();
                        } else if (container.mozRequestFullScreen) {
                            container.mozRequestFullScreen();
                        } else if (container.msRequestFullscreen) {
                            container.msRequestFullscreen();
                        } else {
                            container.webkitRequestFullScreen();
                        }
                    } catch(event) {
                        // Fullscreen doesn't work
                    }
                } else {
                    if (document.exitFullscreen) {
                        document.exitFullscreen();
                    } else if (document.mozCancelFullScreen) {
                        document.mozCancelFullScreen();
                    } else if (document.webkitCancelFullScreen) {
                        document.webkitCancelFullScreen();
                    } else if (document.msExitFullscreen) {
                        document.msExitFullscreen();
                    }
                }
            }
        }

        /**
         * Event handler for fullscreen changes.
         * @private
         */
        function onFullScreenChange(resize) {
            if (document.fullscreenElement || document.fullscreen || document.mozFullScreen || document.webkitIsFullScreen || document.msFullscreenElement) {
                controls.fullscreen.classList.add('pnlm-fullscreen-toggle-button-active');
                fullscreenActive = true;
            } else {
                controls.fullscreen.classList.remove('pnlm-fullscreen-toggle-button-active');
                fullscreenActive = false;
            }
            if (resize !== 'resize')
                fireEvent('fullscreenchange', fullscreenActive);
            // Resize renderer (deal with browser quirks and fixes #155)
            renderer.resize();
            setHfov(config.hfov);
            animateInit();
        }

        /**
         * Increases panorama zoom. For use with zoom button.
         * @private
         */
        function zoomIn() {
            if (loaded) {
                setHfov(config.hfov - 5);
                animateInit();
            }
        }

        /**
         * Decreases panorama zoom. For use with zoom button.
         * @private
         */
        function zoomOut() {
            if (loaded) {
                setHfov(config.hfov + 5);
                animateInit();
            }
        }

        /**
         * Clamps horzontal field of view to viewer's limits.
         * @private
         * @param {number} hfov - Input horizontal field of view (in degrees)
         * @return {number} - Clamped horizontal field of view (in degrees)
         */
        function constrainHfov(hfov) {
            // Keep field of view within bounds
            var minHfov = config.minHfov;
            if (config.type == 'multires' && renderer && !config.multiResMinHfov) {
                minHfov = Math.min(minHfov, renderer.getCanvas().width / (config.multiRes.cubeResolution / 90 * 0.9));
            }
            if (minHfov > config.maxHfov) {
                // Don't change view if bounds don't make sense
                console.log('HFOV bounds do not make sense (minHfov > maxHfov).');
                return config.hfov;
            }
            var newHfov = config.hfov;
            if (hfov < minHfov) {
                newHfov = minHfov;
            } else if (hfov > config.maxHfov) {
                newHfov = config.maxHfov;
            } else {
                newHfov = hfov;
            }
            // Optionally avoid showing background (empty space) on top or bottom by adapting newHfov
            if (config.avoidShowingBackground && renderer) {
                var canvas = renderer.getCanvas();
                newHfov = Math.min(newHfov,
                    Math.atan(Math.tan((config.maxPitch - config.minPitch) / 360 * Math.PI) /
                        canvas.height * canvas.width) * 360 / Math.PI);
            }
            return newHfov;
        }

        /**
         * Sets viewer's horizontal field of view.
         * @private
         * @param {number} hfov - Desired horizontal field of view in degrees.
         */
        function setHfov(hfov) {
            config.hfov = constrainHfov(hfov);
            fireEvent('zoomchange', config.hfov);
        }

        /**
         * Stops auto rotation and animated moves.
         * @private
         */
        function stopAnimation() {
            animatedMove = {};
            autoRotateSpeed = config.autoRotate ? config.autoRotate : autoRotateSpeed;
            config.autoRotate = false;
        }

        /**
         * Loads panorama.
         * @private
         */
        function load() {
            // Since WebGL error handling is very general, first we clear any error box
            // since it is a new scene and the error from previous maybe because of lacking
            // memory etc and not because of a lack of WebGL support etc
            clearError();
            loaded = false;

            controls.load.style.display = 'none';
            infoDisplay.load.box.style.display = 'inline';
            init();
        }

        /**
         * Loads scene.
         * @private
         * @param {string} sceneId - Identifier of scene configuration to merge in.
         * @param {number} targetPitch - Pitch viewer should be centered on once scene loads.
         * @param {number} targetYaw - Yaw viewer should be centered on once scene loads.
         * @param {number} targetHfov - HFOV viewer should use once scene loads.
         * @param {boolean} [fadeDone] - If `true`, fade setup is skipped.
         */
        function loadScene(sceneId, targetPitch, targetYaw, targetHfov, fadeDone) {
            if (!loaded)
                fadeDone = true;    // Don't try to fade when there isn't a scene loaded
            loaded = false;
            animatedMove = {};

            // Set up fade if specified
            var fadeImg, workingPitch, workingYaw, workingHfov;
            if (config.sceneFadeDuration && !fadeDone) {
                var data = renderer.render(config.pitch * Math.PI / 180, config.yaw * Math.PI / 180, config.hfov * Math.PI / 180, {returnImage: true});
                if (data !== undefined) {
                    fadeImg = new Image();
                    fadeImg.className = 'pnlm-fade-img';
                    fadeImg.style.transition = 'opacity ' + (config.sceneFadeDuration / 1000) + 's';
                    fadeImg.style.width = '100%';
                    fadeImg.style.height = '100%';
                    fadeImg.onload = function() {
                        loadScene(sceneId, targetPitch, targetYaw, targetHfov, true);
                    };
                    fadeImg.src = data;
                    renderContainer.appendChild(fadeImg);
                    renderer.fadeImg = fadeImg;
                    return;
                }
            }

            // Set new pointing
            if (targetPitch === 'same') {
                workingPitch = config.pitch;
            } else {
                workingPitch = targetPitch;
            }
            if (targetYaw === 'same') {
                workingYaw = config.yaw;
            } else if (targetYaw === 'sameAzimuth') {
                workingYaw = config.yaw + (config.northOffset || 0) - (initialConfig.scenes[sceneId].northOffset || 0);
            } else {
                workingYaw = targetYaw;
            }
            if (targetHfov === 'same') {
                workingHfov = config.hfov;
            } else {
                workingHfov = targetHfov;
            }

            // Destroy hot spots from previous scene
            destroyHotSpots();

            // Create the new config for the scene
            mergeConfig(sceneId);

            // Stop motion
            speed.yaw = speed.pitch = speed.hfov = 0;

            // Reload scene
            processOptions();
            if (workingPitch !== undefined) {
                config.pitch = workingPitch;
            }
            if (workingYaw !== undefined) {
                config.yaw = workingYaw;
            }
            if (workingHfov !== undefined) {
                config.hfov = workingHfov;
            }
            fireEvent('scenechange', sceneId);
            load();
        }

        /**
         * Stop using device orientation.
         * @private
         */
        function stopOrientation() {
            window.removeEventListener('deviceorientation', orientationListener);
            controls.orientation.classList.remove('pnlm-orientation-button-active');
            orientation = false;
        }

        /**
         * Start using device orientation.
         * @private
         */
        function startOrientation() {
            if (!orientationSupport)
                return;
            if (typeof DeviceMotionEvent !== 'undefined' &&
                typeof DeviceMotionEvent.requestPermission === 'function') {
                DeviceOrientationEvent.requestPermission().then(function(response) {
                    if (response == 'granted') {
                        orientation = 1;
                        window.addEventListener('deviceorientation', orientationListener);
                        controls.orientation.classList.add('pnlm-orientation-button-active');
                    }
                });
            } else {
                orientation = 1;
                window.addEventListener('deviceorientation', orientationListener);
                controls.orientation.classList.add('pnlm-orientation-button-active');
            }
        }

        /**
         * Escapes HTML string (to mitigate possible DOM XSS attacks).
         * @private
         * @param {string} s - String to escape
         * @returns {string} Escaped string
         */
        function escapeHTML(s) {
            if (!initialConfig.escapeHTML)
                return String(s).split('\n').join('<br>');
            return String(s).split(/&/g).join('&amp;')
                .split('"').join('&quot;')
                .split("'").join('&#39;')
                .split('<').join('&lt;')
                .split('>').join('&gt;')
                .split('/').join('&#x2f;')
                .split('\n').join('<br>');  // Allow line breaks
        }

        /**
         * Removes possibility of XSS attacks with URLs.
         * The URL cannot be of protocol 'javascript'.
         * @private
         * @param {string} url - URL to sanitize
         * @param {boolean} href - True if URL is for link (blocks data URIs)
         * @returns {string} Sanitized URL
         */
        function sanitizeURL(url, href) {
            try {
                var decoded_url = decodeURIComponent(unescape(url)).replace(/[^\w:]/g, '').toLowerCase();
            } catch (e) {
                return 'about:blank';
            }
            if (decoded_url.indexOf('javascript:') === 0 ||
                decoded_url.indexOf('vbscript:') === 0) {
                console.log('Script URL removed.');
                return 'about:blank';
            }
            if (href && decoded_url.indexOf('data:') === 0) {
                console.log('Data URI removed from link.');
                return 'about:blank';
            }
            return url;
        }

        /**
         * Unescapes HTML entities.
         * Copied from Marked.js 0.7.0.
         * @private
         * @param {string} url - URL to sanitize
         * @param {boolean} href - True if URL is for link (blocks data URIs)
         * @returns {string} Sanitized URL
         */
        function unescape(html) {
            // Explicitly match decimal, hex, and named HTML entities
            return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig, function(_, n) {
                n = n.toLowerCase();
                if (n === 'colon') return ':';
                if (n.charAt(0) === '#') {
                    return n.charAt(1) === 'x'
                        ? String.fromCharCode(parseInt(n.substring(2), 16))
                        : String.fromCharCode(+n.substring(1));
                }
                return '';
            });
        }

        /**
         * Removes possibility of XSS atacks with URLs for CSS.
         * The URL will be sanitized with `sanitizeURL()` and single quotes
         * and double quotes escaped.
         * @private
         * @param {string} url - URL to sanitize
         * @returns {string} Sanitized URL
         */
        function sanitizeURLForCss(url) {
            return sanitizeURL(url)
                .replace(/"/g, '%22')
                .replace(/'/g, '%27');
        }

        /**
         * Checks whether or not a panorama is loaded.
         * @memberof Viewer
         * @instance
         * @returns {boolean} `true` if a panorama is loaded, else `false`
         */
        this.isLoaded = function() {
            return Boolean(loaded);
        };

        /**
         * Returns the pitch of the center of the view.
         * @memberof Viewer
         * @instance
         * @returns {number} Pitch in degrees
         */
        this.getPitch = function() {
            return config.pitch;
        };

        /**
         * Sets the pitch of the center of the view.
         * @memberof Viewer
         * @instance
         * @param {number} pitch - Pitch in degrees
         * @param {boolean|number} [animated=1000] - Animation duration in milliseconds or false for no animation
         * @param {function} [callback] - Function to call when animation finishes
         * @param {object} [callbackArgs] - Arguments to pass to callback function
         * @returns {Viewer} `this`
         */
        this.setPitch = function(pitch, animated, callback, callbackArgs) {
            latestInteraction = Date.now();
            if (Math.abs(pitch - config.pitch) <= eps) {
                if (typeof callback == 'function')
                    callback(callbackArgs);
                return this;
            }
            animated = animated == undefined ? 1000: Number(animated);
            if (animated) {
                animatedMove.pitch = {
                    'startTime': Date.now(),
                    'startPosition': config.pitch,
                    'endPosition': pitch,
                    'duration': animated
                };
                if (typeof callback == 'function')
                    setTimeout(function(){callback(callbackArgs);}, animated);
            } else {
                config.pitch = pitch;
            }
            animateInit();
            return this;
        };

        /**
         * Returns the minimum and maximum allowed pitches (in degrees).
         * @memberof Viewer
         * @instance
         * @returns {number[]} [minimum pitch, maximum pitch]
         */
        this.getPitchBounds = function() {
            return [config.minPitch, config.maxPitch];
        };

        /**
         * Set the minimum and maximum allowed pitches (in degrees).
         * @memberof Viewer
         * @instance
         * @param {number[]} bounds - [minimum pitch, maximum pitch]
         * @returns {Viewer} `this`
         */
        this.setPitchBounds = function(bounds) {
            config.minPitch = Math.max(-90, Math.min(bounds[0], 90));
            config.maxPitch = Math.max(-90, Math.min(bounds[1], 90));
            return this;
        };

        /**
         * Returns the yaw of the center of the view.
         * @memberof Viewer
         * @instance
         * @returns {number} Yaw in degrees
         */
        this.getYaw = function() {
            return (config.yaw + 540) % 360 - 180;
        };

        /**
         * Sets the yaw of the center of the view.
         * @memberof Viewer
         * @instance
         * @param {number} yaw - Yaw in degrees [-180, 180]
         * @param {boolean|number} [animated=1000] - Animation duration in milliseconds or false for no animation
         * @param {function} [callback] - Function to call when animation finishes
         * @param {object} [callbackArgs] - Arguments to pass to callback function
         * @returns {Viewer} `this`
         */
        this.setYaw = function(yaw, animated, callback, callbackArgs) {
            latestInteraction = Date.now();
            if (Math.abs(yaw - config.yaw) <= eps) {
                if (typeof callback == 'function')
                    callback(callbackArgs);
                return this;
            }
            animated = animated == undefined ? 1000: Number(animated);
            yaw = ((yaw + 180) % 360) - 180; // Keep in bounds
            if (animated) {
                // Animate in shortest direction
                if (config.yaw - yaw > 180)
                    yaw += 360;
                else if (yaw - config.yaw > 180)
                    yaw -= 360;

                animatedMove.yaw = {
                    'startTime': Date.now(),
                    'startPosition': config.yaw,
                    'endPosition': yaw,
                    'duration': animated
                };
                if (typeof callback == 'function')
                    setTimeout(function(){callback(callbackArgs);}, animated);
            } else {
                config.yaw = yaw;
            }
            animateInit();
            return this;
        };

        /**
         * Returns the minimum and maximum allowed pitches (in degrees).
         * @memberof Viewer
         * @instance
         * @returns {number[]} [yaw pitch, maximum yaw]
         */
        this.getYawBounds = function() {
            return [config.minYaw, config.maxYaw];
        };

        /**
         * Set the minimum and maximum allowed yaws (in degrees [-360, 360]).
         * @memberof Viewer
         * @instance
         * @param {number[]} bounds - [minimum yaw, maximum yaw]
         * @returns {Viewer} `this`
         */
        this.setYawBounds = function(bounds) {
            config.minYaw = Math.max(-360, Math.min(bounds[0], 360));
            config.maxYaw = Math.max(-360, Math.min(bounds[1], 360));
            return this;
        };

        /**
         * Returns the horizontal field of view.
         * @memberof Viewer
         * @instance
         * @returns {number} Horizontal field of view in degrees
         */
        this.getHfov = function() {
            return config.hfov;
        };

        /**
         * Sets the horizontal field of view.
         * @memberof Viewer
         * @instance
         * @param {number} hfov - Horizontal field of view in degrees
         * @param {boolean|number} [animated=1000] - Animation duration in milliseconds or false for no animation
         * @param {function} [callback] - Function to call when animation finishes
         * @param {object} [callbackArgs] - Arguments to pass to callback function
         * @returns {Viewer} `this`
         */
        this.setHfov = function(hfov, animated, callback, callbackArgs) {
            latestInteraction = Date.now();
            if (Math.abs(hfov - config.hfov) <= eps) {
                if (typeof callback == 'function')
                    callback(callbackArgs);
                return this;
            }
            animated = animated == undefined ? 1000: Number(animated);
            if (animated) {
                animatedMove.hfov = {
                    'startTime': Date.now(),
                    'startPosition': config.hfov,
                    'endPosition': constrainHfov(hfov),
                    'duration': animated
                };
                if (typeof callback == 'function')
                    setTimeout(function(){callback(callbackArgs);}, animated);
            } else {
                setHfov(hfov);
            }
            animateInit();
            return this;
        };

        /**
         * Returns the minimum and maximum allowed horizontal fields of view
         * (in degrees).
         * @memberof Viewer
         * @instance
         * @returns {number[]} [minimum hfov, maximum hfov]
         */
        this.getHfovBounds = function() {
            return [config.minHfov, config.maxHfov];
        };

        /**
         * Set the minimum and maximum allowed horizontal fields of view (in degrees).
         * @memberof Viewer
         * @instance
         * @param {number[]} bounds - [minimum hfov, maximum hfov]
         * @returns {Viewer} `this`
         */
        this.setHfovBounds = function(bounds) {
            config.minHfov = Math.max(0, bounds[0]);
            config.maxHfov = Math.max(0, bounds[1]);
            return this;
        };

        /**
         * Set a new view. Any parameters not specified remain the same.
         * @memberof Viewer
         * @instance
         * @param {number} [pitch] - Target pitch
         * @param {number} [yaw] - Target yaw
         * @param {number} [hfov] - Target hfov
         * @param {boolean|number} [animated=1000] - Animation duration in milliseconds or false for no animation
         * @param {function} [callback] - Function to call when animation finishes
         * @param {object} [callbackArgs] - Arguments to pass to callback function
         * @returns {Viewer} `this`
         */
        this.lookAt = function(pitch, yaw, hfov, animated, callback, callbackArgs) {
            animated = animated == undefined ? 1000: Number(animated);
            if (pitch !== undefined && Math.abs(pitch - config.pitch) > eps) {
                this.setPitch(pitch, animated, callback, callbackArgs);
                callback = undefined;
            }
            if (yaw !== undefined && Math.abs(yaw - config.yaw) > eps) {
                this.setYaw(yaw, animated, callback, callbackArgs);
                callback = undefined;
            }
            if (hfov !== undefined && Math.abs(hfov - config.hfov) > eps) {
                this.setHfov(hfov, animated, callback, callbackArgs);
                callback = undefined;
            }
            if (typeof callback == 'function')
                callback(callbackArgs);
            return this;
        };

        /**
         * Returns the panorama's north offset.
         * @memberof Viewer
         * @instance
         * @returns {number} North offset in degrees
         */
        this.getNorthOffset = function() {
            return config.northOffset;
        };

        /**
         * Sets the panorama's north offset.
         * @memberof Viewer
         * @instance
         * @param {number} heading - North offset in degrees
         * @returns {Viewer} `this`
         */
        this.setNorthOffset = function(heading) {
            config.northOffset = Math.min(360, Math.max(0, heading));
            animateInit();
            return this;
        };

        /**
         * Returns the panorama's horizon roll.
         * @memberof Viewer
         * @instance
         * @returns {number} Horizon roll in degrees
         */
        this.getHorizonRoll = function() {
            return config.horizonRoll;
        };

        /**
         * Sets the panorama's horizon roll.
         * @memberof Viewer
         * @instance
         * @param {number} roll - Horizon roll in degrees [-90, 90]
         * @returns {Viewer} `this`
         */
        this.setHorizonRoll = function(roll) {
            config.horizonRoll = Math.min(90, Math.max(-90, roll));
            renderer.setPose(config.horizonPitch * Math.PI / 180, config.horizonRoll * Math.PI / 180);
            animateInit();
            return this;
        };

        /**
         * Returns the panorama's horizon pitch.
         * @memberof Viewer
         * @instance
         * @returns {number} Horizon pitch in degrees
         */
        this.getHorizonPitch = function() {
            return config.horizonPitch;
        };

        /**
         * Sets the panorama's horizon pitch.
         * @memberof Viewer
         * @instance
         * @param {number} pitch - Horizon pitch in degrees [-90, 90]
         * @returns {Viewer} `this`
         */
        this.setHorizonPitch = function(pitch) {
            config.horizonPitch = Math.min(90, Math.max(-90, pitch));
            renderer.setPose(config.horizonPitch * Math.PI / 180, config.horizonRoll * Math.PI / 180);
            animateInit();
            return this;
        };

        /**
         * Start auto rotation.
         *
         * Before starting rotation, the viewer is panned to `pitch`.
         * @memberof Viewer
         * @instance
         * @param {number} [speed] - Auto rotation speed / direction. If not specified, previous value is used.
         * @param {number} [pitch] - The pitch to rotate at. If not specified, inital pitch is used.
         * @returns {Viewer} `this`
         */
        this.startAutoRotate = function(speed, pitch) {
            speed = speed || autoRotateSpeed || 1;
            pitch = pitch === undefined ? origPitch : pitch;
            config.autoRotate = speed;
            _this.lookAt(pitch, undefined, origHfov, 3000);
            animateInit();
            return this;
        };

        /**
         * Stop auto rotation.
         * @memberof Viewer
         * @instance
         * @returns {Viewer} `this`
         */
        this.stopAutoRotate = function() {
            autoRotateSpeed = config.autoRotate ? config.autoRotate : autoRotateSpeed;
            config.autoRotate = false;
            config.autoRotateInactivityDelay = -1;
            return this;
        };

        /**
         * Stops all movement.
         * @memberof Viewer
         * @instance
         */
        this.stopMovement = function() {
            stopAnimation();
            speed = {'yaw': 0, 'pitch': 0, 'hfov': 0};
        };

        /**
         * Returns the panorama renderer.
         * @memberof Viewer
         * @instance
         * @returns {Renderer}
         */
        this.getRenderer = function() {
            return renderer;
        };

        /**
         * Sets update flag for dynamic content.
         * @memberof Viewer
         * @instance
         * @param {boolean} bool - Whether or not viewer should update even when still
         * @returns {Viewer} `this`
         */
        this.setUpdate = function(bool) {
            update = bool === true;
            if (renderer === undefined)
                onImageLoad();
            else
                animateInit();
            return this;
        };

        /**
         * Calculate panorama pitch and yaw from location of mouse event.
         * @memberof Viewer
         * @instance
         * @param {MouseEvent} event - Document mouse down event.
         * @returns {number[]} [pitch, yaw]
         */
        this.mouseEventToCoords = function(event) {
            return mouseEventToCoords(event);
        };

        /**
         * Change scene being viewed.
         * @memberof Viewer
         * @instance
         * @param {string} sceneId - Identifier of scene to switch to.
         * @param {number} [pitch] - Pitch to use with new scene
         * @param {number} [yaw] - Yaw to use with new scene
         * @param {number} [hfov] - HFOV to use with new scene
         * @returns {Viewer} `this`
         */
        this.loadScene = function(sceneId, pitch, yaw, hfov) {
            if (loaded !== false)
                loadScene(sceneId, pitch, yaw, hfov);
            return this;
        };

        /**
         * Get ID of current scene.
         * @memberof Viewer
         * @instance
         * @returns {string} ID of current scene
         */
        this.getScene = function() {
            return config.scene;
        };

        /**
         * Add a new scene.
         * @memberof Viewer
         * @instance
         * @param {string} sceneId - The ID of the new scene
         * @param {string} config - The configuration of the new scene
         * @returns {Viewer} `this`
         */
        this.addScene = function(sceneId, config) {
            initialConfig.scenes[sceneId] = config;
            return this;
        };

        /**
         * Remove a scene.
         * @memberof Viewer
         * @instance
         * @param {string} sceneId - The ID of the scene
         * @returns {boolean} False if the scene is the current scene or if the scene doesn't exists, else true
         */
        this.removeScene = function(sceneId) {
            if (config.scene === sceneId || !initialConfig.scenes.hasOwnProperty(sceneId))
                return false;
            delete initialConfig.scenes[sceneId];
            return true;
        };

        /**
         * Toggle fullscreen.
         * @memberof Viewer
         * @instance
         * @returns {Viewer} `this`
         */
        this.toggleFullscreen = function() {
            toggleFullscreen();
            return this;
        };

        /**
         * Get configuration of current scene.
         * @memberof Viewer
         * @instance
         * @returns {Object} Configuration of current scene
         */
        this.getConfig = function() {
            return config;
        };

        /**
         * Get viewer's container element.
         * @memberof Viewer
         * @instance
         * @returns {HTMLElement} Container `div` element
         */
        this.getContainer = function() {
            return container;
        };

        /**
         * Add a new hot spot.
         * @memberof Viewer
         * @instance
         * @param {Object} hs - The configuration for the hot spot
         * @param {string} [sceneId] - Adds hot spot to specified scene if provided, else to current scene
         * @returns {Viewer} `this`
         * @throws Throws an error if the scene ID is provided but invalid
         */
        this.addHotSpot = function(hs, sceneId) {
            if (sceneId === undefined && config.scene === undefined) {
                // Not a tour
                config.hotSpots.push(hs);
            } else {
                // Tour
                var id = sceneId !== undefined ? sceneId : config.scene;
                if (initialConfig.scenes.hasOwnProperty(id)) {
                    if (!initialConfig.scenes[id].hasOwnProperty('hotSpots')) {
                        initialConfig.scenes[id].hotSpots = []; // Create hot spots array if needed
                        if (id == config.scene)
                            config.hotSpots = initialConfig.scenes[id].hotSpots;    // Link to current config
                    }
                    initialConfig.scenes[id].hotSpots.push(hs); // Add hot spot to config
                } else {
                    throw 'Invalid scene ID!';
                }
            }
            if (sceneId === undefined || config.scene == sceneId) {
                // Add to current scene
                createHotSpot(hs);
                if (loaded)
                    renderHotSpot(hs);
            }
            return this;
        };

        /**
         * Remove a hot spot.
         * @memberof Viewer
         * @instance
         * @param {string} hotSpotId - The ID of the hot spot
         * @param {string} [sceneId] - Removes hot spot from specified scene if provided, else from current scene
         * @returns {boolean} True if deletion is successful, else false
         */
        this.removeHotSpot = function(hotSpotId, sceneId) {
            if (sceneId === undefined || config.scene == sceneId) {
                if (!config.hotSpots)
                    return false;
                for (var i = 0; i < config.hotSpots.length; i++) {
                    if (config.hotSpots[i].hasOwnProperty('id') &&
                        config.hotSpots[i].id === hotSpotId) {
                        // Delete hot spot DOM elements
                        var current = config.hotSpots[i].div;
                        while (current.parentNode != renderContainer)
                            current = current.parentNode;
                        renderContainer.removeChild(current);
                        delete config.hotSpots[i].div;
                        // Remove hot spot from configuration
                        config.hotSpots.splice(i, 1);
                        return true;
                    }
                }
            } else {
                if (initialConfig.scenes.hasOwnProperty(sceneId)) {
                    if (!initialConfig.scenes[sceneId].hasOwnProperty('hotSpots'))
                        return false;
                    for (var j = 0; j < initialConfig.scenes[sceneId].hotSpots.length; j++) {
                        if (initialConfig.scenes[sceneId].hotSpots[j].hasOwnProperty('id') &&
                            initialConfig.scenes[sceneId].hotSpots[j].id === hotSpotId) {
                            // Remove hot spot from configuration
                            initialConfig.scenes[sceneId].hotSpots.splice(j, 1);
                            return true;
                        }
                    }
                } else {
                    return false;
                }
            }
        };

        /**
         * This method should be called if the viewer's container is resized.
         * @memberof Viewer
         * @instance
         */
        this.resize = function() {
            if (renderer)
                onDocumentResize();
        };

        /**
         * Check if a panorama is loaded.
         * @memberof Viewer
         * @instance
         * @returns {boolean} True if a panorama is loaded, else false
         */
        this.isLoaded = function() {
            return loaded;
        };

        /**
         * Check if device orientation control is supported.
         * @memberof Viewer
         * @instance
         * @returns {boolean} True if supported, else false
         */
        this.isOrientationSupported = function() {
            return orientationSupport || false;
        };

        /**
         * Stop using device orientation.
         * @memberof Viewer
         * @instance
         */
        this.stopOrientation = function() {
            stopOrientation();
        };

        /**
         * Start using device orientation (does nothing if not supported).
         * @memberof Viewer
         * @instance
         */
        this.startOrientation = function() {
            if (orientationSupport)
                startOrientation();
        };

        /**
         * Check if device orientation control is currently activated.
         * @memberof Viewer
         * @instance
         * @returns {boolean} True if active, else false
         */
        this.isOrientationActive = function() {
            return Boolean(orientation);
        };

        /**
         * Subscribe listener to specified event.
         * @memberof Viewer
         * @instance
         * @param {string} type - Type of event to subscribe to.
         * @param {Function} listener - Listener function to subscribe to event.
         * @returns {Viewer} `this`
         */
        this.on = function(type, listener) {
            externalEventListeners[type] = externalEventListeners[type] || [];
            externalEventListeners[type].push(listener);
            return this;
        };

        /**
         * Remove an event listener (or listeners).
         * @memberof Viewer
         * @param {string} [type] - Type of event to remove listeners from. If not specified, all listeners are removed.
         * @param {Function} [listener] - Listener function to remove. If not specified, all listeners of specified type are removed.
         * @returns {Viewer} `this`
         */
        this.off = function(type, listener) {
            if (!type) {
                // Remove all listeners if type isn't specified
                externalEventListeners = {};
                return this;
            }
            if (listener) {
                var i = externalEventListeners[type].indexOf(listener);
                if (i >= 0) {
                    // Remove listener if found
                    externalEventListeners[type].splice(i, 1);
                }
                if (externalEventListeners[type].length == 0) {
                    // Remove category if empty
                    delete externalEventListeners[type];
                }
            } else {
                // Remove category of listeners if listener isn't specified
                delete externalEventListeners[type];
            }
            return this;
        };

        /**
         * Fire listeners attached to specified event.
         * @private
         * @param {string} [type] - Type of event to fire listeners for.
         */
        function fireEvent(type) {
            if (type in externalEventListeners) {
                // Reverse iteration is useful, if event listener is removed inside its definition
                for (var i = externalEventListeners[type].length; i > 0; i--) {
                    externalEventListeners[type][externalEventListeners[type].length - i].apply(null, [].slice.call(arguments, 1));
                }
            }
        }

        /**
         * Destructor.
         * @instance
         * @memberof Viewer
         */
        this.destroy = function() {
            destroyed = true;
            clearTimeout(autoRotateStart);

            if (renderer)
                renderer.destroy();
            if (listenersAdded) {
                document.removeEventListener('mousemove', onDocumentMouseMove, false);
                document.removeEventListener('compasschange', onDocumentCompassChange, false);
                document.removeEventListener('mouseup', onDocumentMouseUp, false);
                container.removeEventListener('mozfullscreenchange', onFullScreenChange, false);
                container.removeEventListener('webkitfullscreenchange', onFullScreenChange, false);
                container.removeEventListener('msfullscreenchange', onFullScreenChange, false);
                container.removeEventListener('fullscreenchange', onFullScreenChange, false);
                window.removeEventListener('resize', onDocumentResize, false);
                window.removeEventListener('orientationchange', onDocumentResize, false);
                container.removeEventListener('keydown', onDocumentKeyPress, false);
                container.removeEventListener('keyup', onDocumentKeyUp, false);
                container.removeEventListener('blur', clearKeys, false);
                document.removeEventListener('mouseleave', onDocumentMouseUp, false);
            }
            container.innerHTML = '';
            container.classList.remove('pnlm-container');
        };

    }

    return {
        viewer: function(container, config) {
            return new Viewer(container, config);
        }
    };

})(window, document);
// source --> https://eternize360.com.br/wp-content/plugins/wpvr-pro/public/lib/pannellum/src/js/libpannellum.js?ver=1 
/*
 * libpannellum - A WebGL and CSS 3D transform based Panorama Renderer
 * Copyright (c) 2012-2019 Matthew Petroff
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

window.libpannellum = (function(window, document, undefined) {

'use strict';

/**
 * Creates a new panorama renderer.
 * @constructor
 * @param {HTMLElement} container - The container element for the renderer.
 */
function Renderer(container) {
    var canvas = document.createElement('canvas');
    canvas.style.width = canvas.style.height = '100%';
    container.appendChild(canvas);

    var program, gl, vs, fs;
    var fallbackImgSize;
    var world;
    var vtmps;
    var pose;
    var image, imageType, dynamic;
    var texCoordBuffer, cubeVertBuf, cubeVertTexCoordBuf, cubeVertIndBuf;
    var globalParams;

    /**
     * Initialize renderer.
     * @memberof Renderer
     * @instance
     * @param {Image|Array|Object} image - Input image; format varies based on
     *      `imageType`. For `equirectangular`, this is an image; for
     *      `cubemap`, this is an array of images for the cube faces in the
     *      order [+z, +x, -z, -x, +y, -y]; for `multires`, this is a
     *      configuration object.
     * @param {string} imageType - The type of the image: `equirectangular`,
     *      `cubemap`, or `multires`.
     * @param {boolean} dynamic - Whether or not the image is dynamic (e.g. video).
     * @param {number} haov - Initial horizontal angle of view.
     * @param {number} vaov - Initial vertical angle of view.
     * @param {number} voffset - Initial vertical offset angle.
     * @param {function} callback - Load callback function.
     * @param {Object} [params] - Other configuration parameters (`horizonPitch`, `horizonRoll`, `backgroundColor`).
     */
    this.init = function(_image, _imageType, _dynamic, haov, vaov, voffset, callback, params) {
        // Default argument for image type
        if (_imageType === undefined)
            _imageType = 'equirectangular';

        if (_imageType != 'equirectangular' && _imageType != 'cubemap' &&
            _imageType != 'multires') {
            console.log('Error: invalid image type specified!');
            throw {type: 'config error'};
        }

        imageType = _imageType;
        image = _image;
        dynamic = _dynamic;
        globalParams = params || {};

        // Clear old data
        if (program) {
            if (vs) {
                gl.detachShader(program, vs);
                gl.deleteShader(vs);
            }
            if (fs) {
                gl.detachShader(program, fs);
                gl.deleteShader(fs);
            }
            gl.bindBuffer(gl.ARRAY_BUFFER, null);
            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
            if (program.texture)
                gl.deleteTexture(program.texture);
            if (program.nodeCache)
                for (var i = 0; i < program.nodeCache.length; i++)
                    gl.deleteTexture(program.nodeCache[i].texture);
            gl.deleteProgram(program);
            program = undefined;
        }
        pose = undefined;

        var s;
        var faceMissing = false;
        var cubeImgWidth;
        if (imageType == 'cubemap') {
            for (s = 0; s < 6; s++) {
                if (image[s].width > 0) {
                    if (cubeImgWidth === undefined)
                        cubeImgWidth = image[s].width;
                    if (cubeImgWidth != image[s].width)
                        console.log('Cube faces have inconsistent widths: ' + cubeImgWidth + ' vs. ' + image[s].width);
                } else
                    faceMissing = true;
            }
        }
        function fillMissingFaces(imgSize) {
            if (faceMissing) { // Fill any missing fallback/cubemap faces with background
                var nbytes = imgSize * imgSize * 4; // RGB, plus non-functional alpha
                var imageArray = new Uint8ClampedArray(nbytes);
                var rgb = params.backgroundColor ? params.backgroundColor : [0, 0, 0];
                rgb[0] *= 255;
                rgb[1] *= 255;
                rgb[2] *= 255;
                // Maybe filling could be done faster, see e.g. https://stackoverflow.com/questions/1295584/most-efficient-way-to-create-a-zero-filled-javascript-array
                for (var i = 0; i < nbytes; i++) {
                    imageArray[i++] = rgb[0];
                    imageArray[i++] = rgb[1];
                    imageArray[i++] = rgb[2];
                }
                var backgroundSquare = new ImageData(imageArray, imgSize, imgSize);
                for (s = 0; s < 6; s++) {
                    if (image[s].width == 0)
                        image[s] = backgroundSquare;
                }
            }
        }
        
        // This awful browser specific test exists because iOS 8/9 and IE 11
        // don't display non-power-of-two cubemap textures but also don't
        // throw an error (tested on an iPhone 5c / iOS 8.1.3 / iOS 9.2 /
        // iOS 10.3.1).
        // Therefore, the WebGL context is never created for these browsers for
        // NPOT cubemaps, and the CSS 3D transform fallback renderer is used
        // instead.
        if (!(imageType == 'cubemap' &&
            (cubeImgWidth & (cubeImgWidth - 1)) !== 0 &&
            (navigator.userAgent.toLowerCase().match(/(iphone|ipod|ipad).* os 8_/) ||
            navigator.userAgent.toLowerCase().match(/(iphone|ipod|ipad).* os 9_/) ||
            navigator.userAgent.toLowerCase().match(/(iphone|ipod|ipad).* os 10_/) ||
            navigator.userAgent.match(/Trident.*rv[ :]*11\./)))) {
            // Enable WebGL on canvas
            if (!gl)
                gl = canvas.getContext('experimental-webgl', {alpha: false, depth: false});
            if (gl && gl.getError() == 1286)
                handleWebGLError1286();
        }
        
        // If there is no WebGL, fall back to CSS 3D transform renderer.
        // This will discard the image loaded so far and load the fallback image.
        // While browser specific tests are usually frowned upon, the
        // fallback viewer only really works with WebKit/Blink and IE 10/11
        // (it doesn't work properly in Firefox).
        if (!gl && ((imageType == 'multires' && image.hasOwnProperty('fallbackPath')) ||
            imageType == 'cubemap') &&
            ('WebkitAppearance' in document.documentElement.style ||
            navigator.userAgent.match(/Trident.*rv[ :]*11\./) ||
            navigator.appVersion.indexOf('MSIE 10') !== -1)) {
            // Remove old world if it exists
            if (world) {
                container.removeChild(world);
            }
            
            // Initialize renderer
            world = document.createElement('div');
            world.className = 'pnlm-world';
            
            // Add images
            var path;
            if (image.basePath) {
                path = image.basePath + image.fallbackPath;
            } else {
                path = image.fallbackPath;
            }
            var sides = ['f', 'r', 'b', 'l', 'u', 'd'];
            var loaded = 0;
            var onLoad = function() {
                // Draw image on canvas
                var faceCanvas = document.createElement('canvas');
                faceCanvas.className = 'pnlm-face pnlm-' + sides[this.side] + 'face';
                world.appendChild(faceCanvas);
                var faceContext = faceCanvas.getContext('2d');
                faceCanvas.style.width = this.width + 4 + 'px';
                faceCanvas.style.height = this.height + 4 + 'px';
                faceCanvas.width = this.width + 4;
                faceCanvas.height = this.height + 4;
                faceContext.drawImage(this, 2, 2);
                var imgData = faceContext.getImageData(0, 0, faceCanvas.width, faceCanvas.height);
                var data = imgData.data;
                
                // Duplicate edge pixels
                var i;
                var j;
                for (i = 2; i < faceCanvas.width - 2; i++) {
                    for (j = 0; j < 4; j++) {
                        data[(i + faceCanvas.width) * 4 + j] = data[(i + faceCanvas.width * 2) * 4 + j];
                        data[(i + faceCanvas.width * (faceCanvas.height - 2)) * 4 + j] = data[(i + faceCanvas.width * (faceCanvas.height - 3)) * 4 + j];
                    }
                }
                for (i = 2; i < faceCanvas.height - 2; i++) {
                    for (j = 0; j < 4; j++) {
                        data[(i * faceCanvas.width + 1) * 4 + j] = data[(i * faceCanvas.width + 2) * 4 + j];
                        data[((i + 1) * faceCanvas.width - 2) * 4 + j] = data[((i + 1) * faceCanvas.width - 3) * 4 + j];
                    }
                }
                for (j = 0; j < 4; j++) {
                    data[(faceCanvas.width + 1) * 4 + j] = data[(faceCanvas.width * 2 + 2) * 4 + j];
                    data[(faceCanvas.width * 2 - 2) * 4 + j] = data[(faceCanvas.width * 3 - 3) * 4 + j];
                    data[(faceCanvas.width * (faceCanvas.height - 2) + 1) * 4 + j] = data[(faceCanvas.width * (faceCanvas.height - 3) + 2) * 4 + j];
                    data[(faceCanvas.width * (faceCanvas.height - 1) - 2) * 4 + j] = data[(faceCanvas.width * (faceCanvas.height - 2) - 3) * 4 + j];
                }
                for (i = 1; i < faceCanvas.width - 1; i++) {
                    for (j = 0; j < 4; j++) {
                        data[i * 4 + j] = data[(i + faceCanvas.width) * 4 + j];
                        data[(i + faceCanvas.width * (faceCanvas.height - 1)) * 4 + j] = data[(i + faceCanvas.width * (faceCanvas.height - 2)) * 4 + j];
                    }
                }
                for (i = 1; i < faceCanvas.height - 1; i++) {
                    for (j = 0; j < 4; j++) {
                        data[(i * faceCanvas.width) * 4 + j] = data[(i * faceCanvas.width + 1) * 4 + j];
                        data[((i + 1) * faceCanvas.width - 1) * 4 + j] = data[((i + 1) * faceCanvas.width - 2) * 4 + j];
                    }
                }
                for (j = 0; j < 4; j++) {
                    data[j] = data[(faceCanvas.width + 1) * 4 + j];
                    data[(faceCanvas.width - 1) * 4 + j] = data[(faceCanvas.width * 2 - 2) * 4 + j];
                    data[(faceCanvas.width * (faceCanvas.height - 1)) * 4 + j] = data[(faceCanvas.width * (faceCanvas.height - 2) + 1) * 4 + j];
                    data[(faceCanvas.width * faceCanvas.height - 1) * 4 + j] = data[(faceCanvas.width * (faceCanvas.height - 1) - 2) * 4 + j];
                }
                
                // Draw image width duplicated edge pixels on canvas
                faceContext.putImageData(imgData, 0, 0);
                
                incLoaded.call(this);
            };
            var incLoaded = function() {
                if (this.width > 0) {
                    if (fallbackImgSize === undefined)
                        fallbackImgSize = this.width;
                    if (fallbackImgSize != this.width)
                        console.log('Fallback faces have inconsistent widths: ' + fallbackImgSize + ' vs. ' + this.width);
                } else
                    faceMissing = true;
                loaded++;
                if (loaded == 6) {
                    fallbackImgSize = this.width;
                    container.appendChild(world);
                    callback();
                }
            };
            faceMissing = false;
            for (s = 0; s < 6; s++) {
                var faceImg = new Image();
                faceImg.crossOrigin = globalParams.crossOrigin ? globalParams.crossOrigin : 'anonymous';
                faceImg.side = s;
                faceImg.onload = onLoad;
                faceImg.onerror = incLoaded; // ignore missing face to support partial fallback image
                if (imageType == 'multires') {
                    faceImg.src = path.replace('%s', sides[s]) + '.' + image.extension;
                } else {
                    faceImg.src = image[s].src;
                }
            }
            fillMissingFaces(fallbackImgSize);
            return;
        } else if (!gl) {
            console.log('Error: no WebGL support detected!');
            throw {type: 'no webgl'};
        }
        if (imageType == 'cubemap')
            fillMissingFaces(cubeImgWidth);
        if (image.basePath) {
            image.fullpath = image.basePath + image.path;
        } else {
            image.fullpath = image.path;
        }
        image.invTileResolution = 1 / image.tileResolution;
        
        var vertices = createCube();
        vtmps = [];
        for (s = 0; s < 6; s++) {
            vtmps[s] = vertices.slice(s * 12, s * 12 + 12);
            vertices = createCube();
        }
        
        // Make sure image isn't too big
        var maxWidth = 0;
        if (imageType == 'equirectangular') {
            maxWidth = gl.getParameter(gl.MAX_TEXTURE_SIZE);
            if (Math.max(image.width / 2, image.height) > maxWidth) {
                console.log('Error: The image is too big; it\'s ' + image.width + 'px wide, '+
                            'but this device\'s maximum supported size is ' + (maxWidth * 2) + 'px.');
                throw {type: 'webgl size error', width: image.width, maxWidth: maxWidth * 2};
            }
        } else if (imageType == 'cubemap') {
            if (cubeImgWidth > gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE)) {
                console.log('Error: The image is too big; it\'s ' + cubeImgWidth + 'px wide, ' +
                            'but this device\'s maximum supported size is ' + maxWidth + 'px.');
                throw {type: 'webgl size error', width: cubeImgWidth, maxWidth: maxWidth};
            }
        }

        // Store horizon pitch and roll if applicable
        if (params !== undefined && (params.horizonPitch !== undefined || params.horizonRoll !== undefined))
            pose = [params.horizonPitch == undefined ? 0 : params.horizonPitch,
                    params.horizonRoll == undefined ? 0 : params.horizonRoll];

        // Set 2d texture binding
        var glBindType = gl.TEXTURE_2D;

        // Create viewport for entire canvas
        gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);

        // Check precision support
        if (gl.getShaderPrecisionFormat) {
            var precision = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT);
            if (precision && precision.precision < 1) {
                // `highp` precision not supported; https://stackoverflow.com/a/33308927
                fragEquiCubeBase = fragEquiCubeBase.replace('highp', 'mediump');
            }
        }

        // Create vertex shader
        vs = gl.createShader(gl.VERTEX_SHADER);
        var vertexSrc = v;
        if (imageType == 'multires') {
            vertexSrc = vMulti;
        }
        gl.shaderSource(vs, vertexSrc);
        gl.compileShader(vs);

        // Create fragment shader
        fs = gl.createShader(gl.FRAGMENT_SHADER);
        var fragmentSrc = fragEquirectangular;
        if (imageType == 'cubemap') {
            glBindType = gl.TEXTURE_CUBE_MAP;
            fragmentSrc = fragCube;
        } else if (imageType == 'multires') {
            fragmentSrc = fragMulti;
        }
        gl.shaderSource(fs, fragmentSrc);
        gl.compileShader(fs);

        // Link WebGL program
        program = gl.createProgram();
        gl.attachShader(program, vs);
        gl.attachShader(program, fs);
        gl.linkProgram(program);

        // Log errors
        if (!gl.getShaderParameter(vs, gl.COMPILE_STATUS))
            console.log(gl.getShaderInfoLog(vs));
        if (!gl.getShaderParameter(fs, gl.COMPILE_STATUS))
            console.log(gl.getShaderInfoLog(fs));
        if (!gl.getProgramParameter(program, gl.LINK_STATUS))
            console.log(gl.getProgramInfoLog(program));

        // Use WebGL program
        gl.useProgram(program);

        program.drawInProgress = false;

        // Set background clear color (does not apply to cubemap/fallback image)
        var color = params.backgroundColor ? params.backgroundColor : [0, 0, 0];
        gl.clearColor(color[0], color[1], color[2], 1.0);
        gl.clear(gl.COLOR_BUFFER_BIT);

        // Look up texture coordinates location
        program.texCoordLocation = gl.getAttribLocation(program, 'a_texCoord');
        gl.enableVertexAttribArray(program.texCoordLocation);

        if (imageType != 'multires') {
            // Provide texture coordinates for rectangle
            if (!texCoordBuffer)
                texCoordBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1,1,1,1,1,-1,-1,1,1,-1,-1,-1]), gl.STATIC_DRAW);
            gl.vertexAttribPointer(program.texCoordLocation, 2, gl.FLOAT, false, 0, 0);

            // Pass aspect ratio
            program.aspectRatio = gl.getUniformLocation(program, 'u_aspectRatio');
            gl.uniform1f(program.aspectRatio, gl.drawingBufferWidth / gl.drawingBufferHeight);

            // Locate psi, theta, focal length, horizontal extent, vertical extent, and vertical offset
            program.psi = gl.getUniformLocation(program, 'u_psi');
            program.theta = gl.getUniformLocation(program, 'u_theta');
            program.f = gl.getUniformLocation(program, 'u_f');
            program.h = gl.getUniformLocation(program, 'u_h');
            program.v = gl.getUniformLocation(program, 'u_v');
            program.vo = gl.getUniformLocation(program, 'u_vo');
            program.rot = gl.getUniformLocation(program, 'u_rot');

            // Pass horizontal extent, vertical extent, and vertical offset
            gl.uniform1f(program.h, haov / (Math.PI * 2.0));
            gl.uniform1f(program.v, vaov / Math.PI);
            gl.uniform1f(program.vo, voffset / Math.PI * 2);

            // Set background color
            if (imageType == 'equirectangular') {
                program.backgroundColor = gl.getUniformLocation(program, 'u_backgroundColor');
                gl.uniform4fv(program.backgroundColor, color.concat([1]));
            }

            // Create texture
            program.texture = gl.createTexture();
            gl.bindTexture(glBindType, program.texture);

            // Upload images to texture depending on type
            if (imageType == 'cubemap') {
                // Load all six sides of the cube map
                gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image[1]);
                gl.texImage2D(gl.TEXTURE_CUBE_MAP_NEGATIVE_X, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image[3]);
                gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_Y, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image[4]);
                gl.texImage2D(gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image[5]);
                gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_Z, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image[0]);
                gl.texImage2D(gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image[2]);
            } else {
                if (image.width <= maxWidth) {
                    gl.uniform1i(gl.getUniformLocation(program, 'u_splitImage'), 0);
                    // Upload image to the texture
                    gl.texImage2D(glBindType, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image);
                } else {
                    // Image needs to be split into two parts due to texture size limits
                    gl.uniform1i(gl.getUniformLocation(program, 'u_splitImage'), 1);

                    // Draw image on canvas
                    var cropCanvas = document.createElement('canvas');
                    cropCanvas.width = image.width / 2;
                    cropCanvas.height = image.height;
                    var cropContext = cropCanvas.getContext('2d');
                    cropContext.drawImage(image, 0, 0);

                    // Upload first half of image to the texture
                    var cropImage = cropContext.getImageData(0, 0, image.width / 2, image.height);
                    gl.texImage2D(glBindType, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, cropImage);

                    // Create and bind texture for second half of image
                    program.texture2 = gl.createTexture();
                    gl.activeTexture(gl.TEXTURE1);
                    gl.bindTexture(glBindType, program.texture2);
                    gl.uniform1i(gl.getUniformLocation(program, 'u_image1'), 1);

                    // Upload second half of image to the texture
                    cropContext.drawImage(image, -image.width / 2, 0);
                    cropImage = cropContext.getImageData(0, 0, image.width / 2, image.height);
                    gl.texImage2D(glBindType, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, cropImage);

                    // Set parameters for rendering any size
                    gl.texParameteri(glBindType, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
                    gl.texParameteri(glBindType, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
                    gl.texParameteri(glBindType, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
                    gl.texParameteri(glBindType, gl.TEXTURE_MAG_FILTER, gl.LINEAR);

                    // Reactivate first texture unit
                    gl.activeTexture(gl.TEXTURE0);
                }
            }

            // Set parameters for rendering any size
            gl.texParameteri(glBindType, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
            gl.texParameteri(glBindType, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
            gl.texParameteri(glBindType, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
            gl.texParameteri(glBindType, gl.TEXTURE_MAG_FILTER, gl.LINEAR);

        } else {
            // Look up vertex coordinates location
            program.vertPosLocation = gl.getAttribLocation(program, 'a_vertCoord');
            gl.enableVertexAttribArray(program.vertPosLocation);

            // Create buffers
            if (!cubeVertBuf)
                cubeVertBuf = gl.createBuffer();
            if (!cubeVertTexCoordBuf)
                cubeVertTexCoordBuf = gl.createBuffer();
            if (!cubeVertIndBuf)
                cubeVertIndBuf = gl.createBuffer();

            // Bind texture coordinate buffer and pass coordinates to WebGL
            gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertTexCoordBuf);
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0,0,1,0,1,1,0,1]), gl.STATIC_DRAW);

            // Bind square index buffer and pass indicies to WebGL
            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertIndBuf);
            gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0,1,2,0,2,3]), gl.STATIC_DRAW);

            // Find uniforms
            program.perspUniform = gl.getUniformLocation(program, 'u_perspMatrix');
            program.cubeUniform = gl.getUniformLocation(program, 'u_cubeMatrix');
            //program.colorUniform = gl.getUniformLocation(program, 'u_color');

            program.level = -1;

            program.currentNodes = [];
            program.nodeCache = [];
            program.nodeCacheTimestamp = 0;
        }

        // Check if there was an error
        var err = gl.getError();
        if (err !== 0) {
            console.log('Error: Something went wrong with WebGL!', err);
            throw {type: 'webgl error'};
        }

        callback();
     };

    /**
     * Destroy renderer.
     * @memberof Renderer
     * @instance
     */
    this.destroy = function() {
        if (container !== undefined) {
            if (canvas !== undefined && container.contains(canvas)) {
                container.removeChild(canvas);
            }
            if (world !== undefined && container.contains(world)) {
                container.removeChild(world);
            }
        }
        if (gl) {
            // The spec says this is only supposed to simulate losing the WebGL
            // context, but in practice it tends to actually free the memory.
            var extension = gl.getExtension('WEBGL_lose_context');
            if (extension)
                extension.loseContext();
        }
    };

    /**
     * Resize renderer (call after resizing container).
     * @memberof Renderer
     * @instance
     */
    this.resize = function() {
        var pixelRatio = window.devicePixelRatio || 1;
        canvas.width = canvas.clientWidth * pixelRatio;
        canvas.height = canvas.clientHeight * pixelRatio;
        if (gl) {
            if (gl.getError() == 1286)
                handleWebGLError1286();
            gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
            if (imageType != 'multires') {
                gl.uniform1f(program.aspectRatio, canvas.clientWidth / canvas.clientHeight);
            }
        }
    };
    // Initialize canvas size
    this.resize();

    /**
     * Set renderer horizon pitch and roll.
     * @memberof Renderer
     * @instance
     */
    this.setPose = function(horizonPitch, horizonRoll) {
        pose = [horizonPitch, horizonRoll];
    };

    /**
     * Render new view of panorama.
     * @memberof Renderer
     * @instance
     * @param {number} pitch - Pitch to render at (in radians).
     * @param {number} yaw - Yaw to render at (in radians).
     * @param {number} hfov - Horizontal field of view to render with (in radians).
     * @param {Object} [params] - Extra configuration parameters. 
     * @param {number} [params.roll] - Camera roll (in radians).
     * @param {boolean} [params.returnImage] - Return rendered image?
     */
    this.render = function(pitch, yaw, hfov, params) {
        var focal, i, s, roll = 0;
        if (params === undefined)
            params = {};
        if (params.roll)
            roll = params.roll;

        // Apply pitch and roll transformation if applicable
        if (pose !== undefined) {
            var horizonPitch = pose[0],
                horizonRoll = pose[1];

            // Calculate new pitch and yaw
            var orig_pitch = pitch,
                orig_yaw = yaw,
                x = Math.cos(horizonRoll) * Math.sin(pitch) * Math.sin(horizonPitch) +
                    Math.cos(pitch) * (Math.cos(horizonPitch) * Math.cos(yaw) +
                    Math.sin(horizonRoll) * Math.sin(horizonPitch) * Math.sin(yaw)),
                y = -Math.sin(pitch) * Math.sin(horizonRoll) +
                    Math.cos(pitch) * Math.cos(horizonRoll) * Math.sin(yaw),
                z = Math.cos(horizonRoll) * Math.cos(horizonPitch) * Math.sin(pitch) +
                    Math.cos(pitch) * (-Math.cos(yaw) * Math.sin(horizonPitch) +
                    Math.cos(horizonPitch) * Math.sin(horizonRoll) * Math.sin(yaw));
            pitch = Math.asin(Math.max(Math.min(z, 1), -1));
            yaw = Math.atan2(y, x);

            // Calculate roll
            var v = [Math.cos(orig_pitch) * (Math.sin(horizonRoll) * Math.sin(horizonPitch) * Math.cos(orig_yaw) -
                    Math.cos(horizonPitch) * Math.sin(orig_yaw)),
                    Math.cos(orig_pitch) * Math.cos(horizonRoll) * Math.cos(orig_yaw),
                    Math.cos(orig_pitch) * (Math.cos(horizonPitch) * Math.sin(horizonRoll) * Math.cos(orig_yaw) +
                    Math.sin(orig_yaw) * Math.sin(horizonPitch))],
                w = [-Math.cos(pitch) * Math.sin(yaw), Math.cos(pitch) * Math.cos(yaw)];
            var roll_adj = Math.acos(Math.max(Math.min((v[0]*w[0] + v[1]*w[1]) /
                (Math.sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]) *
                Math.sqrt(w[0]*w[0]+w[1]*w[1])), 1), -1));
            if (v[2] < 0)
                roll_adj = 2 * Math.PI - roll_adj;
            roll += roll_adj;
        }

        // If no WebGL
        if (!gl && (imageType == 'multires' || imageType == 'cubemap')) {
            // Determine face transforms
            s = fallbackImgSize / 2;
            
            var transforms = {
                f: 'translate3d(-' + (s + 2) + 'px, -' + (s + 2) + 'px, -' + s + 'px)',
                b: 'translate3d(' + (s + 2) + 'px, -' + (s + 2) + 'px, ' + s + 'px) rotateX(180deg) rotateZ(180deg)',
                u: 'translate3d(-' + (s + 2) + 'px, -' + s + 'px, ' + (s + 2) + 'px) rotateX(270deg)',
                d: 'translate3d(-' + (s + 2) + 'px, ' + s + 'px, -' + (s + 2) + 'px) rotateX(90deg)',
                l: 'translate3d(-' + s + 'px, -' + (s + 2) + 'px, ' + (s + 2) + 'px) rotateX(180deg) rotateY(90deg) rotateZ(180deg)',
                r: 'translate3d(' + s + 'px, -' + (s + 2) + 'px, -' + (s + 2) + 'px) rotateY(270deg)'
            };
            focal = 1 / Math.tan(hfov / 2);
            var zoom = focal * canvas.clientWidth / 2 + 'px';
            var transform = 'perspective(' + zoom + ') translateZ(' + zoom + ') rotateX(' + pitch + 'rad) rotateY(' + yaw + 'rad) ';
            
            // Apply face transforms
            var faces = Object.keys(transforms);
            for (i = 0; i < 6; i++) {
                var face = world.querySelector('.pnlm-' + faces[i] + 'face');
                if (!face)
                    continue; // ignore missing face to support partial cubemap/fallback image
                face.style.webkitTransform = transform + transforms[faces[i]];
                face.style.transform = transform + transforms[faces[i]];
            }
            return;
        }
        
        if (imageType != 'multires') {
            // Calculate focal length from vertical field of view
            var vfov = 2 * Math.atan(Math.tan(hfov * 0.5) / (gl.drawingBufferWidth / gl.drawingBufferHeight));
            focal = 1 / Math.tan(vfov * 0.5);

            // Pass psi, theta, roll, and focal length
            gl.uniform1f(program.psi, yaw);
            gl.uniform1f(program.theta, pitch);
            gl.uniform1f(program.rot, roll);
            gl.uniform1f(program.f, focal);
            
            if (dynamic === true) {
                // Update texture if dynamic
                if (imageType == 'equirectangular') {
                    gl.bindTexture(gl.TEXTURE_2D, program.texture);
                    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image);
                }
            }
            
            // Draw using current buffer
            gl.drawArrays(gl.TRIANGLES, 0, 6);
        
        } else {
            // Create perspective matrix
            var perspMatrix = makePersp(hfov, gl.drawingBufferWidth / gl.drawingBufferHeight, 0.1, 100.0);
            
            // Find correct zoom level
            checkZoom(hfov);
            
            // Create rotation matrix
            var matrix = identityMatrix3();
            matrix = rotateMatrix(matrix, -roll, 'z');
            matrix = rotateMatrix(matrix, -pitch, 'x');
            matrix = rotateMatrix(matrix, yaw, 'y');
            matrix = makeMatrix4(matrix);
            
            // Set matrix uniforms
            gl.uniformMatrix4fv(program.perspUniform, false, new Float32Array(transposeMatrix4(perspMatrix)));
            gl.uniformMatrix4fv(program.cubeUniform, false, new Float32Array(transposeMatrix4(matrix)));
            
            // Find current nodes
            var rotPersp = rotatePersp(perspMatrix, matrix);
            program.nodeCache.sort(multiresNodeSort);
            if (program.nodeCache.length > 200 &&
                program.nodeCache.length > program.currentNodes.length + 50) {
                // Remove older nodes from cache
                var removed = program.nodeCache.splice(200, program.nodeCache.length - 200);
                for (var j = 0; j < removed.length; j++) {
                    // Explicitly delete textures
                    gl.deleteTexture(removed[j].texture);
                }
            }
            program.currentNodes = [];
            
            var sides = ['f', 'b', 'u', 'd', 'l', 'r'];
            for (s = 0; s < 6; s++) {
                var ntmp = new MultiresNode(vtmps[s], sides[s], 1, 0, 0, image.fullpath);
                testMultiresNode(rotPersp, ntmp, pitch, yaw, hfov);
            }
            
            program.currentNodes.sort(multiresNodeRenderSort);
            
            // Unqueue any pending requests for nodes that are no longer visible
            for (i = pendingTextureRequests.length - 1; i >= 0; i--) {
                if (program.currentNodes.indexOf(pendingTextureRequests[i].node) === -1) {
                    pendingTextureRequests[i].node.textureLoad = false;
                    pendingTextureRequests.splice(i, 1);
                }
            }
            
            // Allow one request to be pending, so that we can create a texture buffer for that in advance of loading actually beginning
            if (pendingTextureRequests.length === 0) {
                for (i = 0; i < program.currentNodes.length; i++) {
                    var node = program.currentNodes[i];
                    if (!node.texture && !node.textureLoad) {
                        node.textureLoad = true;
            
                        setTimeout(processNextTile, 0, node);
                        
                        // Only process one tile per frame to improve responsiveness
                        break;
                    }
                }
            }
            
            // Draw tiles
            multiresDraw();
        }
        
        if (params.returnImage !== undefined) {
            return canvas.toDataURL('image/png');
        }
    };
    
    /**
     * Check if images are loading.
     * @memberof Renderer
     * @instance
     * @returns {boolean} Whether or not images are loading.
     */
    this.isLoading = function() {
        if (gl && imageType == 'multires') {
            for ( var i = 0; i < program.currentNodes.length; i++ ) {
                if (!program.currentNodes[i].textureLoaded) {
                    return true;
                }
            }
        }
        return false;
    };
    
    /**
     * Retrieve renderer's canvas.
     * @memberof Renderer
     * @instance
     * @returns {HTMLElement} Renderer's canvas.
     */
    this.getCanvas = function() {
        return canvas;
    };
    
    /**
     * Sorting method for multires nodes.
     * @private
     * @param {MultiresNode} a - First node.
     * @param {MultiresNode} b - Second node.
     * @returns {number} Base tiles first, then higher timestamp first.
     */
    function multiresNodeSort(a, b) {
        // Base tiles are always first
        if (a.level == 1 && b.level != 1) {
            return -1;
        }
        if (b. level == 1 && a.level != 1) {
            return 1;
        }
        
        // Higher timestamp first
        return b.timestamp - a.timestamp;
    }
    
    /**
     * Sorting method for multires node rendering.
     * @private
     * @param {MultiresNode} a - First node.
     * @param {MultiresNode} b - Second node.
     * @returns {number} Lower zoom levels first, then closest to center first.
     */
    function multiresNodeRenderSort(a, b) {
        // Lower zoom levels first
        if (a.level != b.level) {
            return a.level - b.level;
        }
        
        // Lower distance from center first
        return a.diff - b.diff;
    }
    
    /**
     * Draws multires nodes.
     * @private
     */
    function multiresDraw() {
        if (!program.drawInProgress) {
            program.drawInProgress = true;
            gl.clear(gl.COLOR_BUFFER_BIT);
            for ( var i = 0; i < program.currentNodes.length; i++ ) {
                if (program.currentNodes[i].textureLoaded > 1) {
                    //var color = program.currentNodes[i].color;
                    //gl.uniform4f(program.colorUniform, color[0], color[1], color[2], 1.0);
                    
                    // Bind vertex buffer and pass vertices to WebGL
                    gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertBuf);
                    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(program.currentNodes[i].vertices), gl.STATIC_DRAW);
                    gl.vertexAttribPointer(program.vertPosLocation, 3, gl.FLOAT, false, 0, 0);
                    
                    // Prep for texture
                    gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertTexCoordBuf);
                    gl.vertexAttribPointer(program.texCoordLocation, 2, gl.FLOAT, false, 0, 0);
                    
                    // Bind texture and draw tile
                    gl.bindTexture(gl.TEXTURE_2D, program.currentNodes[i].texture); // Bind program.currentNodes[i].texture to TEXTURE0
                    gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);
                }
            }
            program.drawInProgress = false;
        }
    }

    /**
     * Creates new multires node.
     * @constructor
     * @private
     * @param {number[]} vertices - Node's verticies.
     * @param {string} side - Node's cube face.
     * @param {number} level - Node's zoom level.
     * @param {number} x - Node's x position.
     * @param {number} y - Node's y position.
     * @param {string} path - Node's path.
     */
    function MultiresNode(vertices, side, level, x, y, path) {
        this.vertices = vertices;
        this.side = side;
        this.level = level;
        this.x = x;
        this.y = y;
        this.path = path.replace('%s',side).replace('%l',level).replace('%x',x).replace('%y',y);
    }

    /**
     * Test if multires node is visible. If it is, add it to current nodes,
     * load its texture, and load appropriate child nodes.
     * @private
     * @param {number[]} rotPersp - Rotated perspective matrix.
     * @param {MultiresNode} node - Multires node to check.
     * @param {number} pitch - Pitch to check at.
     * @param {number} yaw - Yaw to check at.
     * @param {number} hfov - Horizontal field of view to check at.
     */
    function testMultiresNode(rotPersp, node, pitch, yaw, hfov) {
        if (checkSquareInView(rotPersp, node.vertices)) {
            // Calculate central angle between center of view and center of tile
            var v = node.vertices;
            var x = v[0] + v[3] + v[6] + v[ 9];
            var y = v[1] + v[4] + v[7] + v[10];
            var z = v[2] + v[5] + v[8] + v[11];
            var r = Math.sqrt(x*x + y*y + z*z);
            var theta = Math.asin(z / r);
            var phi = Math.atan2(y, x);
            var ydiff = phi - yaw;
            ydiff += (ydiff > Math.PI) ? -2 * Math.PI : (ydiff < -Math.PI) ? 2 * Math.PI : 0;
            ydiff = Math.abs(ydiff);
            node.diff = Math.acos(Math.sin(pitch) * Math.sin(theta) + Math.cos(pitch) * Math.cos(theta) * Math.cos(ydiff));
            
            // Add node to current nodes and load texture if needed
            var inCurrent = false;
            for (var k = 0; k < program.nodeCache.length; k++) {
                if (program.nodeCache[k].path == node.path) {
                    inCurrent = true;
                    program.nodeCache[k].timestamp = program.nodeCacheTimestamp++;
                    program.nodeCache[k].diff = node.diff;
                    program.currentNodes.push(program.nodeCache[k]);
                    break;
                }
            }
            if (!inCurrent) {
                //node.color = [Math.random(), Math.random(), Math.random()];
                node.timestamp = program.nodeCacheTimestamp++;
                program.currentNodes.push(node);
                program.nodeCache.push(node);
            }
            
            // TODO: Test error
            // Create child nodes
            if (node.level < program.level) {
                var cubeSize = image.cubeResolution * Math.pow(2, node.level - image.maxLevel);
                var numTiles = Math.ceil(cubeSize * image.invTileResolution) - 1;
                var doubleTileSize = cubeSize % image.tileResolution * 2;
                var lastTileSize = (cubeSize * 2) % image.tileResolution;
                if (lastTileSize === 0) {
                    lastTileSize = image.tileResolution;
                }
                if (doubleTileSize === 0) {
                    doubleTileSize = image.tileResolution * 2;
                }
                var f = 0.5;
                if (node.x == numTiles || node.y == numTiles) {
                    f = 1.0 - image.tileResolution / (image.tileResolution + lastTileSize);
                }
                var i = 1.0 - f;
                var children = [];
                var vtmp, ntmp;
                var f1 = f, f2 = f, f3 = f, i1 = i, i2 = i, i3 = i;
                // Handle non-symmetric tiles
                if (lastTileSize < image.tileResolution) {
                    if (node.x == numTiles && node.y != numTiles) {
                        f2 = 0.5;
                        i2 = 0.5;
                        if (node.side == 'd' || node.side == 'u') {
                            f3 = 0.5;
                            i3 = 0.5;
                        }
                    } else if (node.x != numTiles && node.y == numTiles) {
                        f1 = 0.5;
                        i1 = 0.5;
                        if (node.side == 'l' || node.side == 'r') {
                            f3 = 0.5;
                            i3 = 0.5;
                        }
                    }
                }
                // Handle small tiles that have fewer than four children
                if (doubleTileSize <= image.tileResolution) {
                    if (node.x == numTiles) {
                        f1 = 0;
                        i1 = 1;
                        if (node.side == 'l' || node.side == 'r') {
                            f3 = 0;
                            i3 = 1;
                        }
                    }
                    if (node.y == numTiles) {
                        f2 = 0;
                        i2 = 1;
                        if (node.side == 'd' || node.side == 'u') {
                            f3 = 0;
                            i3 = 1;
                        }
                    }
                }
                
                vtmp = [           v[0],             v[1],             v[2],
                        v[0]*f1+v[3]*i1,    v[1]*f+v[4]*i,  v[2]*f3+v[5]*i3,
                        v[0]*f1+v[6]*i1,  v[1]*f2+v[7]*i2,  v[2]*f3+v[8]*i3,
                          v[0]*f+v[9]*i, v[1]*f2+v[10]*i2, v[2]*f3+v[11]*i3
                ];
                ntmp = new MultiresNode(vtmp, node.side, node.level + 1, node.x*2, node.y*2, image.fullpath);
                children.push(ntmp);
                if (!(node.x == numTiles && doubleTileSize <= image.tileResolution)) {
                    vtmp = [v[0]*f1+v[3]*i1,    v[1]*f+v[4]*i,  v[2]*f3+v[5]*i3,
                                       v[3],             v[4],             v[5],
                              v[3]*f+v[6]*i,  v[4]*f2+v[7]*i2,  v[5]*f3+v[8]*i3,
                            v[0]*f1+v[6]*i1,  v[1]*f2+v[7]*i2,  v[2]*f3+v[8]*i3
                    ];
                    ntmp = new MultiresNode(vtmp, node.side, node.level + 1, node.x*2+1, node.y*2, image.fullpath);
                    children.push(ntmp);
                }
                if (!(node.x == numTiles && doubleTileSize <= image.tileResolution) &&
                    !(node.y == numTiles && doubleTileSize <= image.tileResolution)) {
                    vtmp = [v[0]*f1+v[6]*i1,  v[1]*f2+v[7]*i2,  v[2]*f3+v[8]*i3,
                              v[3]*f+v[6]*i,  v[4]*f2+v[7]*i2,  v[5]*f3+v[8]*i3,
                                       v[6],             v[7],             v[8],
                            v[9]*f1+v[6]*i1,   v[10]*f+v[7]*i, v[11]*f3+v[8]*i3
                    ];
                    ntmp = new MultiresNode(vtmp, node.side, node.level + 1, node.x*2+1, node.y*2+1, image.fullpath);
                    children.push(ntmp);
                }
                if (!(node.y == numTiles && doubleTileSize <= image.tileResolution)) {
                    vtmp = [  v[0]*f+v[9]*i, v[1]*f2+v[10]*i2, v[2]*f3+v[11]*i3,
                            v[0]*f1+v[6]*i1,  v[1]*f2+v[7]*i2,  v[2]*f3+v[8]*i3,
                            v[9]*f1+v[6]*i1,   v[10]*f+v[7]*i, v[11]*f3+v[8]*i3,
                                       v[9],            v[10],            v[11]
                    ];
                    ntmp = new MultiresNode(vtmp, node.side, node.level + 1, node.x*2, node.y*2+1, image.fullpath);
                    children.push(ntmp);
                }
                for (var j = 0; j < children.length; j++) {
                    testMultiresNode(rotPersp, children[j], pitch, yaw, hfov);
                }
            }
        }
    }
    
    /**
     * Creates cube vertex array.
     * @private
     * @returns {number[]} Cube vertex array.
     */
    function createCube() {
        return [-1,  1, -1,  1,  1, -1,  1, -1, -1, -1, -1, -1, // Front face
                 1,  1,  1, -1,  1,  1, -1, -1,  1,  1, -1,  1, // Back face
                -1,  1,  1,  1,  1,  1,  1,  1, -1, -1,  1, -1, // Up face
                -1, -1, -1,  1, -1, -1,  1, -1,  1, -1, -1,  1, // Down face
                -1,  1,  1, -1,  1, -1, -1, -1, -1, -1, -1,  1, // Left face
                 1,  1, -1,  1,  1,  1,  1, -1,  1,  1, -1, -1  // Right face
        ];
    }
    
    /**
     * Creates 3x3 identity matrix.
     * @private
     * @returns {number[]} Identity matrix.
     */
    function identityMatrix3() {
        return [
            1, 0, 0,
            0, 1, 0,
            0, 0, 1
        ];
    }
    
    /**
     * Rotates a 3x3 matrix.
     * @private
     * @param {number[]} m - Matrix to rotate.
     * @param {number[]} angle - Angle to rotate by in radians.
     * @param {string} axis - Axis to rotate about (`x`, `y`, or `z`).
     * @returns {number[]} Rotated matrix.
     */
    function rotateMatrix(m, angle, axis) {
        var s = Math.sin(angle);
        var c = Math.cos(angle);
        if (axis == 'x') {
            return [
                m[0], c*m[1] + s*m[2], c*m[2] - s*m[1],
                m[3], c*m[4] + s*m[5], c*m[5] - s*m[4],
                m[6], c*m[7] + s*m[8], c*m[8] - s*m[7]
            ];
        }
        if (axis == 'y') {
            return [
                c*m[0] - s*m[2], m[1], c*m[2] + s*m[0],
                c*m[3] - s*m[5], m[4], c*m[5] + s*m[3],
                c*m[6] - s*m[8], m[7], c*m[8] + s*m[6]
            ];
        }
        if (axis == 'z') {
            return [
                c*m[0] + s*m[1], c*m[1] - s*m[0], m[2],
                c*m[3] + s*m[4], c*m[4] - s*m[3], m[5],
                c*m[6] + s*m[7], c*m[7] - s*m[6], m[8]
            ];
        }
    }
    
    /**
     * Turns a 3x3 matrix into a 4x4 matrix.
     * @private
     * @param {number[]} m - Input matrix.
     * @returns {number[]} Expanded matrix.
     */
    function makeMatrix4(m) {
        return [
            m[0], m[1], m[2],    0,
            m[3], m[4], m[5],    0,
            m[6], m[7], m[8],    0,
               0,    0,    0,    1
        ];
    }
    
    /**
     * Transposes a 4x4 matrix.
     * @private
     * @param {number[]} m - Input matrix.
     * @returns {number[]} Transposed matrix.
     */
    function transposeMatrix4(m) {
        return [
            m[ 0], m[ 4], m[ 8], m[12],
            m[ 1], m[ 5], m[ 9], m[13],
            m[ 2], m[ 6], m[10], m[14],
            m[ 3], m[ 7], m[11], m[15]
        ];
    }
    
    /**
     * Creates a perspective matrix.
     * @private
     * @param {number} hfov - Desired horizontal field of view.
     * @param {number} aspect - Desired aspect ratio.
     * @param {number} znear - Near distance.
     * @param {number} zfar - Far distance.
     * @returns {number[]} Generated perspective matrix.
     */
    function makePersp(hfov, aspect, znear, zfar) {
        var fovy = 2 * Math.atan(Math.tan(hfov/2) * gl.drawingBufferHeight / gl.drawingBufferWidth);
        var f = 1 / Math.tan(fovy/2);
        return [
            f/aspect,   0,  0,  0,
                   0,   f,  0,  0,
                   0,   0,  (zfar+znear)/(znear-zfar), (2*zfar*znear)/(znear-zfar),
                   0,   0, -1,  0
        ];
    }
    
    /**
     * Processes a loaded texture image into a WebGL texture.
     * @private
     * @param {Image} img - Input image.
     * @param {WebGLTexture} tex - Texture to bind image to.
     */
    function processLoadedTexture(img, tex) {
        gl.bindTexture(gl.TEXTURE_2D, tex);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, img);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
        gl.bindTexture(gl.TEXTURE_2D, null);
    }
    
    var pendingTextureRequests = [];

    // Based on http://blog.tojicode.com/2012/03/javascript-memory-optimization-and.html
    var loadTexture = (function() {
        var cacheTop = 4;   // Maximum number of concurrents loads
        var textureImageCache = {};
        var crossOrigin;

        function TextureImageLoader() {
            var self = this;
            this.texture = this.callback = null;
            this.image = new Image();
            this.image.crossOrigin = crossOrigin ? crossOrigin : 'anonymous';
            var loadFn = (function() {
                if (self.image.width > 0 && self.image.height > 0) { // ignore missing tile to supporting partial image
                    processLoadedTexture(self.image, self.texture);
                    self.callback(self.texture, true);
                } else {
                    self.callback(self.texture, false);
                }
                releaseTextureImageLoader(self);
            });
            this.image.addEventListener('load', loadFn);
            this.image.addEventListener('error', loadFn); // ignore missing tile file to support partial image, otherwise retry loop causes high CPU load
        }

        TextureImageLoader.prototype.loadTexture = function(src, texture, callback) {
            this.texture = texture;
            this.callback = callback;
            this.image.src = src;
        };

        function PendingTextureRequest(node, src, texture, callback) {
            this.node = node;
            this.src = src;
            this.texture = texture;
            this.callback = callback;
        }

        function releaseTextureImageLoader(til) {
            if (pendingTextureRequests.length) {
                var req = pendingTextureRequests.shift();
                til.loadTexture(req.src, req.texture, req.callback);
            } else
                textureImageCache[cacheTop++] = til;
        }

        for (var i = 0; i < cacheTop; i++)
            textureImageCache[i] = new TextureImageLoader();

        return function(node, src, callback, _crossOrigin) {
            crossOrigin = _crossOrigin;
            var texture = gl.createTexture();
            if (cacheTop)
                textureImageCache[--cacheTop].loadTexture(src, texture, callback);
            else
                pendingTextureRequests.push(new PendingTextureRequest(node, src, texture, callback));
            return texture;
        };
    })();

    /**
     * Loads image and creates texture for a multires node / tile.
     * @private
     * @param {MultiresNode} node - Input node.
     */
    function processNextTile(node) {
        loadTexture(node, node.path + '.' + image.extension, function(texture, loaded) {
            node.texture = texture;
            node.textureLoaded = loaded ? 2 : 1;
        }, globalParams.crossOrigin);
    }
    
    /**
     * Finds and applies optimal multires zoom level.
     * @private
     * @param {number} hfov - Horizontal field of view to check at.
     */
    function checkZoom(hfov) {
        // Find optimal level
        var newLevel = 1;
        while ( newLevel < image.maxLevel &&
            gl.drawingBufferWidth > image.tileResolution *
            Math.pow(2, newLevel - 1) * Math.tan(hfov / 2) * 0.707 ) {
            newLevel++;
        }
        
        // Apply change
        program.level = newLevel;
    }
    
    /**
     * Rotates perspective matrix.
     * @private
     * @param {number[]} p - Perspective matrix.
     * @param {number[]} r - Rotation matrix.
     * @returns {number[]} Rotated matrix.
     */
    function rotatePersp(p, r) {
        return [
            p[ 0]*r[0], p[ 0]*r[1], p[ 0]*r[ 2],     0,
            p[ 5]*r[4], p[ 5]*r[5], p[ 5]*r[ 6],     0,
            p[10]*r[8], p[10]*r[9], p[10]*r[10], p[11],
                 -r[8],      -r[9],      -r[10],     0
        ];
    }
    
    /**
     * Applies rotated perspective matrix to a 3-vector
     * (last element is inverted).
     * @private
     * @param {number[]} m - Rotated perspective matrix.
     * @param {number[]} v - Input 3-vector.
     * @returns {number[]} Resulting 4-vector.
     */
    function applyRotPerspToVec(m, v) {
        return [
                    m[ 0]*v[0] + m[ 1]*v[1] + m[ 2]*v[2],
                    m[ 4]*v[0] + m[ 5]*v[1] + m[ 6]*v[2],
            m[11] + m[ 8]*v[0] + m[ 9]*v[1] + m[10]*v[2],
                 1/(m[12]*v[0] + m[13]*v[1] + m[14]*v[2])
        ];
    }
    
    /**
     * Checks if a vertex is visible.
     * @private
     * @param {number[]} m - Rotated perspective matrix.
     * @param {number[]} v - Input vertex.
     * @returns {number} 1 or -1 if the vertex is or is not visible,
     *      respectively.
     */
    function checkInView(m, v) {
        var vpp = applyRotPerspToVec(m, v);
        var winX = vpp[0]*vpp[3];
        var winY = vpp[1]*vpp[3];
        var winZ = vpp[2]*vpp[3];
        var ret = [0, 0, 0];
        
        if ( winX < -1 )
            ret[0] = -1;
        if ( winX > 1 )
            ret[0] = 1;
        if ( winY < -1 )
            ret[1] = -1;
        if ( winY > 1 )
            ret[1] = 1;
        if ( winZ < -1 || winZ > 1 )
            ret[2] = 1;
        return ret;
    }
    
    /**
     * Checks if a square (tile) is visible.
     * @private
     * @param {number[]} m - Rotated perspective matrix.
     * @param {number[]} v - Square's vertex array.
     * @returns {boolean} Whether or not the square is visible.
     */
    function checkSquareInView(m, v) {
        var check1 = checkInView(m, v.slice(0, 3));
        var check2 = checkInView(m, v.slice(3, 6));
        var check3 = checkInView(m, v.slice(6, 9));
        var check4 = checkInView(m, v.slice(9, 12));
        var testX = check1[0] + check2[0] + check3[0] + check4[0];
        if ( testX == -4 || testX == 4 )
            return false;
        var testY = check1[1] + check2[1] + check3[1] + check4[1];
        if ( testY == -4 || testY == 4 )
            return false;
        var testZ = check1[2] + check2[2] + check3[2] + check4[2];
        return testZ != 4;
        

    }

    /**
     * On iOS (iPhone 5c, iOS 10.3), this WebGL error occurs when the canvas is
     * too big. Unfortuately, there's no way to test for this beforehand, so we
     * reduce the canvas size if this error is thrown.
     * @private
     */
    function handleWebGLError1286() {
        console.log('Reducing canvas size due to error 1286!');
        canvas.width = Math.round(canvas.width / 2);
        canvas.height = Math.round(canvas.height / 2);
    }
}

// Vertex shader for equirectangular and cube
var v = [
'attribute vec2 a_texCoord;',
'varying vec2 v_texCoord;',

'void main() {',
    // Set position
    'gl_Position = vec4(a_texCoord, 0.0, 1.0);',
    
    // Pass the coordinates to the fragment shader
    'v_texCoord = a_texCoord;',
'}'
].join('');

// Vertex shader for multires
var vMulti = [
'attribute vec3 a_vertCoord;',
'attribute vec2 a_texCoord;',

'uniform mat4 u_cubeMatrix;',
'uniform mat4 u_perspMatrix;',

'varying mediump vec2 v_texCoord;',

'void main(void) {',
    // Set position
    'gl_Position = u_perspMatrix * u_cubeMatrix * vec4(a_vertCoord, 1.0);',
    
    // Pass the coordinates to the fragment shader
    'v_texCoord = a_texCoord;',
'}'
].join('');

// Fragment shader
var fragEquiCubeBase = [
'precision highp float;', // mediump looks bad on some mobile devices

'uniform float u_aspectRatio;',
'uniform float u_psi;',
'uniform float u_theta;',
'uniform float u_f;',
'uniform float u_h;',
'uniform float u_v;',
'uniform float u_vo;',
'uniform float u_rot;',

'const float PI = 3.14159265358979323846264;',

// Texture
'uniform sampler2D u_image0;',
'uniform sampler2D u_image1;',
'uniform bool u_splitImage;',
'uniform samplerCube u_imageCube;',

// Coordinates passed in from vertex shader
'varying vec2 v_texCoord;',

// Background color (display for partial panoramas)
'uniform vec4 u_backgroundColor;',

'void main() {',
    // Map canvas/camera to sphere
    'float x = v_texCoord.x * u_aspectRatio;',
    'float y = v_texCoord.y;',
    'float sinrot = sin(u_rot);',
    'float cosrot = cos(u_rot);',
    'float rot_x = x * cosrot - y * sinrot;',
    'float rot_y = x * sinrot + y * cosrot;',
    'float sintheta = sin(u_theta);',
    'float costheta = cos(u_theta);',
    'float a = u_f * costheta - rot_y * sintheta;',
    'float root = sqrt(rot_x * rot_x + a * a);',
    'float lambda = atan(rot_x / root, a / root) + u_psi;',
    'float phi = atan((rot_y * costheta + u_f * sintheta) / root);',
].join('\n');

// Fragment shader
var fragCube = fragEquiCubeBase + [
    // Look up color from texture
    'float cosphi = cos(phi);',
    'gl_FragColor = textureCube(u_imageCube, vec3(cosphi*sin(lambda), sin(phi), cosphi*cos(lambda)));',
'}'
].join('\n');

// Fragment shader
var fragEquirectangular = fragEquiCubeBase + [
    // Wrap image
    'lambda = mod(lambda + PI, PI * 2.0) - PI;',

    // Map texture to sphere
    'vec2 coord = vec2(lambda / PI, phi / (PI / 2.0));',

    // Look up color from texture
    // Map from [-1,1] to [0,1] and flip y-axis
    'if(coord.x < -u_h || coord.x > u_h || coord.y < -u_v + u_vo || coord.y > u_v + u_vo)',
        'gl_FragColor = u_backgroundColor;',
    'else {',
        'if(u_splitImage) {',
            // Image was split into two textures to work around texture size limits
            'if(coord.x < 0.0)',
                'gl_FragColor = texture2D(u_image0, vec2((coord.x + u_h) / u_h, (-coord.y + u_v + u_vo) / (u_v * 2.0)));',
            'else',
                'gl_FragColor = texture2D(u_image1, vec2((coord.x + u_h) / u_h - 1.0, (-coord.y + u_v + u_vo) / (u_v * 2.0)));',
        '} else {',
            'gl_FragColor = texture2D(u_image0, vec2((coord.x + u_h) / (u_h * 2.0), (-coord.y + u_v + u_vo) / (u_v * 2.0)));',
        '}',
    '}',
'}'
].join('\n');

// Fragment shader
var fragMulti = [
'varying mediump vec2 v_texCoord;',
'uniform sampler2D u_sampler;',
//'uniform mediump vec4 u_color;',

'void main(void) {',
    // Look up color from texture
    'gl_FragColor = texture2D(u_sampler, v_texCoord);',
//    'gl_FragColor = u_color;',
'}'
].join('');

return {
    renderer: function(container, image, imagetype, dynamic) {
        return new Renderer(container, image, imagetype, dynamic);
    }
};

})(window, document);
// source --> https://eternize360.com.br/wp-content/plugins/wpvr-pro/admin/lib/video.js?ver=1 
/**
 * @license
 * Video.js 7.18.1 <http://videojs.com/>
 * Copyright Brightcove, Inc. <https://www.brightcove.com/>
 * Available under Apache License Version 2.0
 * <https://github.com/videojs/video.js/blob/main/LICENSE>
 *
 * Includes vtt.js <https://github.com/mozilla/vtt.js>
 * Available under Apache License Version 2.0
 * <https://github.com/mozilla/vtt.js/blob/main/LICENSE>
 */
 !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).videojs=t()}(this,function(){"use strict";for(var e,u="7.18.1",i={},a=function(e,t){return i[e]=i[e]||[],t&&(i[e]=i[e].concat(t)),i[e]},n=function(e,t){t=a(e).indexOf(t);return!(t<=-1)&&(i[e]=i[e].slice(),i[e].splice(t,1),!0)},l={prefixed:!0},t=[["requestFullscreen","exitFullscreen","fullscreenElement","fullscreenEnabled","fullscreenchange","fullscreenerror","fullscreen"],["webkitRequestFullscreen","webkitExitFullscreen","webkitFullscreenElement","webkitFullscreenEnabled","webkitfullscreenchange","webkitfullscreenerror","-webkit-full-screen"],["mozRequestFullScreen","mozCancelFullScreen","mozFullScreenElement","mozFullScreenEnabled","mozfullscreenchange","mozfullscreenerror","-moz-full-screen"],["msRequestFullscreen","msExitFullscreen","msFullscreenElement","msFullscreenEnabled","MSFullscreenChange","MSFullscreenError","-ms-fullscreen"]],r=t[0],s=0;s<t.length;s++)if(t[s][1]in document){e=t[s];break}if(e){for(var o=0;o<e.length;o++)l[r[o]]=e[o];l.prefixed=e[0]!==r[0]}var c=[],d=function(a,s){return function(e,t,i){var n,r=s.levels[t],t=new RegExp("^("+r+")$");"log"!==e&&i.unshift(e.toUpperCase()+":"),i.unshift(a+":"),c&&(c.push([].concat(i)),n=c.length-1e3,c.splice(0,0<n?n:0)),!window.console||(n=!(n=window.console[e])&&"debug"===e?window.console.info||window.console.log:n)&&r&&t.test(e)&&n[Array.isArray(i)?"apply":"call"](window.console,i)}};var h=function t(i){function n(){for(var e=arguments.length,t=new Array(e),i=0;i<e;i++)t[i]=arguments[i];a("log",r,t)}var r="info",a=d(i,n);return n.createLogger=function(e){return t(i+": "+e)},n.levels={all:"debug|log|warn|error",off:"",debug:"debug|log|warn|error",info:"log|warn|error",warn:"warn|error",error:"error",DEFAULT:r},n.level=function(e){if("string"==typeof e){if(!n.levels.hasOwnProperty(e))throw new Error('"'+e+'" in not a valid log level');r=e}return r},(n.history=function(){return c?[].concat(c):[]}).filter=function(t){return(c||[]).filter(function(e){return new RegExp(".*"+t+".*").test(e[0])})},n.history.clear=function(){c&&(c.length=0)},n.history.disable=function(){null!==c&&(c.length=0,c=null)},n.history.enable=function(){null===c&&(c=[])},n.error=function(){for(var e=arguments.length,t=new Array(e),i=0;i<e;i++)t[i]=arguments[i];return a("error",r,t)},n.warn=function(){for(var e=arguments.length,t=new Array(e),i=0;i<e;i++)t[i]=arguments[i];return a("warn",r,t)},n.debug=function(){for(var e=arguments.length,t=new Array(e),i=0;i<e;i++)t[i]=arguments[i];return a("debug",r,t)},n}("VIDEOJS"),p=h.createLogger,f="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function m(e,t){return e(t={exports:{}},t.exports),t.exports}var g=m(function(e){function t(){return e.exports=t=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var i,n=arguments[t];for(i in n)Object.prototype.hasOwnProperty.call(n,i)&&(e[i]=n[i])}return e},t.apply(this,arguments)}e.exports=t}),y=Object.prototype.toString,v=function(e){return T(e)?Object.keys(e):[]};function _(t,i){v(t).forEach(function(e){return i(t[e],e)})}function b(i){for(var e=arguments.length,t=new Array(1<e?e-1:0),n=1;n<e;n++)t[n-1]=arguments[n];return Object.assign?g.apply(void 0,[i].concat(t)):(t.forEach(function(e){e&&_(e,function(e,t){i[t]=e})}),i)}function T(e){return!!e&&"object"==typeof e}function S(e){return T(e)&&"[object Object]"===y.call(e)&&e.constructor===Object}function w(e,t){if(!e||!t)return"";if("function"!=typeof window.getComputedStyle)return"";var i;try{i=window.getComputedStyle(e)}catch(e){return""}return i?i.getPropertyValue(t)||i[t]:""}var E=window.navigator&&window.navigator.userAgent||"",k=/AppleWebKit\/([\d.]+)/i.exec(E),C=k?parseFloat(k.pop()):null,I=/iPod/i.test(E),x=(jt=E.match(/OS (\d+)_/i))&&jt[1]?jt[1]:null,A=/Android/i.test(E),P=function(){var e=E.match(/Android (\d+)(?:\.(\d+))?(?:\.(\d+))*/i);if(!e)return null;var t=e[1]&&parseFloat(e[1]),i=e[2]&&parseFloat(e[2]);return t&&i?parseFloat(e[1]+"."+e[2]):t||null}(),L=A&&P<5&&C<537,D=/Firefox/i.test(E),O=/Edg/i.test(E),R=!O&&(/Chrome/i.test(E)||/CriOS/i.test(E)),M=(zt=E.match(/(Chrome|CriOS)\/(\d+)/))&&zt[2]?parseFloat(zt[2]):null,N=Xt=!(Xt=(Xt=/MSIE\s(\d+)\.\d/.exec(E))&&parseFloat(Xt[1]))&&/Trident\/7.0/i.test(E)&&/rv:11.0/.test(E)?11:Xt,U=/Safari/i.test(E)&&!R&&!A&&!O,B=/Windows/i.test(E),F=Boolean(X()&&("ontouchstart"in window||window.navigator.maxTouchPoints||window.DocumentTouch&&window.document instanceof window.DocumentTouch)),j=/iPad/i.test(E)||U&&F&&!/iPhone/i.test(E),H=/iPhone/i.test(E)&&!j,q=H||j||I,V=(U||q)&&!R,W=Object.freeze({__proto__:null,IS_IPOD:I,IOS_VERSION:x,IS_ANDROID:A,ANDROID_VERSION:P,IS_NATIVE_ANDROID:L,IS_FIREFOX:D,IS_EDGE:O,IS_CHROME:R,CHROME_VERSION:M,IE_VERSION:N,IS_SAFARI:U,IS_WINDOWS:B,TOUCH_ENABLED:F,IS_IPAD:j,IS_IPHONE:H,IS_IOS:q,IS_ANY_SAFARI:V});function G(e){return"string"==typeof e&&Boolean(e.trim())}function z(e){if(0<=e.indexOf(" "))throw new Error("class has illegal whitespace characters")}function X(){return document===window.document}function K(e){return T(e)&&1===e.nodeType}function Y(){try{return window.parent!==window.self}catch(e){return!0}}function Q(i){return function(e,t){if(!G(e))return document[i](null);t=K(t=G(t)?document.querySelector(t):t)?t:document;return t[i]&&t[i](e)}}function $(e,i,t,n){void 0===e&&(e="div"),void 0===i&&(i={}),void 0===t&&(t={});var r=document.createElement(e);return Object.getOwnPropertyNames(i).forEach(function(e){var t=i[e];-1!==e.indexOf("aria-")||"role"===e||"type"===e?(h.warn("Setting attributes in the second argument of createEl()\nhas been deprecated. Use the third argument instead.\ncreateEl(type, properties, attributes). Attempting to set "+e+" to "+t+"."),r.setAttribute(e,t)):"textContent"===e?J(r,t):r[e]===t&&"tabIndex"!==e||(r[e]=t)}),Object.getOwnPropertyNames(t).forEach(function(e){r.setAttribute(e,t[e])}),n&&ye(r,n),r}function J(e,t){return"undefined"==typeof e.textContent?e.innerText=t:e.textContent=t,e}function Z(e,t){t.firstChild?t.insertBefore(e,t.firstChild):t.appendChild(e)}function ee(e,t){return z(t),e.classList?e.classList.contains(t):new RegExp("(^|\\s)"+t+"($|\\s)").test(e.className)}function te(e,t){return e.classList?e.classList.add(t):ee(e,t)||(e.className=(e.className+" "+t).trim()),e}function ie(e,t){return e?(e.classList?e.classList.remove(t):(z(t),e.className=e.className.split(/\s+/).filter(function(e){return e!==t}).join(" ")),e):(h.warn("removeClass was called with an element that doesn't exist"),null)}function ne(e,t,i){var n=ee(e,t);if((i="boolean"!=typeof(i="function"==typeof i?i(e,t):i)?!n:i)!==n)return(i?te:ie)(e,t),e}function re(i,n){Object.getOwnPropertyNames(n).forEach(function(e){var t=n[e];null===t||"undefined"==typeof t||!1===t?i.removeAttribute(e):i.setAttribute(e,!0===t?"":t)})}function ae(e){var t={},i=",autoplay,controls,playsinline,loop,muted,default,defaultMuted,";if(e&&e.attributes&&0<e.attributes.length)for(var n=e.attributes,r=n.length-1;0<=r;r--){var a=n[r].name,s=n[r].value;"boolean"!=typeof e[a]&&-1===i.indexOf(","+a+",")||(s=null!==s),t[a]=s}return t}function se(e,t){return e.getAttribute(t)}function oe(e,t,i){e.setAttribute(t,i)}function ue(e,t){e.removeAttribute(t)}function le(){document.body.focus(),document.onselectstart=function(){return!1}}function ce(){document.onselectstart=function(){return!0}}function de(e){if(e&&e.getBoundingClientRect&&e.parentNode){var t=e.getBoundingClientRect(),i={};return["bottom","height","left","right","top","width"].forEach(function(e){void 0!==t[e]&&(i[e]=t[e])}),i.height||(i.height=parseFloat(w(e,"height"))),i.width||(i.width=parseFloat(w(e,"width"))),i}}function he(e){if(!e||e&&!e.offsetParent)return{left:0,top:0,width:0,height:0};for(var t=e.offsetWidth,i=e.offsetHeight,n=0,r=0;e.offsetParent&&e!==document[l.fullscreenElement];)n+=e.offsetLeft,r+=e.offsetTop,e=e.offsetParent;return{left:n,top:r,width:t,height:i}}function pe(e,t){var i={x:0,y:0};if(q)for(var n=e;n&&"html"!==n.nodeName.toLowerCase();){var r,a=w(n,"transform");/^matrix/.test(a)?(r=a.slice(7,-1).split(/,\s/).map(Number),i.x+=r[4],i.y+=r[5]):/^matrix3d/.test(a)&&(a=a.slice(9,-1).split(/,\s/).map(Number),i.x+=a[12],i.y+=a[13]),n=n.parentNode}var s={},o=he(t.target),u=he(e),l=u.width,c=u.height,e=t.offsetY-(u.top-o.top),o=t.offsetX-(u.left-o.left);return t.changedTouches&&(o=t.changedTouches[0].pageX-u.left,e=t.changedTouches[0].pageY+u.top,q&&(o-=i.x,e-=i.y)),s.y=1-Math.max(0,Math.min(1,e/c)),s.x=Math.max(0,Math.min(1,o/l)),s}function fe(e){return T(e)&&3===e.nodeType}function me(e){for(;e.firstChild;)e.removeChild(e.firstChild);return e}function ge(e){return"function"==typeof e&&(e=e()),(Array.isArray(e)?e:[e]).map(function(e){return K(e="function"==typeof e?e():e)||fe(e)?e:"string"==typeof e&&/\S/.test(e)?document.createTextNode(e):void 0}).filter(function(e){return e})}function ye(t,e){return ge(e).forEach(function(e){return t.appendChild(e)}),t}function ve(e,t){return ye(me(e),t)}function _e(e){return void 0===e.button&&void 0===e.buttons||(0===e.button&&void 0===e.buttons||("mouseup"===e.type&&0===e.button&&0===e.buttons||0===e.button&&1===e.buttons))}var be,Te=Q("querySelector"),Se=Q("querySelectorAll"),we=Object.freeze({__proto__:null,isReal:X,isEl:K,isInFrame:Y,createEl:$,textContent:J,prependTo:Z,hasClass:ee,addClass:te,removeClass:ie,toggleClass:ne,setAttributes:re,getAttributes:ae,getAttribute:se,setAttribute:oe,removeAttribute:ue,blockTextSelection:le,unblockTextSelection:ce,getBoundingClientRect:de,findPosition:he,getPointerPosition:pe,isTextNode:fe,emptyEl:me,normalizeContent:ge,appendContent:ye,insertContent:ve,isSingleLeftClick:_e,$:Te,$$:Se}),Ee=!1,ke=function(){if(!1!==be.options.autoSetup){var e=Array.prototype.slice.call(document.getElementsByTagName("video")),t=Array.prototype.slice.call(document.getElementsByTagName("audio")),i=Array.prototype.slice.call(document.getElementsByTagName("video-js")),n=e.concat(t,i);if(n&&0<n.length)for(var r=0,a=n.length;r<a;r++){var s=n[r];if(!s||!s.getAttribute){Ce(1);break}void 0===s.player&&null!==s.getAttribute("data-setup")&&be(s)}else Ee||Ce(1)}};function Ce(e,t){X()&&(t&&(be=t),window.setTimeout(ke,e))}function Ie(){Ee=!0,window.removeEventListener("load",Ie)}X()&&("complete"===document.readyState?Ie():window.addEventListener("load",Ie));function xe(e){var t=document.createElement("style");return t.className=e,t}function Ae(e,t){e.styleSheet?e.styleSheet.cssText=t:e.textContent=t}var Pe=3;window.WeakMap||(ui=function(){function e(){this.vdata="vdata"+Math.floor(window.performance&&window.performance.now()||Date.now()),this.data={}}var t=e.prototype;return t.set=function(e,t){var i=e[this.vdata]||Pe++;return e[this.vdata]||(e[this.vdata]=i),this.data[i]=t,this},t.get=function(e){var t=e[this.vdata];if(t)return this.data[t];h("We have no data for this element",e)},t.has=function(e){return e[this.vdata]in this.data},t.delete=function(e){var t=e[this.vdata];t&&(delete this.data[t],delete e[this.vdata])},e}());var Le,De=new(window.WeakMap?WeakMap:ui);function Oe(e,t){var i;De.has(e)&&(0===(i=De.get(e)).handlers[t].length&&(delete i.handlers[t],e.removeEventListener?e.removeEventListener(t,i.dispatcher,!1):e.detachEvent&&e.detachEvent("on"+t,i.dispatcher)),Object.getOwnPropertyNames(i.handlers).length<=0&&(delete i.handlers,delete i.dispatcher,delete i.disabled),0===Object.getOwnPropertyNames(i).length&&De.delete(e))}function Re(t,i,e,n){e.forEach(function(e){t(i,e,n)})}function Me(e){if(e.fixed_)return e;function t(){return!0}function i(){return!1}if(!e||!e.isPropagationStopped||!e.isImmediatePropagationStopped){var n,r,a,s=e||window.event;for(n in e={},s)"layerX"!==n&&"layerY"!==n&&"keyLocation"!==n&&"webkitMovementX"!==n&&"webkitMovementY"!==n&&("returnValue"===n&&s.preventDefault||(e[n]=s[n]));e.target||(e.target=e.srcElement||document),e.relatedTarget||(e.relatedTarget=e.fromElement===e.target?e.toElement:e.fromElement),e.preventDefault=function(){s.preventDefault&&s.preventDefault(),e.returnValue=!1,s.returnValue=!1,e.defaultPrevented=!0},e.defaultPrevented=!1,e.stopPropagation=function(){s.stopPropagation&&s.stopPropagation(),e.cancelBubble=!0,s.cancelBubble=!0,e.isPropagationStopped=t},e.isPropagationStopped=i,e.stopImmediatePropagation=function(){s.stopImmediatePropagation&&s.stopImmediatePropagation(),e.isImmediatePropagationStopped=t,e.stopPropagation()},e.isImmediatePropagationStopped=i,null!==e.clientX&&void 0!==e.clientX&&(r=document.documentElement,a=document.body,e.pageX=e.clientX+(r&&r.scrollLeft||a&&a.scrollLeft||0)-(r&&r.clientLeft||a&&a.clientLeft||0),e.pageY=e.clientY+(r&&r.scrollTop||a&&a.scrollTop||0)-(r&&r.clientTop||a&&a.clientTop||0)),e.which=e.charCode||e.keyCode,null!==e.button&&void 0!==e.button&&(e.button=1&e.button?0:4&e.button?1:2&e.button?2:0)}return e.fixed_=!0,e}var Ne=function(){if("boolean"!=typeof Le){Le=!1;try{var e=Object.defineProperty({},"passive",{get:function(){Le=!0}});window.addEventListener("test",null,e),window.removeEventListener("test",null,e)}catch(e){}}return Le},Ue=["touchstart","touchmove"];function Be(s,e,t){if(Array.isArray(e))return Re(Be,s,e,t);De.has(s)||De.set(s,{});var o=De.get(s);o.handlers||(o.handlers={}),o.handlers[e]||(o.handlers[e]=[]),t.guid||(t.guid=Pe++),o.handlers[e].push(t),o.dispatcher||(o.disabled=!1,o.dispatcher=function(e,t){if(!o.disabled){e=Me(e);var i=o.handlers[e.type];if(i)for(var n=i.slice(0),r=0,a=n.length;r<a&&!e.isImmediatePropagationStopped();r++)try{n[r].call(s,e,t)}catch(e){h.error(e)}}}),1===o.handlers[e].length&&(s.addEventListener?(t=!1,Ne()&&-1<Ue.indexOf(e)&&(t={passive:!0}),s.addEventListener(e,o.dispatcher,t)):s.attachEvent&&s.attachEvent("on"+e,o.dispatcher))}function Fe(e,t,i){if(De.has(e)){var n=De.get(e);if(n.handlers){if(Array.isArray(t))return Re(Fe,e,t,i);var r=function(e,t){n.handlers[t]=[],Oe(e,t)};if(void 0!==t){var a=n.handlers[t];if(a)if(i){if(i.guid)for(var s=0;s<a.length;s++)a[s].guid===i.guid&&a.splice(s--,1);Oe(e,t)}else r(e,t)}else for(var o in n.handlers)Object.prototype.hasOwnProperty.call(n.handlers||{},o)&&r(e,o)}}}function je(e,t,i){var n=De.has(e)?De.get(e):{},r=e.parentNode||e.ownerDocument;return"string"==typeof t?t={type:t,target:e}:t.target||(t.target=e),t=Me(t),n.dispatcher&&n.dispatcher.call(e,t,i),r&&!t.isPropagationStopped()&&!0===t.bubbles?je.call(null,r,t,i):!r&&!t.defaultPrevented&&t.target&&t.target[t.type]&&(De.has(t.target)||De.set(t.target,{}),r=De.get(t.target),t.target[t.type]&&(r.disabled=!0,"function"==typeof t.target[t.type]&&t.target[t.type](),r.disabled=!1)),!t.defaultPrevented}function He(e,t,i){if(Array.isArray(t))return Re(He,e,t,i);function n(){Fe(e,t,n),i.apply(this,arguments)}n.guid=i.guid=i.guid||Pe++,Be(e,t,n)}function qe(e,t,i){function n(){Fe(e,t,n),i.apply(this,arguments)}n.guid=i.guid=i.guid||Pe++,Be(e,t,n)}function Ve(e,t,i){return t.guid||(t.guid=Pe++),(e=t.bind(e)).guid=i?i+"_"+t.guid:t.guid,e}function We(t,i){var n=window.performance.now();return function(){var e=window.performance.now();i<=e-n&&(t.apply(void 0,arguments),n=e)}}function Ge(n,r,a,s){var o;function e(){var e=this,t=arguments,i=function(){i=o=null,a||n.apply(e,t)};!o&&a&&n.apply(e,t),s.clearTimeout(o),o=s.setTimeout(i,r)}return void 0===s&&(s=window),e.cancel=function(){s.clearTimeout(o),o=null},e}function ze(){}var Xe,Ke=Object.freeze({__proto__:null,fixEvent:Me,on:Be,off:Fe,trigger:je,one:He,any:qe});ze.prototype.allowedEvents_={},ze.prototype.addEventListener=ze.prototype.on=function(e,t){var i=this.addEventListener;this.addEventListener=function(){},Be(this,e,t),this.addEventListener=i},ze.prototype.removeEventListener=ze.prototype.off=function(e,t){Fe(this,e,t)},ze.prototype.one=function(e,t){var i=this.addEventListener;this.addEventListener=function(){},He(this,e,t),this.addEventListener=i},ze.prototype.any=function(e,t){var i=this.addEventListener;this.addEventListener=function(){},qe(this,e,t),this.addEventListener=i},ze.prototype.dispatchEvent=ze.prototype.trigger=function(e){var t=e.type||e;e=Me(e="string"==typeof e?{type:t}:e),this.allowedEvents_[t]&&this["on"+t]&&this["on"+t](e),je(this,e)},ze.prototype.queueTrigger=function(e){var t=this;Xe=Xe||new Map;var i=e.type||e,n=Xe.get(this);n||(n=new Map,Xe.set(this,n));var r=n.get(i);n.delete(i),window.clearTimeout(r);r=window.setTimeout(function(){0===n.size&&(n=null,Xe.delete(t)),t.trigger(e)},0);n.set(i,r)};function Ye(e){return"function"==typeof e.name?e.name():"string"==typeof e.name?e.name:e.name_||(e.constructor&&e.constructor.name?e.constructor.name:typeof e)}function Qe(e){return"string"==typeof e&&/\S/.test(e)||Array.isArray(e)&&!!e.length}function $e(e,t,i){if(!e||!e.nodeName&&!it(e))throw new Error("Invalid target for "+Ye(t)+"#"+i+"; must be a DOM node or evented object.")}function Je(e,t,i){if(!Qe(e))throw new Error("Invalid event type for "+Ye(t)+"#"+i+"; must be a non-empty string or array.")}function Ze(e,t,i){if("function"!=typeof e)throw new Error("Invalid listener for "+Ye(t)+"#"+i+"; must be a function.")}function et(e,t,i){var n,r,a=t.length<3||t[0]===e||t[0]===e.eventBusEl_,t=a?(n=e.eventBusEl_,3<=t.length&&t.shift(),r=t[0],t[1]):(n=t[0],r=t[1],t[2]);return $e(n,e,i),Je(r,e,i),Ze(t,e,i),{isTargetingSelf:a,target:n,type:r,listener:t=Ve(e,t)}}function tt(e,t,i,n){$e(e,e,t),e.nodeName?Ke[t](e,i,n):e[t](i,n)}var it=function(t){return t instanceof ze||!!t.eventBusEl_&&["on","one","off","trigger"].every(function(e){return"function"==typeof t[e]})},nt={on:function(){for(var e=this,t=arguments.length,i=new Array(t),n=0;n<t;n++)i[n]=arguments[n];var r,a=et(this,i,"on"),s=a.isTargetingSelf,o=a.target,u=a.type,l=a.listener;tt(o,"on",u,l),s||((r=function(){return e.off(o,u,l)}).guid=l.guid,(s=function(){return e.off("dispose",r)}).guid=l.guid,tt(this,"on","dispose",r),tt(o,"on","dispose",s))},one:function(){for(var r=this,e=arguments.length,t=new Array(e),i=0;i<e;i++)t[i]=arguments[i];var n=et(this,t,"one"),a=n.isTargetingSelf,s=n.target,o=n.type,u=n.listener;a?tt(s,"one",o,u):((a=function e(){r.off(s,o,e);for(var t=arguments.length,i=new Array(t),n=0;n<t;n++)i[n]=arguments[n];u.apply(null,i)}).guid=u.guid,tt(s,"one",o,a))},any:function(){for(var r=this,e=arguments.length,t=new Array(e),i=0;i<e;i++)t[i]=arguments[i];var n=et(this,t,"any"),a=n.isTargetingSelf,s=n.target,o=n.type,u=n.listener;a?tt(s,"any",o,u):((a=function e(){r.off(s,o,e);for(var t=arguments.length,i=new Array(t),n=0;n<t;n++)i[n]=arguments[n];u.apply(null,i)}).guid=u.guid,tt(s,"any",o,a))},off:function(e,t,i){!e||Qe(e)?Fe(this.eventBusEl_,e,t):(t=t,$e(e=e,this,"off"),Je(t,this,"off"),Ze(i,this,"off"),i=Ve(this,i),this.off("dispose",i),e.nodeName?(Fe(e,t,i),Fe(e,"dispose",i)):it(e)&&(e.off(t,i),e.off("dispose",i)))},trigger:function(e,t){$e(this.eventBusEl_,this,"trigger");var i=e&&"string"!=typeof e?e.type:e;if(!Qe(i)){i="Invalid event type for "+Ye(this)+"#trigger; must be a non-empty string or object with a type key that has a non-empty value.";if(!e)throw new Error(i);(this.log||h).error(i)}return je(this.eventBusEl_,e,t)}};function rt(e,t){t=(t=void 0===t?{}:t).eventBusKey;if(t){if(!e[t].nodeName)throw new Error('The eventBusKey "'+t+'" does not refer to an element.');e.eventBusEl_=e[t]}else e.eventBusEl_=$("span",{className:"vjs-event-bus"});return b(e,nt),e.eventedCallbacks&&e.eventedCallbacks.forEach(function(e){e()}),e.on("dispose",function(){e.off(),[e,e.el_,e.eventBusEl_].forEach(function(e){e&&De.has(e)&&De.delete(e)}),window.setTimeout(function(){e.eventBusEl_=null},0)}),e}var at={state:{},setState:function(e){var i,n=this;return _(e="function"==typeof e?e():e,function(e,t){n.state[t]!==e&&((i=i||{})[t]={from:n.state[t],to:e}),n.state[t]=e}),i&&it(this)&&this.trigger({changes:i,type:"statechanged"}),i}};function st(e,t){return b(e,at),e.state=b({},e.state,t),"function"==typeof e.handleStateChanged&&it(e)&&e.on("statechanged",e.handleStateChanged),e}function ot(e){return"string"!=typeof e?e:e.replace(/./,function(e){return e.toLowerCase()})}function ut(e){return"string"!=typeof e?e:e.replace(/./,function(e){return e.toUpperCase()})}function lt(){for(var i={},e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];return t.forEach(function(e){e&&_(e,function(e,t){S(e)?(S(i[t])||(i[t]={}),i[t]=lt(i[t],e)):i[t]=e})}),i}var ct=window.Map||function(){function e(){this.map_={}}var t=e.prototype;return t.has=function(e){return e in this.map_},t.delete=function(e){var t=this.has(e);return delete this.map_[e],t},t.set=function(e,t){return this.map_[e]=t,this},t.forEach=function(e,t){for(var i in this.map_)e.call(t,this.map_[i],i,this)},e}(),dt=window.Set||function(){function e(){this.set_={}}var t=e.prototype;return t.has=function(e){return e in this.set_},t.delete=function(e){var t=this.has(e);return delete this.set_[e],t},t.add=function(e){return this.set_[e]=1,this},t.forEach=function(e,t){for(var i in this.set_)e.call(t,i,i,this)},e}(),ht=m(function(e,t){function i(e){if(!e||"object"!=typeof e||(t=e.which||e.keyCode||e.charCode)&&(e=t),"number"==typeof e)return o[e];var t=String(e),e=n[t.toLowerCase()];return e||((e=r[t.toLowerCase()])?e:1===t.length?t.charCodeAt(0):void 0)}i.isEventKey=function(e,t){if(e&&"object"==typeof e){var i=e.which||e.keyCode||e.charCode;if(null==i)return!1;if("string"==typeof t){e=n[t.toLowerCase()];if(e)return e===i;if(e=r[t.toLowerCase()])return e===i}else if("number"==typeof t)return t===i;return!1}};for(var n=(t=e.exports=i).code=t.codes={backspace:8,tab:9,enter:13,shift:16,ctrl:17,alt:18,"pause/break":19,"caps lock":20,esc:27,space:32,"page up":33,"page down":34,end:35,home:36,left:37,up:38,right:39,down:40,insert:45,delete:46,command:91,"left command":91,"right command":93,"numpad *":106,"numpad +":107,"numpad -":109,"numpad .":110,"numpad /":111,"num lock":144,"scroll lock":145,"my computer":182,"my calculator":183,";":186,"=":187,",":188,"-":189,".":190,"/":191,"`":192,"[":219,"\\":220,"]":221,"'":222},r=t.aliases={windows:91,"⇧":16,"⌥":18,"⌃":17,"⌘":91,ctl:17,control:17,option:18,pause:19,break:19,caps:20,return:13,escape:27,spc:32,spacebar:32,pgup:33,pgdn:34,ins:45,del:46,cmd:91},a=97;a<123;a++)n[String.fromCharCode(a)]=a-32;for(var a=48;a<58;a++)n[a-48]=a;for(a=1;a<13;a++)n["f"+a]=a+111;for(a=0;a<10;a++)n["numpad "+a]=a+96;var s,o=t.names=t.title={};for(a in n)o[n[a]]=a;for(s in r)n[s]=r[s]});ht.code,ht.codes,ht.aliases,ht.names,ht.title;var pt=function(){function s(e,t,i){!e&&this.play?this.player_=e=this:this.player_=e,this.isDisposed_=!1,this.parentComponent_=null,this.options_=lt({},this.options_),t=this.options_=lt(this.options_,t),this.id_=t.id||t.el&&t.el.id,this.id_||(e=e&&e.id&&e.id()||"no_player",this.id_=e+"_component_"+Pe++),this.name_=t.name||null,t.el?this.el_=t.el:!1!==t.createEl&&(this.el_=this.createEl()),!1!==t.evented&&(rt(this,{eventBusKey:this.el_?"el_":null}),this.handleLanguagechange=this.handleLanguagechange.bind(this),this.on(this.player_,"languagechange",this.handleLanguagechange)),st(this,this.constructor.defaultState),this.children_=[],this.childIndex_={},this.childNameIndex_={},this.setTimeoutIds_=new dt,this.setIntervalIds_=new dt,this.rafIds_=new dt,this.namedRafs_=new ct,(this.clearingTimersOnDispose_=!1)!==t.initChildren&&this.initChildren(),this.ready(i),!1!==t.reportTouchActivity&&this.enableTouchActivity()}var e=s.prototype;return e.dispose=function(){if(!this.isDisposed_){if(this.readyQueue_&&(this.readyQueue_.length=0),this.trigger({type:"dispose",bubbles:!1}),this.isDisposed_=!0,this.children_)for(var e=this.children_.length-1;0<=e;e--)this.children_[e].dispose&&this.children_[e].dispose();this.children_=null,this.childIndex_=null,this.childNameIndex_=null,this.parentComponent_=null,this.el_&&(this.el_.parentNode&&this.el_.parentNode.removeChild(this.el_),this.el_=null),this.player_=null}},e.isDisposed=function(){return Boolean(this.isDisposed_)},e.player=function(){return this.player_},e.options=function(e){return e&&(this.options_=lt(this.options_,e)),this.options_},e.el=function(){return this.el_},e.createEl=function(e,t,i){return $(e,t,i)},e.localize=function(e,i,t){void 0===t&&(t=e);var n=this.player_.language&&this.player_.language(),r=this.player_.languages&&this.player_.languages(),a=r&&r[n],n=n&&n.split("-")[0],n=r&&r[n],t=t;return a&&a[e]?t=a[e]:n&&n[e]&&(t=n[e]),t=i?t.replace(/\{(\d+)\}/g,function(e,t){t=i[t-1];return"undefined"==typeof t?e:t}):t},e.handleLanguagechange=function(){},e.contentEl=function(){return this.contentEl_||this.el_},e.id=function(){return this.id_},e.name=function(){return this.name_},e.children=function(){return this.children_},e.getChildById=function(e){return this.childIndex_[e]},e.getChild=function(e){if(e)return this.childNameIndex_[e]},e.getDescendant=function(){for(var e=arguments.length,t=new Array(e),i=0;i<e;i++)t[i]=arguments[i];for(var t=t.reduce(function(e,t){return e.concat(t)},[]),n=this,r=0;r<t.length;r++)if(!(n=n.getChild(t[r]))||!n.getChild)return;return n},e.addChild=function(e,t,i){if(void 0===t&&(t={}),void 0===i&&(i=this.children_.length),"string"==typeof e){var n=ut(e),r=t.componentClass||n;t.name=n;var a=s.getComponent(r);if(!a)throw new Error("Component "+r+" does not exist");if("function"!=typeof a)return null;a=new a(this.player_||this,t)}else a=e;return a.parentComponent_&&a.parentComponent_.removeChild(a),this.children_.splice(i,0,a),a.parentComponent_=this,"function"==typeof a.id&&(this.childIndex_[a.id()]=a),(n=n||a.name&&ut(a.name()))&&(this.childNameIndex_[n]=a,this.childNameIndex_[ot(n)]=a),"function"==typeof a.el&&a.el()&&(n=null,this.children_[i+1]&&(this.children_[i+1].el_?n=this.children_[i+1].el_:K(this.children_[i+1])&&(n=this.children_[i+1])),this.contentEl().insertBefore(a.el(),n)),a},e.removeChild=function(e){if((e="string"==typeof e?this.getChild(e):e)&&this.children_){for(var t,i=!1,n=this.children_.length-1;0<=n;n--)if(this.children_[n]===e){i=!0,this.children_.splice(n,1);break}i&&(e.parentComponent_=null,this.childIndex_[e.id()]=null,this.childNameIndex_[ut(e.name())]=null,this.childNameIndex_[ot(e.name())]=null,(t=e.el())&&t.parentNode===this.contentEl()&&this.contentEl().removeChild(e.el()))}},e.initChildren=function(){var i,t,e,n=this,r=this.options_.children;r&&(i=this.options_,t=s.getComponent("Tech"),(e=Array.isArray(r)?r:Object.keys(r)).concat(Object.keys(this.options_).filter(function(t){return!e.some(function(e){return"string"==typeof e?t===e:t===e.name})})).map(function(e){var t,e="string"==typeof e?r[t=e]||n.options_[t]||{}:(t=e.name,e);return{name:t,opts:e}}).filter(function(e){e=s.getComponent(e.opts.componentClass||ut(e.name));return e&&!t.isTech(e)}).forEach(function(e){var t=e.name,e=e.opts;!1!==(e=void 0!==i[t]?i[t]:e)&&((e=!0===e?{}:e).playerOptions=n.options_.playerOptions,(e=n.addChild(t,e))&&(n[t]=e))}))},e.buildCSSClass=function(){return""},e.ready=function(e,t){if(void 0===t&&(t=!1),e)return this.isReady_?void(t?e.call(this):this.setTimeout(e,1)):(this.readyQueue_=this.readyQueue_||[],void this.readyQueue_.push(e))},e.triggerReady=function(){this.isReady_=!0,this.setTimeout(function(){var e=this.readyQueue_;this.readyQueue_=[],e&&0<e.length&&e.forEach(function(e){e.call(this)},this),this.trigger("ready")},1)},e.$=function(e,t){return Te(e,t||this.contentEl())},e.$$=function(e,t){return Se(e,t||this.contentEl())},e.hasClass=function(e){return ee(this.el_,e)},e.addClass=function(e){te(this.el_,e)},e.removeClass=function(e){ie(this.el_,e)},e.toggleClass=function(e,t){ne(this.el_,e,t)},e.show=function(){this.removeClass("vjs-hidden")},e.hide=function(){this.addClass("vjs-hidden")},e.lockShowing=function(){this.addClass("vjs-lock-showing")},e.unlockShowing=function(){this.removeClass("vjs-lock-showing")},e.getAttribute=function(e){return se(this.el_,e)},e.setAttribute=function(e,t){oe(this.el_,e,t)},e.removeAttribute=function(e){ue(this.el_,e)},e.width=function(e,t){return this.dimension("width",e,t)},e.height=function(e,t){return this.dimension("height",e,t)},e.dimensions=function(e,t){this.width(e,!0),this.height(t)},e.dimension=function(e,t,i){if(void 0!==t)return-1!==(""+(t=null===t||t!=t?0:t)).indexOf("%")||-1!==(""+t).indexOf("px")?this.el_.style[e]=t:this.el_.style[e]="auto"===t?"":t+"px",void(i||this.trigger("componentresize"));if(!this.el_)return 0;t=this.el_.style[e],i=t.indexOf("px");return-1!==i?parseInt(t.slice(0,i),10):parseInt(this.el_["offset"+ut(e)],10)},e.currentDimension=function(e){var t=0;if("width"!==e&&"height"!==e)throw new Error("currentDimension only accepts width or height value");return t=w(this.el_,e),0!==(t=parseFloat(t))&&!isNaN(t)||(e="offset"+ut(e),t=this.el_[e]),t},e.currentDimensions=function(){return{width:this.currentDimension("width"),height:this.currentDimension("height")}},e.currentWidth=function(){return this.currentDimension("width")},e.currentHeight=function(){return this.currentDimension("height")},e.focus=function(){this.el_.focus()},e.blur=function(){this.el_.blur()},e.handleKeyDown=function(e){this.player_&&(ht.isEventKey(e,"Tab")||e.stopPropagation(),this.player_.handleKeyDown(e))},e.handleKeyPress=function(e){this.handleKeyDown(e)},e.emitTapEvents=function(){var i,t=0,n=null;this.on("touchstart",function(e){1===e.touches.length&&(n={pageX:e.touches[0].pageX,pageY:e.touches[0].pageY},t=window.performance.now(),i=!0)}),this.on("touchmove",function(e){var t;1<e.touches.length?i=!1:n&&(t=e.touches[0].pageX-n.pageX,e=e.touches[0].pageY-n.pageY,10<Math.sqrt(t*t+e*e)&&(i=!1))});function e(){i=!1}this.on("touchleave",e),this.on("touchcancel",e),this.on("touchend",function(e){!(n=null)===i&&window.performance.now()-t<200&&(e.preventDefault(),this.trigger("tap"))})},e.enableTouchActivity=function(){var t,i,e;this.player()&&this.player().reportUserActivity&&(t=Ve(this.player(),this.player().reportUserActivity),this.on("touchstart",function(){t(),this.clearInterval(i),i=this.setInterval(t,250)}),e=function(e){t(),this.clearInterval(i)},this.on("touchmove",t),this.on("touchend",e),this.on("touchcancel",e))},e.setTimeout=function(e,t){var i,n=this;return e=Ve(this,e),this.clearTimersOnDispose_(),i=window.setTimeout(function(){n.setTimeoutIds_.has(i)&&n.setTimeoutIds_.delete(i),e()},t),this.setTimeoutIds_.add(i),i},e.clearTimeout=function(e){return this.setTimeoutIds_.has(e)&&(this.setTimeoutIds_.delete(e),window.clearTimeout(e)),e},e.setInterval=function(e,t){e=Ve(this,e),this.clearTimersOnDispose_();t=window.setInterval(e,t);return this.setIntervalIds_.add(t),t},e.clearInterval=function(e){return this.setIntervalIds_.has(e)&&(this.setIntervalIds_.delete(e),window.clearInterval(e)),e},e.requestAnimationFrame=function(e){var t,i=this;return this.supportsRaf_?(this.clearTimersOnDispose_(),e=Ve(this,e),t=window.requestAnimationFrame(function(){i.rafIds_.has(t)&&i.rafIds_.delete(t),e()}),this.rafIds_.add(t),t):this.setTimeout(e,1e3/60)},e.requestNamedAnimationFrame=function(e,t){var i=this;if(!this.namedRafs_.has(e)){this.clearTimersOnDispose_(),t=Ve(this,t);var n=this.requestAnimationFrame(function(){t(),i.namedRafs_.has(e)&&i.namedRafs_.delete(e)});return this.namedRafs_.set(e,n),e}},e.cancelNamedAnimationFrame=function(e){this.namedRafs_.has(e)&&(this.cancelAnimationFrame(this.namedRafs_.get(e)),this.namedRafs_.delete(e))},e.cancelAnimationFrame=function(e){return this.supportsRaf_?(this.rafIds_.has(e)&&(this.rafIds_.delete(e),window.cancelAnimationFrame(e)),e):this.clearTimeout(e)},e.clearTimersOnDispose_=function(){var n=this;this.clearingTimersOnDispose_||(this.clearingTimersOnDispose_=!0,this.one("dispose",function(){[["namedRafs_","cancelNamedAnimationFrame"],["rafIds_","cancelAnimationFrame"],["setTimeoutIds_","clearTimeout"],["setIntervalIds_","clearInterval"]].forEach(function(e){var t=e[0],i=e[1];n[t].forEach(function(e,t){return n[i](t)})}),n.clearingTimersOnDispose_=!1}))},s.registerComponent=function(e,t){if("string"!=typeof e||!e)throw new Error('Illegal component name, "'+e+'"; must be a non-empty string.');var i=s.getComponent("Tech"),n=i&&i.isTech(t),i=s===t||s.prototype.isPrototypeOf(t.prototype);if(n||!i){var r=n?"techs must be registered using Tech.registerTech()":"must be a Component subclass";throw new Error('Illegal component, "'+e+'"; '+r+".")}e=ut(e),s.components_||(s.components_={});r=s.getComponent("Player");if("Player"===e&&r&&r.players){var a=r.players,r=Object.keys(a);if(a&&0<r.length&&r.map(function(e){return a[e]}).every(Boolean))throw new Error("Can not register Player component after player has been created.")}return s.components_[e]=t,s.components_[ot(e)]=t},s.getComponent=function(e){if(e&&s.components_)return s.components_[e]},s}();pt.prototype.supportsRaf_="function"==typeof window.requestAnimationFrame&&"function"==typeof window.cancelAnimationFrame,pt.registerComponent("Component",pt);var ft=function(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e};var mt=function(e,t){e.prototype=Object.create(t.prototype),(e.prototype.constructor=e).__proto__=t};function gt(e,t,i,n){return function(e,t,i){if("number"!=typeof t||t<0||i<t)throw new Error("Failed to execute '"+e+"' on 'TimeRanges': The index provided ("+t+") is non-numeric or out of bounds (0-"+i+").")}(e,n,i.length-1),i[n][t]}function yt(e){var t=void 0===e||0===e.length?{length:0,start:function(){throw new Error("This TimeRanges object is empty")},end:function(){throw new Error("This TimeRanges object is empty")}}:{length:e.length,start:gt.bind(null,"start",0,e),end:gt.bind(null,"end",1,e)};return window.Symbol&&window.Symbol.iterator&&(t[window.Symbol.iterator]=function(){return(e||[]).values()}),t}function vt(e,t){return Array.isArray(e)?yt(e):void 0===e||void 0===t?yt():yt([[e,t]])}function _t(e,t){var i,n,r=0;if(!t)return 0;e&&e.length||(e=vt(0,0));for(var a=0;a<e.length;a++)i=e.start(a),r+=(n=t<(n=e.end(a))?t:n)-i;return r/t}function bt(e){if(e instanceof bt)return e;"number"==typeof e?this.code=e:"string"==typeof e?this.message=e:T(e)&&("number"==typeof e.code&&(this.code=e.code),b(this,e)),this.message||(this.message=bt.defaultMessages[this.code]||"")}bt.prototype.code=0,bt.prototype.message="",bt.prototype.status=null,bt.errorTypes=["MEDIA_ERR_CUSTOM","MEDIA_ERR_ABORTED","MEDIA_ERR_NETWORK","MEDIA_ERR_DECODE","MEDIA_ERR_SRC_NOT_SUPPORTED","MEDIA_ERR_ENCRYPTED"],bt.defaultMessages={1:"You aborted the media playback",2:"A network error caused the media download to fail part-way.",3:"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.",4:"The media could not be loaded, either because the server or network failed or because the format is not supported.",5:"The media is encrypted and we do not have the keys to decrypt it."};for(var Tt=0;Tt<bt.errorTypes.length;Tt++)bt[bt.errorTypes[Tt]]=Tt,bt.prototype[bt.errorTypes[Tt]]=Tt;var St=function(e,t){var i,n=null;try{i=JSON.parse(e,t)}catch(e){n=e}return[n,i]};function wt(e){return null!=e&&"function"==typeof e.then}function Et(e){wt(e)&&e.then(null,function(e){})}function kt(n){return["kind","label","language","id","inBandMetadataTrackDispatchType","mode","src"].reduce(function(e,t,i){return n[t]&&(e[t]=n[t]),e},{cues:n.cues&&Array.prototype.map.call(n.cues,function(e){return{startTime:e.startTime,endTime:e.endTime,text:e.text,id:e.id}})})}var Ct=function(e){var t=e.$$("track"),i=Array.prototype.map.call(t,function(e){return e.track});return Array.prototype.map.call(t,function(e){var t=kt(e.track);return e.src&&(t.src=e.src),t}).concat(Array.prototype.filter.call(e.textTracks(),function(e){return-1===i.indexOf(e)}).map(kt))},It=function(e,i){return e.forEach(function(e){var t=i.addRemoteTextTrack(e).track;!e.src&&e.cues&&e.cues.forEach(function(e){return t.addCue(e)})}),i.textTracks()},xt="vjs-modal-dialog",At=function(n){function e(e,t){var i=n.call(this,e,t)||this;return i.handleKeyDown_=function(e){return i.handleKeyDown(e)},i.close_=function(e){return i.close(e)},i.opened_=i.hasBeenOpened_=i.hasBeenFilled_=!1,i.closeable(!i.options_.uncloseable),i.content(i.options_.content),i.contentEl_=$("div",{className:xt+"-content"},{role:"document"}),i.descEl_=$("p",{className:xt+"-description vjs-control-text",id:i.el().getAttribute("aria-describedby")}),J(i.descEl_,i.description()),i.el_.appendChild(i.descEl_),i.el_.appendChild(i.contentEl_),i}mt(e,n);var t=e.prototype;return t.createEl=function(){return n.prototype.createEl.call(this,"div",{className:this.buildCSSClass(),tabIndex:-1},{"aria-describedby":this.id()+"_description","aria-hidden":"true","aria-label":this.label(),role:"dialog"})},t.dispose=function(){this.contentEl_=null,this.descEl_=null,this.previouslyActiveEl_=null,n.prototype.dispose.call(this)},t.buildCSSClass=function(){return xt+" vjs-hidden "+n.prototype.buildCSSClass.call(this)},t.label=function(){return this.localize(this.options_.label||"Modal Window")},t.description=function(){var e=this.options_.description||this.localize("This is a modal window.");return this.closeable()&&(e+=" "+this.localize("This modal can be closed by pressing the Escape key or activating the close button.")),e},t.open=function(){var e;this.opened_||(e=this.player(),this.trigger("beforemodalopen"),this.opened_=!0,!this.options_.fillAlways&&(this.hasBeenOpened_||this.hasBeenFilled_)||this.fill(),this.wasPlaying_=!e.paused(),this.options_.pauseOnOpen&&this.wasPlaying_&&e.pause(),this.on("keydown",this.handleKeyDown_),this.hadControls_=e.controls(),e.controls(!1),this.show(),this.conditionalFocus_(),this.el().setAttribute("aria-hidden","false"),this.trigger("modalopen"),this.hasBeenOpened_=!0)},t.opened=function(e){return"boolean"==typeof e&&this[e?"open":"close"](),this.opened_},t.close=function(){var e;this.opened_&&(e=this.player(),this.trigger("beforemodalclose"),this.opened_=!1,this.wasPlaying_&&this.options_.pauseOnOpen&&e.play(),this.off("keydown",this.handleKeyDown_),this.hadControls_&&e.controls(!0),this.hide(),this.el().setAttribute("aria-hidden","true"),this.trigger("modalclose"),this.conditionalBlur_(),this.options_.temporary&&this.dispose())},t.closeable=function(e){var t,i;return"boolean"==typeof e&&(t=this.closeable_=!!e,i=this.getChild("closeButton"),t&&!i&&(e=this.contentEl_,this.contentEl_=this.el_,i=this.addChild("closeButton",{controlText:"Close Modal Dialog"}),this.contentEl_=e,this.on(i,"close",this.close_)),!t&&i&&(this.off(i,"close",this.close_),this.removeChild(i),i.dispose())),this.closeable_},t.fill=function(){this.fillWith(this.content())},t.fillWith=function(e){var t=this.contentEl(),i=t.parentNode,n=t.nextSibling;this.trigger("beforemodalfill"),this.hasBeenFilled_=!0,i.removeChild(t),this.empty(),ve(t,e),this.trigger("modalfill"),n?i.insertBefore(t,n):i.appendChild(t);t=this.getChild("closeButton");t&&i.appendChild(t.el_)},t.empty=function(){this.trigger("beforemodalempty"),me(this.contentEl()),this.trigger("modalempty")},t.content=function(e){return"undefined"!=typeof e&&(this.content_=e),this.content_},t.conditionalFocus_=function(){var e=document.activeElement,t=this.player_.el_;this.previouslyActiveEl_=null,!t.contains(e)&&t!==e||(this.previouslyActiveEl_=e,this.focus())},t.conditionalBlur_=function(){this.previouslyActiveEl_&&(this.previouslyActiveEl_.focus(),this.previouslyActiveEl_=null)},t.handleKeyDown=function(e){if(e.stopPropagation(),ht.isEventKey(e,"Escape")&&this.closeable())return e.preventDefault(),void this.close();if(ht.isEventKey(e,"Tab")){for(var t,i=this.focusableEls_(),n=this.el_.querySelector(":focus"),r=0;r<i.length;r++)if(n===i[r]){t=r;break}document.activeElement===this.el_&&(t=0),e.shiftKey&&0===t?(i[i.length-1].focus(),e.preventDefault()):e.shiftKey||t!==i.length-1||(i[0].focus(),e.preventDefault())}},t.focusableEls_=function(){var e=this.el_.querySelectorAll("*");return Array.prototype.filter.call(e,function(e){return(e instanceof window.HTMLAnchorElement||e instanceof window.HTMLAreaElement)&&e.hasAttribute("href")||(e instanceof window.HTMLInputElement||e instanceof window.HTMLSelectElement||e instanceof window.HTMLTextAreaElement||e instanceof window.HTMLButtonElement)&&!e.hasAttribute("disabled")||e instanceof window.HTMLIFrameElement||e instanceof window.HTMLObjectElement||e instanceof window.HTMLEmbedElement||e.hasAttribute("tabindex")&&-1!==e.getAttribute("tabindex")||e.hasAttribute("contenteditable")})},e}(pt);At.prototype.options_={pauseOnOpen:!0,temporary:!0},pt.registerComponent("ModalDialog",At);var Pt,Lt=function(n){function e(e){var t;void 0===e&&(e=[]),(t=n.call(this)||this).tracks_=[],Object.defineProperty(ft(t),"length",{get:function(){return this.tracks_.length}});for(var i=0;i<e.length;i++)t.addTrack(e[i]);return t}mt(e,n);var t=e.prototype;return t.addTrack=function(e){var t=this,i=this.tracks_.length;""+i in this||Object.defineProperty(this,i,{get:function(){return this.tracks_[i]}}),-1===this.tracks_.indexOf(e)&&(this.tracks_.push(e),this.trigger({track:e,type:"addtrack",target:this})),e.labelchange_=function(){t.trigger({track:e,type:"labelchange",target:t})},it(e)&&e.addEventListener("labelchange",e.labelchange_)},t.removeTrack=function(e){for(var t,i=0,n=this.length;i<n;i++)if(this[i]===e){(t=this[i]).off&&t.off(),this.tracks_.splice(i,1);break}t&&this.trigger({track:t,type:"removetrack",target:this})},t.getTrackById=function(e){for(var t=null,i=0,n=this.length;i<n;i++){var r=this[i];if(r.id===e){t=r;break}}return t},e}(ze);for(Pt in Lt.prototype.allowedEvents_={change:"change",addtrack:"addtrack",removetrack:"removetrack",labelchange:"labelchange"},Lt.prototype.allowedEvents_)Lt.prototype["on"+Pt]=null;function Dt(e,t){for(var i=0;i<e.length;i++)Object.keys(e[i]).length&&t.id!==e[i].id&&(e[i].enabled=!1)}function Ot(e,t){for(var i=0;i<e.length;i++)Object.keys(e[i]).length&&t.id!==e[i].id&&(e[i].selected=!1)}function Rt(e){var t=["protocol","hostname","port","pathname","search","hash","host"],i=document.createElement("a");i.href=e;for(var n={},r=0;r<t.length;r++)n[t[r]]=i[t[r]];return"http:"===n.protocol&&(n.host=n.host.replace(/:80$/,"")),"https:"===n.protocol&&(n.host=n.host.replace(/:443$/,"")),n.protocol||(n.protocol=window.location.protocol),n.host||(n.host=window.location.host),n}function Mt(e){var t;return e.match(/^https?:\/\//)||((t=document.createElement("a")).href=e,e=t.href),e}function Nt(e){if("string"==typeof e){e=/^(\/?)([\s\S]*?)((?:\.{1,2}|[^\/]+?)(\.([^\.\/\?]+)))(?:[\/]*|[\?].*)$/.exec(e);if(e)return e.pop().toLowerCase()}return""}function Ut(e,t){return void 0===t&&(t=window.location),(":"===(e=Rt(e)).protocol?t:e).protocol+e.host!==t.protocol+t.host}var Bt=function(n){function e(e){for(var t,i=(e=void 0===e?[]:e).length-1;0<=i;i--)if(e[i].enabled){Dt(e,e[i]);break}return(t=n.call(this,e)||this).changing_=!1,t}mt(e,n);var t=e.prototype;return t.addTrack=function(e){var t=this;e.enabled&&Dt(this,e),n.prototype.addTrack.call(this,e),e.addEventListener&&(e.enabledChange_=function(){t.changing_||(t.changing_=!0,Dt(t,e),t.changing_=!1,t.trigger("change"))},e.addEventListener("enabledchange",e.enabledChange_))},t.removeTrack=function(e){n.prototype.removeTrack.call(this,e),e.removeEventListener&&e.enabledChange_&&(e.removeEventListener("enabledchange",e.enabledChange_),e.enabledChange_=null)},e}(Lt),Ft=function(n){function e(e){for(var t,i=(e=void 0===e?[]:e).length-1;0<=i;i--)if(e[i].selected){Ot(e,e[i]);break}return(t=n.call(this,e)||this).changing_=!1,Object.defineProperty(ft(t),"selectedIndex",{get:function(){for(var e=0;e<this.length;e++)if(this[e].selected)return e;return-1},set:function(){}}),t}mt(e,n);var t=e.prototype;return t.addTrack=function(e){var t=this;e.selected&&Ot(this,e),n.prototype.addTrack.call(this,e),e.addEventListener&&(e.selectedChange_=function(){t.changing_||(t.changing_=!0,Ot(t,e),t.changing_=!1,t.trigger("change"))},e.addEventListener("selectedchange",e.selectedChange_))},t.removeTrack=function(e){n.prototype.removeTrack.call(this,e),e.removeEventListener&&e.selectedChange_&&(e.removeEventListener("selectedchange",e.selectedChange_),e.selectedChange_=null)},e}(Lt),k=function(i){function e(){return i.apply(this,arguments)||this}mt(e,i);var t=e.prototype;return t.addTrack=function(e){var t=this;i.prototype.addTrack.call(this,e),this.queueChange_||(this.queueChange_=function(){return t.queueTrigger("change")}),this.triggerSelectedlanguagechange||(this.triggerSelectedlanguagechange_=function(){return t.trigger("selectedlanguagechange")}),e.addEventListener("modechange",this.queueChange_);-1===["metadata","chapters"].indexOf(e.kind)&&e.addEventListener("modechange",this.triggerSelectedlanguagechange_)},t.removeTrack=function(e){i.prototype.removeTrack.call(this,e),e.removeEventListener&&(this.queueChange_&&e.removeEventListener("modechange",this.queueChange_),this.selectedlanguagechange_&&e.removeEventListener("modechange",this.triggerSelectedlanguagechange_))},e}(Lt),jt=function(){function e(e){void 0===e&&(e=[]),this.trackElements_=[],Object.defineProperty(this,"length",{get:function(){return this.trackElements_.length}});for(var t=0,i=e.length;t<i;t++)this.addTrackElement_(e[t])}var t=e.prototype;return t.addTrackElement_=function(e){var t=this.trackElements_.length;""+t in this||Object.defineProperty(this,t,{get:function(){return this.trackElements_[t]}}),-1===this.trackElements_.indexOf(e)&&this.trackElements_.push(e)},t.getTrackElementByTrack_=function(e){for(var t,i=0,n=this.trackElements_.length;i<n;i++)if(e===this.trackElements_[i].track){t=this.trackElements_[i];break}return t},t.removeTrackElement_=function(e){for(var t=0,i=this.trackElements_.length;t<i;t++)if(e===this.trackElements_[t]){this.trackElements_[t].track&&"function"==typeof this.trackElements_[t].track.off&&this.trackElements_[t].track.off(),"function"==typeof this.trackElements_[t].off&&this.trackElements_[t].off(),this.trackElements_.splice(t,1);break}},e}(),Ht=function(){function t(e){t.prototype.setCues_.call(this,e),Object.defineProperty(this,"length",{get:function(){return this.length_}})}var e=t.prototype;return e.setCues_=function(e){var t=this.length||0,i=0,n=e.length;this.cues_=e,this.length_=e.length;function r(e){""+e in this||Object.defineProperty(this,""+e,{get:function(){return this.cues_[e]}})}if(t<n)for(i=t;i<n;i++)r.call(this,i)},e.getCueById=function(e){for(var t=null,i=0,n=this.length;i<n;i++){var r=this[i];if(r.id===e){t=r;break}}return t},t}(),qt={alternative:"alternative",captions:"captions",main:"main",sign:"sign",subtitles:"subtitles",commentary:"commentary"},Vt={alternative:"alternative",descriptions:"descriptions",main:"main","main-desc":"main-desc",translation:"translation",commentary:"commentary"},Wt={subtitles:"subtitles",captions:"captions",descriptions:"descriptions",chapters:"chapters",metadata:"metadata"},Gt={disabled:"disabled",hidden:"hidden",showing:"showing"},C=function(a){function e(e){void 0===e&&(e={});var t,i=a.call(this)||this,n={id:e.id||"vjs_track_"+Pe++,kind:e.kind||"",language:e.language||""},r=e.label||"";for(t in n)!function(e){Object.defineProperty(ft(i),e,{get:function(){return n[e]},set:function(){}})}(t);return Object.defineProperty(ft(i),"label",{get:function(){return r},set:function(e){e!==r&&(r=e,this.trigger("labelchange"))}}),i}return mt(e,a),e}(ze),zt=Object.freeze({__proto__:null,parseUrl:Rt,getAbsoluteURL:Mt,getFileExtension:Nt,isCrossOrigin:Ut}),Xt="undefined"!=typeof window?window:"undefined"!=typeof f?f:"undefined"!=typeof self?self:{},Kt=Xt,Yt=function(e){if(!e)return!1;var t=Qt.call(e);return"[object Function]"===t||"function"==typeof e&&"[object RegExp]"!==t||"undefined"!=typeof window&&(e===window.setTimeout||e===window.alert||e===window.confirm||e===window.prompt)},Qt=Object.prototype.toString;ei.httpHandler=function(n,r){return void 0===r&&(r=!1),function(e,t,i){if(e)n(e);else if(400<=t.statusCode&&t.statusCode<=599){e=i;if(r)if(Kt.TextDecoder){t=function(e){void 0===e&&(e="");return e.toLowerCase().split(";").reduce(function(e,t){var i=t.split("="),t=i[0],i=i[1];return"charset"===t.trim()?i.trim():e},"utf-8")}(t.headers&&t.headers["content-type"]);try{e=new TextDecoder(t).decode(i)}catch(e){}}else e=String.fromCharCode.apply(null,new Uint8Array(i));n({cause:e})}else n(null,i)}};
 /**
    * @license
    * slighly modified parse-headers 2.0.2 <https://github.com/kesla/parse-headers/>
    * Copyright (c) 2014 David Björklund
    * Available under the MIT license
    * <https://github.com/kesla/parse-headers/blob/master/LICENCE>
    */
 var $t=function(e){var n={};return e&&e.trim().split("\n").forEach(function(e){var t=e.indexOf(":"),i=e.slice(0,t).trim().toLowerCase(),t=e.slice(t+1).trim();"undefined"==typeof n[i]?n[i]=t:Array.isArray(n[i])?n[i].push(t):n[i]=[n[i],t]}),n},Jt=ei,I=ei;function Zt(e,t,i){var n=e;return Yt(t)?(i=t,"string"==typeof e&&(n={uri:e})):n=g({},t,{uri:e}),n.callback=i,n}function ei(e,t,i){return ti(t=Zt(e,t,i))}function ti(n){if("undefined"==typeof n.callback)throw new Error("callback argument missing");var r=!1,a=function(e,t,i){r||(r=!0,n.callback(e,t,i))};function s(){var e=void 0,e=l.response||l.responseText||function(e){try{if("document"===e.responseType)return e.responseXML;var t=e.responseXML&&"parsererror"===e.responseXML.documentElement.nodeName;if(""===e.responseType&&!t)return e.responseXML}catch(e){}return null}(l);if(m)try{e=JSON.parse(e)}catch(e){}return e}function t(e){return clearTimeout(u),(e=!(e instanceof Error)?new Error(""+(e||"Unknown XMLHttpRequest Error")):e).statusCode=0,a(e,g)}function e(){if(!o){clearTimeout(u);var e=n.useXDR&&void 0===l.status?200:1223===l.status?204:l.status,t=g,i=null;return 0!==e?(t={body:s(),statusCode:e,method:d,headers:{},url:c,rawRequest:l},l.getAllResponseHeaders&&(t.headers=$t(l.getAllResponseHeaders()))):i=new Error("Internal XMLHttpRequest Error"),a(i,t,t.body)}}var i,o,u,l=n.xhr||null,c=(l=l||new(n.cors||n.useXDR?ei.XDomainRequest:ei.XMLHttpRequest)).url=n.uri||n.url,d=l.method=n.method||"GET",h=n.body||n.data,p=l.headers=n.headers||{},f=!!n.sync,m=!1,g={body:void 0,headers:{},statusCode:0,method:d,url:c,rawRequest:l};if("json"in n&&!1!==n.json&&(m=!0,p.accept||p.Accept||(p.Accept="application/json"),"GET"!==d&&"HEAD"!==d&&(p["content-type"]||p["Content-Type"]||(p["Content-Type"]="application/json"),h=JSON.stringify(!0===n.json?h:n.json))),l.onreadystatechange=function(){4===l.readyState&&setTimeout(e,0)},l.onload=e,l.onerror=t,l.onprogress=function(){},l.onabort=function(){o=!0},l.ontimeout=t,l.open(d,c,!f,n.username,n.password),f||(l.withCredentials=!!n.withCredentials),!f&&0<n.timeout&&(u=setTimeout(function(){var e;o||(o=!0,l.abort("timeout"),(e=new Error("XMLHttpRequest timeout")).code="ETIMEDOUT",t(e))},n.timeout)),l.setRequestHeader)for(i in p)p.hasOwnProperty(i)&&l.setRequestHeader(i,p[i]);else if(n.headers&&!function(e){for(var t in e)if(e.hasOwnProperty(t))return;return 1}(n.headers))throw new Error("Headers cannot be set on an XDomainRequest object");return"responseType"in n&&(l.responseType=n.responseType),"beforeSend"in n&&"function"==typeof n.beforeSend&&n.beforeSend(l),l.send(h||null),l}ei.XMLHttpRequest=Kt.XMLHttpRequest||function(){},ei.XDomainRequest="withCredentials"in new ei.XMLHttpRequest?ei.XMLHttpRequest:Kt.XDomainRequest,function(e,t){for(var i=0;i<e.length;i++)t(e[i])}(["get","put","post","patch","head","delete"],function(n){ei["delete"===n?"del":n]=function(e,t,i){return(t=Zt(e,t,i)).method=n.toUpperCase(),ti(t)}}),Jt.default=I;function ii(e,t){var i=new window.WebVTT.Parser(window,window.vttjs,window.WebVTT.StringDecoder()),n=[];i.oncue=function(e){t.addCue(e)},i.onparsingerror=function(e){n.push(e)},i.onflush=function(){t.trigger({type:"loadeddata",target:t})},i.parse(e),0<n.length&&(window.console&&window.console.groupCollapsed&&window.console.groupCollapsed("Text Track parsing errors for "+t.src),n.forEach(function(e){return h.error(e)}),window.console&&window.console.groupEnd&&window.console.groupEnd()),i.flush()}function ni(e,n){var t={uri:e};(e=Ut(e))&&(t.cors=e),(e="use-credentials"===n.tech_.crossOrigin())&&(t.withCredentials=e),Jt(t,Ve(this,function(e,t,i){return e?h.error(e,t):(n.loaded_=!0,void("function"!=typeof window.WebVTT?n.tech_&&n.tech_.any(["vttjsloaded","vttjserror"],function(e){return"vttjserror"!==e.type?ii(i,n):void h.error("vttjs failed to load, stopping trying to process "+n.src)}):ii(i,n)))}))}var ri=function(u){function e(e){var t;if(!(e=void 0===e?{}:e).tech)throw new Error("A tech was not provided.");var e=lt(e,{kind:Wt[e.kind]||"subtitles",language:e.language||e.srclang||""}),i=Gt[e.mode]||"disabled",n=e.default;"metadata"!==e.kind&&"chapters"!==e.kind||(i="hidden"),(t=u.call(this,e)||this).tech_=e.tech,t.cues_=[],t.activeCues_=[],t.preload_=!1!==t.tech_.preloadTextTracks;var r=new Ht(t.cues_),s=new Ht(t.activeCues_),o=!1,a=Ve(ft(t),function(){this.tech_.isReady_&&!this.tech_.isDisposed()&&(this.activeCues=this.activeCues,o&&(this.trigger("cuechange"),o=!1))});return t.tech_.one("dispose",function(){t.tech_.off("timeupdate",a)}),"disabled"!==i&&t.tech_.on("timeupdate",a),Object.defineProperties(ft(t),{default:{get:function(){return n},set:function(){}},mode:{get:function(){return i},set:function(e){Gt[e]&&i!==e&&(i=e,this.preload_||"disabled"===i||0!==this.cues.length||ni(this.src,this),this.tech_.off("timeupdate",a),"disabled"!==i&&this.tech_.on("timeupdate",a),this.trigger("modechange"))}},cues:{get:function(){return this.loaded_?r:null},set:function(){}},activeCues:{get:function(){if(!this.loaded_)return null;if(0===this.cues.length)return s;for(var e=this.tech_.currentTime(),t=[],i=0,n=this.cues.length;i<n;i++){var r=this.cues[i];(r.startTime<=e&&r.endTime>=e||r.startTime===r.endTime&&r.startTime<=e&&r.startTime+.5>=e)&&t.push(r)}if(o=!1,t.length!==this.activeCues_.length)o=!0;else for(var a=0;a<t.length;a++)-1===this.activeCues_.indexOf(t[a])&&(o=!0);return this.activeCues_=t,s.setCues_(this.activeCues_),s},set:function(){}}}),e.src?(t.src=e.src,t.preload_||(t.loaded_=!0),(t.preload_||"subtitles"!==e.kind&&"captions"!==e.kind)&&ni(t.src,ft(t))):t.loaded_=!0,t}mt(e,u);var t=e.prototype;return t.addCue=function(e){var t=e;if(window.vttjs&&!(e instanceof window.vttjs.VTTCue)){for(var i in t=new window.vttjs.VTTCue(e.startTime,e.endTime,e.text),e)i in t||(t[i]=e[i]);t.id=e.id,t.originalCue_=e}for(var n=this.tech_.textTracks(),r=0;r<n.length;r++)n[r]!==this&&n[r].removeCue(t);this.cues_.push(t),this.cues.setCues_(this.cues_)},t.removeCue=function(e){for(var t=this.cues_.length;t--;){var i=this.cues_[t];if(i===e||i.originalCue_&&i.originalCue_===e){this.cues_.splice(t,1),this.cues.setCues_(this.cues_);break}}},e}(C);ri.prototype.allowedEvents_={cuechange:"cuechange"};x=function(n){function e(e){var t=lt(e=void 0===e?{}:e,{kind:Vt[e.kind]||""}),e=n.call(this,t)||this,i=!1;return Object.defineProperty(ft(e),"enabled",{get:function(){return i},set:function(e){"boolean"==typeof e&&e!==i&&(i=e,this.trigger("enabledchange"))}}),t.enabled&&(e.enabled=t.enabled),e.loaded_=!0,e}return mt(e,n),e}(C),U=function(n){function e(e){var t=lt(e=void 0===e?{}:e,{kind:qt[e.kind]||""}),e=n.call(this,t)||this,i=!1;return Object.defineProperty(ft(e),"selected",{get:function(){return i},set:function(e){"boolean"==typeof e&&e!==i&&(i=e,this.trigger("selectedchange"))}}),t.selected&&(e.selected=t.selected),e}return mt(e,n),e}(C),j=function(r){function e(e){var t;void 0===e&&(e={});var i=r.call(this)||this,n=new ri(e);return i.kind=n.kind,i.src=n.src,i.srclang=n.language,i.label=n.label,i.default=n.default,Object.defineProperties(ft(i),{readyState:{get:function(){return t}},track:{get:function(){return n}}}),t=0,n.addEventListener("loadeddata",function(){t=2,i.trigger({type:"load",target:ft(i)})}),i}return mt(e,r),e}(ze);j.prototype.allowedEvents_={load:"load"},j.NONE=0,j.LOADING=1,j.LOADED=2,j.ERROR=3;var ai={audio:{ListClass:Bt,TrackClass:x,capitalName:"Audio"},video:{ListClass:Ft,TrackClass:U,capitalName:"Video"},text:{ListClass:k,TrackClass:ri,capitalName:"Text"}};Object.keys(ai).forEach(function(e){ai[e].getterName=e+"Tracks",ai[e].privateName=e+"Tracks_"});var si={remoteText:{ListClass:k,TrackClass:ri,capitalName:"RemoteText",getterName:"remoteTextTracks",privateName:"remoteTextTracks_"},remoteTextEl:{ListClass:jt,TrackClass:j,capitalName:"RemoteTextTrackEls",getterName:"remoteTextTrackEls",privateName:"remoteTextTrackEls_"}},oi=g({},ai,si);si.names=Object.keys(si),ai.names=Object.keys(ai),oi.names=[].concat(si.names).concat(ai.names);var ui="undefined"!=typeof f?f:"undefined"!=typeof window?window:{},li="undefined"!=typeof document?document:(li=ui["__GLOBAL_DOCUMENT_CACHE@4"])||(ui["__GLOBAL_DOCUMENT_CACHE@4"]={}),Xt=li,ci=Object.create||function(e){if(1!==arguments.length)throw new Error("Object.create shim only accepts one parameter.");return di.prototype=e,new di};function di(){}function hi(e,t){this.name="ParsingError",this.code=e.code,this.message=t||e.message}function pi(e){function t(e,t,i,n){return 3600*(0|e)+60*(0|t)+(0|i)+(0|n)/1e3}e=e.match(/^(\d+):(\d{1,2})(:\d{1,2})?\.(\d{3})/);return e?e[3]?t(e[1],e[2],e[3].replace(":",""),e[4]):59<e[1]?t(e[1],e[2],0,e[4]):t(0,e[1],e[2],e[4]):null}function fi(){this.values=ci(null)}function mi(e,t,i,n){var r,a,s=n?e.split(n):[e];for(r in s)"string"==typeof s[r]&&(2===(a=s[r].split(i)).length&&t(a[0],a[1]))}function gi(t,e,s){var i=t;function n(){var e=pi(t);if(null===e)throw new hi(hi.Errors.BadTimeStamp,"Malformed timestamp: "+i);return t=t.replace(/^[^\sa-zA-Z-]+/,""),e}function r(){t=t.replace(/^\s+/,"")}if(r(),e.startTime=n(),r(),"--\x3e"!==t.substr(0,3))throw new hi(hi.Errors.BadTimeStamp,"Malformed time stamp (time stamps must be separated by '--\x3e'): "+i);t=t.substr(3),r(),e.endTime=n(),r(),function(e,t){var a=new fi;mi(e,function(e,t){switch(e){case"region":for(var i=s.length-1;0<=i;i--)if(s[i].id===t){a.set(e,s[i].region);break}break;case"vertical":a.alt(e,t,["rl","lr"]);break;case"line":var n=t.split(","),r=n[0];a.integer(e,r),a.percent(e,r)&&a.set("snapToLines",!1),a.alt(e,r,["auto"]),2===n.length&&a.alt("lineAlign",n[1],["start","center","end"]);break;case"position":n=t.split(","),a.percent(e,n[0]),2===n.length&&a.alt("positionAlign",n[1],["start","center","end"]);break;case"size":a.percent(e,t);break;case"align":a.alt(e,t,["start","center","end","left","right"])}},/:/,/\s/),t.region=a.get("region",null),t.vertical=a.get("vertical","");try{t.line=a.get("line","auto")}catch(e){}t.lineAlign=a.get("lineAlign","start"),t.snapToLines=a.get("snapToLines",!0),t.size=a.get("size",100);try{t.align=a.get("align","center")}catch(e){t.align=a.get("align","middle")}try{t.position=a.get("position","auto")}catch(e){t.position=a.get("position",{start:0,left:0,center:50,middle:50,end:100,right:100},t.align)}t.positionAlign=a.get("positionAlign",{start:"start",left:"start",center:"center",middle:"center",end:"end",right:"end"},t.align)}(t,e)}((hi.prototype=ci(Error.prototype)).constructor=hi).Errors={BadSignature:{code:0,message:"Malformed WebVTT signature."},BadTimeStamp:{code:1,message:"Malformed time stamp."}},fi.prototype={set:function(e,t){this.get(e)||""===t||(this.values[e]=t)},get:function(e,t,i){return i?this.has(e)?this.values[e]:t[i]:this.has(e)?this.values[e]:t},has:function(e){return e in this.values},alt:function(e,t,i){for(var n=0;n<i.length;++n)if(t===i[n]){this.set(e,t);break}},integer:function(e,t){/^-?\d+$/.test(t)&&this.set(e,parseInt(t,10))},percent:function(e,t){return!!(t.match(/^([\d]{1,3})(\.[\d]*)?%$/)&&0<=(t=parseFloat(t))&&t<=100)&&(this.set(e,t),!0)}};var yi=Xt.createElement&&Xt.createElement("textarea"),vi={c:"span",i:"i",b:"b",u:"u",ruby:"ruby",rt:"rt",v:"span",lang:"span"},_i={white:"rgba(255,255,255,1)",lime:"rgba(0,255,0,1)",cyan:"rgba(0,255,255,1)",red:"rgba(255,0,0,1)",yellow:"rgba(255,255,0,1)",magenta:"rgba(255,0,255,1)",blue:"rgba(0,0,255,1)",black:"rgba(0,0,0,1)"},bi={v:"title",lang:"lang"},Ti={rt:"ruby"};function Si(e,t){for(var i,n,r,a,s,o,u,l,c,d,h=e.document.createElement("div"),p=h,f=[];null!==(i=function(){if(!t)return null;var e=t.match(/^([^<]*)(<[^>]*>?)?/);return e=e[1]||e[2],t=t.substr(e.length),e}());)"<"!==i[0]?p.appendChild(e.document.createTextNode((s=i,yi.innerHTML=s,s=yi.textContent,yi.textContent="",s))):"/"!==i[1]?(a=pi(i.substr(1,i.length-2)))?(n=e.document.createProcessingInstruction("timestamp",a),p.appendChild(n)):(r=i.match(/^<([^.\s/0-9>]+)(\.[^\s\\>]+)?([^>\\]+)?(\\?)>?$/))&&(l=r[1],c=r[3],d=void 0,d=vi[l],(n=d?(d=e.document.createElement(d),(l=bi[l])&&c&&(d[l]=c.trim()),d):null)&&(o=p,Ti[(u=n).localName]&&Ti[u.localName]!==o.localName||(r[2]&&((a=r[2].split(".")).forEach(function(e){var t=/^bg_/.test(e),e=t?e.slice(3):e;_i.hasOwnProperty(e)&&(e=_i[e],n.style[t?"background-color":"color"]=e)}),n.className=a.join(" ")),f.push(r[1]),p.appendChild(n),p=n))):f.length&&f[f.length-1]===i.substr(2).replace(">","")&&(f.pop(),p=p.parentNode);return h}var wi=[[1470,1470],[1472,1472],[1475,1475],[1478,1478],[1488,1514],[1520,1524],[1544,1544],[1547,1547],[1549,1549],[1563,1563],[1566,1610],[1645,1647],[1649,1749],[1765,1766],[1774,1775],[1786,1805],[1807,1808],[1810,1839],[1869,1957],[1969,1969],[1984,2026],[2036,2037],[2042,2042],[2048,2069],[2074,2074],[2084,2084],[2088,2088],[2096,2110],[2112,2136],[2142,2142],[2208,2208],[2210,2220],[8207,8207],[64285,64285],[64287,64296],[64298,64310],[64312,64316],[64318,64318],[64320,64321],[64323,64324],[64326,64449],[64467,64829],[64848,64911],[64914,64967],[65008,65020],[65136,65140],[65142,65276],[67584,67589],[67592,67592],[67594,67637],[67639,67640],[67644,67644],[67647,67669],[67671,67679],[67840,67867],[67872,67897],[67903,67903],[67968,68023],[68030,68031],[68096,68096],[68112,68115],[68117,68119],[68121,68147],[68160,68167],[68176,68184],[68192,68223],[68352,68405],[68416,68437],[68440,68466],[68472,68479],[68608,68680],[126464,126467],[126469,126495],[126497,126498],[126500,126500],[126503,126503],[126505,126514],[126516,126519],[126521,126521],[126523,126523],[126530,126530],[126535,126535],[126537,126537],[126539,126539],[126541,126543],[126545,126546],[126548,126548],[126551,126551],[126553,126553],[126555,126555],[126557,126557],[126559,126559],[126561,126562],[126564,126564],[126567,126570],[126572,126578],[126580,126583],[126585,126588],[126590,126590],[126592,126601],[126603,126619],[126625,126627],[126629,126633],[126635,126651],[1114109,1114109]];function Ei(e){var t=[],i="";if(!e||!e.childNodes)return"ltr";function a(e,t){for(var i=t.childNodes.length-1;0<=i;i--)e.push(t.childNodes[i])}for(a(t,e);i=function e(t){if(!t||!t.length)return null;var i=t.pop(),n=i.textContent||i.innerText;if(n){var r=n.match(/^.*(\n|\r)/);return r?r[t.length=0]:n}return"ruby"===i.tagName?e(t):i.childNodes?(a(t,i),e(t)):void 0}(t);)for(var n=0;n<i.length;n++)if(function(e){for(var t=0;t<wi.length;t++){var i=wi[t];if(e>=i[0]&&e<=i[1])return 1}}(i.charCodeAt(n)))return"rtl";return"ltr"}function ki(){}function Ci(e,t,i){ki.call(this),this.cue=t,this.cueDiv=Si(e,t.text);var n={color:"rgba(255, 255, 255, 1)",backgroundColor:"rgba(0, 0, 0, 0.8)",position:"relative",left:0,right:0,top:0,bottom:0,display:"inline",writingMode:""===t.vertical?"horizontal-tb":"lr"===t.vertical?"vertical-lr":"vertical-rl",unicodeBidi:"plaintext"};this.applyStyles(n,this.cueDiv),this.div=e.document.createElement("div"),n={direction:Ei(this.cueDiv),writingMode:""===t.vertical?"horizontal-tb":"lr"===t.vertical?"vertical-lr":"vertical-rl",unicodeBidi:"plaintext",textAlign:"middle"===t.align?"center":t.align,font:i.font,whiteSpace:"pre-line",position:"absolute"},this.applyStyles(n),this.div.appendChild(this.cueDiv);var r=0;switch(t.positionAlign){case"start":r=t.position;break;case"center":r=t.position-t.size/2;break;case"end":r=t.position-t.size}""===t.vertical?this.applyStyles({left:this.formatStyle(r,"%"),width:this.formatStyle(t.size,"%")}):this.applyStyles({top:this.formatStyle(r,"%"),height:this.formatStyle(t.size,"%")}),this.move=function(e){this.applyStyles({top:this.formatStyle(e.top,"px"),bottom:this.formatStyle(e.bottom,"px"),left:this.formatStyle(e.left,"px"),right:this.formatStyle(e.right,"px"),height:this.formatStyle(e.height,"px"),width:this.formatStyle(e.width,"px")})}}function Ii(e){var t,i,n,r;e.div&&(t=e.div.offsetHeight,i=e.div.offsetWidth,n=e.div.offsetTop,r=(r=e.div.childNodes)&&(r=r[0])&&r.getClientRects&&r.getClientRects(),e=e.div.getBoundingClientRect(),r=r?Math.max(r[0]&&r[0].height||0,e.height/r.length):0),this.left=e.left,this.right=e.right,this.top=e.top||n,this.height=e.height||t,this.bottom=e.bottom||n+(e.height||t),this.width=e.width||i,this.lineHeight=void 0!==r?r:e.lineHeight}function xi(e,t,o,u){var i,n=new Ii(t),r=t.cue,a=function(e){if("number"==typeof e.line&&(e.snapToLines||0<=e.line&&e.line<=100))return e.line;if(!e.track||!e.track.textTrackList||!e.track.textTrackList.mediaElement)return-1;for(var t=e.track,i=t.textTrackList,n=0,r=0;r<i.length&&i[r]!==t;r++)"showing"===i[r].mode&&n++;return-1*++n}(r),s=[];if(r.snapToLines){switch(r.vertical){case"":s=["+y","-y"],i="height";break;case"rl":s=["+x","-x"],i="width";break;case"lr":s=["-x","+x"],i="width"}var l=n.lineHeight,c=l*Math.round(a),d=o[i]+l,h=s[0];Math.abs(c)>d&&(c=c<0?-1:1,c*=Math.ceil(d/l)*l),a<0&&(c+=""===r.vertical?o.height:o.width,s=s.reverse()),n.move(h,c)}else{var p=n.lineHeight/o.height*100;switch(r.lineAlign){case"center":a-=p/2;break;case"end":a-=p}switch(r.vertical){case"":t.applyStyles({top:t.formatStyle(a,"%")});break;case"rl":t.applyStyles({left:t.formatStyle(a,"%")});break;case"lr":t.applyStyles({right:t.formatStyle(a,"%")})}s=["+y","-x","+x","-y"],n=new Ii(t)}n=function(e,t){for(var i,n=new Ii(e),r=1,a=0;a<t.length;a++){for(;e.overlapsOppositeAxis(o,t[a])||e.within(o)&&e.overlapsAny(u);)e.move(t[a]);if(e.within(o))return e;var s=e.intersectPercentage(o);s<r&&(i=new Ii(e),r=s),e=new Ii(n)}return i||n}(n,s);t.move(n.toCSSCompatValues(o))}function Ai(){}ki.prototype.applyStyles=function(e,t){for(var i in t=t||this.div,e)e.hasOwnProperty(i)&&(t.style[i]=e[i])},ki.prototype.formatStyle=function(e,t){return 0===e?0:e+t},(Ci.prototype=ci(ki.prototype)).constructor=Ci,Ii.prototype.move=function(e,t){switch(t=void 0!==t?t:this.lineHeight,e){case"+x":this.left+=t,this.right+=t;break;case"-x":this.left-=t,this.right-=t;break;case"+y":this.top+=t,this.bottom+=t;break;case"-y":this.top-=t,this.bottom-=t}},Ii.prototype.overlaps=function(e){return this.left<e.right&&this.right>e.left&&this.top<e.bottom&&this.bottom>e.top},Ii.prototype.overlapsAny=function(e){for(var t=0;t<e.length;t++)if(this.overlaps(e[t]))return!0;return!1},Ii.prototype.within=function(e){return this.top>=e.top&&this.bottom<=e.bottom&&this.left>=e.left&&this.right<=e.right},Ii.prototype.overlapsOppositeAxis=function(e,t){switch(t){case"+x":return this.left<e.left;case"-x":return this.right>e.right;case"+y":return this.top<e.top;case"-y":return this.bottom>e.bottom}},Ii.prototype.intersectPercentage=function(e){return Math.max(0,Math.min(this.right,e.right)-Math.max(this.left,e.left))*Math.max(0,Math.min(this.bottom,e.bottom)-Math.max(this.top,e.top))/(this.height*this.width)},Ii.prototype.toCSSCompatValues=function(e){return{top:this.top-e.top,bottom:e.bottom-this.bottom,left:this.left-e.left,right:e.right-this.right,height:this.height,width:this.width}},Ii.getSimpleBoxPosition=function(e){var t=e.div?e.div.offsetHeight:e.tagName?e.offsetHeight:0,i=e.div?e.div.offsetWidth:e.tagName?e.offsetWidth:0,n=e.div?e.div.offsetTop:e.tagName?e.offsetTop:0;return{left:(e=e.div?e.div.getBoundingClientRect():e.tagName?e.getBoundingClientRect():e).left,right:e.right,top:e.top||n,height:e.height||t,bottom:e.bottom||n+(e.height||t),width:e.width||i}},Ai.StringDecoder=function(){return{decode:function(e){if(!e)return"";if("string"!=typeof e)throw new Error("Error - expected string data.");return decodeURIComponent(encodeURIComponent(e))}}},Ai.convertCueToDOMTree=function(e,t){return e&&t?Si(e,t):null};Ai.processCues=function(n,r,e){if(!n||!r||!e)return null;for(;e.firstChild;)e.removeChild(e.firstChild);var a=n.document.createElement("div");if(a.style.position="absolute",a.style.left="0",a.style.right="0",a.style.top="0",a.style.bottom="0",a.style.margin="1.5%",e.appendChild(a),function(e){for(var t=0;t<e.length;t++)if(e[t].hasBeenReset||!e[t].displayState)return 1}(r)){var s=[],o=Ii.getSimpleBoxPosition(a),u={font:Math.round(.05*o.height*100)/100+"px sans-serif"};!function(){for(var e,t,i=0;i<r.length;i++)t=r[i],e=new Ci(n,t,u),a.appendChild(e.div),xi(0,e,o,s),t.displayState=e.div,s.push(Ii.getSimpleBoxPosition(e))}()}else for(var t=0;t<r.length;t++)a.appendChild(r[t].displayState)},(Ai.Parser=function(e,t,i){i||(i=t,t={}),t=t||{},this.window=e,this.vttjs=t,this.state="INITIAL",this.buffer="",this.decoder=i||new TextDecoder("utf8"),this.regionList=[]}).prototype={reportOrThrowError:function(e){if(!(e instanceof hi))throw e;this.onparsingerror&&this.onparsingerror(e)},parse:function(e){var n=this;function t(){for(var e=n.buffer,t=0;t<e.length&&"\r"!==e[t]&&"\n"!==e[t];)++t;var i=e.substr(0,t);return"\r"===e[t]&&++t,"\n"===e[t]&&++t,n.buffer=e.substr(t),i}function i(e){e.match(/X-TIMESTAMP-MAP/)?mi(e,function(e,t){var i;"X-TIMESTAMP-MAP"===e&&(t=t,i=new fi,mi(t,function(e,t){switch(e){case"MPEGT":i.integer(e+"S",t);break;case"LOCA":i.set(e+"L",pi(t))}},/[^\d]:/,/,/),n.ontimestampmap&&n.ontimestampmap({MPEGTS:i.get("MPEGTS"),LOCAL:i.get("LOCAL")}))},/=/):mi(e,function(e,t){var r;"Region"===e&&(t=t,r=new fi,mi(t,function(e,t){switch(e){case"id":r.set(e,t);break;case"width":r.percent(e,t);break;case"lines":r.integer(e,t);break;case"regionanchor":case"viewportanchor":var i=t.split(",");if(2!==i.length)break;var n=new fi;if(n.percent("x",i[0]),n.percent("y",i[1]),!n.has("x")||!n.has("y"))break;r.set(e+"X",n.get("x")),r.set(e+"Y",n.get("y"));break;case"scroll":r.alt(e,t,["up"])}},/=/,/\s/),r.has("id")&&((t=new(n.vttjs.VTTRegion||n.window.VTTRegion)).width=r.get("width",100),t.lines=r.get("lines",3),t.regionAnchorX=r.get("regionanchorX",0),t.regionAnchorY=r.get("regionanchorY",100),t.viewportAnchorX=r.get("viewportanchorX",0),t.viewportAnchorY=r.get("viewportanchorY",100),t.scroll=r.get("scroll",""),n.onregion&&n.onregion(t),n.regionList.push({id:r.get("id"),region:t})))},/:/)}e&&(n.buffer+=n.decoder.decode(e,{stream:!0}));try{if("INITIAL"===n.state){if(!/\r\n|\n/.test(n.buffer))return this;var r,a=(r=t()).match(/^WEBVTT([ \t].*)?$/);if(!a||!a[0])throw new hi(hi.Errors.BadSignature);n.state="HEADER"}for(var s=!1;n.buffer;){if(!/\r\n|\n/.test(n.buffer))return this;switch(s?s=!1:r=t(),n.state){case"HEADER":/:/.test(r)?i(r):r||(n.state="ID");continue;case"NOTE":r||(n.state="ID");continue;case"ID":if(/^NOTE($|[ \t])/.test(r)){n.state="NOTE";break}if(!r)continue;n.cue=new(n.vttjs.VTTCue||n.window.VTTCue)(0,0,"");try{n.cue.align="center"}catch(e){n.cue.align="middle"}if(n.state="CUE",-1===r.indexOf("--\x3e")){n.cue.id=r;continue}case"CUE":try{gi(r,n.cue,n.regionList)}catch(e){n.reportOrThrowError(e),n.cue=null,n.state="BADCUE";continue}n.state="CUETEXT";continue;case"CUETEXT":var o=-1!==r.indexOf("--\x3e");if(!r||o&&(s=!0)){n.oncue&&n.oncue(n.cue),n.cue=null,n.state="ID";continue}n.cue.text&&(n.cue.text+="\n"),n.cue.text+=r.replace(/\u2028/g,"\n").replace(/u2029/g,"\n");continue;case"BADCUE":r||(n.state="ID");continue}}}catch(e){n.reportOrThrowError(e),"CUETEXT"===n.state&&n.cue&&n.oncue&&n.oncue(n.cue),n.cue=null,n.state="INITIAL"===n.state?"BADWEBVTT":"BADCUE"}return this},flush:function(){var t=this;try{if(t.buffer+=t.decoder.decode(),!t.cue&&"HEADER"!==t.state||(t.buffer+="\n\n",t.parse()),"INITIAL"===t.state)throw new hi(hi.Errors.BadSignature)}catch(e){t.reportOrThrowError(e)}return t.onflush&&t.onflush(),this}};var Pi=Ai,Li={"":1,lr:1,rl:1},Di={start:1,center:1,end:1,left:1,right:1,auto:1,"line-left":1,"line-right":1};function Oi(e){return"string"==typeof e&&(!!Di[e.toLowerCase()]&&e.toLowerCase())}function Ri(e,t,i){this.hasBeenReset=!1;var n="",r=!1,a=e,s=t,o=i,u=null,l="",c=!0,d="auto",h="start",p="auto",f="auto",m=100,g="center";Object.defineProperties(this,{id:{enumerable:!0,get:function(){return n},set:function(e){n=""+e}},pauseOnExit:{enumerable:!0,get:function(){return r},set:function(e){r=!!e}},startTime:{enumerable:!0,get:function(){return a},set:function(e){if("number"!=typeof e)throw new TypeError("Start time must be set to a number.");a=e,this.hasBeenReset=!0}},endTime:{enumerable:!0,get:function(){return s},set:function(e){if("number"!=typeof e)throw new TypeError("End time must be set to a number.");s=e,this.hasBeenReset=!0}},text:{enumerable:!0,get:function(){return o},set:function(e){o=""+e,this.hasBeenReset=!0}},region:{enumerable:!0,get:function(){return u},set:function(e){u=e,this.hasBeenReset=!0}},vertical:{enumerable:!0,get:function(){return l},set:function(e){e="string"==typeof(e=e)&&(!!Li[e.toLowerCase()]&&e.toLowerCase());if(!1===e)throw new SyntaxError("Vertical: an invalid or illegal direction string was specified.");l=e,this.hasBeenReset=!0}},snapToLines:{enumerable:!0,get:function(){return c},set:function(e){c=!!e,this.hasBeenReset=!0}},line:{enumerable:!0,get:function(){return d},set:function(e){if("number"!=typeof e&&"auto"!==e)throw new SyntaxError("Line: an invalid number or illegal string was specified.");d=e,this.hasBeenReset=!0}},lineAlign:{enumerable:!0,get:function(){return h},set:function(e){e=Oi(e);e&&(h=e,this.hasBeenReset=!0)}},position:{enumerable:!0,get:function(){return p},set:function(e){if(e<0||100<e)throw new Error("Position must be between 0 and 100.");p=e,this.hasBeenReset=!0}},positionAlign:{enumerable:!0,get:function(){return f},set:function(e){e=Oi(e);e&&(f=e,this.hasBeenReset=!0)}},size:{enumerable:!0,get:function(){return m},set:function(e){if(e<0||100<e)throw new Error("Size must be between 0 and 100.");m=e,this.hasBeenReset=!0}},align:{enumerable:!0,get:function(){return g},set:function(e){e=Oi(e);if(!e)throw new SyntaxError("align: an invalid or illegal alignment string was specified.");g=e,this.hasBeenReset=!0}}}),this.displayState=void 0}Ri.prototype.getCueAsHTML=function(){return WebVTT.convertCueToDOMTree(window,this.text)};var Mi=Ri,Ni={"":!0,up:!0};function Ui(e){return"number"==typeof e&&0<=e&&e<=100}function Bi(){var t=100,i=3,n=0,r=100,a=0,s=100,o="";Object.defineProperties(this,{width:{enumerable:!0,get:function(){return t},set:function(e){if(!Ui(e))throw new Error("Width must be between 0 and 100.");t=e}},lines:{enumerable:!0,get:function(){return i},set:function(e){if("number"!=typeof e)throw new TypeError("Lines must be set to a number.");i=e}},regionAnchorY:{enumerable:!0,get:function(){return r},set:function(e){if(!Ui(e))throw new Error("RegionAnchorX must be between 0 and 100.");r=e}},regionAnchorX:{enumerable:!0,get:function(){return n},set:function(e){if(!Ui(e))throw new Error("RegionAnchorY must be between 0 and 100.");n=e}},viewportAnchorY:{enumerable:!0,get:function(){return s},set:function(e){if(!Ui(e))throw new Error("ViewportAnchorY must be between 0 and 100.");s=e}},viewportAnchorX:{enumerable:!0,get:function(){return a},set:function(e){if(!Ui(e))throw new Error("ViewportAnchorX must be between 0 and 100.");a=e}},scroll:{enumerable:!0,get:function(){return o},set:function(e){e="string"==typeof(e=e)&&(!!Ni[e.toLowerCase()]&&e.toLowerCase());!1===e||(o=e)}}})}var Fi=m(function(e){e=e.exports={WebVTT:Pi,VTTCue:Mi,VTTRegion:Bi};Kt.vttjs=e,Kt.WebVTT=e.WebVTT;var t=e.VTTCue,i=e.VTTRegion,n=Kt.VTTCue,r=Kt.VTTRegion;e.shim=function(){Kt.VTTCue=t,Kt.VTTRegion=i},e.restore=function(){Kt.VTTCue=n,Kt.VTTRegion=r},Kt.VTTCue||e.shim()});Fi.WebVTT,Fi.VTTCue,Fi.VTTRegion;var ji=function(n){function i(t,e){var i;return void 0===e&&(e=function(){}),(t=void 0===t?{}:t).reportTouchActivity=!1,(i=n.call(this,null,t,e)||this).onDurationChange_=function(e){return i.onDurationChange(e)},i.trackProgress_=function(e){return i.trackProgress(e)},i.trackCurrentTime_=function(e){return i.trackCurrentTime(e)},i.stopTrackingCurrentTime_=function(e){return i.stopTrackingCurrentTime(e)},i.disposeSourceHandler_=function(e){return i.disposeSourceHandler(e)},i.hasStarted_=!1,i.on("playing",function(){this.hasStarted_=!0}),i.on("loadstart",function(){this.hasStarted_=!1}),oi.names.forEach(function(e){e=oi[e];t&&t[e.getterName]&&(i[e.privateName]=t[e.getterName])}),i.featuresProgressEvents||i.manualProgressOn(),i.featuresTimeupdateEvents||i.manualTimeUpdatesOn(),["Text","Audio","Video"].forEach(function(e){!1===t["native"+e+"Tracks"]&&(i["featuresNative"+e+"Tracks"]=!1)}),!1===t.nativeCaptions||!1===t.nativeTextTracks?i.featuresNativeTextTracks=!1:!0!==t.nativeCaptions&&!0!==t.nativeTextTracks||(i.featuresNativeTextTracks=!0),i.featuresNativeTextTracks||i.emulateTextTracks(),i.preloadTextTracks=!1!==t.preloadTextTracks,i.autoRemoteTextTracks_=new oi.text.ListClass,i.initTrackListeners(),t.nativeControlsForTouch||i.emitTapEvents(),i.constructor&&(i.name_=i.constructor.name||"Unknown Tech"),i}mt(i,n);var e=i.prototype;return e.triggerSourceset=function(e){var t=this;this.isReady_||this.one("ready",function(){return t.setTimeout(function(){return t.triggerSourceset(e)},1)}),this.trigger({src:e,type:"sourceset"})},e.manualProgressOn=function(){this.on("durationchange",this.onDurationChange_),this.manualProgress=!0,this.one("ready",this.trackProgress_)},e.manualProgressOff=function(){this.manualProgress=!1,this.stopTrackingProgress(),this.off("durationchange",this.onDurationChange_)},e.trackProgress=function(e){this.stopTrackingProgress(),this.progressInterval=this.setInterval(Ve(this,function(){var e=this.bufferedPercent();this.bufferedPercent_!==e&&this.trigger("progress"),1===(this.bufferedPercent_=e)&&this.stopTrackingProgress()}),500)},e.onDurationChange=function(e){this.duration_=this.duration()},e.buffered=function(){return vt(0,0)},e.bufferedPercent=function(){return _t(this.buffered(),this.duration_)},e.stopTrackingProgress=function(){this.clearInterval(this.progressInterval)},e.manualTimeUpdatesOn=function(){this.manualTimeUpdates=!0,this.on("play",this.trackCurrentTime_),this.on("pause",this.stopTrackingCurrentTime_)},e.manualTimeUpdatesOff=function(){this.manualTimeUpdates=!1,this.stopTrackingCurrentTime(),this.off("play",this.trackCurrentTime_),this.off("pause",this.stopTrackingCurrentTime_)},e.trackCurrentTime=function(){this.currentTimeInterval&&this.stopTrackingCurrentTime(),this.currentTimeInterval=this.setInterval(function(){this.trigger({type:"timeupdate",target:this,manuallyTriggered:!0})},250)},e.stopTrackingCurrentTime=function(){this.clearInterval(this.currentTimeInterval),this.trigger({type:"timeupdate",target:this,manuallyTriggered:!0})},e.dispose=function(){this.clearTracks(ai.names),this.manualProgress&&this.manualProgressOff(),this.manualTimeUpdates&&this.manualTimeUpdatesOff(),n.prototype.dispose.call(this)},e.clearTracks=function(e){var r=this;(e=[].concat(e)).forEach(function(e){for(var t=r[e+"Tracks"]()||[],i=t.length;i--;){var n=t[i];"text"===e&&r.removeRemoteTextTrack(n),t.removeTrack(n)}})},e.cleanupAutoTextTracks=function(){for(var e=this.autoRemoteTextTracks_||[],t=e.length;t--;){var i=e[t];this.removeRemoteTextTrack(i)}},e.reset=function(){},e.crossOrigin=function(){},e.setCrossOrigin=function(){},e.error=function(e){return void 0!==e&&(this.error_=new bt(e),this.trigger("error")),this.error_},e.played=function(){return this.hasStarted_?vt(0,0):vt()},e.play=function(){},e.setScrubbing=function(){},e.scrubbing=function(){},e.setCurrentTime=function(){this.manualTimeUpdates&&this.trigger({type:"timeupdate",target:this,manuallyTriggered:!0})},e.initTrackListeners=function(){var r=this;ai.names.forEach(function(e){function t(){r.trigger(e+"trackchange")}var i=ai[e],n=r[i.getterName]();n.addEventListener("removetrack",t),n.addEventListener("addtrack",t),r.on("dispose",function(){n.removeEventListener("removetrack",t),n.removeEventListener("addtrack",t)})})},e.addWebVttScript_=function(){var e,t=this;window.WebVTT||(document.body.contains(this.el())?!this.options_["vtt.js"]&&S(Fi)&&0<Object.keys(Fi).length?this.trigger("vttjsloaded"):((e=document.createElement("script")).src=this.options_["vtt.js"]||"https://vjs.zencdn.net/vttjs/0.14.1/vtt.min.js",e.onload=function(){t.trigger("vttjsloaded")},e.onerror=function(){t.trigger("vttjserror")},this.on("dispose",function(){e.onload=null,e.onerror=null}),window.WebVTT=!0,this.el().parentNode.appendChild(e)):this.ready(this.addWebVttScript_))},e.emulateTextTracks=function(){function t(e){return n.addTrack(e.track)}function i(e){return n.removeTrack(e.track)}var e=this,n=this.textTracks(),r=this.remoteTextTracks();r.on("addtrack",t),r.on("removetrack",i),this.addWebVttScript_();function a(){return e.trigger("texttrackchange")}function s(){a();for(var e=0;e<n.length;e++){var t=n[e];t.removeEventListener("cuechange",a),"showing"===t.mode&&t.addEventListener("cuechange",a)}}s(),n.addEventListener("change",s),n.addEventListener("addtrack",s),n.addEventListener("removetrack",s),this.on("dispose",function(){r.off("addtrack",t),r.off("removetrack",i),n.removeEventListener("change",s),n.removeEventListener("addtrack",s),n.removeEventListener("removetrack",s);for(var e=0;e<n.length;e++)n[e].removeEventListener("cuechange",a)})},e.addTextTrack=function(e,t,i){if(!e)throw new Error("TextTrack kind is required but was not provided");return function(e,t,i,n,r){void 0===r&&(r={});var a=e.textTracks();return r.kind=t,i&&(r.label=i),n&&(r.language=n),r.tech=e,r=new oi.text.TrackClass(r),a.addTrack(r),r}(this,e,t,i)},e.createRemoteTextTrack=function(e){e=lt(e,{tech:this});return new si.remoteTextEl.TrackClass(e)},e.addRemoteTextTrack=function(e,t){var i=this,n=this.createRemoteTextTrack(e=void 0===e?{}:e);return!0!==t&&!1!==t&&(h.warn('Calling addRemoteTextTrack without explicitly setting the "manualCleanup" parameter to `true` is deprecated and default to `false` in future version of video.js'),t=!0),this.remoteTextTrackEls().addTrackElement_(n),this.remoteTextTracks().addTrack(n.track),!0!==t&&this.ready(function(){return i.autoRemoteTextTracks_.addTrack(n.track)}),n},e.removeRemoteTextTrack=function(e){var t=this.remoteTextTrackEls().getTrackElementByTrack_(e);this.remoteTextTrackEls().removeTrackElement_(t),this.remoteTextTracks().removeTrack(e),this.autoRemoteTextTracks_.removeTrack(e)},e.getVideoPlaybackQuality=function(){return{}},e.requestPictureInPicture=function(){var e=this.options_.Promise||window.Promise;if(e)return e.reject()},e.disablePictureInPicture=function(){return!0},e.setDisablePictureInPicture=function(){},e.setPoster=function(){},e.playsinline=function(){},e.setPlaysinline=function(){},e.overrideNativeAudioTracks=function(){},e.overrideNativeVideoTracks=function(){},e.canPlayType=function(){return""},i.canPlayType=function(){return""},i.canPlaySource=function(e,t){return i.canPlayType(e.type)},i.isTech=function(e){return e.prototype instanceof i||e instanceof i||e===i},i.registerTech=function(e,t){if(i.techs_||(i.techs_={}),!i.isTech(t))throw new Error("Tech "+e+" must be a Tech");if(!i.canPlayType)throw new Error("Techs must have a static canPlayType method on them");if(!i.canPlaySource)throw new Error("Techs must have a static canPlaySource method on them");return e=ut(e),i.techs_[e]=t,i.techs_[ot(e)]=t,"Tech"!==e&&i.defaultTechOrder_.push(e),t},i.getTech=function(e){if(e)return i.techs_&&i.techs_[e]?i.techs_[e]:(e=ut(e),window&&window.videojs&&window.videojs[e]?(h.warn("The "+e+" tech was added to the videojs object when it should be registered using videojs.registerTech(name, tech)"),window.videojs[e]):void 0)},i}(pt);oi.names.forEach(function(e){var t=oi[e];ji.prototype[t.getterName]=function(){return this[t.privateName]=this[t.privateName]||new t.ListClass,this[t.privateName]}}),ji.prototype.featuresVolumeControl=!0,ji.prototype.featuresMuteControl=!0,ji.prototype.featuresFullscreenResize=!1,ji.prototype.featuresPlaybackRate=!1,ji.prototype.featuresProgressEvents=!1,ji.prototype.featuresSourceset=!1,ji.prototype.featuresTimeupdateEvents=!1,ji.prototype.featuresNativeTextTracks=!1,ji.withSourceHandlers=function(r){r.registerSourceHandler=function(e,t){var i=(i=r.sourceHandlers)||(r.sourceHandlers=[]);void 0===t&&(t=i.length),i.splice(t,0,e)},r.canPlayType=function(e){for(var t,i=r.sourceHandlers||[],n=0;n<i.length;n++)if(t=i[n].canPlayType(e))return t;return""},r.selectSourceHandler=function(e,t){for(var i=r.sourceHandlers||[],n=0;n<i.length;n++)if(i[n].canHandleSource(e,t))return i[n];return null},r.canPlaySource=function(e,t){var i=r.selectSourceHandler(e,t);return i?i.canHandleSource(e,t):""};["seekable","seeking","duration"].forEach(function(e){var t=this[e];"function"==typeof t&&(this[e]=function(){return this.sourceHandler_&&this.sourceHandler_[e]?this.sourceHandler_[e].apply(this.sourceHandler_,arguments):t.apply(this,arguments)})},r.prototype),r.prototype.setSource=function(e){var t=r.selectSourceHandler(e,this.options_);t||(r.nativeSourceHandler?t=r.nativeSourceHandler:h.error("No source handler found for the current source.")),this.disposeSourceHandler(),this.off("dispose",this.disposeSourceHandler_),t!==r.nativeSourceHandler&&(this.currentSource_=e),this.sourceHandler_=t.handleSource(e,this,this.options_),this.one("dispose",this.disposeSourceHandler_)},r.prototype.disposeSourceHandler=function(){this.currentSource_&&(this.clearTracks(["audio","video"]),this.currentSource_=null),this.cleanupAutoTextTracks(),this.sourceHandler_&&(this.sourceHandler_.dispose&&this.sourceHandler_.dispose(),this.sourceHandler_=null)}},pt.registerComponent("Tech",ji),ji.registerTech("Tech",ji),ji.defaultTechOrder_=[];var Hi={},qi={},Vi={};function Wi(e,t,i){e.setTimeout(function(){return function i(n,e,r,a,s,o){void 0===n&&(n={});void 0===e&&(e=[]);void 0===s&&(s=[]);void 0===o&&(o=!1);var t=e,e=t[0],u=t.slice(1);if("string"==typeof e)i(n,Hi[e],r,a,s,o);else if(e){var l=Qi(a,e);if(!l.setSource)return s.push(l),i(n,u,r,a,s,o);l.setSource(b({},n),function(e,t){return e?i(n,u,r,a,s,o):(s.push(l),void i(t,n.type===t.type?u:Hi[t.type],r,a,s,o))})}else u.length?i(n,u,r,a,s,o):o?r(n,s):i(n,Hi["*"],r,a,s,!0)}(t,Hi[t.type],i,e)},1)}function Gi(e,t,i,n){void 0===n&&(n=null);var r="call"+ut(i),r=e.reduce(Yi(r),n),n=r===Vi,r=n?null:t[i](r);return function(e,t,i,n){for(var r=e.length-1;0<=r;r--){var a=e[r];a[t]&&a[t](n,i)}}(e,i,r,n),r}var zi={buffered:1,currentTime:1,duration:1,muted:1,played:1,paused:1,seekable:1,volume:1,ended:1},Xi={setCurrentTime:1,setMuted:1,setVolume:1},Ki={play:1,pause:1};function Yi(i){return function(e,t){return e===Vi?Vi:t[i]?t[i](e):e}}function Qi(e,t){var i=qi[e.id()],n=null;if(null==i)return n=t(e),qi[e.id()]=[[t,n]],n;for(var r=0;r<i.length;r++){var a=i[r],s=a[0],a=a[1];s===t&&(n=a)}return null===n&&(n=t(e),i.push([t,n])),n}function $i(e){return e=Nt(e=void 0===e?"":e),Zi[e.toLowerCase()]||""}function Ji(e){var t;return e=Array.isArray(e)?(t=[],e.forEach(function(e){e=Ji(e),Array.isArray(e)?t=t.concat(e):T(e)&&t.push(e)}),t):"string"==typeof e&&e.trim()?[en({src:e})]:T(e)&&"string"==typeof e.src&&e.src&&e.src.trim()?[en(e)]:[]}var Zi={opus:"video/ogg",ogv:"video/ogg",mp4:"video/mp4",mov:"video/mp4",m4v:"video/mp4",mkv:"video/x-matroska",m4a:"audio/mp4",mp3:"audio/mpeg",aac:"audio/aac",caf:"audio/x-caf",flac:"audio/flac",oga:"audio/ogg",wav:"audio/wav",m3u8:"application/x-mpegURL",jpg:"image/jpeg",jpeg:"image/jpeg",gif:"image/gif",png:"image/png",svg:"image/svg+xml",webp:"image/webp"};function en(e){var t;return e.type||(t=$i(e.src))&&(e.type=t),e}I=function(u){function e(e,t,i){var n=lt({createEl:!1},t),i=u.call(this,e,n,i)||this;if(t.playerOptions.sources&&0!==t.playerOptions.sources.length)e.src(t.playerOptions.sources);else for(var r=0,a=t.playerOptions.techOrder;r<a.length;r++){var s=ut(a[r]),o=ji.getTech(s);if((o=!s?pt.getComponent(s):o)&&o.isSupported()){e.loadTech_(s);break}}return i}return mt(e,u),e}(pt);pt.registerComponent("MediaLoader",I);C=function(n){function e(e,t){var i=n.call(this,e,t)||this;return i.handleMouseOver_=function(e){return i.handleMouseOver(e)},i.handleMouseOut_=function(e){return i.handleMouseOut(e)},i.handleClick_=function(e){return i.handleClick(e)},i.handleKeyDown_=function(e){return i.handleKeyDown(e)},i.emitTapEvents(),i.enable(),i}mt(e,n);var t=e.prototype;return t.createEl=function(e,t,i){void 0===e&&(e="div"),void 0===t&&(t={}),void 0===i&&(i={}),t=b({className:this.buildCSSClass(),tabIndex:0},t),"button"===e&&h.error("Creating a ClickableComponent with an HTML element of "+e+" is not supported; use a Button instead."),i=b({role:"button"},i),this.tabIndex_=t.tabIndex;i=$(e,t,i);return i.appendChild($("span",{className:"vjs-icon-placeholder"},{"aria-hidden":!0})),this.createControlTextEl(i),i},t.dispose=function(){this.controlTextEl_=null,n.prototype.dispose.call(this)},t.createControlTextEl=function(e){return this.controlTextEl_=$("span",{className:"vjs-control-text"},{"aria-live":"polite"}),e&&e.appendChild(this.controlTextEl_),this.controlText(this.controlText_,e),this.controlTextEl_},t.controlText=function(e,t){if(void 0===t&&(t=this.el()),void 0===e)return this.controlText_||"Need Text";var i=this.localize(e);this.controlText_=e,J(this.controlTextEl_,i),this.nonIconControl||this.player_.options_.noUITitleAttributes||t.setAttribute("title",i)},t.buildCSSClass=function(){return"vjs-control vjs-button "+n.prototype.buildCSSClass.call(this)},t.enable=function(){this.enabled_||(this.enabled_=!0,this.removeClass("vjs-disabled"),this.el_.setAttribute("aria-disabled","false"),"undefined"!=typeof this.tabIndex_&&this.el_.setAttribute("tabIndex",this.tabIndex_),this.on(["tap","click"],this.handleClick_),this.on("keydown",this.handleKeyDown_))},t.disable=function(){this.enabled_=!1,this.addClass("vjs-disabled"),this.el_.setAttribute("aria-disabled","true"),"undefined"!=typeof this.tabIndex_&&this.el_.removeAttribute("tabIndex"),this.off("mouseover",this.handleMouseOver_),this.off("mouseout",this.handleMouseOut_),this.off(["tap","click"],this.handleClick_),this.off("keydown",this.handleKeyDown_)},t.handleLanguagechange=function(){this.controlText(this.controlText_)},t.handleClick=function(e){this.options_.clickHandler&&this.options_.clickHandler.call(this,arguments)},t.handleKeyDown=function(e){ht.isEventKey(e,"Space")||ht.isEventKey(e,"Enter")?(e.preventDefault(),e.stopPropagation(),this.trigger("click")):n.prototype.handleKeyDown.call(this,e)},e}(pt);pt.registerComponent("ClickableComponent",C),pt.registerComponent("PosterImage",function(n){function e(e,t){var i=n.call(this,e,t)||this;return i.update(),i.update_=function(e){return i.update(e)},e.on("posterchange",i.update_),i}mt(e,n);var t=e.prototype;return t.dispose=function(){this.player().off("posterchange",this.update_),n.prototype.dispose.call(this)},t.createEl=function(){return $("div",{className:"vjs-poster",tabIndex:-1})},t.update=function(e){var t=this.player().poster();this.setSrc(t),t?this.show():this.hide()},t.setSrc=function(e){this.el_.style.backgroundImage=e?'url("'+e+'")':""},t.handleClick=function(e){var t;this.player_.controls()&&(t=this.player_.usingPlugin("eme")&&this.player_.eme.sessions&&0<this.player_.eme.sessions.length,!this.player_.tech(!0)||(N||O)&&t||this.player_.tech(!0).focus(),this.player_.paused()?Et(this.player_.play()):this.player_.pause())},e}(C));var tn="#222",nn={monospace:"monospace",sansSerif:"sans-serif",serif:"serif",monospaceSansSerif:'"Andale Mono", "Lucida Console", monospace',monospaceSerif:'"Courier New", monospace',proportionalSansSerif:"sans-serif",proportionalSerif:"serif",casual:'"Comic Sans MS", Impact, fantasy',script:'"Monotype Corsiva", cursive',smallcaps:'"Andale Mono", "Lucida Console", monospace, sans-serif'};function rn(e,t){var i;if(4===e.length)i=e[1]+e[1]+e[2]+e[2]+e[3]+e[3];else{if(7!==e.length)throw new Error("Invalid color code provided, "+e+"; must be formatted as e.g. #f0e or #f604e2.");i=e.slice(1)}return"rgba("+parseInt(i.slice(0,2),16)+","+parseInt(i.slice(2,4),16)+","+parseInt(i.slice(4,6),16)+","+t+")"}function an(e,t,i){try{e.style[t]=i}catch(e){return}}pt.registerComponent("TextTrackDisplay",function(a){function e(i,e,t){function n(e){return r.updateDisplay(e)}var r=a.call(this,i,e,t)||this;return i.on("loadstart",function(e){return r.toggleDisplay(e)}),i.on("texttrackchange",n),i.on("loadedmetadata",function(e){return r.preselectTrack(e)}),i.ready(Ve(ft(r),function(){if(i.tech_&&i.tech_.featuresNativeTextTracks)this.hide();else{i.on("fullscreenchange",n),i.on("playerresize",n),window.addEventListener("orientationchange",n),i.on("dispose",function(){return window.removeEventListener("orientationchange",n)});for(var e=this.options_.playerOptions.tracks||[],t=0;t<e.length;t++)this.player_.addRemoteTextTrack(e[t],!0);this.preselectTrack()}})),r}mt(e,a);var t=e.prototype;return t.preselectTrack=function(){for(var e,t,i,n={captions:1,subtitles:1},r=this.player_.textTracks(),a=this.player_.cache_.selectedLanguage,s=0;s<r.length;s++){var o=r[s];a&&a.enabled&&a.language&&a.language===o.language&&o.kind in n?i=o.kind!==a.kind&&i||o:a&&!a.enabled?t=e=i=null:o.default&&("descriptions"!==o.kind||e?o.kind in n&&!t&&(t=o):e=o)}i?i.mode="showing":t?t.mode="showing":e&&(e.mode="showing")},t.toggleDisplay=function(){this.player_.tech_&&this.player_.tech_.featuresNativeTextTracks?this.hide():this.show()},t.createEl=function(){return a.prototype.createEl.call(this,"div",{className:"vjs-text-track-display"},{translate:"yes","aria-live":"off","aria-atomic":"true"})},t.clearDisplay=function(){"function"==typeof window.WebVTT&&window.WebVTT.processCues(window,[],this.el_)},t.updateDisplay=function(){var e=this.player_.textTracks(),t=this.options_.allowMultipleShowingTracks;if(this.clearDisplay(),t){for(var i=[],n=0;n<e.length;++n){var r=e[n];"showing"===r.mode&&i.push(r)}this.updateForTrack(i)}else{for(var a=null,s=null,o=e.length;o--;){var u=e[o];"showing"===u.mode&&("descriptions"===u.kind?a=u:s=u)}s?("off"!==this.getAttribute("aria-live")&&this.setAttribute("aria-live","off"),this.updateForTrack(s)):a&&("assertive"!==this.getAttribute("aria-live")&&this.setAttribute("aria-live","assertive"),this.updateForTrack(a))}},t.updateDisplayState=function(e){for(var t=this.player_.textTrackSettings.getValues(),i=e.activeCues,n=i.length;n--;){var r,a=i[n];a&&(r=a.displayState,t.color&&(r.firstChild.style.color=t.color),t.textOpacity&&an(r.firstChild,"color",rn(t.color||"#fff",t.textOpacity)),t.backgroundColor&&(r.firstChild.style.backgroundColor=t.backgroundColor),t.backgroundOpacity&&an(r.firstChild,"backgroundColor",rn(t.backgroundColor||"#000",t.backgroundOpacity)),t.windowColor&&(t.windowOpacity?an(r,"backgroundColor",rn(t.windowColor,t.windowOpacity)):r.style.backgroundColor=t.windowColor),t.edgeStyle&&("dropshadow"===t.edgeStyle?r.firstChild.style.textShadow="2px 2px 3px #222, 2px 2px 4px #222, 2px 2px 5px "+tn:"raised"===t.edgeStyle?r.firstChild.style.textShadow="1px 1px #222, 2px 2px #222, 3px 3px "+tn:"depressed"===t.edgeStyle?r.firstChild.style.textShadow="1px 1px #ccc, 0 1px #ccc, -1px -1px #222, 0 -1px "+tn:"uniform"===t.edgeStyle&&(r.firstChild.style.textShadow="0 0 4px #222, 0 0 4px #222, 0 0 4px #222, 0 0 4px "+tn)),t.fontPercent&&1!==t.fontPercent&&(a=window.parseFloat(r.style.fontSize),r.style.fontSize=a*t.fontPercent+"px",r.style.height="auto",r.style.top="auto"),t.fontFamily&&"default"!==t.fontFamily&&("small-caps"===t.fontFamily?r.firstChild.style.fontVariant="small-caps":r.firstChild.style.fontFamily=nn[t.fontFamily]))}},t.updateForTrack=function(e){if(Array.isArray(e)||(e=[e]),"function"==typeof window.WebVTT&&!e.every(function(e){return!e.activeCues})){for(var t=[],i=0;i<e.length;++i)for(var n=e[i],r=0;r<n.activeCues.length;++r)t.push(n.activeCues[r]);window.WebVTT.processCues(window,t,this.el_);for(var a=0;a<e.length;++a){for(var s=e[a],o=0;o<s.activeCues.length;++o){var u=s.activeCues[o].displayState;te(u,"vjs-text-track-cue"),te(u,"vjs-text-track-cue-"+(s.language||a)),s.language&&oe(u,"lang",s.language)}this.player_.textTrackSettings&&this.updateDisplayState(s)}}},e}(pt)),pt.registerComponent("LoadingSpinner",function(i){function e(){return i.apply(this,arguments)||this}return mt(e,i),e.prototype.createEl=function(){var e=this.player_.isAudio(),t=this.localize(e?"Audio Player":"Video Player"),e=$("span",{className:"vjs-control-text",textContent:this.localize("{1} is loading.",[t])}),t=i.prototype.createEl.call(this,"div",{className:"vjs-loading-spinner",dir:"ltr"});return t.appendChild(e),t},e}(pt));var sn=function(t){function e(){return t.apply(this,arguments)||this}mt(e,t);var i=e.prototype;return i.createEl=function(e,t,i){void 0===t&&(t={}),void 0===i&&(i={});i=$("button",t=b({className:this.buildCSSClass()},t),i=b({type:"button"},i));return i.appendChild($("span",{className:"vjs-icon-placeholder"},{"aria-hidden":!0})),this.createControlTextEl(i),i},i.addChild=function(e,t){void 0===t&&(t={});var i=this.constructor.name;return h.warn("Adding an actionable (user controllable) child to a Button ("+i+") is not supported; use a ClickableComponent instead."),pt.prototype.addChild.call(this,e,t)},i.enable=function(){t.prototype.enable.call(this),this.el_.removeAttribute("disabled")},i.disable=function(){t.prototype.disable.call(this),this.el_.setAttribute("disabled","disabled")},i.handleKeyDown=function(e){ht.isEventKey(e,"Space")||ht.isEventKey(e,"Enter")?e.stopPropagation():t.prototype.handleKeyDown.call(this,e)},e}(C);pt.registerComponent("Button",sn);Bt=function(n){function e(e,t){var i=n.call(this,e,t)||this;return i.mouseused_=!1,i.on("mousedown",function(e){return i.handleMouseDown(e)}),i}mt(e,n);var t=e.prototype;return t.buildCSSClass=function(){return"vjs-big-play-button"},t.handleClick=function(e){var t=this.player_.play();if(this.mouseused_&&e.clientX&&e.clientY){var i=this.player_.usingPlugin("eme")&&this.player_.eme.sessions&&0<this.player_.eme.sessions.length;return Et(t),void(!this.player_.tech(!0)||(N||O)&&i||this.player_.tech(!0).focus())}var i=this.player_.getChild("controlBar"),n=i&&i.getChild("playToggle");n?(i=function(){return n.focus()},wt(t)?t.then(i,function(){}):this.setTimeout(i,1)):this.player_.tech(!0).focus()},t.handleKeyDown=function(e){this.mouseused_=!1,n.prototype.handleKeyDown.call(this,e)},t.handleMouseDown=function(e){this.mouseused_=!0},e}(sn);Bt.prototype.controlText_="Play Video",pt.registerComponent("BigPlayButton",Bt),pt.registerComponent("CloseButton",function(i){function e(e,t){e=i.call(this,e,t)||this;return e.controlText(t&&t.controlText||e.localize("Close")),e}mt(e,i);var t=e.prototype;return t.buildCSSClass=function(){return"vjs-close-button "+i.prototype.buildCSSClass.call(this)},t.handleClick=function(e){this.trigger({type:"close",bubbles:!1})},t.handleKeyDown=function(e){ht.isEventKey(e,"Esc")?(e.preventDefault(),e.stopPropagation(),this.trigger("click")):i.prototype.handleKeyDown.call(this,e)},e}(sn));Ft=function(n){function e(e,t){var i=n.call(this,e,t=void 0===t?{}:t)||this;return t.replay=void 0===t.replay||t.replay,i.on(e,"play",function(e){return i.handlePlay(e)}),i.on(e,"pause",function(e){return i.handlePause(e)}),t.replay&&i.on(e,"ended",function(e){return i.handleEnded(e)}),i}mt(e,n);var t=e.prototype;return t.buildCSSClass=function(){return"vjs-play-control "+n.prototype.buildCSSClass.call(this)},t.handleClick=function(e){this.player_.paused()?Et(this.player_.play()):this.player_.pause()},t.handleSeeked=function(e){this.removeClass("vjs-ended"),this.player_.paused()?this.handlePause(e):this.handlePlay(e)},t.handlePlay=function(e){this.removeClass("vjs-ended"),this.removeClass("vjs-paused"),this.addClass("vjs-playing"),this.controlText("Pause")},t.handlePause=function(e){this.removeClass("vjs-playing"),this.addClass("vjs-paused"),this.controlText("Play")},t.handleEnded=function(e){var t=this;this.removeClass("vjs-playing"),this.addClass("vjs-ended"),this.controlText("Replay"),this.one(this.player_,"seeked",function(e){return t.handleSeeked(e)})},e}(sn);Ft.prototype.controlText_="Play",pt.registerComponent("PlayToggle",Ft);function on(e,t){e=e<0?0:e;var i=Math.floor(e%60),n=Math.floor(e/60%60),r=Math.floor(e/3600),a=Math.floor(t/60%60),t=Math.floor(t/3600);return(r=0<(r=isNaN(e)||e===1/0?n=i="-":r)||0<t?r+":":"")+(n=((r||10<=a)&&n<10?"0"+n:n)+":")+(i=i<10?"0"+i:i)}var un=on;function ln(e,t){return un(e,t=void 0===t?e:t)}k=function(n){function e(e,t){var i=n.call(this,e,t)||this;return i.on(e,["timeupdate","ended"],function(e){return i.updateContent(e)}),i.updateTextNode_(),i}mt(e,n);var t=e.prototype;return t.createEl=function(){var e=this.buildCSSClass(),t=n.prototype.createEl.call(this,"div",{className:e+" vjs-time-control vjs-control"}),i=$("span",{className:"vjs-control-text",textContent:this.localize(this.labelText_)+" "},{role:"presentation"});return t.appendChild(i),this.contentEl_=$("span",{className:e+"-display"},{"aria-live":"off",role:"presentation"}),t.appendChild(this.contentEl_),t},t.dispose=function(){this.contentEl_=null,this.textNode_=null,n.prototype.dispose.call(this)},t.updateTextNode_=function(e){var t=this;e=ln(e=void 0===e?0:e),this.formattedTime_!==e&&(this.formattedTime_=e,this.requestNamedAnimationFrame("TimeDisplay#updateTextNode_",function(){var e;t.contentEl_&&((e=t.textNode_)&&t.contentEl_.firstChild!==e&&(e=null,h.warn("TimeDisplay#updateTextnode_: Prevented replacement of text node element since it was no longer a child of this node. Appending a new node instead.")),t.textNode_=document.createTextNode(t.formattedTime_),t.textNode_&&(e?t.contentEl_.replaceChild(t.textNode_,e):t.contentEl_.appendChild(t.textNode_)))}))},t.updateContent=function(e){},e}(pt);k.prototype.labelText_="Time",k.prototype.controlText_="Time",pt.registerComponent("TimeDisplay",k);jt=function(e){function t(){return e.apply(this,arguments)||this}mt(t,e);var i=t.prototype;return i.buildCSSClass=function(){return"vjs-current-time"},i.updateContent=function(e){var t=this.player_.ended()?this.player_.duration():this.player_.scrubbing()?this.player_.getCache().currentTime:this.player_.currentTime();this.updateTextNode_(t)},t}(k);jt.prototype.labelText_="Current Time",jt.prototype.controlText_="Current Time",pt.registerComponent("CurrentTimeDisplay",jt);j=function(n){function e(e,t){var i=n.call(this,e,t)||this,t=function(e){return i.updateContent(e)};return i.on(e,"durationchange",t),i.on(e,"loadstart",t),i.on(e,"loadedmetadata",t),i}mt(e,n);var t=e.prototype;return t.buildCSSClass=function(){return"vjs-duration"},t.updateContent=function(e){var t=this.player_.duration();this.updateTextNode_(t)},e}(k);j.prototype.labelText_="Duration",j.prototype.controlText_="Duration",pt.registerComponent("DurationDisplay",j),pt.registerComponent("TimeDivider",function(n){function e(){return n.apply(this,arguments)||this}return mt(e,n),e.prototype.createEl=function(){var e=n.prototype.createEl.call(this,"div",{className:"vjs-time-control vjs-time-divider"},{"aria-hidden":!0}),t=n.prototype.createEl.call(this,"div"),i=n.prototype.createEl.call(this,"span",{textContent:"/"});return t.appendChild(i),e.appendChild(t),e},e}(pt));f=function(n){function e(e,t){var i=n.call(this,e,t)||this;return i.on(e,"durationchange",function(e){return i.updateContent(e)}),i}mt(e,n);var t=e.prototype;return t.buildCSSClass=function(){return"vjs-remaining-time"},t.createEl=function(){var e=n.prototype.createEl.call(this);return!1!==this.options_.displayNegative&&e.insertBefore($("span",{},{"aria-hidden":!0},"-"),this.contentEl_),e},t.updateContent=function(e){var t;"number"==typeof this.player_.duration()&&(t=this.player_.ended()?0:this.player_.remainingTimeDisplay?this.player_.remainingTimeDisplay():this.player_.remainingTime(),this.updateTextNode_(t))},e}(k);f.prototype.labelText_="Remaining Time",f.prototype.controlText_="Remaining Time",pt.registerComponent("RemainingTimeDisplay",f),pt.registerComponent("LiveDisplay",function(n){function e(e,t){var i=n.call(this,e,t)||this;return i.updateShowing(),i.on(i.player(),"durationchange",function(e){return i.updateShowing(e)}),i}mt(e,n);var t=e.prototype;return t.createEl=function(){var e=n.prototype.createEl.call(this,"div",{className:"vjs-live-control vjs-control"});return this.contentEl_=$("div",{className:"vjs-live-display"},{"aria-live":"off"}),this.contentEl_.appendChild($("span",{className:"vjs-control-text",textContent:this.localize("Stream Type")+" "})),this.contentEl_.appendChild(document.createTextNode(this.localize("LIVE"))),e.appendChild(this.contentEl_),e},t.dispose=function(){this.contentEl_=null,n.prototype.dispose.call(this)},t.updateShowing=function(e){this.player().duration()===1/0?this.show():this.hide()},e}(pt));ui=function(n){function e(e,t){var i=n.call(this,e,t)||this;return i.updateLiveEdgeStatus(),i.player_.liveTracker&&(i.updateLiveEdgeStatusHandler_=function(e){return i.updateLiveEdgeStatus(e)},i.on(i.player_.liveTracker,"liveedgechange",i.updateLiveEdgeStatusHandler_)),i}mt(e,n);var t=e.prototype;return t.createEl=function(){var e=n.prototype.createEl.call(this,"button",{className:"vjs-seek-to-live-control vjs-control"});return this.textEl_=$("span",{className:"vjs-seek-to-live-text",textContent:this.localize("LIVE")},{"aria-hidden":"true"}),e.appendChild(this.textEl_),e},t.updateLiveEdgeStatus=function(){!this.player_.liveTracker||this.player_.liveTracker.atLiveEdge()?(this.setAttribute("aria-disabled",!0),this.addClass("vjs-at-live-edge"),this.controlText("Seek to live, currently playing live")):(this.setAttribute("aria-disabled",!1),this.removeClass("vjs-at-live-edge"),this.controlText("Seek to live, currently behind live"))},t.handleClick=function(){this.player_.liveTracker.seekToLiveEdge()},t.dispose=function(){this.player_.liveTracker&&this.off(this.player_.liveTracker,"liveedgechange",this.updateLiveEdgeStatusHandler_),this.textEl_=null,n.prototype.dispose.call(this)},e}(sn);ui.prototype.controlText_="Seek to live, currently playing live",pt.registerComponent("SeekToLive",ui);function cn(e,t,i){return e=Number(e),Math.min(i,Math.max(t,isNaN(e)?t:e))}li=function(n){function e(e,t){var i=n.call(this,e,t)||this;return i.handleMouseDown_=function(e){return i.handleMouseDown(e)},i.handleMouseUp_=function(e){return i.handleMouseUp(e)},i.handleKeyDown_=function(e){return i.handleKeyDown(e)},i.handleClick_=function(e){return i.handleClick(e)},i.handleMouseMove_=function(e){return i.handleMouseMove(e)},i.update_=function(e){return i.update(e)},i.bar=i.getChild(i.options_.barName),i.vertical(!!i.options_.vertical),i.enable(),i}mt(e,n);var t=e.prototype;return t.enabled=function(){return this.enabled_},t.enable=function(){this.enabled()||(this.on("mousedown",this.handleMouseDown_),this.on("touchstart",this.handleMouseDown_),this.on("keydown",this.handleKeyDown_),this.on("click",this.handleClick_),this.on(this.player_,"controlsvisible",this.update),this.playerEvent&&this.on(this.player_,this.playerEvent,this.update),this.removeClass("disabled"),this.setAttribute("tabindex",0),this.enabled_=!0)},t.disable=function(){var e;this.enabled()&&(e=this.bar.el_.ownerDocument,this.off("mousedown",this.handleMouseDown_),this.off("touchstart",this.handleMouseDown_),this.off("keydown",this.handleKeyDown_),this.off("click",this.handleClick_),this.off(this.player_,"controlsvisible",this.update_),this.off(e,"mousemove",this.handleMouseMove_),this.off(e,"mouseup",this.handleMouseUp_),this.off(e,"touchmove",this.handleMouseMove_),this.off(e,"touchend",this.handleMouseUp_),this.removeAttribute("tabindex"),this.addClass("disabled"),this.playerEvent&&this.off(this.player_,this.playerEvent,this.update),this.enabled_=!1)},t.createEl=function(e,t,i){return void 0===i&&(i={}),(t=void 0===t?{}:t).className=t.className+" vjs-slider",t=b({tabIndex:0},t),i=b({role:"slider","aria-valuenow":0,"aria-valuemin":0,"aria-valuemax":100,tabIndex:0},i),n.prototype.createEl.call(this,e,t,i)},t.handleMouseDown=function(e){var t=this.bar.el_.ownerDocument;"mousedown"===e.type&&e.preventDefault(),"touchstart"!==e.type||R||e.preventDefault(),le(),this.addClass("vjs-sliding"),this.trigger("slideractive"),this.on(t,"mousemove",this.handleMouseMove_),this.on(t,"mouseup",this.handleMouseUp_),this.on(t,"touchmove",this.handleMouseMove_),this.on(t,"touchend",this.handleMouseUp_),this.handleMouseMove(e,!0)},t.handleMouseMove=function(e){},t.handleMouseUp=function(){var e=this.bar.el_.ownerDocument;ce(),this.removeClass("vjs-sliding"),this.trigger("sliderinactive"),this.off(e,"mousemove",this.handleMouseMove_),this.off(e,"mouseup",this.handleMouseUp_),this.off(e,"touchmove",this.handleMouseMove_),this.off(e,"touchend",this.handleMouseUp_),this.update()},t.update=function(){var t=this;if(this.el_&&this.bar){var i=this.getProgress();return i===this.progress_?i:(this.progress_=i,this.requestNamedAnimationFrame("Slider#update",function(){var e=t.vertical()?"height":"width";t.bar.el().style[e]=(100*i).toFixed(2)+"%"}),i)}},t.getProgress=function(){return Number(cn(this.getPercent(),0,1).toFixed(4))},t.calculateDistance=function(e){e=pe(this.el_,e);return this.vertical()?e.y:e.x},t.handleKeyDown=function(e){ht.isEventKey(e,"Left")||ht.isEventKey(e,"Down")?(e.preventDefault(),e.stopPropagation(),this.stepBack()):ht.isEventKey(e,"Right")||ht.isEventKey(e,"Up")?(e.preventDefault(),e.stopPropagation(),this.stepForward()):n.prototype.handleKeyDown.call(this,e)},t.handleClick=function(e){e.stopPropagation(),e.preventDefault()},t.vertical=function(e){if(void 0===e)return this.vertical_||!1;this.vertical_=!!e,this.vertical_?this.addClass("vjs-slider-vertical"):this.addClass("vjs-slider-horizontal")},e}(pt);pt.registerComponent("Slider",li);function dn(e,t){return cn(e/t*100,0,100).toFixed(2)+"%"}pt.registerComponent("LoadProgressBar",function(r){function e(e,t){var i=r.call(this,e,t)||this;return i.partEls_=[],i.on(e,"progress",function(e){return i.update(e)}),i}mt(e,r);var t=e.prototype;return t.createEl=function(){var e=r.prototype.createEl.call(this,"div",{className:"vjs-load-progress"}),t=$("span",{className:"vjs-control-text"}),i=$("span",{textContent:this.localize("Loaded")}),n=document.createTextNode(": ");return this.percentageEl_=$("span",{className:"vjs-control-text-loaded-percentage",textContent:"0%"}),e.appendChild(t),t.appendChild(i),t.appendChild(n),t.appendChild(this.percentageEl_),e},t.dispose=function(){this.partEls_=null,this.percentageEl_=null,r.prototype.dispose.call(this)},t.update=function(e){var l=this;this.requestNamedAnimationFrame("LoadProgressBar#update",function(){var e=l.player_.liveTracker,t=l.player_.buffered(),e=e&&e.isLive()?e.seekableEnd():l.player_.duration(),i=l.player_.bufferedEnd(),n=l.partEls_,e=dn(i,e);l.percent_!==e&&(l.el_.style.width=e,J(l.percentageEl_,e),l.percent_=e);for(var r=0;r<t.length;r++){var a=t.start(r),s=t.end(r),o=n[r];o||(o=l.el_.appendChild($()),n[r]=o),o.dataset.start===a&&o.dataset.end===s||(o.dataset.start=a,o.dataset.end=s,o.style.left=dn(a,i),o.style.width=dn(s-a,i))}for(var u=n.length;u>t.length;u--)l.el_.removeChild(n[u-1]);n.length=t.length})},e}(pt)),pt.registerComponent("TimeTooltip",function(i){function e(e,t){t=i.call(this,e,t)||this;return t.update=We(Ve(ft(t),t.update),30),t}mt(e,i);var t=e.prototype;return t.createEl=function(){return i.prototype.createEl.call(this,"div",{className:"vjs-time-tooltip"},{"aria-hidden":"true"})},t.update=function(e,t,i){var n=he(this.el_),r=de(this.player_.el()),a=e.width*t;r&&n&&(t=e.left-r.left+a,r=e.width-a+(r.right-e.right),t<(e=n.width/2)?e+=e-t:r<e&&(e=r),e<0?e=0:e>n.width&&(e=n.width),e=Math.round(e),this.el_.style.right="-"+e+"px",this.write(i))},t.write=function(e){J(this.el_,e)},t.updateTime=function(n,r,a,s){var o=this;this.requestNamedAnimationFrame("TimeTooltip#updateTime",function(){var e,t,i=o.player_.duration();i=o.player_.liveTracker&&o.player_.liveTracker.isLive()?((t=(e=o.player_.liveTracker.liveWindow())-r*e)<1?"":"-")+ln(t,e):ln(a,i),o.update(n,r,i),s&&s()})},e}(pt));Xt=function(i){function e(e,t){t=i.call(this,e,t)||this;return t.update=We(Ve(ft(t),t.update),30),t}mt(e,i);var t=e.prototype;return t.createEl=function(){return i.prototype.createEl.call(this,"div",{className:"vjs-play-progress vjs-slider-bar"},{"aria-hidden":"true"})},t.update=function(e,t){var i,n=this.getChild("timeTooltip");n&&(i=this.player_.scrubbing()?this.player_.getCache().currentTime:this.player_.currentTime(),n.updateTime(e,t,i))},e}(pt);Xt.prototype.options_={children:[]},q||A||Xt.prototype.options_.children.push("timeTooltip"),pt.registerComponent("PlayProgressBar",Xt);I=function(i){function e(e,t){t=i.call(this,e,t)||this;return t.update=We(Ve(ft(t),t.update),30),t}mt(e,i);var t=e.prototype;return t.createEl=function(){return i.prototype.createEl.call(this,"div",{className:"vjs-mouse-display"})},t.update=function(e,t){var i=this,n=t*this.player_.duration();this.getChild("timeTooltip").updateTime(e,t,n,function(){i.el_.style.left=e.width*t+"px"})},e}(pt);I.prototype.options_={children:["timeTooltip"]},pt.registerComponent("MouseTimeDisplay",I);Bt=function(a){function e(e,t){t=a.call(this,e,t)||this;return t.setEventHandlers_(),t}mt(e,a);var t=e.prototype;return t.setEventHandlers_=function(){var t=this;this.update_=Ve(this,this.update),this.update=We(this.update_,30),this.on(this.player_,["ended","durationchange","timeupdate"],this.update),this.player_.liveTracker&&this.on(this.player_.liveTracker,"liveedgechange",this.update),this.updateInterval=null,this.enableIntervalHandler_=function(e){return t.enableInterval_(e)},this.disableIntervalHandler_=function(e){return t.disableInterval_(e)},this.on(this.player_,["playing"],this.enableIntervalHandler_),this.on(this.player_,["ended","pause","waiting"],this.disableIntervalHandler_),"hidden"in document&&"visibilityState"in document&&this.on(document,"visibilitychange",this.toggleVisibility_)},t.toggleVisibility_=function(e){"hidden"===document.visibilityState?(this.cancelNamedAnimationFrame("SeekBar#update"),this.cancelNamedAnimationFrame("Slider#update"),this.disableInterval_(e)):(this.player_.ended()||this.player_.paused()||this.enableInterval_(),this.update())},t.enableInterval_=function(){this.updateInterval||(this.updateInterval=this.setInterval(this.update,30))},t.disableInterval_=function(e){this.player_.liveTracker&&this.player_.liveTracker.isLive()&&e&&"ended"!==e.type||this.updateInterval&&(this.clearInterval(this.updateInterval),this.updateInterval=null)},t.createEl=function(){return a.prototype.createEl.call(this,"div",{className:"vjs-progress-holder"},{"aria-label":this.localize("Progress Bar")})},t.update=function(e){var n=this;if("hidden"!==document.visibilityState){var r=a.prototype.update.call(this);return this.requestNamedAnimationFrame("SeekBar#update",function(){var e=n.player_.ended()?n.player_.duration():n.getCurrentTime_(),t=n.player_.liveTracker,i=n.player_.duration();t&&t.isLive()&&(i=n.player_.liveTracker.liveCurrentTime()),n.percent_!==r&&(n.el_.setAttribute("aria-valuenow",(100*r).toFixed(2)),n.percent_=r),n.currentTime_===e&&n.duration_===i||(n.el_.setAttribute("aria-valuetext",n.localize("progress bar timing: currentTime={1} duration={2}",[ln(e,i),ln(i,i)],"{1} of {2}")),n.currentTime_=e,n.duration_=i),n.bar&&n.bar.update(de(n.el()),n.getProgress())}),r}},t.userSeek_=function(e){this.player_.liveTracker&&this.player_.liveTracker.isLive()&&this.player_.liveTracker.nextSeekedFromUser(),this.player_.currentTime(e)},t.getCurrentTime_=function(){return this.player_.scrubbing()?this.player_.getCache().currentTime:this.player_.currentTime()},t.getPercent=function(){var e,t=this.getCurrentTime_(),i=this.player_.liveTracker;return i&&i.isLive()?(e=(t-i.seekableStart())/i.liveWindow(),i.atLiveEdge()&&(e=1)):e=t/this.player_.duration(),e},t.handleMouseDown=function(e){_e(e)&&(e.stopPropagation(),this.videoWasPlaying=!this.player_.paused(),this.player_.pause(),a.prototype.handleMouseDown.call(this,e))},t.handleMouseMove=function(e,t){if(void 0===t&&(t=!1),_e(e)){t||this.player_.scrubbing()||this.player_.scrubbing(!0);var i=this.calculateDistance(e),n=this.player_.liveTracker;if(n&&n.isLive()){if(.99<=i)return void n.seekToLiveEdge();var r,t=n.seekableStart(),e=n.liveCurrentTime();if((r=(r=e<=(r=t+i*n.liveWindow())?e:r)<=t?t+.1:r)===1/0)return}else(r=i*this.player_.duration())===this.player_.duration()&&(r-=.1);this.userSeek_(r)}},t.enable=function(){a.prototype.enable.call(this);var e=this.getChild("mouseTimeDisplay");e&&e.show()},t.disable=function(){a.prototype.disable.call(this);var e=this.getChild("mouseTimeDisplay");e&&e.hide()},t.handleMouseUp=function(e){a.prototype.handleMouseUp.call(this,e),e&&e.stopPropagation(),this.player_.scrubbing(!1),this.player_.trigger({type:"timeupdate",target:this,manuallyTriggered:!0}),this.videoWasPlaying?Et(this.player_.play()):this.update_()},t.stepForward=function(){this.userSeek_(this.player_.currentTime()+5)},t.stepBack=function(){this.userSeek_(this.player_.currentTime()-5)},t.handleAction=function(e){this.player_.paused()?this.player_.play():this.player_.pause()},t.handleKeyDown=function(e){var t,i=this.player_.liveTracker;ht.isEventKey(e,"Space")||ht.isEventKey(e,"Enter")?(e.preventDefault(),e.stopPropagation(),this.handleAction(e)):ht.isEventKey(e,"Home")?(e.preventDefault(),e.stopPropagation(),this.userSeek_(0)):ht.isEventKey(e,"End")?(e.preventDefault(),e.stopPropagation(),i&&i.isLive()?this.userSeek_(i.liveCurrentTime()):this.userSeek_(this.player_.duration())):/^[0-9]$/.test(ht(e))?(e.preventDefault(),e.stopPropagation(),t=10*(ht.codes[ht(e)]-ht.codes[0])/100,i&&i.isLive()?this.userSeek_(i.seekableStart()+i.liveWindow()*t):this.userSeek_(this.player_.duration()*t)):ht.isEventKey(e,"PgDn")?(e.preventDefault(),e.stopPropagation(),this.userSeek_(this.player_.currentTime()-60)):ht.isEventKey(e,"PgUp")?(e.preventDefault(),e.stopPropagation(),this.userSeek_(this.player_.currentTime()+60)):a.prototype.handleKeyDown.call(this,e)},t.dispose=function(){this.disableInterval_(),this.off(this.player_,["ended","durationchange","timeupdate"],this.update),this.player_.liveTracker&&this.off(this.player_.liveTracker,"liveedgechange",this.update),this.off(this.player_,["playing"],this.enableIntervalHandler_),this.off(this.player_,["ended","pause","waiting"],this.disableIntervalHandler_),"hidden"in document&&"visibilityState"in document&&this.off(document,"visibilitychange",this.toggleVisibility_),a.prototype.dispose.call(this)},e}(li);Bt.prototype.options_={children:["loadProgressBar","playProgressBar"],barName:"playProgressBar"},q||A||Bt.prototype.options_.children.splice(1,0,"mouseTimeDisplay"),pt.registerComponent("SeekBar",Bt);Ft=function(n){function e(e,t){var i=n.call(this,e,t)||this;return i.handleMouseMove=We(Ve(ft(i),i.handleMouseMove),30),i.throttledHandleMouseSeek=We(Ve(ft(i),i.handleMouseSeek),30),i.handleMouseUpHandler_=function(e){return i.handleMouseUp(e)},i.handleMouseDownHandler_=function(e){return i.handleMouseDown(e)},i.enable(),i}mt(e,n);var t=e.prototype;return t.createEl=function(){return n.prototype.createEl.call(this,"div",{className:"vjs-progress-control vjs-control"})},t.handleMouseMove=function(e){var t,i,n,r,a=this.getChild("seekBar");a&&(t=a.getChild("playProgressBar"),i=a.getChild("mouseTimeDisplay"),(t||i)&&(r=he(n=a.el()),e=pe(n,e).x,e=cn(e,0,1),i&&i.update(r,e),t&&t.update(r,a.getProgress())))},t.handleMouseSeek=function(e){var t=this.getChild("seekBar");t&&t.handleMouseMove(e)},t.enabled=function(){return this.enabled_},t.disable=function(){var e;this.children().forEach(function(e){return e.disable&&e.disable()}),this.enabled()&&(this.off(["mousedown","touchstart"],this.handleMouseDownHandler_),this.off(this.el_,"mousemove",this.handleMouseMove),this.removeListenersAddedOnMousedownAndTouchstart(),this.addClass("disabled"),this.enabled_=!1,this.player_.scrubbing()&&(e=this.getChild("seekBar"),this.player_.scrubbing(!1),e.videoWasPlaying&&Et(this.player_.play())))},t.enable=function(){this.children().forEach(function(e){return e.enable&&e.enable()}),this.enabled()||(this.on(["mousedown","touchstart"],this.handleMouseDownHandler_),this.on(this.el_,"mousemove",this.handleMouseMove),this.removeClass("disabled"),this.enabled_=!0)},t.removeListenersAddedOnMousedownAndTouchstart=function(){var e=this.el_.ownerDocument;this.off(e,"mousemove",this.throttledHandleMouseSeek),this.off(e,"touchmove",this.throttledHandleMouseSeek),this.off(e,"mouseup",this.handleMouseUpHandler_),this.off(e,"touchend",this.handleMouseUpHandler_)},t.handleMouseDown=function(e){var t=this.el_.ownerDocument,i=this.getChild("seekBar");i&&i.handleMouseDown(e),this.on(t,"mousemove",this.throttledHandleMouseSeek),this.on(t,"touchmove",this.throttledHandleMouseSeek),this.on(t,"mouseup",this.handleMouseUpHandler_),this.on(t,"touchend",this.handleMouseUpHandler_)},t.handleMouseUp=function(e){var t=this.getChild("seekBar");t&&t.handleMouseUp(e),this.removeListenersAddedOnMousedownAndTouchstart()},e}(pt);Ft.prototype.options_={children:["seekBar"]},pt.registerComponent("ProgressControl",Ft);jt=function(n){function e(e,t){var i=n.call(this,e,t)||this;return i.on(e,["enterpictureinpicture","leavepictureinpicture"],function(e){return i.handlePictureInPictureChange(e)}),i.on(e,["disablepictureinpicturechanged","loadedmetadata"],function(e){return i.handlePictureInPictureEnabledChange(e)}),i.disable(),i}mt(e,n);var t=e.prototype;return t.buildCSSClass=function(){return"vjs-picture-in-picture-control "+n.prototype.buildCSSClass.call(this)},t.handlePictureInPictureEnabledChange=function(){document.pictureInPictureEnabled&&!1===this.player_.disablePictureInPicture()?this.enable():this.disable()},t.handlePictureInPictureChange=function(e){this.player_.isInPictureInPicture()?this.controlText("Exit Picture-in-Picture"):this.controlText("Picture-in-Picture"),this.handlePictureInPictureEnabledChange()},t.handleClick=function(e){this.player_.isInPictureInPicture()?this.player_.exitPictureInPicture():this.player_.requestPictureInPicture()},e}(sn);jt.prototype.controlText_="Picture-in-Picture",pt.registerComponent("PictureInPictureToggle",jt);j=function(n){function e(e,t){var i=n.call(this,e,t)||this;return i.on(e,"fullscreenchange",function(e){return i.handleFullscreenChange(e)}),!1===document[e.fsApi_.fullscreenEnabled]&&i.disable(),i}mt(e,n);var t=e.prototype;return t.buildCSSClass=function(){return"vjs-fullscreen-control "+n.prototype.buildCSSClass.call(this)},t.handleFullscreenChange=function(e){this.player_.isFullscreen()?this.controlText("Non-Fullscreen"):this.controlText("Fullscreen")},t.handleClick=function(e){this.player_.isFullscreen()?this.player_.exitFullscreen():this.player_.requestFullscreen()},e}(sn);j.prototype.controlText_="Fullscreen",pt.registerComponent("FullscreenToggle",j);pt.registerComponent("VolumeLevel",function(t){function e(){return t.apply(this,arguments)||this}return mt(e,t),e.prototype.createEl=function(){var e=t.prototype.createEl.call(this,"div",{className:"vjs-volume-level"});return e.appendChild(t.prototype.createEl.call(this,"span",{className:"vjs-control-text"})),e},e}(pt)),pt.registerComponent("VolumeLevelTooltip",function(i){function e(e,t){t=i.call(this,e,t)||this;return t.update=We(Ve(ft(t),t.update),30),t}mt(e,i);var t=e.prototype;return t.createEl=function(){return i.prototype.createEl.call(this,"div",{className:"vjs-volume-tooltip"},{"aria-hidden":"true"})},t.update=function(e,t,i,n){if(!i){var r=de(this.el_),a=de(this.player_.el()),i=e.width*t;if(!a||!r)return;t=e.left-a.left+i,a=e.width-i+(a.right-e.right),e=r.width/2;t<e?e+=e-t:a<e&&(e=a),e<0?e=0:e>r.width&&(e=r.width),this.el_.style.right="-"+e+"px"}this.write(n+"%")},t.write=function(e){J(this.el_,e)},t.updateVolume=function(e,t,i,n,r){var a=this;this.requestNamedAnimationFrame("VolumeLevelTooltip#updateVolume",function(){a.update(e,t,i,n.toFixed(0)),r&&r()})},e}(pt));k=function(i){function e(e,t){t=i.call(this,e,t)||this;return t.update=We(Ve(ft(t),t.update),30),t}mt(e,i);var t=e.prototype;return t.createEl=function(){return i.prototype.createEl.call(this,"div",{className:"vjs-mouse-display"})},t.update=function(e,t,i){var n=this,r=100*t;this.getChild("volumeLevelTooltip").updateVolume(e,t,i,r,function(){i?n.el_.style.bottom=e.height*t+"px":n.el_.style.left=e.width*t+"px"})},e}(pt);k.prototype.options_={children:["volumeLevelTooltip"]},pt.registerComponent("MouseVolumeLevelDisplay",k);f=function(n){function e(e,t){var i=n.call(this,e,t)||this;return i.on("slideractive",function(e){return i.updateLastVolume_(e)}),i.on(e,"volumechange",function(e){return i.updateARIAAttributes(e)}),e.ready(function(){return i.updateARIAAttributes()}),i}mt(e,n);var t=e.prototype;return t.createEl=function(){return n.prototype.createEl.call(this,"div",{className:"vjs-volume-bar vjs-slider-bar"},{"aria-label":this.localize("Volume Level"),"aria-live":"polite"})},t.handleMouseDown=function(e){_e(e)&&n.prototype.handleMouseDown.call(this,e)},t.handleMouseMove=function(e){var t,i,n,r=this.getChild("mouseVolumeLevelDisplay");r&&(t=de(n=this.el()),i=this.vertical(),n=pe(n,e),n=i?n.y:n.x,n=cn(n,0,1),r.update(t,n,i)),_e(e)&&(this.checkMuted(),this.player_.volume(this.calculateDistance(e)))},t.checkMuted=function(){this.player_.muted()&&this.player_.muted(!1)},t.getPercent=function(){return this.player_.muted()?0:this.player_.volume()},t.stepForward=function(){this.checkMuted(),this.player_.volume(this.player_.volume()+.1)},t.stepBack=function(){this.checkMuted(),this.player_.volume(this.player_.volume()-.1)},t.updateARIAAttributes=function(e){var t=this.player_.muted()?0:this.volumeAsPercentage_();this.el_.setAttribute("aria-valuenow",t),this.el_.setAttribute("aria-valuetext",t+"%")},t.volumeAsPercentage_=function(){return Math.round(100*this.player_.volume())},t.updateLastVolume_=function(){var e=this,t=this.player_.volume();this.one("sliderinactive",function(){0===e.player_.volume()&&e.player_.lastVolume_(t)})},e}(li);f.prototype.options_={children:["volumeLevel"],barName:"volumeLevel"},q||A||f.prototype.options_.children.splice(0,0,"mouseVolumeLevelDisplay"),f.prototype.playerEvent="volumechange",pt.registerComponent("VolumeBar",f);ui=function(a){function e(e,t){var i,n,r;return(t=void 0===t?{}:t).vertical=t.vertical||!1,"undefined"!=typeof t.volumeBar&&!S(t.volumeBar)||(t.volumeBar=t.volumeBar||{},t.volumeBar.vertical=t.vertical),i=a.call(this,e,t)||this,n=ft(i),(r=e).tech_&&!r.tech_.featuresVolumeControl&&n.addClass("vjs-hidden"),n.on(r,"loadstart",function(){r.tech_.featuresVolumeControl?n.removeClass("vjs-hidden"):n.addClass("vjs-hidden")}),i.throttledHandleMouseMove=We(Ve(ft(i),i.handleMouseMove),30),i.handleMouseUpHandler_=function(e){return i.handleMouseUp(e)},i.on("mousedown",function(e){return i.handleMouseDown(e)}),i.on("touchstart",function(e){return i.handleMouseDown(e)}),i.on("mousemove",function(e){return i.handleMouseMove(e)}),i.on(i.volumeBar,["focus","slideractive"],function(){i.volumeBar.addClass("vjs-slider-active"),i.addClass("vjs-slider-active"),i.trigger("slideractive")}),i.on(i.volumeBar,["blur","sliderinactive"],function(){i.volumeBar.removeClass("vjs-slider-active"),i.removeClass("vjs-slider-active"),i.trigger("sliderinactive")}),i}mt(e,a);var t=e.prototype;return t.createEl=function(){var e="vjs-volume-horizontal";return this.options_.vertical&&(e="vjs-volume-vertical"),a.prototype.createEl.call(this,"div",{className:"vjs-volume-control vjs-control "+e})},t.handleMouseDown=function(e){var t=this.el_.ownerDocument;this.on(t,"mousemove",this.throttledHandleMouseMove),this.on(t,"touchmove",this.throttledHandleMouseMove),this.on(t,"mouseup",this.handleMouseUpHandler_),this.on(t,"touchend",this.handleMouseUpHandler_)},t.handleMouseUp=function(e){var t=this.el_.ownerDocument;this.off(t,"mousemove",this.throttledHandleMouseMove),this.off(t,"touchmove",this.throttledHandleMouseMove),this.off(t,"mouseup",this.handleMouseUpHandler_),this.off(t,"touchend",this.handleMouseUpHandler_)},t.handleMouseMove=function(e){this.volumeBar.handleMouseMove(e)},e}(pt);ui.prototype.options_={children:["volumeBar"]},pt.registerComponent("VolumeControl",ui);Xt=function(a){function e(e,t){var i,n,r=a.call(this,e,t)||this;return i=ft(r),(n=e).tech_&&!n.tech_.featuresMuteControl&&i.addClass("vjs-hidden"),i.on(n,"loadstart",function(){n.tech_.featuresMuteControl?i.removeClass("vjs-hidden"):i.addClass("vjs-hidden")}),r.on(e,["loadstart","volumechange"],function(e){return r.update(e)}),r}mt(e,a);var t=e.prototype;return t.buildCSSClass=function(){return"vjs-mute-control "+a.prototype.buildCSSClass.call(this)},t.handleClick=function(e){var t=this.player_.volume(),i=this.player_.lastVolume_();0===t?(this.player_.volume(i<.1?.1:i),this.player_.muted(!1)):this.player_.muted(!this.player_.muted())},t.update=function(e){this.updateIcon_(),this.updateControlText_()},t.updateIcon_=function(){var e=this.player_.volume(),t=3;q&&this.player_.tech_&&this.player_.tech_.el_&&this.player_.muted(this.player_.tech_.el_.muted),0===e||this.player_.muted()?t=0:e<.33?t=1:e<.67&&(t=2);for(var i=0;i<4;i++)ie(this.el_,"vjs-vol-"+i);te(this.el_,"vjs-vol-"+t)},t.updateControlText_=function(){var e=this.player_.muted()||0===this.player_.volume()?"Unmute":"Mute";this.controlText()!==e&&this.controlText(e)},e}(sn);Xt.prototype.controlText_="Mute",pt.registerComponent("MuteToggle",Xt);I=function(n){function e(e,t){var i;return"undefined"!=typeof(t=void 0===t?{}:t).inline?t.inline=t.inline:t.inline=!0,"undefined"!=typeof t.volumeControl&&!S(t.volumeControl)||(t.volumeControl=t.volumeControl||{},t.volumeControl.vertical=!t.inline),(i=n.call(this,e,t)||this).handleKeyPressHandler_=function(e){return i.handleKeyPress(e)},i.on(e,["loadstart"],function(e){return i.volumePanelState_(e)}),i.on(i.muteToggle,"keyup",function(e){return i.handleKeyPress(e)}),i.on(i.volumeControl,"keyup",function(e){return i.handleVolumeControlKeyUp(e)}),i.on("keydown",function(e){return i.handleKeyPress(e)}),i.on("mouseover",function(e){return i.handleMouseOver(e)}),i.on("mouseout",function(e){return i.handleMouseOut(e)}),i.on(i.volumeControl,["slideractive"],i.sliderActive_),i.on(i.volumeControl,["sliderinactive"],i.sliderInactive_),i}mt(e,n);var t=e.prototype;return t.sliderActive_=function(){this.addClass("vjs-slider-active")},t.sliderInactive_=function(){this.removeClass("vjs-slider-active")},t.volumePanelState_=function(){this.volumeControl.hasClass("vjs-hidden")&&this.muteToggle.hasClass("vjs-hidden")&&this.addClass("vjs-hidden"),this.volumeControl.hasClass("vjs-hidden")&&!this.muteToggle.hasClass("vjs-hidden")&&this.addClass("vjs-mute-toggle-only")},t.createEl=function(){var e="vjs-volume-panel-horizontal";return this.options_.inline||(e="vjs-volume-panel-vertical"),n.prototype.createEl.call(this,"div",{className:"vjs-volume-panel vjs-control "+e})},t.dispose=function(){this.handleMouseOut(),n.prototype.dispose.call(this)},t.handleVolumeControlKeyUp=function(e){ht.isEventKey(e,"Esc")&&this.muteToggle.focus()},t.handleMouseOver=function(e){this.addClass("vjs-hover"),Be(document,"keyup",this.handleKeyPressHandler_)},t.handleMouseOut=function(e){this.removeClass("vjs-hover"),Fe(document,"keyup",this.handleKeyPressHandler_)},t.handleKeyPress=function(e){ht.isEventKey(e,"Esc")&&this.handleMouseOut()},e}(pt);I.prototype.options_={children:["muteToggle","volumeControl"]},pt.registerComponent("VolumePanel",I);var hn=function(n){function e(e,t){var i=n.call(this,e,t)||this;return t&&(i.menuButton_=t.menuButton),i.focusedChild_=-1,i.on("keydown",function(e){return i.handleKeyDown(e)}),i.boundHandleBlur_=function(e){return i.handleBlur(e)},i.boundHandleTapClick_=function(e){return i.handleTapClick(e)},i}mt(e,n);var t=e.prototype;return t.addEventListenerForItem=function(e){e instanceof pt&&(this.on(e,"blur",this.boundHandleBlur_),this.on(e,["tap","click"],this.boundHandleTapClick_))},t.removeEventListenerForItem=function(e){e instanceof pt&&(this.off(e,"blur",this.boundHandleBlur_),this.off(e,["tap","click"],this.boundHandleTapClick_))},t.removeChild=function(e){"string"==typeof e&&(e=this.getChild(e)),this.removeEventListenerForItem(e),n.prototype.removeChild.call(this,e)},t.addItem=function(e){e=this.addChild(e);e&&this.addEventListenerForItem(e)},t.createEl=function(){var e=this.options_.contentElType||"ul";this.contentEl_=$(e,{className:"vjs-menu-content"}),this.contentEl_.setAttribute("role","menu");e=n.prototype.createEl.call(this,"div",{append:this.contentEl_,className:"vjs-menu"});return e.appendChild(this.contentEl_),Be(e,"click",function(e){e.preventDefault(),e.stopImmediatePropagation()}),e},t.dispose=function(){this.contentEl_=null,this.boundHandleBlur_=null,this.boundHandleTapClick_=null,n.prototype.dispose.call(this)},t.handleBlur=function(e){var t=e.relatedTarget||document.activeElement;this.children().some(function(e){return e.el()===t})||(e=this.menuButton_)&&e.buttonPressed_&&t!==e.el().firstChild&&e.unpressButton()},t.handleTapClick=function(t){var e;this.menuButton_&&(this.menuButton_.unpressButton(),e=this.children(),!Array.isArray(e)||(e=e.filter(function(e){return e.el()===t.target})[0])&&"CaptionSettingsMenuItem"!==e.name()&&this.menuButton_.focus())},t.handleKeyDown=function(e){ht.isEventKey(e,"Left")||ht.isEventKey(e,"Down")?(e.preventDefault(),e.stopPropagation(),this.stepForward()):(ht.isEventKey(e,"Right")||ht.isEventKey(e,"Up"))&&(e.preventDefault(),e.stopPropagation(),this.stepBack())},t.stepForward=function(){var e=0;void 0!==this.focusedChild_&&(e=this.focusedChild_+1),this.focus(e)},t.stepBack=function(){var e=0;void 0!==this.focusedChild_&&(e=this.focusedChild_-1),this.focus(e)},t.focus=function(e){void 0===e&&(e=0);var t=this.children().slice();t.length&&t[0].hasClass("vjs-menu-title")&&t.shift(),0<t.length&&(e<0?e=0:e>=t.length&&(e=t.length-1),t[this.focusedChild_=e].el_.focus())},e}(pt);pt.registerComponent("Menu",hn);Bt=function(n){function e(e,t){var i;(i=n.call(this,e,t=void 0===t?{}:t)||this).menuButton_=new sn(e,t),i.menuButton_.controlText(i.controlText_),i.menuButton_.el_.setAttribute("aria-haspopup","true");t=sn.prototype.buildCSSClass();i.menuButton_.el_.className=i.buildCSSClass()+" "+t,i.menuButton_.removeClass("vjs-control"),i.addChild(i.menuButton_),i.update(),i.enabled_=!0;t=function(e){return i.handleClick(e)};return i.handleMenuKeyUp_=function(e){return i.handleMenuKeyUp(e)},i.on(i.menuButton_,"tap",t),i.on(i.menuButton_,"click",t),i.on(i.menuButton_,"keydown",function(e){return i.handleKeyDown(e)}),i.on(i.menuButton_,"mouseenter",function(){i.addClass("vjs-hover"),i.menu.show(),Be(document,"keyup",i.handleMenuKeyUp_)}),i.on("mouseleave",function(e){return i.handleMouseLeave(e)}),i.on("keydown",function(e){return i.handleSubmenuKeyDown(e)}),i}mt(e,n);var t=e.prototype;return t.update=function(){var e=this.createMenu();this.menu&&(this.menu.dispose(),this.removeChild(this.menu)),this.menu=e,this.addChild(e),this.buttonPressed_=!1,this.menuButton_.el_.setAttribute("aria-expanded","false"),this.items&&this.items.length<=this.hideThreshold_?this.hide():this.show()},t.createMenu=function(){var e,t=new hn(this.player_,{menuButton:this});if(this.hideThreshold_=0,this.options_.title&&(e=$("li",{className:"vjs-menu-title",textContent:ut(this.options_.title),tabIndex:-1}),e=new pt(this.player_,{el:e}),t.addItem(e)),this.items=this.createItems(),this.items)for(var i=0;i<this.items.length;i++)t.addItem(this.items[i]);return t},t.createItems=function(){},t.createEl=function(){return n.prototype.createEl.call(this,"div",{className:this.buildWrapperCSSClass()},{})},t.buildWrapperCSSClass=function(){var e="vjs-menu-button";return!0===this.options_.inline?e+="-inline":e+="-popup","vjs-menu-button "+e+" "+sn.prototype.buildCSSClass()+" "+n.prototype.buildCSSClass.call(this)},t.buildCSSClass=function(){var e="vjs-menu-button";return!0===this.options_.inline?e+="-inline":e+="-popup","vjs-menu-button "+e+" "+n.prototype.buildCSSClass.call(this)},t.controlText=function(e,t){return void 0===t&&(t=this.menuButton_.el()),this.menuButton_.controlText(e,t)},t.dispose=function(){this.handleMouseLeave(),n.prototype.dispose.call(this)},t.handleClick=function(e){this.buttonPressed_?this.unpressButton():this.pressButton()},t.handleMouseLeave=function(e){this.removeClass("vjs-hover"),Fe(document,"keyup",this.handleMenuKeyUp_)},t.focus=function(){this.menuButton_.focus()},t.blur=function(){this.menuButton_.blur()},t.handleKeyDown=function(e){ht.isEventKey(e,"Esc")||ht.isEventKey(e,"Tab")?(this.buttonPressed_&&this.unpressButton(),ht.isEventKey(e,"Tab")||(e.preventDefault(),this.menuButton_.focus())):(ht.isEventKey(e,"Up")||ht.isEventKey(e,"Down"))&&(this.buttonPressed_||(e.preventDefault(),this.pressButton()))},t.handleMenuKeyUp=function(e){(ht.isEventKey(e,"Esc")||ht.isEventKey(e,"Tab"))&&this.removeClass("vjs-hover")},t.handleSubmenuKeyPress=function(e){this.handleSubmenuKeyDown(e)},t.handleSubmenuKeyDown=function(e){(ht.isEventKey(e,"Esc")||ht.isEventKey(e,"Tab"))&&(this.buttonPressed_&&this.unpressButton(),ht.isEventKey(e,"Tab")||(e.preventDefault(),this.menuButton_.focus()))},t.pressButton=function(){this.enabled_&&(this.buttonPressed_=!0,this.menu.show(),this.menu.lockShowing(),this.menuButton_.el_.setAttribute("aria-expanded","true"),q&&Y()||this.menu.focus())},t.unpressButton=function(){this.enabled_&&(this.buttonPressed_=!1,this.menu.unlockShowing(),this.menu.hide(),this.menuButton_.el_.setAttribute("aria-expanded","false"))},t.disable=function(){this.unpressButton(),this.enabled_=!1,this.addClass("vjs-disabled"),this.menuButton_.disable()},t.enable=function(){this.enabled_=!0,this.removeClass("vjs-disabled"),this.menuButton_.enable()},e}(pt);pt.registerComponent("MenuButton",Bt);Ft=function(r){function e(e,t){var i=t.tracks,t=r.call(this,e,t)||this;if(t.items.length<=1&&t.hide(),!i)return ft(t);var n=Ve(ft(t),t.update);return i.addEventListener("removetrack",n),i.addEventListener("addtrack",n),i.addEventListener("labelchange",n),t.player_.on("ready",n),t.player_.on("dispose",function(){i.removeEventListener("removetrack",n),i.removeEventListener("addtrack",n),i.removeEventListener("labelchange",n)}),t}return mt(e,r),e}(Bt);pt.registerComponent("TrackButton",Ft);var pn=["Tab","Esc","Up","Down","Right","Left"],jt=function(n){function e(e,t){e=n.call(this,e,t)||this;return e.selectable=t.selectable,e.isSelected_=t.selected||!1,e.multiSelectable=t.multiSelectable,e.selected(e.isSelected_),e.selectable?e.multiSelectable?e.el_.setAttribute("role","menuitemcheckbox"):e.el_.setAttribute("role","menuitemradio"):e.el_.setAttribute("role","menuitem"),e}mt(e,n);var t=e.prototype;return t.createEl=function(e,t,i){this.nonIconControl=!0;i=n.prototype.createEl.call(this,"li",b({className:"vjs-menu-item",tabIndex:-1},t),i);return i.replaceChild($("span",{className:"vjs-menu-item-text",textContent:this.localize(this.options_.label)}),i.querySelector(".vjs-icon-placeholder")),i},t.handleKeyDown=function(t){pn.some(function(e){return ht.isEventKey(t,e)})||n.prototype.handleKeyDown.call(this,t)},t.handleClick=function(e){this.selected(!0)},t.selected=function(e){this.selectable&&(e?(this.addClass("vjs-selected"),this.el_.setAttribute("aria-checked","true"),this.controlText(", selected"),this.isSelected_=!0):(this.removeClass("vjs-selected"),this.el_.setAttribute("aria-checked","false"),this.controlText(""),this.isSelected_=!1))},e}(C);pt.registerComponent("MenuItem",jt);var fn=function(u){function e(e,t){var n,i=t.track,r=e.textTracks();t.label=i.label||i.language||"Unknown",t.selected="showing"===i.mode,(n=u.call(this,e,t)||this).track=i,n.kinds=(t.kinds||[t.kind||n.track.kind]).filter(Boolean);function a(){for(var e=arguments.length,t=new Array(e),i=0;i<e;i++)t[i]=arguments[i];n.handleTracksChange.apply(ft(n),t)}function s(){for(var e=arguments.length,t=new Array(e),i=0;i<e;i++)t[i]=arguments[i];n.handleSelectedLanguageChange.apply(ft(n),t)}var o;return e.on(["loadstart","texttrackchange"],a),r.addEventListener("change",a),r.addEventListener("selectedlanguagechange",s),n.on("dispose",function(){e.off(["loadstart","texttrackchange"],a),r.removeEventListener("change",a),r.removeEventListener("selectedlanguagechange",s)}),void 0===r.onchange&&n.on(["tap","click"],function(){if("object"!=typeof window.Event)try{o=new window.Event("change")}catch(e){}o||(o=document.createEvent("Event")).initEvent("change",!0,!0),r.dispatchEvent(o)}),n.handleTracksChange(),n}mt(e,u);var t=e.prototype;return t.handleClick=function(e){var t=this.track,i=this.player_.textTracks();if(u.prototype.handleClick.call(this,e),i)for(var n=0;n<i.length;n++){var r=i[n];-1!==this.kinds.indexOf(r.kind)&&(r===t?"showing"!==r.mode&&(r.mode="showing"):"disabled"!==r.mode&&(r.mode="disabled"))}},t.handleTracksChange=function(e){var t="showing"===this.track.mode;t!==this.isSelected_&&this.selected(t)},t.handleSelectedLanguageChange=function(e){var t;"showing"===this.track.mode&&((t=this.player_.cache_.selectedLanguage)&&t.enabled&&t.language===this.track.language&&t.kind!==this.track.kind||(this.player_.cache_.selectedLanguage={enabled:!0,language:this.track.language,kind:this.track.kind}))},t.dispose=function(){this.track=null,u.prototype.dispose.call(this)},e}(jt);pt.registerComponent("TextTrackMenuItem",fn);var mn=function(i){function e(e,t){return t.track={player:e,kind:t.kind,kinds:t.kinds,default:!1,mode:"disabled"},t.kinds||(t.kinds=[t.kind]),t.label?t.track.label=t.label:t.track.label=t.kinds.join(" and ")+" off",t.selectable=!0,t.multiSelectable=!1,i.call(this,e,t)||this}mt(e,i);var t=e.prototype;return t.handleTracksChange=function(e){for(var t=this.player().textTracks(),i=!0,n=0,r=t.length;n<r;n++){var a=t[n];if(-1<this.options_.kinds.indexOf(a.kind)&&"showing"===a.mode){i=!1;break}}i!==this.isSelected_&&this.selected(i)},t.handleSelectedLanguageChange=function(e){for(var t=this.player().textTracks(),i=!0,n=0,r=t.length;n<r;n++){var a=t[n];if(-1<["captions","descriptions","subtitles"].indexOf(a.kind)&&"showing"===a.mode){i=!1;break}}i&&(this.player_.cache_.selectedLanguage={enabled:!1})},e}(fn);pt.registerComponent("OffTextTrackMenuItem",mn);j=function(i){function e(e,t){return(t=void 0===t?{}:t).tracks=e.textTracks(),i.call(this,e,t)||this}return mt(e,i),e.prototype.createItems=function(e,t){var i;void 0===t&&(t=fn),this.label_&&(i=this.label_+" off"),(e=void 0===e?[]:e).push(new mn(this.player_,{kinds:this.kinds_,kind:this.kind_,label:i})),this.hideThreshold_+=1;var n=this.player_.textTracks();Array.isArray(this.kinds_)||(this.kinds_=[this.kind_]);for(var r=0;r<n.length;r++){var a,s=n[r];-1<this.kinds_.indexOf(s.kind)&&((a=new t(this.player_,{track:s,kinds:this.kinds_,kind:this.kind_,selectable:!0,multiSelectable:!1})).addClass("vjs-"+s.kind+"-menu-item"),e.push(a))}return e},e}(Ft);pt.registerComponent("TextTrackButton",j);var gn=function(a){function e(e,t){var i=t.track,n=t.cue,r=e.currentTime();return t.selectable=!0,t.multiSelectable=!1,t.label=n.text,t.selected=n.startTime<=r&&r<n.endTime,(t=a.call(this,e,t)||this).track=i,t.cue=n,i.addEventListener("cuechange",Ve(ft(t),t.update)),t}mt(e,a);var t=e.prototype;return t.handleClick=function(e){a.prototype.handleClick.call(this),this.player_.currentTime(this.cue.startTime),this.update(this.cue.startTime)},t.update=function(e){var t=this.cue,i=this.player_.currentTime();this.selected(t.startTime<=i&&i<t.endTime)},e}(jt);pt.registerComponent("ChaptersTrackMenuItem",gn);k=function(n){function e(e,t,i){return n.call(this,e,t,i)||this}mt(e,n);var t=e.prototype;return t.buildCSSClass=function(){return"vjs-chapters-button "+n.prototype.buildCSSClass.call(this)},t.buildWrapperCSSClass=function(){return"vjs-chapters-button "+n.prototype.buildWrapperCSSClass.call(this)},t.update=function(e){this.track_&&(!e||"addtrack"!==e.type&&"removetrack"!==e.type)||this.setTrack(this.findChaptersTrack()),n.prototype.update.call(this)},t.setTrack=function(e){var t;this.track_!==e&&(this.updateHandler_||(this.updateHandler_=this.update.bind(this)),this.track_&&((t=this.player_.remoteTextTrackEls().getTrackElementByTrack_(this.track_))&&t.removeEventListener("load",this.updateHandler_),this.track_=null),this.track_=e,this.track_&&(this.track_.mode="hidden",(e=this.player_.remoteTextTrackEls().getTrackElementByTrack_(this.track_))&&e.addEventListener("load",this.updateHandler_)))},t.findChaptersTrack=function(){for(var e=this.player_.textTracks()||[],t=e.length-1;0<=t;t--){var i=e[t];if(i.kind===this.kind_)return i}},t.getMenuCaption=function(){return this.track_&&this.track_.label?this.track_.label:this.localize(ut(this.kind_))},t.createMenu=function(){return this.options_.title=this.getMenuCaption(),n.prototype.createMenu.call(this)},t.createItems=function(){var e=[];if(!this.track_)return e;var t=this.track_.cues;if(!t)return e;for(var i=0,n=t.length;i<n;i++){var r=t[i],r=new gn(this.player_,{track:this.track_,cue:r});e.push(r)}return e},e}(j);k.prototype.kind_="chapters",k.prototype.controlText_="Chapters",pt.registerComponent("ChaptersButton",k);li=function(a){function e(e,t,i){var i=a.call(this,e,t,i)||this,n=e.textTracks(),r=Ve(ft(i),i.handleTracksChange);return n.addEventListener("change",r),i.on("dispose",function(){n.removeEventListener("change",r)}),i}mt(e,a);var t=e.prototype;return t.handleTracksChange=function(e){for(var t=this.player().textTracks(),i=!1,n=0,r=t.length;n<r;n++){var a=t[n];if(a.kind!==this.kind_&&"showing"===a.mode){i=!0;break}}i?this.disable():this.enable()},t.buildCSSClass=function(){return"vjs-descriptions-button "+a.prototype.buildCSSClass.call(this)},t.buildWrapperCSSClass=function(){return"vjs-descriptions-button "+a.prototype.buildWrapperCSSClass.call(this)},e}(j);li.prototype.kind_="descriptions",li.prototype.controlText_="Descriptions",pt.registerComponent("DescriptionsButton",li);f=function(n){function e(e,t,i){return n.call(this,e,t,i)||this}mt(e,n);var t=e.prototype;return t.buildCSSClass=function(){return"vjs-subtitles-button "+n.prototype.buildCSSClass.call(this)},t.buildWrapperCSSClass=function(){return"vjs-subtitles-button "+n.prototype.buildWrapperCSSClass.call(this)},e}(j);f.prototype.kind_="subtitles",f.prototype.controlText_="Subtitles",pt.registerComponent("SubtitlesButton",f);var yn=function(i){function e(e,t){return t.track={player:e,kind:t.kind,label:t.kind+" settings",selectable:!1,default:!1,mode:"disabled"},t.selectable=!1,t.name="CaptionSettingsMenuItem",(e=i.call(this,e,t)||this).addClass("vjs-texttrack-settings"),e.controlText(", opens "+t.kind+" settings dialog"),e}return mt(e,i),e.prototype.handleClick=function(e){this.player().getChild("textTrackSettings").open()},e}(fn);pt.registerComponent("CaptionSettingsMenuItem",yn);ui=function(n){function e(e,t,i){return n.call(this,e,t,i)||this}mt(e,n);var t=e.prototype;return t.buildCSSClass=function(){return"vjs-captions-button "+n.prototype.buildCSSClass.call(this)},t.buildWrapperCSSClass=function(){return"vjs-captions-button "+n.prototype.buildWrapperCSSClass.call(this)},t.createItems=function(){var e=[];return this.player().tech_&&this.player().tech_.featuresNativeTextTracks||!this.player().getChild("textTrackSettings")||(e.push(new yn(this.player_,{kind:this.kind_})),this.hideThreshold_+=1),n.prototype.createItems.call(this,e)},e}(j);ui.prototype.kind_="captions",ui.prototype.controlText_="Captions",pt.registerComponent("CaptionsButton",ui);var vn=function(n){function e(){return n.apply(this,arguments)||this}return mt(e,n),e.prototype.createEl=function(e,t,i){t=n.prototype.createEl.call(this,e,t,i),i=t.querySelector(".vjs-menu-item-text");return"captions"===this.options_.track.kind&&(i.appendChild($("span",{className:"vjs-icon-placeholder"},{"aria-hidden":!0})),i.appendChild($("span",{className:"vjs-control-text",textContent:" "+this.localize("Captions")}))),t},e}(fn);pt.registerComponent("SubsCapsMenuItem",vn);Xt=function(i){function e(e,t){return(t=i.call(this,e,t=void 0===t?{}:t)||this).label_="subtitles",-1<["en","en-us","en-ca","fr-ca"].indexOf(t.player_.language_)&&(t.label_="captions"),t.menuButton_.controlText(ut(t.label_)),t}mt(e,i);var t=e.prototype;return t.buildCSSClass=function(){return"vjs-subs-caps-button "+i.prototype.buildCSSClass.call(this)},t.buildWrapperCSSClass=function(){return"vjs-subs-caps-button "+i.prototype.buildWrapperCSSClass.call(this)},t.createItems=function(){var e=[];return this.player().tech_&&this.player().tech_.featuresNativeTextTracks||!this.player().getChild("textTrackSettings")||(e.push(new yn(this.player_,{kind:this.label_})),this.hideThreshold_+=1),e=i.prototype.createItems.call(this,e,vn)},e}(j);Xt.prototype.kinds_=["captions","subtitles"],Xt.prototype.controlText_="Subtitles",pt.registerComponent("SubsCapsButton",Xt);var _n=function(s){function e(e,t){var n,i=t.track,r=e.audioTracks();t.label=i.label||i.language||"Unknown",t.selected=i.enabled,(n=s.call(this,e,t)||this).track=i,n.addClass("vjs-"+i.kind+"-menu-item");function a(){for(var e=arguments.length,t=new Array(e),i=0;i<e;i++)t[i]=arguments[i];n.handleTracksChange.apply(ft(n),t)}return r.addEventListener("change",a),n.on("dispose",function(){r.removeEventListener("change",a)}),n}mt(e,s);var t=e.prototype;return t.createEl=function(e,t,i){t=s.prototype.createEl.call(this,e,t,i),i=t.querySelector(".vjs-menu-item-text");return"main-desc"===this.options_.track.kind&&(i.appendChild($("span",{className:"vjs-icon-placeholder"},{"aria-hidden":!0})),i.appendChild($("span",{className:"vjs-control-text",textContent:" "+this.localize("Descriptions")}))),t},t.handleClick=function(e){if(s.prototype.handleClick.call(this,e),this.track.enabled=!0,this.player_.tech_.featuresNativeAudioTracks)for(var t=this.player_.audioTracks(),i=0;i<t.length;i++){var n=t[i];n!==this.track&&(n.enabled=n===this.track)}},t.handleTracksChange=function(e){this.selected(this.track.enabled)},e}(jt);pt.registerComponent("AudioTrackMenuItem",_n);I=function(i){function e(e,t){return(t=void 0===t?{}:t).tracks=e.audioTracks(),i.call(this,e,t)||this}mt(e,i);var t=e.prototype;return t.buildCSSClass=function(){return"vjs-audio-button "+i.prototype.buildCSSClass.call(this)},t.buildWrapperCSSClass=function(){return"vjs-audio-button "+i.prototype.buildWrapperCSSClass.call(this)},t.createItems=function(e){void 0===e&&(e=[]),this.hideThreshold_=1;for(var t=this.player_.audioTracks(),i=0;i<t.length;i++){var n=t[i];e.push(new _n(this.player_,{track:n,selectable:!0,multiSelectable:!1}))}return e},e}(Ft);I.prototype.controlText_="Audio Track",pt.registerComponent("AudioTrackButton",I);var bn=function(a){function e(e,t){var i,n=t.rate,r=parseFloat(n,10);return t.label=n,t.selected=r===e.playbackRate(),t.selectable=!0,t.multiSelectable=!1,(i=a.call(this,e,t)||this).label=n,i.rate=r,i.on(e,"ratechange",function(e){return i.update(e)}),i}mt(e,a);var t=e.prototype;return t.handleClick=function(e){a.prototype.handleClick.call(this),this.player().playbackRate(this.rate)},t.update=function(e){this.selected(this.player().playbackRate()===this.rate)},e}(jt);bn.prototype.contentElType="button",pt.registerComponent("PlaybackRateMenuItem",bn);C=function(n){function e(e,t){var i=n.call(this,e,t)||this;return i.menuButton_.el_.setAttribute("aria-describedby",i.labelElId_),i.updateVisibility(),i.updateLabel(),i.on(e,"loadstart",function(e){return i.updateVisibility(e)}),i.on(e,"ratechange",function(e){return i.updateLabel(e)}),i.on(e,"playbackrateschange",function(e){return i.handlePlaybackRateschange(e)}),i}mt(e,n);var t=e.prototype;return t.createEl=function(){var e=n.prototype.createEl.call(this);return this.labelElId_="vjs-playback-rate-value-label-"+this.id_,this.labelEl_=$("div",{className:"vjs-playback-rate-value",id:this.labelElId_,textContent:"1x"}),e.appendChild(this.labelEl_),e},t.dispose=function(){this.labelEl_=null,n.prototype.dispose.call(this)},t.buildCSSClass=function(){return"vjs-playback-rate "+n.prototype.buildCSSClass.call(this)},t.buildWrapperCSSClass=function(){return"vjs-playback-rate "+n.prototype.buildWrapperCSSClass.call(this)},t.createItems=function(){for(var e=this.playbackRates(),t=[],i=e.length-1;0<=i;i--)t.push(new bn(this.player(),{rate:e[i]+"x"}));return t},t.updateARIAAttributes=function(){this.el().setAttribute("aria-valuenow",this.player().playbackRate())},t.handleClick=function(e){var t=this.player().playbackRate(),i=this.playbackRates(),t=(i.indexOf(t)+1)%i.length;this.player().playbackRate(i[t])},t.handlePlaybackRateschange=function(e){this.update()},t.playbackRates=function(){var e=this.player();return e.playbackRates&&e.playbackRates()||[]},t.playbackRateSupported=function(){return this.player().tech_&&this.player().tech_.featuresPlaybackRate&&this.playbackRates()&&0<this.playbackRates().length},t.updateVisibility=function(e){this.playbackRateSupported()?this.removeClass("vjs-hidden"):this.addClass("vjs-hidden")},t.updateLabel=function(e){this.playbackRateSupported()&&(this.labelEl_.textContent=this.player().playbackRate()+"x")},e}(Bt);C.prototype.controlText_="Playback Rate",pt.registerComponent("PlaybackRateMenuButton",C);k=function(n){function e(){return n.apply(this,arguments)||this}mt(e,n);var t=e.prototype;return t.buildCSSClass=function(){return"vjs-spacer "+n.prototype.buildCSSClass.call(this)},t.createEl=function(e,t,i){return void 0===e&&(e="div"),void 0===i&&(i={}),(t=void 0===t?{}:t).className||(t.className=this.buildCSSClass()),n.prototype.createEl.call(this,e,t,i)},e}(pt);pt.registerComponent("Spacer",k),pt.registerComponent("CustomControlSpacer",function(e){function t(){return e.apply(this,arguments)||this}mt(t,e);var i=t.prototype;return i.buildCSSClass=function(){return"vjs-custom-control-spacer "+e.prototype.buildCSSClass.call(this)},i.createEl=function(){return e.prototype.createEl.call(this,"div",{className:this.buildCSSClass(),textContent:" "})},t}(k));li=function(e){function t(){return e.apply(this,arguments)||this}return mt(t,e),t.prototype.createEl=function(){return e.prototype.createEl.call(this,"div",{className:"vjs-control-bar",dir:"ltr"})},t}(pt);li.prototype.options_={children:["playToggle","volumePanel","currentTimeDisplay","timeDivider","durationDisplay","progressControl","liveDisplay","seekToLive","remainingTimeDisplay","customControlSpacer","playbackRateMenuButton","chaptersButton","descriptionsButton","subsCapsButton","audioTrackButton","fullscreenToggle"]},"exitPictureInPicture"in document&&li.prototype.options_.children.splice(li.prototype.options_.children.length-1,0,"pictureInPictureToggle"),pt.registerComponent("ControlBar",li);f=function(n){function e(e,t){var i=n.call(this,e,t)||this;return i.on(e,"error",function(e){return i.open(e)}),i}mt(e,n);var t=e.prototype;return t.buildCSSClass=function(){return"vjs-error-display "+n.prototype.buildCSSClass.call(this)},t.content=function(){var e=this.player().error();return e?this.localize(e.message):""},e}(At);f.prototype.options_=g({},At.prototype.options_,{pauseOnOpen:!1,fillAlways:!0,temporary:!1,uncloseable:!0}),pt.registerComponent("ErrorDisplay",f);var Tn="vjs-text-track-settings",ui=["#000","Black"],j=["#00F","Blue"],Xt=["#0FF","Cyan"],Ft=["#0F0","Green"],I=["#F0F","Magenta"],jt=["#F00","Red"],Bt=["#FFF","White"],C=["#FF0","Yellow"],k=["1","Opaque"],li=["0.5","Semi-Transparent"],f=["0","Transparent"],Sn={backgroundColor:{selector:".vjs-bg-color > select",id:"captions-background-color-%s",label:"Color",options:[ui,Bt,jt,Ft,j,C,I,Xt]},backgroundOpacity:{selector:".vjs-bg-opacity > select",id:"captions-background-opacity-%s",label:"Transparency",options:[k,li,f]},color:{selector:".vjs-fg-color > select",id:"captions-foreground-color-%s",label:"Color",options:[Bt,ui,jt,Ft,j,C,I,Xt]},edgeStyle:{selector:".vjs-edge-style > select",id:"%s",label:"Text Edge Style",options:[["none","None"],["raised","Raised"],["depressed","Depressed"],["uniform","Uniform"],["dropshadow","Dropshadow"]]},fontFamily:{selector:".vjs-font-family > select",id:"captions-font-family-%s",label:"Font Family",options:[["proportionalSansSerif","Proportional Sans-Serif"],["monospaceSansSerif","Monospace Sans-Serif"],["proportionalSerif","Proportional Serif"],["monospaceSerif","Monospace Serif"],["casual","Casual"],["script","Script"],["small-caps","Small Caps"]]},fontPercent:{selector:".vjs-font-percent > select",id:"captions-font-size-%s",label:"Font Size",options:[["0.50","50%"],["0.75","75%"],["1.00","100%"],["1.25","125%"],["1.50","150%"],["1.75","175%"],["2.00","200%"],["3.00","300%"],["4.00","400%"]],default:2,parser:function(e){return"1.00"===e?null:Number(e)}},textOpacity:{selector:".vjs-text-opacity > select",id:"captions-foreground-opacity-%s",label:"Transparency",options:[k,li]},windowColor:{selector:".vjs-window-color > select",id:"captions-window-color-%s",label:"Color"},windowOpacity:{selector:".vjs-window-opacity > select",id:"captions-window-opacity-%s",label:"Transparency",options:[f,li,k]}};function wn(e,t){if((e=t?t(e):e)&&"none"!==e)return e}Sn.windowColor.options=Sn.backgroundColor.options,pt.registerComponent("TextTrackSettings",function(n){function e(e,t){var i;return t.temporary=!1,(i=n.call(this,e,t)||this).updateDisplay=i.updateDisplay.bind(ft(i)),i.fill(),i.hasBeenOpened_=i.hasBeenFilled_=!0,i.endDialog=$("p",{className:"vjs-control-text",textContent:i.localize("End of dialog window.")}),i.el().appendChild(i.endDialog),i.setDefaults(),void 0===t.persistTextTrackSettings&&(i.options_.persistTextTrackSettings=i.options_.playerOptions.persistTextTrackSettings),i.on(i.$(".vjs-done-button"),"click",function(){i.saveSettings(),i.close()}),i.on(i.$(".vjs-default-button"),"click",function(){i.setDefaults(),i.updateDisplay()}),_(Sn,function(e){i.on(i.$(e.selector),"change",i.updateDisplay)}),i.options_.persistTextTrackSettings&&i.restoreSettings(),i}mt(e,n);var t=e.prototype;return t.dispose=function(){this.endDialog=null,n.prototype.dispose.call(this)},t.createElSelect_=function(e,t,i){var n=this;void 0===t&&(t=""),void 0===i&&(i="label");var e=Sn[e],r=e.id.replace("%s",this.id_),a=[t,r].join(" ").trim();return["<"+i+' id="'+r+'" class="'+("label"===i?"vjs-label":"")+'">',this.localize(e.label),"</"+i+">",'<select aria-labelledby="'+a+'">'].concat(e.options.map(function(e){var t=r+"-"+e[1].replace(/\W+/g,"");return['<option id="'+t+'" value="'+e[0]+'" ','aria-labelledby="'+a+" "+t+'">',n.localize(e[1]),"</option>"].join("")})).concat("</select>").join("")},t.createElFgColor_=function(){var e="captions-text-legend-"+this.id_;return['<fieldset class="vjs-fg-color vjs-track-setting">','<legend id="'+e+'">',this.localize("Text"),"</legend>",this.createElSelect_("color",e),'<span class="vjs-text-opacity vjs-opacity">',this.createElSelect_("textOpacity",e),"</span>","</fieldset>"].join("")},t.createElBgColor_=function(){var e="captions-background-"+this.id_;return['<fieldset class="vjs-bg-color vjs-track-setting">','<legend id="'+e+'">',this.localize("Background"),"</legend>",this.createElSelect_("backgroundColor",e),'<span class="vjs-bg-opacity vjs-opacity">',this.createElSelect_("backgroundOpacity",e),"</span>","</fieldset>"].join("")},t.createElWinColor_=function(){var e="captions-window-"+this.id_;return['<fieldset class="vjs-window-color vjs-track-setting">','<legend id="'+e+'">',this.localize("Window"),"</legend>",this.createElSelect_("windowColor",e),'<span class="vjs-window-opacity vjs-opacity">',this.createElSelect_("windowOpacity",e),"</span>","</fieldset>"].join("")},t.createElColors_=function(){return $("div",{className:"vjs-track-settings-colors",innerHTML:[this.createElFgColor_(),this.createElBgColor_(),this.createElWinColor_()].join("")})},t.createElFont_=function(){return $("div",{className:"vjs-track-settings-font",innerHTML:['<fieldset class="vjs-font-percent vjs-track-setting">',this.createElSelect_("fontPercent","","legend"),"</fieldset>",'<fieldset class="vjs-edge-style vjs-track-setting">',this.createElSelect_("edgeStyle","","legend"),"</fieldset>",'<fieldset class="vjs-font-family vjs-track-setting">',this.createElSelect_("fontFamily","","legend"),"</fieldset>"].join("")})},t.createElControls_=function(){var e=this.localize("restore all settings to the default values");return $("div",{className:"vjs-track-settings-controls",innerHTML:['<button type="button" class="vjs-default-button" title="'+e+'">',this.localize("Reset"),'<span class="vjs-control-text"> '+e+"</span>","</button>",'<button type="button" class="vjs-done-button">'+this.localize("Done")+"</button>"].join("")})},t.content=function(){return[this.createElColors_(),this.createElFont_(),this.createElControls_()]},t.label=function(){return this.localize("Caption Settings Dialog")},t.description=function(){return this.localize("Beginning of dialog window. Escape will cancel and close the window.")},t.buildCSSClass=function(){return n.prototype.buildCSSClass.call(this)+" vjs-text-track-settings"},t.getValues=function(){var i,n,e,r=this;return n=function(e,t,i){var n,t=(n=r.$(t.selector),t=t.parser,wn(n.options[n.options.selectedIndex].value,t));return void 0!==t&&(e[i]=t),e},void 0===(e={})&&(e=0),v(i=Sn).reduce(function(e,t){return n(e,i[t],t)},e)},t.setValues=function(i){var n=this;_(Sn,function(e,t){!function(e,t,i){if(t)for(var n=0;n<e.options.length;n++)if(wn(e.options[n].value,i)===t){e.selectedIndex=n;break}}(n.$(e.selector),i[t],e.parser)})},t.setDefaults=function(){var i=this;_(Sn,function(e){var t=e.hasOwnProperty("default")?e.default:0;i.$(e.selector).selectedIndex=t})},t.restoreSettings=function(){var e;try{e=JSON.parse(window.localStorage.getItem(Tn))}catch(e){h.warn(e)}e&&this.setValues(e)},t.saveSettings=function(){if(this.options_.persistTextTrackSettings){var e=this.getValues();try{Object.keys(e).length?window.localStorage.setItem(Tn,JSON.stringify(e)):window.localStorage.removeItem(Tn)}catch(e){h.warn(e)}}},t.updateDisplay=function(){var e=this.player_.getChild("textTrackDisplay");e&&e.updateDisplay()},t.conditionalBlur_=function(){this.previouslyActiveEl_=null;var e=this.player_.controlBar,t=e&&e.subsCapsButton,e=e&&e.captionsButton;t?t.focus():e&&e.focus()},e}(At)),pt.registerComponent("ResizeManager",function(a){function e(e,t){var i,n=t.ResizeObserver||window.ResizeObserver,r=lt({createEl:!(n=null===t.ResizeObserver?!1:n),reportTouchActivity:!1},t);return(i=a.call(this,e,r)||this).ResizeObserver=t.ResizeObserver||window.ResizeObserver,i.loadListener_=null,i.resizeObserver_=null,i.debouncedHandler_=Ge(function(){i.resizeHandler()},100,!1,ft(i)),n?(i.resizeObserver_=new i.ResizeObserver(i.debouncedHandler_),i.resizeObserver_.observe(e.el())):(i.loadListener_=function(){var e,t;i.el_&&i.el_.contentWindow&&(e=i.debouncedHandler_,t=i.unloadListener_=function(){Fe(this,"resize",e),Fe(this,"unload",t),t=null},Be(i.el_.contentWindow,"unload",t),Be(i.el_.contentWindow,"resize",e))},i.one("load",i.loadListener_)),i}mt(e,a);var t=e.prototype;return t.createEl=function(){return a.prototype.createEl.call(this,"iframe",{className:"vjs-resize-manager",tabIndex:-1},{"aria-hidden":"true"})},t.resizeHandler=function(){this.player_&&this.player_.trigger&&this.player_.trigger("playerresize")},t.dispose=function(){this.debouncedHandler_&&this.debouncedHandler_.cancel(),this.resizeObserver_&&(this.player_.el()&&this.resizeObserver_.unobserve(this.player_.el()),this.resizeObserver_.disconnect()),this.loadListener_&&this.off("load",this.loadListener_),this.el_&&this.el_.contentWindow&&this.unloadListener_&&this.unloadListener_.call(this.el_.contentWindow),this.ResizeObserver=null,this.resizeObserver=null,this.debouncedHandler_=null,this.loadListener_=null,a.prototype.dispose.call(this)},e}(pt));var En={trackingThreshold:20,liveTolerance:15};pt.registerComponent("LiveTracker",function(n){function e(e,t){var t=lt(En,t,{createEl:!1}),i=n.call(this,e,t)||this;return i.handleVisibilityChange_=function(e){return i.handleVisibilityChange(e)},i.trackLiveHandler_=function(){return i.trackLive_()},i.handlePlay_=function(e){return i.handlePlay(e)},i.handleFirstTimeupdate_=function(e){return i.handleFirstTimeupdate(e)},i.handleSeeked_=function(e){return i.handleSeeked(e)},i.seekToLiveEdge_=function(e){return i.seekToLiveEdge(e)},i.reset_(),i.on(i.player_,"durationchange",function(e){return i.handleDurationchange(e)}),i.on(i.player_,"canplay",function(){return i.toggleTracking()}),N&&"hidden"in document&&"visibilityState"in document&&i.on(document,"visibilitychange",i.handleVisibilityChange_),i}mt(e,n);var t=e.prototype;return t.handleVisibilityChange=function(){this.player_.duration()===1/0&&(document.hidden?this.stopTracking():this.startTracking())},t.trackLive_=function(){var e,t=this.player_.seekable();t&&t.length&&(e=Number(window.performance.now().toFixed(4)),t=-1===this.lastTime_?0:(e-this.lastTime_)/1e3,this.lastTime_=e,this.pastSeekEnd_=this.pastSeekEnd()+t,e=this.liveCurrentTime(),t=this.player_.currentTime(),t=this.player_.paused()||this.seekedBehindLive_||Math.abs(e-t)>this.options_.liveTolerance,(t=!this.timeupdateSeen_||e===1/0?!1:t)!==this.behindLiveEdge_&&(this.behindLiveEdge_=t,this.trigger("liveedgechange")))},t.handleDurationchange=function(){this.toggleTracking()},t.toggleTracking=function(){this.player_.duration()===1/0&&this.liveWindow()>=this.options_.trackingThreshold?(this.player_.options_.liveui&&this.player_.addClass("vjs-liveui"),this.startTracking()):(this.player_.removeClass("vjs-liveui"),this.stopTracking())},t.startTracking=function(){this.isTracking()||(this.timeupdateSeen_||(this.timeupdateSeen_=this.player_.hasStarted()),this.trackingInterval_=this.setInterval(this.trackLiveHandler_,30),this.trackLive_(),this.on(this.player_,["play","pause"],this.trackLiveHandler_),this.timeupdateSeen_?this.on(this.player_,"seeked",this.handleSeeked_):(this.one(this.player_,"play",this.handlePlay_),this.one(this.player_,"timeupdate",this.handleFirstTimeupdate_)))},t.handleFirstTimeupdate=function(){this.timeupdateSeen_=!0,this.on(this.player_,"seeked",this.handleSeeked_)},t.handleSeeked=function(){var e=Math.abs(this.liveCurrentTime()-this.player_.currentTime());this.seekedBehindLive_=this.nextSeekedFromUser_&&2<e,this.nextSeekedFromUser_=!1,this.trackLive_()},t.handlePlay=function(){this.one(this.player_,"timeupdate",this.seekToLiveEdge_)},t.reset_=function(){this.lastTime_=-1,this.pastSeekEnd_=0,this.lastSeekEnd_=-1,this.behindLiveEdge_=!0,this.timeupdateSeen_=!1,this.seekedBehindLive_=!1,this.nextSeekedFromUser_=!1,this.clearInterval(this.trackingInterval_),this.trackingInterval_=null,this.off(this.player_,["play","pause"],this.trackLiveHandler_),this.off(this.player_,"seeked",this.handleSeeked_),this.off(this.player_,"play",this.handlePlay_),this.off(this.player_,"timeupdate",this.handleFirstTimeupdate_),this.off(this.player_,"timeupdate",this.seekToLiveEdge_)},t.nextSeekedFromUser=function(){this.nextSeekedFromUser_=!0},t.stopTracking=function(){this.isTracking()&&(this.reset_(),this.trigger("liveedgechange"))},t.seekableEnd=function(){for(var e=this.player_.seekable(),t=[],i=e?e.length:0;i--;)t.push(e.end(i));return t.length?t.sort()[t.length-1]:1/0},t.seekableStart=function(){for(var e=this.player_.seekable(),t=[],i=e?e.length:0;i--;)t.push(e.start(i));return t.length?t.sort()[0]:0},t.liveWindow=function(){var e=this.liveCurrentTime();return e===1/0?0:e-this.seekableStart()},t.isLive=function(){return this.isTracking()},t.atLiveEdge=function(){return!this.behindLiveEdge()},t.liveCurrentTime=function(){return this.pastSeekEnd()+this.seekableEnd()},t.pastSeekEnd=function(){var e=this.seekableEnd();return-1!==this.lastSeekEnd_&&e!==this.lastSeekEnd_&&(this.pastSeekEnd_=0),this.lastSeekEnd_=e,this.pastSeekEnd_},t.behindLiveEdge=function(){return this.behindLiveEdge_},t.isTracking=function(){return"number"==typeof this.trackingInterval_},t.seekToLiveEdge=function(){this.seekedBehindLive_=!1,this.atLiveEdge()||(this.nextSeekedFromUser_=!1,this.player_.currentTime(this.liveCurrentTime()))},t.dispose=function(){this.off(document,"visibilitychange",this.handleVisibilityChange_),this.stopTracking(),n.prototype.dispose.call(this)},e}(pt));function kn(e){if((n=e.el()).hasAttribute("src"))return e.triggerSourceset(n.src),1;var t=e.$$("source"),i=[],n="";if(t.length){for(var r=0;r<t.length;r++){var a=t[r].src;a&&-1===i.indexOf(a)&&i.push(a)}return!!i.length&&(1===i.length&&(n=i[0]),e.triggerSourceset(n),!0)}}function Cn(e,t){for(var i={},n=0;n<e.length&&!((i=Object.getOwnPropertyDescriptor(e[n],t))&&i.set&&i.get);n++);return i.enumerable=!0,i.configurable=!0,i}function In(a){var t,e,i,s=a.el();s.resetSourceWatch_||(t={},e=Cn([a.el(),window.HTMLMediaElement.prototype,window.Element.prototype,Ln],"innerHTML"),i=function(r){return function(){for(var e=arguments.length,t=new Array(e),i=0;i<e;i++)t[i]=arguments[i];var n=r.apply(s,t);return kn(a),n}},["append","appendChild","insertAdjacentHTML"].forEach(function(e){s[e]&&(t[e]=s[e],s[e]=i(t[e]))}),Object.defineProperty(s,"innerHTML",lt(e,{set:i(e.set)})),s.resetSourceWatch_=function(){s.resetSourceWatch_=null,Object.keys(t).forEach(function(e){s[e]=t[e]}),Object.defineProperty(s,"innerHTML",e)},a.one("sourceset",s.resetSourceWatch_))}function xn(i){var n,t,r,a;i.featuresSourceset&&((n=i.el()).resetSourceset_||(t=Cn([i.el(),window.HTMLMediaElement.prototype,Dn],"src"),r=n.setAttribute,a=n.load,Object.defineProperty(n,"src",lt(t,{set:function(e){e=t.set.call(n,e);return i.triggerSourceset(n.src),e}})),n.setAttribute=function(e,t){t=r.call(n,e,t);return/src/i.test(e)&&i.triggerSourceset(n.src),t},n.load=function(){var e=a.call(n);return kn(i)||(i.triggerSourceset(""),In(i)),e},n.currentSrc?i.triggerSourceset(n.currentSrc):kn(i)||In(i),n.resetSourceset_=function(){n.resetSourceset_=null,n.load=a,n.setAttribute=r,Object.defineProperty(n,"src",t),n.resetSourceWatch_&&n.resetSourceWatch_()}))}function An(t,i,n,e){function r(e){return Object.defineProperty(t,i,{value:e,enumerable:!0,writable:!0})}var a={configurable:!0,enumerable:!0,get:function(){var e=n();return r(e),e}};return(e=void 0===e?!0:e)&&(a.set=r),Object.defineProperty(t,i,a)}var Pn,Ln=Object.defineProperty({},"innerHTML",{get:function(){return this.cloneNode(!0).innerHTML},set:function(e){var t=document.createElement(this.nodeName.toLowerCase());t.innerHTML=e;for(var i=document.createDocumentFragment();t.childNodes.length;)i.appendChild(t.childNodes[0]);return this.innerText="",window.Element.prototype.appendChild.call(this,i),this.innerHTML}}),Dn=Object.defineProperty({},"src",{get:function(){return this.hasAttribute("src")?Mt(window.Element.prototype.getAttribute.call(this,"src")):""},set:function(e){return window.Element.prototype.setAttribute.call(this,"src",e),e}}),On=function(l){function s(e,t){var i=l.call(this,e,t)||this,t=e.source,n=!1;if(t&&(i.el_.currentSrc!==t.src||e.tag&&3===e.tag.initNetworkState_)?i.setSource(t):i.handleLateInit_(i.el_),e.enableSourceset&&i.setupSourcesetHandling_(),i.isScrubbing_=!1,i.el_.hasChildNodes()){for(var r=i.el_.childNodes,a=r.length,s=[];a--;){var o=r[a];"track"===o.nodeName.toLowerCase()&&(i.featuresNativeTextTracks?(i.remoteTextTrackEls().addTrackElement_(o),i.remoteTextTracks().addTrack(o.track),i.textTracks().addTrack(o.track),n||i.el_.hasAttribute("crossorigin")||!Ut(o.src)||(n=!0)):s.push(o))}for(var u=0;u<s.length;u++)i.el_.removeChild(s[u])}return i.proxyNativeTracks_(),i.featuresNativeTextTracks&&n&&h.warn("Text Tracks are being loaded from another origin but the crossorigin attribute isn't used.\nThis may prevent text tracks from loading."),i.restoreMetadataTracksInIOSNativePlayer_(),(F||H||L)&&!0===e.nativeControlsForTouch&&i.setControls(!0),i.proxyWebkitFullscreen_(),i.triggerReady(),i}mt(s,l);var e=s.prototype;return e.dispose=function(){this.el_&&this.el_.resetSourceset_&&this.el_.resetSourceset_(),s.disposeMediaElement(this.el_),this.options_=null,l.prototype.dispose.call(this)},e.setupSourcesetHandling_=function(){xn(this)},e.restoreMetadataTracksInIOSNativePlayer_=function(){function e(){i=[];for(var e=0;e<n.length;e++){var t=n[e];"metadata"===t.kind&&i.push({track:t,storedMode:t.mode})}}var i,n=this.textTracks();e(),n.addEventListener("change",e),this.on("dispose",function(){return n.removeEventListener("change",e)});function r(){for(var e=0;e<i.length;e++){var t=i[e];"disabled"===t.track.mode&&t.track.mode!==t.storedMode&&(t.track.mode=t.storedMode)}n.removeEventListener("change",r)}this.on("webkitbeginfullscreen",function(){n.removeEventListener("change",e),n.removeEventListener("change",r),n.addEventListener("change",r)}),this.on("webkitendfullscreen",function(){n.removeEventListener("change",e),n.addEventListener("change",e),n.removeEventListener("change",r)})},e.overrideNative_=function(e,t){var i,n=this;t===this["featuresNative"+e+"Tracks"]&&(this[(i=e.toLowerCase())+"TracksListeners_"]&&Object.keys(this[i+"TracksListeners_"]).forEach(function(e){n.el()[i+"Tracks"].removeEventListener(e,n[i+"TracksListeners_"][e])}),this["featuresNative"+e+"Tracks"]=!t,this[i+"TracksListeners_"]=null,this.proxyNativeTracksForType_(i))},e.overrideNativeAudioTracks=function(e){this.overrideNative_("Audio",e)},e.overrideNativeVideoTracks=function(e){this.overrideNative_("Video",e)},e.proxyNativeTracksForType_=function(i){var e,t,n=this,r=ai[i],a=this.el()[r.getterName],s=this[r.getterName]();this["featuresNative"+r.capitalName+"Tracks"]&&a&&a.addEventListener&&(t=function(){for(var e=[],t=0;t<s.length;t++){for(var i=!1,n=0;n<a.length;n++)if(a[n]===s[t]){i=!0;break}i||e.push(s[t])}for(;e.length;)s.removeTrack(e.shift())},this[r.getterName+"Listeners_"]=e={change:function(e){var t={type:"change",target:s,currentTarget:s,srcElement:s};s.trigger(t),"text"===i&&n[si.remoteText.getterName]().trigger(t)},addtrack:function(e){s.addTrack(e.track)},removetrack:function(e){s.removeTrack(e.track)}},Object.keys(e).forEach(function(t){var i=e[t];a.addEventListener(t,i),n.on("dispose",function(e){return a.removeEventListener(t,i)})}),this.on("loadstart",t),this.on("dispose",function(e){return n.off("loadstart",t)}))},e.proxyNativeTracks_=function(){var t=this;ai.names.forEach(function(e){t.proxyNativeTracksForType_(e)})},e.createEl=function(){var e,t=this.options_.tag;t&&(this.options_.playerElIngest||this.movingMediaElementInDOM)||(t?(e=t.cloneNode(!0),t.parentNode&&t.parentNode.insertBefore(e,t),s.disposeMediaElement(t),t=e):(t=document.createElement("video"),e=lt({},this.options_.tag&&ae(this.options_.tag)),F&&!0===this.options_.nativeControlsForTouch||delete e.controls,re(t,b(e,{id:this.options_.techId,class:"vjs-tech"}))),t.playerId=this.options_.playerId),"undefined"!=typeof this.options_.preload&&oe(t,"preload",this.options_.preload),void 0!==this.options_.disablePictureInPicture&&(t.disablePictureInPicture=this.options_.disablePictureInPicture);for(var i=["loop","muted","playsinline","autoplay"],n=0;n<i.length;n++){var r=i[n],a=this.options_[r];"undefined"!=typeof a&&(a?oe(t,r,r):ue(t,r),t[r]=a)}return t},e.handleLateInit_=function(e){if(0!==e.networkState&&3!==e.networkState){if(0===e.readyState){var t=!1,i=function(){t=!0};this.on("loadstart",i);var n=function(){t||this.trigger("loadstart")};return this.on("loadedmetadata",n),void this.ready(function(){this.off("loadstart",i),this.off("loadedmetadata",n),t||this.trigger("loadstart")})}var r=["loadstart"];r.push("loadedmetadata"),2<=e.readyState&&r.push("loadeddata"),3<=e.readyState&&r.push("canplay"),4<=e.readyState&&r.push("canplaythrough"),this.ready(function(){r.forEach(function(e){this.trigger(e)},this)})}},e.setScrubbing=function(e){this.isScrubbing_=e},e.scrubbing=function(){return this.isScrubbing_},e.setCurrentTime=function(e){try{this.isScrubbing_&&this.el_.fastSeek&&V?this.el_.fastSeek(e):this.el_.currentTime=e}catch(e){h(e,"Video is not ready. (Video.js)")}},e.duration=function(){var t=this;return this.el_.duration===1/0&&A&&R&&0===this.el_.currentTime?(this.on("timeupdate",function e(){0<t.el_.currentTime&&(t.el_.duration===1/0&&t.trigger("durationchange"),t.off("timeupdate",e))}),NaN):this.el_.duration||NaN},e.width=function(){return this.el_.offsetWidth},e.height=function(){return this.el_.offsetHeight},e.proxyWebkitFullscreen_=function(){var e,t,i=this;"webkitDisplayingFullscreen"in this.el_&&(e=function(){this.trigger("fullscreenchange",{isFullscreen:!1}),this.el_.controls&&!this.options_.nativeControlsForTouch&&this.controls()&&(this.el_.controls=!1)},t=function(){"webkitPresentationMode"in this.el_&&"picture-in-picture"!==this.el_.webkitPresentationMode&&(this.one("webkitendfullscreen",e),this.trigger("fullscreenchange",{isFullscreen:!0,nativeIOSFullscreen:!0}))},this.on("webkitbeginfullscreen",t),this.on("dispose",function(){i.off("webkitbeginfullscreen",t),i.off("webkitendfullscreen",e)}))},e.supportsFullScreen=function(){if("function"==typeof this.el_.webkitEnterFullScreen){var e=window.navigator&&window.navigator.userAgent||"";if(/Android/.test(e)||!/Chrome|Mac OS X 10.5/.test(e))return!0}return!1},e.enterFullScreen=function(){var e=this.el_;if(e.paused&&e.networkState<=e.HAVE_METADATA)Et(this.el_.play()),this.setTimeout(function(){e.pause();try{e.webkitEnterFullScreen()}catch(e){this.trigger("fullscreenerror",e)}},0);else try{e.webkitEnterFullScreen()}catch(e){this.trigger("fullscreenerror",e)}},e.exitFullScreen=function(){this.el_.webkitDisplayingFullscreen?this.el_.webkitExitFullScreen():this.trigger("fullscreenerror",new Error("The video is not fullscreen"))},e.requestPictureInPicture=function(){return this.el_.requestPictureInPicture()},e.src=function(e){if(void 0===e)return this.el_.src;this.setSrc(e)},e.reset=function(){s.resetMediaElement(this.el_)},e.currentSrc=function(){return this.currentSource_?this.currentSource_.src:this.el_.currentSrc},e.setControls=function(e){this.el_.controls=!!e},e.addTextTrack=function(e,t,i){return this.featuresNativeTextTracks?this.el_.addTextTrack(e,t,i):l.prototype.addTextTrack.call(this,e,t,i)},e.createRemoteTextTrack=function(e){if(!this.featuresNativeTextTracks)return l.prototype.createRemoteTextTrack.call(this,e);var t=document.createElement("track");return e.kind&&(t.kind=e.kind),e.label&&(t.label=e.label),(e.language||e.srclang)&&(t.srclang=e.language||e.srclang),e.default&&(t.default=e.default),e.id&&(t.id=e.id),e.src&&(t.src=e.src),t},e.addRemoteTextTrack=function(e,t){t=l.prototype.addRemoteTextTrack.call(this,e,t);return this.featuresNativeTextTracks&&this.el().appendChild(t),t},e.removeRemoteTextTrack=function(e){if(l.prototype.removeRemoteTextTrack.call(this,e),this.featuresNativeTextTracks)for(var t=this.$$("track"),i=t.length;i--;)e!==t[i]&&e!==t[i].track||this.el().removeChild(t[i])},e.getVideoPlaybackQuality=function(){if("function"==typeof this.el().getVideoPlaybackQuality)return this.el().getVideoPlaybackQuality();var e={};return"undefined"!=typeof this.el().webkitDroppedFrameCount&&"undefined"!=typeof this.el().webkitDecodedFrameCount&&(e.droppedVideoFrames=this.el().webkitDroppedFrameCount,e.totalVideoFrames=this.el().webkitDecodedFrameCount),window.performance&&"function"==typeof window.performance.now?e.creationTime=window.performance.now():window.performance&&window.performance.timing&&"number"==typeof window.performance.timing.navigationStart&&(e.creationTime=window.Date.now()-window.performance.timing.navigationStart),e},s}(ji);An(On,"TEST_VID",function(){if(X()){var e=document.createElement("video"),t=document.createElement("track");return t.kind="captions",t.srclang="en",t.label="English",e.appendChild(t),e}}),On.isSupported=function(){try{On.TEST_VID.volume=.5}catch(e){return!1}return!(!On.TEST_VID||!On.TEST_VID.canPlayType)},On.canPlayType=function(e){return On.TEST_VID.canPlayType(e)},On.canPlaySource=function(e,t){return On.canPlayType(e.type)},On.canControlVolume=function(){try{var e=On.TEST_VID.volume;On.TEST_VID.volume=e/2+.1;var t=e!==On.TEST_VID.volume;return t&&q?(window.setTimeout(function(){On&&On.prototype&&(On.prototype.featuresVolumeControl=e!==On.TEST_VID.volume)}),!1):t}catch(e){return!1}},On.canMuteVolume=function(){try{var e=On.TEST_VID.muted;return On.TEST_VID.muted=!e,On.TEST_VID.muted?oe(On.TEST_VID,"muted","muted"):ue(On.TEST_VID,"muted"),e!==On.TEST_VID.muted}catch(e){return!1}},On.canControlPlaybackRate=function(){if(A&&R&&M<58)return!1;try{var e=On.TEST_VID.playbackRate;return On.TEST_VID.playbackRate=e/2+.1,e!==On.TEST_VID.playbackRate}catch(e){return!1}},On.canOverrideAttributes=function(){try{var e=function(){};Object.defineProperty(document.createElement("video"),"src",{get:e,set:e}),Object.defineProperty(document.createElement("audio"),"src",{get:e,set:e}),Object.defineProperty(document.createElement("video"),"innerHTML",{get:e,set:e}),Object.defineProperty(document.createElement("audio"),"innerHTML",{get:e,set:e})}catch(e){return!1}return!0},On.supportsNativeTextTracks=function(){return V||q&&R},On.supportsNativeVideoTracks=function(){return!(!On.TEST_VID||!On.TEST_VID.videoTracks)},On.supportsNativeAudioTracks=function(){return!(!On.TEST_VID||!On.TEST_VID.audioTracks)},On.Events=["loadstart","suspend","abort","error","emptied","stalled","loadedmetadata","loadeddata","canplay","canplaythrough","playing","waiting","seeking","seeked","ended","durationchange","timeupdate","progress","play","pause","ratechange","resize","volumechange"],[["featuresMuteControl","canMuteVolume"],["featuresPlaybackRate","canControlPlaybackRate"],["featuresSourceset","canOverrideAttributes"],["featuresNativeTextTracks","supportsNativeTextTracks"],["featuresNativeVideoTracks","supportsNativeVideoTracks"],["featuresNativeAudioTracks","supportsNativeAudioTracks"]].forEach(function(e){var t=e[0],i=e[1];An(On.prototype,t,function(){return On[i]()},!0)}),On.prototype.featuresVolumeControl=On.canControlVolume(),On.prototype.movingMediaElementInDOM=!q,On.prototype.featuresFullscreenResize=!0,On.prototype.featuresProgressEvents=!0,On.prototype.featuresTimeupdateEvents=!0,On.patchCanPlayType=function(){4<=P&&!D&&!R&&(Pn=On.TEST_VID&&On.TEST_VID.constructor.prototype.canPlayType,On.TEST_VID.constructor.prototype.canPlayType=function(e){return e&&/^application\/(?:x-|vnd\.apple\.)mpegurl/i.test(e)?"maybe":Pn.call(this,e)})},On.unpatchCanPlayType=function(){var e=On.TEST_VID.constructor.prototype.canPlayType;return Pn&&(On.TEST_VID.constructor.prototype.canPlayType=Pn),e},On.patchCanPlayType(),On.disposeMediaElement=function(e){if(e){for(e.parentNode&&e.parentNode.removeChild(e);e.hasChildNodes();)e.removeChild(e.firstChild);e.removeAttribute("src"),"function"==typeof e.load&&function(){try{e.load()}catch(e){}}()}},On.resetMediaElement=function(e){if(e){for(var t=e.querySelectorAll("source"),i=t.length;i--;)e.removeChild(t[i]);e.removeAttribute("src"),"function"==typeof e.load&&function(){try{e.load()}catch(e){}}()}},["muted","defaultMuted","autoplay","controls","loop","playsinline"].forEach(function(e){On.prototype[e]=function(){return this.el_[e]||this.el_.hasAttribute(e)}}),["muted","defaultMuted","autoplay","loop","playsinline"].forEach(function(t){On.prototype["set"+ut(t)]=function(e){(this.el_[t]=e)?this.el_.setAttribute(t,t):this.el_.removeAttribute(t)}}),["paused","currentTime","buffered","volume","poster","preload","error","seeking","seekable","ended","playbackRate","defaultPlaybackRate","disablePictureInPicture","played","networkState","readyState","videoWidth","videoHeight","crossOrigin"].forEach(function(e){On.prototype[e]=function(){return this.el_[e]}}),["volume","src","poster","preload","playbackRate","defaultPlaybackRate","disablePictureInPicture","crossOrigin"].forEach(function(t){On.prototype["set"+ut(t)]=function(e){this.el_[t]=e}}),["pause","load","play"].forEach(function(e){On.prototype[e]=function(){return this.el_[e]()}}),ji.withSourceHandlers(On),On.nativeSourceHandler={},On.nativeSourceHandler.canPlayType=function(e){try{return On.TEST_VID.canPlayType(e)}catch(e){return""}},On.nativeSourceHandler.canHandleSource=function(e,t){if(e.type)return On.nativeSourceHandler.canPlayType(e.type);if(e.src){e=Nt(e.src);return On.nativeSourceHandler.canPlayType("video/"+e)}return""},On.nativeSourceHandler.handleSource=function(e,t,i){t.setSrc(e.src)},On.nativeSourceHandler.dispose=function(){},On.registerSourceHandler(On.nativeSourceHandler),ji.registerTech("Html5",On);var Rn=["progress","abort","suspend","emptied","stalled","loadedmetadata","loadeddata","timeupdate","resize","volumechange","texttrackchange"],Mn={canplay:"CanPlay",canplaythrough:"CanPlayThrough",playing:"Playing",seeked:"Seeked"},Nn=["tiny","xsmall","small","medium","large","xlarge","huge"],Un={};Nn.forEach(function(e){var t="x"===e.charAt(0)?"x-"+e.substring(1):e;Un[e]="vjs-layout-"+t});var Bn={tiny:210,xsmall:320,small:425,medium:768,large:1440,xlarge:2560,huge:1/0},Fn=function(c){function o(e,t,i){var n,r;if(e.id=e.id||t.id||"vjs_video_"+Pe++,(t=b(o.getTagSettings(e),t)).initChildren=!1,t.createEl=!1,t.evented=!1,t.reportTouchActivity=!1,!t.language)if("function"==typeof e.closest){var a=e.closest("[lang]");a&&a.getAttribute&&(t.language=a.getAttribute("lang"))}else for(var s=e;s&&1===s.nodeType;){if(ae(s).hasOwnProperty("lang")){t.language=s.getAttribute("lang");break}s=s.parentNode}if((n=c.call(this,null,t,i)||this).boundDocumentFullscreenChange_=function(e){return n.documentFullscreenChange_(e)},n.boundFullWindowOnEscKey_=function(e){return n.fullWindowOnEscKey(e)},n.boundUpdateStyleEl_=function(e){return n.updateStyleEl_(e)},n.boundApplyInitTime_=function(e){return n.applyInitTime_(e)},n.boundUpdateCurrentBreakpoint_=function(e){return n.updateCurrentBreakpoint_(e)},n.boundHandleTechClick_=function(e){return n.handleTechClick_(e)},n.boundHandleTechDoubleClick_=function(e){return n.handleTechDoubleClick_(e)},n.boundHandleTechTouchStart_=function(e){return n.handleTechTouchStart_(e)},n.boundHandleTechTouchMove_=function(e){return n.handleTechTouchMove_(e)},n.boundHandleTechTouchEnd_=function(e){return n.handleTechTouchEnd_(e)},n.boundHandleTechTap_=function(e){return n.handleTechTap_(e)},n.isFullscreen_=!1,n.log=p(n.id_),n.fsApi_=l,n.isPosterFromTech_=!1,n.queuedCallbacks_=[],n.isReady_=!1,n.hasStarted_=!1,n.userActive_=!1,n.debugEnabled_=!1,!n.options_||!n.options_.techOrder||!n.options_.techOrder.length)throw new Error("No techOrder specified. Did you overwrite videojs.options instead of just changing the properties you want to override?");n.tag=e,n.tagAttributes=e&&ae(e),n.language(n.options_.language),t.languages?(r={},Object.getOwnPropertyNames(t.languages).forEach(function(e){r[e.toLowerCase()]=t.languages[e]}),n.languages_=r):n.languages_=o.prototype.options_.languages,n.resetCache_(),n.poster_=t.poster||"",n.controls_=!!t.controls,e.controls=!1,e.removeAttribute("controls"),n.changingSrc_=!1,n.playCallbacks_=[],n.playTerminatedQueue_=[],e.hasAttribute("autoplay")?n.autoplay(!0):n.autoplay(n.options_.autoplay),t.plugins&&Object.keys(t.plugins).forEach(function(e){if("function"!=typeof n[e])throw new Error('plugin "'+e+'" does not exist')}),n.scrubbing_=!1,n.el_=n.createEl(),rt(ft(n),{eventBusKey:"el_"}),n.fsApi_.requestFullscreen&&(Be(document,n.fsApi_.fullscreenchange,n.boundDocumentFullscreenChange_),n.on(n.fsApi_.fullscreenchange,n.boundDocumentFullscreenChange_)),n.fluid_&&n.on(["playerreset","resize"],n.boundUpdateStyleEl_);i=lt(n.options_);t.plugins&&Object.keys(t.plugins).forEach(function(e){n[e](t.plugins[e])}),t.debug&&n.debug(!0),n.options_.playerOptions=i,n.middleware_=[],n.playbackRates(t.playbackRates),n.initChildren(),n.isAudio("audio"===e.nodeName.toLowerCase()),n.controls()?n.addClass("vjs-controls-enabled"):n.addClass("vjs-controls-disabled"),n.el_.setAttribute("role","region"),n.isAudio()?n.el_.setAttribute("aria-label",n.localize("Audio Player")):n.el_.setAttribute("aria-label",n.localize("Video Player")),n.isAudio()&&n.addClass("vjs-audio"),n.flexNotSupported_()&&n.addClass("vjs-no-flex"),F&&n.addClass("vjs-touch-enabled"),q||n.addClass("vjs-workinghover"),o.players[n.id_]=ft(n);e=u.split(".")[0];return n.addClass("vjs-v"+e),n.userActive(!0),n.reportUserActivity(),n.one("play",function(e){return n.listenForUserActivity_(e)}),n.on("stageclick",function(e){return n.handleStageClick_(e)}),n.on("keydown",function(e){return n.handleKeyDown(e)}),n.on("languagechange",function(e){return n.handleLanguagechange(e)}),n.breakpoints(n.options_.breakpoints),n.responsive(n.options_.responsive),n}mt(o,c);var e=o.prototype;return e.dispose=function(){var t=this;this.trigger("dispose"),this.off("dispose"),Fe(document,this.fsApi_.fullscreenchange,this.boundDocumentFullscreenChange_),Fe(document,"keydown",this.boundFullWindowOnEscKey_),this.styleEl_&&this.styleEl_.parentNode&&(this.styleEl_.parentNode.removeChild(this.styleEl_),this.styleEl_=null),o.players[this.id_]=null,this.tag&&this.tag.player&&(this.tag.player=null),this.el_&&this.el_.player&&(this.el_.player=null),this.tech_&&(this.tech_.dispose(),this.isPosterFromTech_=!1,this.poster_=""),this.playerElIngest_&&(this.playerElIngest_=null),this.tag&&(this.tag=null),qi[this.id()]=null,oi.names.forEach(function(e){e=oi[e],e=t[e.getterName]();e&&e.off&&e.off()}),c.prototype.dispose.call(this)},e.createEl=function(){var t,i=this.tag,e=this.playerElIngest_=i.parentNode&&i.parentNode.hasAttribute&&i.parentNode.hasAttribute("data-vjs-player"),n="video-js"===this.tag.tagName.toLowerCase();e?t=this.el_=i.parentNode:n||(t=this.el_=c.prototype.createEl.call(this,"div"));var r,a,s=ae(i);if(n){for(t=this.el_=i,i=this.tag=document.createElement("video");t.children.length;)i.appendChild(t.firstChild);ee(t,"video-js")||te(t,"video-js"),t.appendChild(i),e=this.playerElIngest_=t,Object.keys(t).forEach(function(e){try{i[e]=t[e]}catch(e){}})}i.setAttribute("tabindex","-1"),s.tabindex="-1",(N||R&&B)&&(i.setAttribute("role","application"),s.role="application"),i.removeAttribute("width"),i.removeAttribute("height"),"width"in s&&delete s.width,"height"in s&&delete s.height,Object.getOwnPropertyNames(s).forEach(function(e){n&&"class"===e||t.setAttribute(e,s[e]),n&&i.setAttribute(e,s[e])}),i.playerId=i.id,i.id+="_html5_api",i.className="vjs-tech",(i.player=t.player=this).addClass("vjs-paused"),!0!==window.VIDEOJS_NO_DYNAMIC_STYLE&&(this.styleEl_=xe("vjs-styles-dimensions"),r=Te(".vjs-styles-defaults"),(a=Te("head")).insertBefore(this.styleEl_,r?r.nextSibling:a.firstChild)),this.fill_=!1,this.fluid_=!1,this.width(this.options_.width),this.height(this.options_.height),this.fill(this.options_.fill),this.fluid(this.options_.fluid),this.aspectRatio(this.options_.aspectRatio),this.crossOrigin(this.options_.crossOrigin||this.options_.crossorigin);for(var o=i.getElementsByTagName("a"),u=0;u<o.length;u++){var l=o.item(u);te(l,"vjs-hidden"),l.setAttribute("hidden","hidden")}return i.initNetworkState_=i.networkState,i.parentNode&&!e&&i.parentNode.insertBefore(t,i),Z(i,t),this.children_.unshift(i),this.el_.setAttribute("lang",this.language_),this.el_.setAttribute("translate","no"),this.el_=t},e.crossOrigin=function(e){if(!e)return this.techGet_("crossOrigin");"anonymous"===e||"use-credentials"===e?this.techCall_("setCrossOrigin",e):h.warn('crossOrigin must be "anonymous" or "use-credentials", given "'+e+'"')},e.width=function(e){return this.dimension("width",e)},e.height=function(e){return this.dimension("height",e)},e.dimension=function(e,t){var i=e+"_";if(void 0===t)return this[i]||0;if(""===t||"auto"===t)return this[i]=void 0,void this.updateStyleEl_();var n=parseFloat(t);isNaN(n)?h.error('Improper value "'+t+'" supplied for for '+e):(this[i]=n,this.updateStyleEl_())},e.fluid=function(e){var t,i=this;if(void 0===e)return!!this.fluid_;this.fluid_=!!e,it(this)&&this.off(["playerreset","resize"],this.boundUpdateStyleEl_),e?(this.addClass("vjs-fluid"),this.fill(!1),t=function(){i.on(["playerreset","resize"],i.boundUpdateStyleEl_)},it(e=this)?t():(e.eventedCallbacks||(e.eventedCallbacks=[]),e.eventedCallbacks.push(t))):this.removeClass("vjs-fluid"),this.updateStyleEl_()},e.fill=function(e){if(void 0===e)return!!this.fill_;this.fill_=!!e,e?(this.addClass("vjs-fill"),this.fluid(!1)):this.removeClass("vjs-fill")},e.aspectRatio=function(e){if(void 0===e)return this.aspectRatio_;if(!/^\d+\:\d+$/.test(e))throw new Error("Improper value supplied for aspect ratio. The format should be width:height, for example 16:9.");this.aspectRatio_=e,this.fluid(!0),this.updateStyleEl_()},e.updateStyleEl_=function(){var e,t,i,n;!0!==window.VIDEOJS_NO_DYNAMIC_STYLE?(n=(i=(void 0!==this.aspectRatio_&&"auto"!==this.aspectRatio_?this.aspectRatio_:0<this.videoWidth()?this.videoWidth()+":"+this.videoHeight():"16:9").split(":"))[1]/i[0],e=void 0!==this.width_?this.width_:void 0!==this.height_?this.height_/n:this.videoWidth()||300,t=void 0!==this.height_?this.height_:e*n,i=/^[^a-zA-Z]/.test(this.id())?"dimensions-"+this.id():this.id()+"-dimensions",this.addClass(i),Ae(this.styleEl_,"\n      ."+i+" {\n        width: "+e+"px;\n        height: "+t+"px;\n      }\n\n      ."+i+".vjs-fluid {\n        padding-top: "+100*n+"%;\n      }\n    ")):(t="number"==typeof this.width_?this.width_:this.options_.width,i="number"==typeof this.height_?this.height_:this.options_.height,(n=this.tech_&&this.tech_.el())&&(0<=t&&(n.width=t),0<=i&&(n.height=i)))},e.loadTech_=function(e,t){var i=this;this.tech_&&this.unloadTech_();var n=ut(e),r=e.charAt(0).toLowerCase()+e.slice(1);"Html5"!==n&&this.tag&&(ji.getTech("Html5").disposeMediaElement(this.tag),this.tag.player=null,this.tag=null),this.techName_=n,this.isReady_=!1;var a=this.autoplay(),s={source:t,autoplay:a="string"==typeof this.autoplay()||!0===this.autoplay()&&this.options_.normalizeAutoplay?!1:a,nativeControlsForTouch:this.options_.nativeControlsForTouch,playerId:this.id(),techId:this.id()+"_"+r+"_api",playsinline:this.options_.playsinline,preload:this.options_.preload,loop:this.options_.loop,disablePictureInPicture:this.options_.disablePictureInPicture,muted:this.options_.muted,poster:this.poster(),language:this.language(),playerElIngest:this.playerElIngest_||!1,"vtt.js":this.options_["vtt.js"],canOverridePoster:!!this.options_.techCanOverridePoster,enableSourceset:this.options_.enableSourceset,Promise:this.options_.Promise};oi.names.forEach(function(e){e=oi[e];s[e.getterName]=i[e.privateName]}),b(s,this.options_[n]),b(s,this.options_[r]),b(s,this.options_[e.toLowerCase()]),this.tag&&(s.tag=this.tag),t&&t.src===this.cache_.src&&0<this.cache_.currentTime&&(s.startTime=this.cache_.currentTime);e=ji.getTech(e);if(!e)throw new Error("No Tech named '"+n+"' exists! '"+n+"' should be registered using videojs.registerTech()'");this.tech_=new e(s),this.tech_.ready(Ve(this,this.handleTechReady_),!0),It(this.textTracksJson_||[],this.tech_),Rn.forEach(function(t){i.on(i.tech_,t,function(e){return i["handleTech"+ut(t)+"_"](e)})}),Object.keys(Mn).forEach(function(t){i.on(i.tech_,t,function(e){0===i.tech_.playbackRate()&&i.tech_.seeking()?i.queuedCallbacks_.push({callback:i["handleTech"+Mn[t]+"_"].bind(i),event:e}):i["handleTech"+Mn[t]+"_"](e)})}),this.on(this.tech_,"loadstart",function(e){return i.handleTechLoadStart_(e)}),this.on(this.tech_,"sourceset",function(e){return i.handleTechSourceset_(e)}),this.on(this.tech_,"waiting",function(e){return i.handleTechWaiting_(e)}),this.on(this.tech_,"ended",function(e){return i.handleTechEnded_(e)}),this.on(this.tech_,"seeking",function(e){return i.handleTechSeeking_(e)}),this.on(this.tech_,"play",function(e){return i.handleTechPlay_(e)}),this.on(this.tech_,"firstplay",function(e){return i.handleTechFirstPlay_(e)}),this.on(this.tech_,"pause",function(e){return i.handleTechPause_(e)}),this.on(this.tech_,"durationchange",function(e){return i.handleTechDurationChange_(e)}),this.on(this.tech_,"fullscreenchange",function(e,t){return i.handleTechFullscreenChange_(e,t)}),this.on(this.tech_,"fullscreenerror",function(e,t){return i.handleTechFullscreenError_(e,t)}),this.on(this.tech_,"enterpictureinpicture",function(e){return i.handleTechEnterPictureInPicture_(e)}),this.on(this.tech_,"leavepictureinpicture",function(e){return i.handleTechLeavePictureInPicture_(e)}),this.on(this.tech_,"error",function(e){return i.handleTechError_(e)}),this.on(this.tech_,"posterchange",function(e){return i.handleTechPosterChange_(e)}),this.on(this.tech_,"textdata",function(e){return i.handleTechTextData_(e)}),this.on(this.tech_,"ratechange",function(e){return i.handleTechRateChange_(e)}),this.on(this.tech_,"loadedmetadata",this.boundUpdateStyleEl_),this.usingNativeControls(this.techGet_("controls")),this.controls()&&!this.usingNativeControls()&&this.addTechControlsListeners_(),this.tech_.el().parentNode===this.el()||"Html5"===n&&this.tag||Z(this.tech_.el(),this.el()),this.tag&&(this.tag.player=null,this.tag=null)},e.unloadTech_=function(){var t=this;oi.names.forEach(function(e){e=oi[e];t[e.privateName]=t[e.getterName]()}),this.textTracksJson_=Ct(this.tech_),this.isReady_=!1,this.tech_.dispose(),this.tech_=!1,this.isPosterFromTech_&&(this.poster_="",this.trigger("posterchange")),this.isPosterFromTech_=!1},e.tech=function(e){return void 0===e&&h.warn("Using the tech directly can be dangerous. I hope you know what you're doing.\nSee https://github.com/videojs/video.js/issues/2617 for more info.\n"),this.tech_},e.addTechControlsListeners_=function(){this.removeTechControlsListeners_(),this.on(this.tech_,"click",this.boundHandleTechClick_),this.on(this.tech_,"dblclick",this.boundHandleTechDoubleClick_),this.on(this.tech_,"touchstart",this.boundHandleTechTouchStart_),this.on(this.tech_,"touchmove",this.boundHandleTechTouchMove_),this.on(this.tech_,"touchend",this.boundHandleTechTouchEnd_),this.on(this.tech_,"tap",this.boundHandleTechTap_)},e.removeTechControlsListeners_=function(){this.off(this.tech_,"tap",this.boundHandleTechTap_),this.off(this.tech_,"touchstart",this.boundHandleTechTouchStart_),this.off(this.tech_,"touchmove",this.boundHandleTechTouchMove_),this.off(this.tech_,"touchend",this.boundHandleTechTouchEnd_),this.off(this.tech_,"click",this.boundHandleTechClick_),this.off(this.tech_,"dblclick",this.boundHandleTechDoubleClick_)},e.handleTechReady_=function(){this.triggerReady(),this.cache_.volume&&this.techCall_("setVolume",this.cache_.volume),this.handleTechPosterChange_(),this.handleTechDurationChange_()},e.handleTechLoadStart_=function(){this.removeClass("vjs-ended"),this.removeClass("vjs-seeking"),this.error(null),this.handleTechDurationChange_(),this.paused()?(this.hasStarted(!1),this.trigger("loadstart")):(this.trigger("loadstart"),this.trigger("firstplay")),this.manualAutoplay_(!0===this.autoplay()&&this.options_.normalizeAutoplay?"play":this.autoplay())},e.manualAutoplay_=function(e){var n=this;if(this.tech_&&"string"==typeof e){var t,i=function(){var e=n.muted();n.muted(!0);function t(){n.muted(e)}n.playTerminatedQueue_.push(t);var i=n.play();if(wt(i))return i.catch(function(e){throw t(),new Error("Rejection at manualAutoplay. Restoring muted value. "+(e||""))})};if("any"!==e||this.muted()?t="muted"!==e||this.muted()?this.play():i():wt(t=this.play())&&(t=t.catch(i)),wt(t))return t.then(function(){n.trigger({type:"autoplay-success",autoplay:e})}).catch(function(){n.trigger({type:"autoplay-failure",autoplay:e})})}},e.updateSourceCaches_=function(e){var t=e=void 0===e?"":e,i="";"string"!=typeof t&&(t=e.src,i=e.type),this.cache_.source=this.cache_.source||{},this.cache_.sources=this.cache_.sources||[],t&&!i&&(i=function(e,t){if(!t)return"";if(e.cache_.source.src===t&&e.cache_.source.type)return e.cache_.source.type;var i=e.cache_.sources.filter(function(e){return e.src===t});if(i.length)return i[0].type;for(var n=e.$$("source"),r=0;r<n.length;r++){var a=n[r];if(a.type&&a.src&&a.src===t)return a.type}return $i(t)}(this,t)),this.cache_.source=lt({},e,{src:t,type:i});for(var i=this.cache_.sources.filter(function(e){return e.src&&e.src===t}),n=[],r=this.$$("source"),a=[],s=0;s<r.length;s++){var o=ae(r[s]);n.push(o),o.src&&o.src===t&&a.push(o.src)}a.length&&!i.length?this.cache_.sources=n:i.length||(this.cache_.sources=[this.cache_.source]),this.cache_.src=t},e.handleTechSourceset_=function(e){var t,i,n,r=this;this.changingSrc_||(t=function(e){return r.updateSourceCaches_(e)},i=this.currentSource().src,n=e.src,i&&!/^blob:/.test(i)&&/^blob:/.test(n)&&(this.lastSource_&&(this.lastSource_.tech===n||this.lastSource_.player===i)||(t=function(){})),t(n),e.src||this.tech_.any(["sourceset","loadstart"],function(e){"sourceset"!==e.type&&(e=r.techGet("currentSrc"),r.lastSource_.tech=e,r.updateSourceCaches_(e))})),this.lastSource_={player:this.currentSource().src,tech:e.src},this.trigger({src:e.src,type:"sourceset"})},e.hasStarted=function(e){if(void 0===e)return this.hasStarted_;e!==this.hasStarted_&&(this.hasStarted_=e,this.hasStarted_?(this.addClass("vjs-has-started"),this.trigger("firstplay")):this.removeClass("vjs-has-started"))},e.handleTechPlay_=function(){this.removeClass("vjs-ended"),this.removeClass("vjs-paused"),this.addClass("vjs-playing"),this.hasStarted(!0),this.trigger("play")},e.handleTechRateChange_=function(){0<this.tech_.playbackRate()&&0===this.cache_.lastPlaybackRate&&(this.queuedCallbacks_.forEach(function(e){return e.callback(e.event)}),this.queuedCallbacks_=[]),this.cache_.lastPlaybackRate=this.tech_.playbackRate(),this.trigger("ratechange")},e.handleTechWaiting_=function(){var t=this;this.addClass("vjs-waiting"),this.trigger("waiting");var i=this.currentTime();this.on("timeupdate",function e(){i!==t.currentTime()&&(t.removeClass("vjs-waiting"),t.off("timeupdate",e))})},e.handleTechCanPlay_=function(){this.removeClass("vjs-waiting"),this.trigger("canplay")},e.handleTechCanPlayThrough_=function(){this.removeClass("vjs-waiting"),this.trigger("canplaythrough")},e.handleTechPlaying_=function(){this.removeClass("vjs-waiting"),this.trigger("playing")},e.handleTechSeeking_=function(){this.addClass("vjs-seeking"),this.trigger("seeking")},e.handleTechSeeked_=function(){this.removeClass("vjs-seeking"),this.removeClass("vjs-ended"),this.trigger("seeked")},e.handleTechFirstPlay_=function(){this.options_.starttime&&(h.warn("Passing the `starttime` option to the player will be deprecated in 6.0"),this.currentTime(this.options_.starttime)),this.addClass("vjs-has-started"),this.trigger("firstplay")},e.handleTechPause_=function(){this.removeClass("vjs-playing"),this.addClass("vjs-paused"),this.trigger("pause")},e.handleTechEnded_=function(){this.addClass("vjs-ended"),this.removeClass("vjs-waiting"),this.options_.loop?(this.currentTime(0),this.play()):this.paused()||this.pause(),this.trigger("ended")},e.handleTechDurationChange_=function(){this.duration(this.techGet_("duration"))},e.handleTechClick_=function(e){this.controls_&&(void 0!==this.options_&&void 0!==this.options_.userActions&&void 0!==this.options_.userActions.click&&!1===this.options_.userActions.click||(void 0!==this.options_&&void 0!==this.options_.userActions&&"function"==typeof this.options_.userActions.click?this.options_.userActions.click.call(this,e):this.paused()?Et(this.play()):this.pause()))},e.handleTechDoubleClick_=function(t){this.controls_&&(Array.prototype.some.call(this.$$(".vjs-control-bar, .vjs-modal-dialog"),function(e){return e.contains(t.target)})||void 0!==this.options_&&void 0!==this.options_.userActions&&void 0!==this.options_.userActions.doubleClick&&!1===this.options_.userActions.doubleClick||(void 0!==this.options_&&void 0!==this.options_.userActions&&"function"==typeof this.options_.userActions.doubleClick?this.options_.userActions.doubleClick.call(this,t):this.isFullscreen()?this.exitFullscreen():this.requestFullscreen()))},e.handleTechTap_=function(){this.userActive(!this.userActive())},e.handleTechTouchStart_=function(){this.userWasActive=this.userActive()},e.handleTechTouchMove_=function(){this.userWasActive&&this.reportUserActivity()},e.handleTechTouchEnd_=function(e){e.cancelable&&e.preventDefault()},e.handleStageClick_=function(){this.reportUserActivity()},e.toggleFullscreenClass_=function(){this.isFullscreen()?this.addClass("vjs-fullscreen"):this.removeClass("vjs-fullscreen")},e.documentFullscreenChange_=function(e){var t=e.target.player;t&&t!==this||(e=this.el(),!(t=document[this.fsApi_.fullscreenElement]===e)&&e.matches?t=e.matches(":"+this.fsApi_.fullscreen):!t&&e.msMatchesSelector&&(t=e.msMatchesSelector(":"+this.fsApi_.fullscreen)),this.isFullscreen(t))},e.handleTechFullscreenChange_=function(e,t){var i=this;t&&(t.nativeIOSFullscreen&&(this.addClass("vjs-ios-native-fs"),this.tech_.one("webkitendfullscreen",function(){i.removeClass("vjs-ios-native-fs")})),this.isFullscreen(t.isFullscreen))},e.handleTechFullscreenError_=function(e,t){this.trigger("fullscreenerror",t)},e.togglePictureInPictureClass_=function(){this.isInPictureInPicture()?this.addClass("vjs-picture-in-picture"):this.removeClass("vjs-picture-in-picture")},e.handleTechEnterPictureInPicture_=function(e){this.isInPictureInPicture(!0)},e.handleTechLeavePictureInPicture_=function(e){this.isInPictureInPicture(!1)},e.handleTechError_=function(){var e=this.tech_.error();this.error(e)},e.handleTechTextData_=function(){this.trigger("textdata",1<arguments.length?arguments[1]:null)},e.getCache=function(){return this.cache_},e.resetCache_=function(){this.cache_={currentTime:0,initTime:0,inactivityTimeout:this.options_.inactivityTimeout,duration:NaN,lastVolume:1,lastPlaybackRate:this.defaultPlaybackRate(),media:null,src:"",source:{},sources:[],playbackRates:[],volume:1}},e.techCall_=function(n,r){this.ready(function(){if(n in Xi)return e=this.middleware_,t=this.tech_,i=r,t[t=n](e.reduce(Yi(t),i));if(n in Ki)return Gi(this.middleware_,this.tech_,n,r);var e,t,i;try{this.tech_&&this.tech_[n](r)}catch(e){throw h(e),e}},!0)},e.techGet_=function(t){if(this.tech_&&this.tech_.isReady_){if(t in zi)return e=this.middleware_,i=this.tech_,n=t,e.reduceRight(Yi(n),i[n]());if(t in Ki)return Gi(this.middleware_,this.tech_,t);var e,i,n;try{return this.tech_[t]()}catch(e){if(void 0===this.tech_[t])throw h("Video.js: "+t+" method not defined for "+this.techName_+" playback technology.",e),e;if("TypeError"===e.name)throw h("Video.js: "+t+" unavailable on "+this.techName_+" playback technology element.",e),this.tech_.isReady_=!1,e;throw h(e),e}}},e.play=function(){var t=this,e=this.options_.Promise||window.Promise;return e?new e(function(e){t.play_(e)}):this.play_()},e.play_=function(e){var t=this;this.playCallbacks_.push(e=void 0===e?Et:e);e=Boolean(!this.changingSrc_&&(this.src()||this.currentSrc()));if(this.waitToPlay_&&(this.off(["ready","loadstart"],this.waitToPlay_),this.waitToPlay_=null),!this.isReady_||!e)return this.waitToPlay_=function(e){t.play_()},this.one(["ready","loadstart"],this.waitToPlay_),void(e||!V&&!q||this.load());e=this.techGet_("play");null===e?this.runPlayTerminatedQueue_():this.runPlayCallbacks_(e)},e.runPlayTerminatedQueue_=function(){var e=this.playTerminatedQueue_.slice(0);this.playTerminatedQueue_=[],e.forEach(function(e){e()})},e.runPlayCallbacks_=function(t){var e=this.playCallbacks_.slice(0);this.playCallbacks_=[],this.playTerminatedQueue_=[],e.forEach(function(e){e(t)})},e.pause=function(){this.techCall_("pause")},e.paused=function(){return!1!==this.techGet_("paused")},e.played=function(){return this.techGet_("played")||vt(0,0)},e.scrubbing=function(e){if("undefined"==typeof e)return this.scrubbing_;this.scrubbing_=!!e,this.techCall_("setScrubbing",this.scrubbing_),e?this.addClass("vjs-scrubbing"):this.removeClass("vjs-scrubbing")},e.currentTime=function(e){return"undefined"!=typeof e?(e<0&&(e=0),this.isReady_&&!this.changingSrc_&&this.tech_&&this.tech_.isReady_?(this.techCall_("setCurrentTime",e),void(this.cache_.initTime=0)):(this.cache_.initTime=e,this.off("canplay",this.boundApplyInitTime_),void this.one("canplay",this.boundApplyInitTime_))):(this.cache_.currentTime=this.techGet_("currentTime")||0,this.cache_.currentTime)},e.applyInitTime_=function(){this.currentTime(this.cache_.initTime)},e.duration=function(e){if(void 0===e)return void 0!==this.cache_.duration?this.cache_.duration:NaN;(e=(e=parseFloat(e))<0?1/0:e)!==this.cache_.duration&&((this.cache_.duration=e)===1/0?this.addClass("vjs-live"):this.removeClass("vjs-live"),isNaN(e)||this.trigger("durationchange"))},e.remainingTime=function(){return this.duration()-this.currentTime()},e.remainingTimeDisplay=function(){return Math.floor(this.duration())-Math.floor(this.currentTime())},e.buffered=function(){var e;return e=!(e=this.techGet_("buffered"))||!e.length?vt(0,0):e},e.bufferedPercent=function(){return _t(this.buffered(),this.duration())},e.bufferedEnd=function(){var e=this.buffered(),t=this.duration(),e=e.end(e.length-1);return e=t<e?t:e},e.volume=function(e){var t;return void 0!==e?(t=Math.max(0,Math.min(1,parseFloat(e))),this.cache_.volume=t,this.techCall_("setVolume",t),void(0<t&&this.lastVolume_(t))):(t=parseFloat(this.techGet_("volume")),isNaN(t)?1:t)},e.muted=function(e){if(void 0===e)return this.techGet_("muted")||!1;this.techCall_("setMuted",e)},e.defaultMuted=function(e){return void 0!==e?this.techCall_("setDefaultMuted",e):this.techGet_("defaultMuted")||!1},e.lastVolume_=function(e){if(void 0===e||0===e)return this.cache_.lastVolume;this.cache_.lastVolume=e},e.supportsFullScreen=function(){return this.techGet_("supportsFullScreen")||!1},e.isFullscreen=function(e){if(void 0===e)return this.isFullscreen_;var t=this.isFullscreen_;this.isFullscreen_=Boolean(e),this.isFullscreen_!==t&&this.fsApi_.prefixed&&this.trigger("fullscreenchange"),this.toggleFullscreenClass_()},e.requestFullscreen=function(s){var e=this.options_.Promise||window.Promise;if(e){var o=this;return new e(function(e,i){function n(){o.off("fullscreenerror",r),o.off("fullscreenchange",t)}function t(){n(),e()}function r(e,t){n(),i(t)}o.one("fullscreenchange",t),o.one("fullscreenerror",r);var a=o.requestFullscreenHelper_(s);a&&(a.then(n,n),a.then(e,i))})}return this.requestFullscreenHelper_()},e.requestFullscreenHelper_=function(e){var t=this;if(this.fsApi_.prefixed||(i=this.options_.fullscreen&&this.options_.fullscreen.options||{},void 0!==e&&(i=e)),this.fsApi_.requestFullscreen){var i=this.el_[this.fsApi_.requestFullscreen](i);return i&&i.then(function(){return t.isFullscreen(!0)},function(){return t.isFullscreen(!1)}),i}this.tech_.supportsFullScreen()&&!0==!this.options_.preferFullWindow?this.techCall_("enterFullScreen"):this.enterFullWindow()},e.exitFullscreen=function(){var e=this.options_.Promise||window.Promise;if(e){var s=this;return new e(function(e,i){function n(){s.off("fullscreenerror",r),s.off("fullscreenchange",t)}function t(){n(),e()}function r(e,t){n(),i(t)}s.one("fullscreenchange",t),s.one("fullscreenerror",r);var a=s.exitFullscreenHelper_();a&&(a.then(n,n),a.then(e,i))})}return this.exitFullscreenHelper_()},e.exitFullscreenHelper_=function(){var e=this;if(this.fsApi_.requestFullscreen){var t=document[this.fsApi_.exitFullscreen]();return t&&Et(t.then(function(){return e.isFullscreen(!1)})),t}this.tech_.supportsFullScreen()&&!0==!this.options_.preferFullWindow?this.techCall_("exitFullScreen"):this.exitFullWindow()},e.enterFullWindow=function(){this.isFullscreen(!0),this.isFullWindow=!0,this.docOrigOverflow=document.documentElement.style.overflow,Be(document,"keydown",this.boundFullWindowOnEscKey_),document.documentElement.style.overflow="hidden",te(document.body,"vjs-full-window"),this.trigger("enterFullWindow")},e.fullWindowOnEscKey=function(e){ht.isEventKey(e,"Esc")&&!0===this.isFullscreen()&&(this.isFullWindow?this.exitFullWindow():this.exitFullscreen())},e.exitFullWindow=function(){this.isFullscreen(!1),this.isFullWindow=!1,Fe(document,"keydown",this.boundFullWindowOnEscKey_),document.documentElement.style.overflow=this.docOrigOverflow,ie(document.body,"vjs-full-window"),this.trigger("exitFullWindow")},e.disablePictureInPicture=function(e){if(void 0===e)return this.techGet_("disablePictureInPicture");this.techCall_("setDisablePictureInPicture",e),this.options_.disablePictureInPicture=e,this.trigger("disablepictureinpicturechanged")},e.isInPictureInPicture=function(e){return void 0!==e?(this.isInPictureInPicture_=!!e,void this.togglePictureInPictureClass_()):!!this.isInPictureInPicture_},e.requestPictureInPicture=function(){if("pictureInPictureEnabled"in document&&!1===this.disablePictureInPicture())return this.techGet_("requestPictureInPicture")},e.exitPictureInPicture=function(){if("pictureInPictureEnabled"in document)return document.exitPictureInPicture()},e.handleKeyDown=function(e){var t=this.options_.userActions;t&&t.hotkeys&&(function(e){var t=e.tagName.toLowerCase();if(e.isContentEditable)return!0;if("input"===t)return-1===["button","checkbox","hidden","radio","reset","submit"].indexOf(e.type);return-1!==["textarea"].indexOf(t)}(this.el_.ownerDocument.activeElement)||("function"==typeof t.hotkeys?t.hotkeys.call(this,e):this.handleHotkeys(e)))},e.handleHotkeys=function(e){var t=this.options_.userActions?this.options_.userActions.hotkeys:{},i=t.fullscreenKey,n=t.muteKey,n=void 0===n?function(e){return ht.isEventKey(e,"m")}:n,t=t.playPauseKey,t=void 0===t?function(e){return ht.isEventKey(e,"k")||ht.isEventKey(e,"Space")}:t;(void 0===i?function(e){return ht.isEventKey(e,"f")}:i).call(this,e)?(e.preventDefault(),e.stopPropagation(),i=pt.getComponent("FullscreenToggle"),!1!==document[this.fsApi_.fullscreenEnabled]&&i.prototype.handleClick.call(this,e)):n.call(this,e)?(e.preventDefault(),e.stopPropagation(),pt.getComponent("MuteToggle").prototype.handleClick.call(this,e)):t.call(this,e)&&(e.preventDefault(),e.stopPropagation(),pt.getComponent("PlayToggle").prototype.handleClick.call(this,e))},e.canPlayType=function(e){for(var t,i=0,n=this.options_.techOrder;i<n.length;i++){var r=n[i],a=ji.getTech(r);if(a=a||pt.getComponent(r)){if(a.isSupported()&&(t=a.canPlayType(e)))return t}else h.error('The "'+r+'" tech is undefined. Skipped browser support check for that tech.')}return""},e.selectSource=function(e){function t(e,i,n){var r;return e.some(function(t){return i.some(function(e){if(r=n(t,e))return!0})}),r}var i,n=this,r=this.options_.techOrder.map(function(e){return[e,ji.getTech(e)]}).filter(function(e){var t=e[0],e=e[1];return e?e.isSupported():(h.error('The "'+t+'" tech is undefined. Skipped browser support check for that tech.'),!1)}),a=function(e,t){var i=e[0];if(e[1].canPlaySource(t,n.options_[i.toLowerCase()]))return{source:t,tech:i}},a=this.options_.sourceOrder?t(e,r,(i=a,function(e,t){return i(t,e)})):t(r,e,a);return a||!1},e.handleSrc_=function(e,n){var r=this;if("undefined"==typeof e)return this.cache_.src||"";this.resetRetryOnError_&&this.resetRetryOnError_();var t,i,a=Ji(e);a.length?(this.changingSrc_=!0,n||(this.cache_.sources=a),this.updateSourceCaches_(a[0]),Wi(this,a[0],function(e,t){var i;return r.middleware_=t,n||(r.cache_.sources=a),r.updateSourceCaches_(e),r.src_(e)?1<a.length?r.handleSrc_(a.slice(1)):(r.changingSrc_=!1,r.setTimeout(function(){this.error({code:4,message:this.localize(this.options_.notSupportedMessage)})},0),void r.triggerReady()):(t=t,i=r.tech_,void t.forEach(function(e){return e.setTech&&e.setTech(i)}))}),this.options_.retryOnError&&1<a.length&&(i=function(){r.off("error",t)},this.one("error",t=function(){r.error(null),r.handleSrc_(a.slice(1),!0)}),this.one("playing",i),this.resetRetryOnError_=function(){r.off("error",t),r.off("playing",i)})):this.setTimeout(function(){this.error({code:4,message:this.localize(this.options_.notSupportedMessage)})},0)},e.src=function(e){return this.handleSrc_(e,!1)},e.src_=function(e){var t,i,n=this,r=this.selectSource([e]);return!r||(t=r.tech,i=this.techName_,ut(t)!==ut(i)?(this.changingSrc_=!0,this.loadTech_(r.tech,r.source),this.tech_.ready(function(){n.changingSrc_=!1})):this.ready(function(){this.tech_.constructor.prototype.hasOwnProperty("setSource")?this.techCall_("setSource",e):this.techCall_("src",e.src),this.changingSrc_=!1},!0),!1)},e.load=function(){this.techCall_("load")},e.reset=function(){var e=this,t=this.options_.Promise||window.Promise;this.paused()||!t?this.doReset_():Et(this.play().then(function(){return e.doReset_()}))},e.doReset_=function(){this.tech_&&this.tech_.clearTracks("text"),this.resetCache_(),this.poster(""),this.loadTech_(this.options_.techOrder[0],null),this.techCall_("reset"),this.resetControlBarUI_(),it(this)&&this.trigger("playerreset")},e.resetControlBarUI_=function(){this.resetProgressBar_(),this.resetPlaybackRate_(),this.resetVolumeBar_()},e.resetProgressBar_=function(){this.currentTime(0);var e=this.controlBar,t=e.durationDisplay,e=e.remainingTimeDisplay;t&&t.updateContent(),e&&e.updateContent()},e.resetPlaybackRate_=function(){this.playbackRate(this.defaultPlaybackRate()),this.handleTechRateChange_()},e.resetVolumeBar_=function(){this.volume(1),this.trigger("volumechange")},e.currentSources=function(){var e=this.currentSource(),t=[];return 0!==Object.keys(e).length&&t.push(e),this.cache_.sources||t},e.currentSource=function(){return this.cache_.source||{}},e.currentSrc=function(){return this.currentSource()&&this.currentSource().src||""},e.currentType=function(){return this.currentSource()&&this.currentSource().type||""},e.preload=function(e){return void 0!==e?(this.techCall_("setPreload",e),void(this.options_.preload=e)):this.techGet_("preload")},e.autoplay=function(e){if(void 0===e)return this.options_.autoplay||!1;var t;"string"==typeof e&&/(any|play|muted)/.test(e)||!0===e&&this.options_.normalizeAutoplay?(this.options_.autoplay=e,this.manualAutoplay_("string"==typeof e?e:"play"),t=!1):this.options_.autoplay=!!e,t="undefined"==typeof t?this.options_.autoplay:t,this.tech_&&this.techCall_("setAutoplay",t)},e.playsinline=function(e){return void 0!==e?(this.techCall_("setPlaysinline",e),this.options_.playsinline=e,this):this.techGet_("playsinline")},e.loop=function(e){return void 0!==e?(this.techCall_("setLoop",e),void(this.options_.loop=e)):this.techGet_("loop")},e.poster=function(e){if(void 0===e)return this.poster_;(e=e||"")!==this.poster_&&(this.poster_=e,this.techCall_("setPoster",e),this.isPosterFromTech_=!1,this.trigger("posterchange"))},e.handleTechPosterChange_=function(){var e;this.poster_&&!this.options_.techCanOverridePoster||!this.tech_||!this.tech_.poster||(e=this.tech_.poster()||"")!==this.poster_&&(this.poster_=e,this.isPosterFromTech_=!0,this.trigger("posterchange"))},e.controls=function(e){if(void 0===e)return!!this.controls_;this.controls_!==(e=!!e)&&(this.controls_=e,this.usingNativeControls()&&this.techCall_("setControls",e),this.controls_?(this.removeClass("vjs-controls-disabled"),this.addClass("vjs-controls-enabled"),this.trigger("controlsenabled"),this.usingNativeControls()||this.addTechControlsListeners_()):(this.removeClass("vjs-controls-enabled"),this.addClass("vjs-controls-disabled"),this.trigger("controlsdisabled"),this.usingNativeControls()||this.removeTechControlsListeners_()))},e.usingNativeControls=function(e){if(void 0===e)return!!this.usingNativeControls_;this.usingNativeControls_!==(e=!!e)&&(this.usingNativeControls_=e,this.usingNativeControls_?(this.addClass("vjs-using-native-controls"),this.trigger("usingnativecontrols")):(this.removeClass("vjs-using-native-controls"),this.trigger("usingcustomcontrols")))},e.error=function(t){var i=this;if(void 0===t)return this.error_||null;if(a("beforeerror").forEach(function(e){e=e(i,t);T(e)&&!Array.isArray(e)||"string"==typeof e||"number"==typeof e||null===e?t=e:i.log.error("please return a value that MediaError expects in beforeerror hooks")}),this.options_.suppressNotSupportedError&&t&&4===t.code){var e=function(){this.error(t)};return this.options_.suppressNotSupportedError=!1,this.any(["click","touchstart"],e),void this.one("loadstart",function(){this.off(["click","touchstart"],e)})}if(null===t)return this.error_=t,this.removeClass("vjs-error"),void(this.errorDisplay&&this.errorDisplay.close());this.error_=new bt(t),this.addClass("vjs-error"),h.error("(CODE:"+this.error_.code+" "+bt.errorTypes[this.error_.code]+")",this.error_.message,this.error_),this.trigger("error"),a("error").forEach(function(e){return e(i,i.error_)})},e.reportUserActivity=function(e){this.userActivity_=!0},e.userActive=function(e){if(void 0===e)return this.userActive_;if((e=!!e)!==this.userActive_){if(this.userActive_=e,this.userActive_)return this.userActivity_=!0,this.removeClass("vjs-user-inactive"),this.addClass("vjs-user-active"),void this.trigger("useractive");this.tech_&&this.tech_.one("mousemove",function(e){e.stopPropagation(),e.preventDefault()}),this.userActivity_=!1,this.removeClass("vjs-user-active"),this.addClass("vjs-user-inactive"),this.trigger("userinactive")}},e.listenForUserActivity_=function(){var t,i,n,r=Ve(this,this.reportUserActivity),e=function(e){r(),this.clearInterval(t)};this.on("mousedown",function(){r(),this.clearInterval(t),t=this.setInterval(r,250)}),this.on("mousemove",function(e){e.screenX===i&&e.screenY===n||(i=e.screenX,n=e.screenY,r())}),this.on("mouseup",e),this.on("mouseleave",e);var a,e=this.getChild("controlBar");!e||q||A||(e.on("mouseenter",function(e){0!==this.player().options_.inactivityTimeout&&(this.player().cache_.inactivityTimeout=this.player().options_.inactivityTimeout),this.player().options_.inactivityTimeout=0}),e.on("mouseleave",function(e){this.player().options_.inactivityTimeout=this.player().cache_.inactivityTimeout})),this.on("keydown",r),this.on("keyup",r),this.setInterval(function(){var e;this.userActivity_&&(this.userActivity_=!1,this.userActive(!0),this.clearTimeout(a),(e=this.options_.inactivityTimeout)<=0||(a=this.setTimeout(function(){this.userActivity_||this.userActive(!1)},e)))},250)},e.playbackRate=function(e){if(void 0===e)return this.tech_&&this.tech_.featuresPlaybackRate?this.cache_.lastPlaybackRate||this.techGet_("playbackRate"):1;this.techCall_("setPlaybackRate",e)},e.defaultPlaybackRate=function(e){return void 0!==e?this.techCall_("setDefaultPlaybackRate",e):this.tech_&&this.tech_.featuresPlaybackRate?this.techGet_("defaultPlaybackRate"):1},e.isAudio=function(e){if(void 0===e)return!!this.isAudio_;this.isAudio_=!!e},e.addTextTrack=function(e,t,i){if(this.tech_)return this.tech_.addTextTrack(e,t,i)},e.addRemoteTextTrack=function(e,t){if(this.tech_)return this.tech_.addRemoteTextTrack(e,t)},e.removeRemoteTextTrack=function(e){var t=(t=(e=void 0===e?{}:e).track)||e;if(this.tech_)return this.tech_.removeRemoteTextTrack(t)},e.getVideoPlaybackQuality=function(){return this.techGet_("getVideoPlaybackQuality")},e.videoWidth=function(){return this.tech_&&this.tech_.videoWidth&&this.tech_.videoWidth()||0},e.videoHeight=function(){return this.tech_&&this.tech_.videoHeight&&this.tech_.videoHeight()||0},e.language=function(e){if(void 0===e)return this.language_;this.language_!==String(e).toLowerCase()&&(this.language_=String(e).toLowerCase(),it(this)&&this.trigger("languagechange"))},e.languages=function(){return lt(o.prototype.options_.languages,this.languages_)},e.toJSON=function(){var e=lt(this.options_),t=e.tracks;e.tracks=[];for(var i=0;i<t.length;i++){var n=t[i];(n=lt(n)).player=void 0,e.tracks[i]=n}return e},e.createModal=function(e,t){var i=this;(t=t||{}).content=e||"";var n=new At(this,t);return this.addChild(n),n.on("dispose",function(){i.removeChild(n)}),n.open(),n},e.updateCurrentBreakpoint_=function(){if(this.responsive())for(var e=this.currentBreakpoint(),t=this.currentWidth(),i=0;i<Nn.length;i++){var n=Nn[i];if(t<=this.breakpoints_[n]){if(e===n)return;e&&this.removeClass(Un[e]),this.addClass(Un[n]),this.breakpoint_=n;break}}},e.removeCurrentBreakpoint_=function(){var e=this.currentBreakpointClass();this.breakpoint_="",e&&this.removeClass(e)},e.breakpoints=function(e){return void 0===e||(this.breakpoint_="",this.breakpoints_=b({},Bn,e),this.updateCurrentBreakpoint_()),b(this.breakpoints_)},e.responsive=function(e){return void 0===e?this.responsive_:(e=Boolean(e))!==this.responsive_?((this.responsive_=e)?(this.on("playerresize",this.boundUpdateCurrentBreakpoint_),this.updateCurrentBreakpoint_()):(this.off("playerresize",this.boundUpdateCurrentBreakpoint_),this.removeCurrentBreakpoint_()),e):void 0},e.currentBreakpoint=function(){return this.breakpoint_},e.currentBreakpointClass=function(){return Un[this.breakpoint_]||""},e.loadMedia=function(e,t){var i,n,r,a=this;e&&"object"==typeof e&&(this.reset(),this.cache_.media=lt(e),i=(r=this.cache_.media).artwork,n=r.poster,e=r.src,r=r.textTracks,!i&&n&&(this.cache_.media.artwork=[{src:n,type:$i(n)}]),e&&this.src(e),n&&this.poster(n),Array.isArray(r)&&r.forEach(function(e){return a.addRemoteTextTrack(e,!1)}),this.ready(t))},e.getMedia=function(){if(this.cache_.media)return lt(this.cache_.media);var e=this.poster(),t={src:this.currentSources(),textTracks:Array.prototype.map.call(this.remoteTextTracks(),function(e){return{kind:e.kind,label:e.label,language:e.language,src:e.src}})};return e&&(t.poster=e,t.artwork=[{src:t.poster,type:$i(t.poster)}]),t},o.getTagSettings=function(e){var t,i={sources:[],tracks:[]},n=ae(e),r=n["data-setup"];if(ee(e,"vjs-fill")&&(n.fill=!0),ee(e,"vjs-fluid")&&(n.fluid=!0),null!==r&&(r=(t=St(r||"{}"))[0],t=t[1],r&&h.error(r),b(n,t)),b(i,n),e.hasChildNodes())for(var a=e.childNodes,s=0,o=a.length;s<o;s++){var u=a[s],l=u.nodeName.toLowerCase();"source"===l?i.sources.push(ae(u)):"track"===l&&i.tracks.push(ae(u))}return i},e.flexNotSupported_=function(){var e=document.createElement("i");return!("flexBasis"in e.style||"webkitFlexBasis"in e.style||"mozFlexBasis"in e.style||"msFlexBasis"in e.style||"msFlexOrder"in e.style)},e.debug=function(e){if(void 0===e)return this.debugEnabled_;e?(this.trigger("debugon"),this.previousLogLevel_=this.log.level,this.log.level("debug"),this.debugEnabled_=!0):(this.trigger("debugoff"),this.log.level(this.previousLogLevel_),this.previousLogLevel_=void 0,this.debugEnabled_=!1)},e.playbackRates=function(e){if(void 0===e)return this.cache_.playbackRates;Array.isArray(e)&&e.every(function(e){return"number"==typeof e})&&(this.cache_.playbackRates=e,this.trigger("playbackrateschange"))},o}(pt);oi.names.forEach(function(e){var t=oi[e];Fn.prototype[t.getterName]=function(){return this.tech_?this.tech_[t.getterName]():(this[t.privateName]=this[t.privateName]||new t.ListClass,this[t.privateName])}}),Fn.prototype.crossorigin=Fn.prototype.crossOrigin,Fn.players={};k=window.navigator;Fn.prototype.options_={techOrder:ji.defaultTechOrder_,html5:{},inactivityTimeout:2e3,playbackRates:[],liveui:!1,children:["mediaLoader","posterImage","textTrackDisplay","loadingSpinner","bigPlayButton","liveTracker","controlBar","errorDisplay","textTrackSettings","resizeManager"],language:k&&(k.languages&&k.languages[0]||k.userLanguage||k.language)||"en",languages:{},notSupportedMessage:"No compatible source was found for this media.",normalizeAutoplay:!1,fullscreen:{options:{navigationUI:"hide"}},breakpoints:{},responsive:!1},["ended","seeking","seekable","networkState","readyState"].forEach(function(e){Fn.prototype[e]=function(){return this.techGet_(e)}}),Rn.forEach(function(e){Fn.prototype["handleTech"+ut(e)+"_"]=function(){return this.trigger(e)}}),pt.registerComponent("Player",Fn);var jn=m(function(i){function n(e,t){return i.exports=n=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e},n(e,t)}i.exports=n});function Hn(e){return Qn.hasOwnProperty(e)}function qn(e){return Hn(e)?Qn[e]:void 0}function Vn(e,t,i){i=(i?"before":"")+"pluginsetup",e.trigger(i,t),e.trigger(i+":"+t.name,t)}function Wn(t,i){function n(){Vn(this,{name:t,plugin:i,instance:null},!0);var e=i.apply(this,arguments);return $n(this,t),Vn(this,{name:t,plugin:i,instance:e}),e}return Object.keys(i).forEach(function(e){n[e]=i[e]}),n}function Gn(r,a){return a.prototype.name=r,function(){Vn(this,{name:r,plugin:a,instance:null},!0);for(var e=arguments.length,t=new Array(e),i=0;i<e;i++)t[i]=arguments[i];var n=Xn(a,[this].concat(t));return this[r]=function(){return n},Vn(this,n.getEventHash()),n}}var zn=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],function(){})),!0}catch(e){return!1}},Xn=m(function(n){function r(e,t,i){return zn()?n.exports=r=Reflect.construct:n.exports=r=function(e,t,i){var n=[null];n.push.apply(n,t);n=new(Function.bind.apply(e,n));return i&&jn(n,i.prototype),n},r.apply(null,arguments)}n.exports=r}),Kn="plugin",Yn="activePlugins_",Qn={},$n=function(e,t){e[Yn]=e[Yn]||{},e[Yn][t]=!0},Jn=function(){function i(e){if(this.constructor===i)throw new Error("Plugin must be sub-classed; not directly instantiated.");this.player=e,this.log||(this.log=this.player.log.createLogger(this.name)),rt(this),delete this.trigger,st(this,this.constructor.defaultState),$n(e,this.name),this.dispose=this.dispose.bind(this),e.on("dispose",this.dispose)}var e=i.prototype;return e.version=function(){return this.constructor.VERSION},e.getEventHash=function(e){return(e=void 0===e?{}:e).name=this.name,e.plugin=this.constructor,e.instance=this,e},e.trigger=function(e,t){return je(this.eventBusEl_,e,this.getEventHash(t=void 0===t?{}:t))},e.handleStateChanged=function(e){},e.dispose=function(){var e=this.name,t=this.player;this.trigger("dispose"),this.off(),t.off("dispose",this.dispose),t[Yn][e]=!1,this.player=this.state=null,t[e]=Gn(e,Qn[e])},i.isBasic=function(e){e="string"==typeof e?qn(e):e;return"function"==typeof e&&!i.prototype.isPrototypeOf(e.prototype)},i.registerPlugin=function(e,t){if("string"!=typeof e)throw new Error('Illegal plugin name, "'+e+'", must be a string, was '+typeof e+".");if(Hn(e))h.warn('A plugin named "'+e+'" already exists. You may want to avoid re-registering plugins!');else if(Fn.prototype.hasOwnProperty(e))throw new Error('Illegal plugin name, "'+e+'", cannot share a name with an existing player method!');if("function"!=typeof t)throw new Error('Illegal plugin for "'+e+'", must be a function, was '+typeof t+".");return Qn[e]=t,e!==Kn&&(i.isBasic(t)?Fn.prototype[e]=Wn(e,t):Fn.prototype[e]=Gn(e,t)),t},i.deregisterPlugin=function(e){if(e===Kn)throw new Error("Cannot de-register base plugin.");Hn(e)&&(delete Qn[e],delete Fn.prototype[e])},i.getPlugins=function(e){var i;return(e=void 0===e?Object.keys(Qn):e).forEach(function(e){var t=qn(e);t&&((i=i||{})[e]=t)}),i},i.getPluginVersion=function(e){e=qn(e);return e&&e.VERSION||""},i}();Jn.getPlugin=qn,Jn.BASE_PLUGIN_NAME=Kn,Jn.registerPlugin(Kn,Jn),Fn.prototype.usingPlugin=function(e){return!!this[Yn]&&!0===this[Yn][e]},Fn.prototype.hasPlugin=function(e){return!!Hn(e)};var Zn=function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&jn(e,t)},er=function(e){return 0===e.indexOf("#")?e.slice(1):e};function tr(e,t,i){if(r=tr.getPlayer(e))return t&&h.warn('Player "'+e+'" is already initialised. Options will not be applied.'),i&&r.ready(i),r;var n="string"==typeof e?Te("#"+er(e)):e;if(!K(n))throw new TypeError("The element or ID supplied is not valid. (videojs)");n.ownerDocument.defaultView&&n.ownerDocument.body.contains(n)||h.warn("The element supplied is not included in the DOM"),t=t||{},a("beforesetup").forEach(function(e){e=e(n,lt(t));T(e)&&!Array.isArray(e)?t=lt(t,e):h.error("please return an object in beforesetup hooks")});var r=new(pt.getComponent("Player"))(n,t,i);return a("setup").forEach(function(e){return e(r)}),r}tr.hooks_=i,tr.hooks=a,tr.hook=function(e,t){a(e,t)},tr.hookOnce=function(i,e){a(i,[].concat(e).map(function(t){return function e(){return n(i,e),t.apply(void 0,arguments)}}))},tr.removeHook=n,!0!==window.VIDEOJS_NO_DYNAMIC_STYLE&&X()&&((ar=Te(".vjs-styles-defaults"))||(ar=xe("vjs-styles-defaults"),(k=Te("head"))&&k.insertBefore(ar,k.firstChild),Ae(ar,"\n      .video-js {\n        width: 300px;\n        height: 150px;\n      }\n\n      .vjs-fluid {\n        padding-top: 56.25%\n      }\n    "))),Ce(1,tr),tr.VERSION=u,tr.options=Fn.prototype.options_,tr.getPlayers=function(){return Fn.players},tr.getPlayer=function(e){var t=Fn.players;if("string"==typeof e){var i=er(e),n=t[i];if(n)return n;i=Te("#"+i)}else i=e;if(K(i)){e=i.player,i=i.playerId;if(e||t[i])return e||t[i]}},tr.getAllPlayers=function(){return Object.keys(Fn.players).map(function(e){return Fn.players[e]}).filter(Boolean)},tr.players=Fn.players,tr.getComponent=pt.getComponent,tr.registerComponent=function(e,t){ji.isTech(t)&&h.warn("The "+e+" tech was registered as a component. It should instead be registered using videojs.registerTech(name, tech)"),pt.registerComponent.call(pt,e,t)},tr.getTech=ji.getTech,tr.registerTech=ji.registerTech,tr.use=function(e,t){Hi[e]=Hi[e]||[],Hi[e].push(t)},Object.defineProperty(tr,"middleware",{value:{},writeable:!1,enumerable:!0}),Object.defineProperty(tr.middleware,"TERMINATOR",{value:Vi,writeable:!1,enumerable:!0}),tr.browser=W,tr.TOUCH_ENABLED=F,tr.extend=function(e,t){var i,n=function(){e.apply(this,arguments)},r={};for(i in"object"==typeof(t=void 0===t?{}:t)?(t.constructor!==Object.prototype.constructor&&(n=t.constructor),r=t):"function"==typeof t&&(n=t),Zn(n,e),e&&(n.super_=e),r)r.hasOwnProperty(i)&&(n.prototype[i]=r[i]);return n},tr.mergeOptions=lt,tr.bind=Ve,tr.registerPlugin=Jn.registerPlugin,tr.deregisterPlugin=Jn.deregisterPlugin,tr.plugin=function(e,t){return h.warn("videojs.plugin() is deprecated; use videojs.registerPlugin() instead"),Jn.registerPlugin(e,t)},tr.getPlugins=Jn.getPlugins,tr.getPlugin=Jn.getPlugin,tr.getPluginVersion=Jn.getPluginVersion,tr.addLanguage=function(e,t){var i;return e=(""+e).toLowerCase(),tr.options.languages=lt(tr.options.languages,((i={})[e]=t,i)),tr.options.languages[e]},tr.log=h,tr.createLogger=p,tr.createTimeRange=tr.createTimeRanges=vt,tr.formatTime=ln,tr.setFormatTime=function(e){un=e},tr.resetFormatTime=function(){un=on},tr.parseUrl=Rt,tr.isCrossOrigin=Ut,tr.EventTarget=ze,tr.on=Be,tr.one=He,tr.off=Fe,tr.trigger=je,tr.xhr=Jt,tr.TextTrack=ri,tr.AudioTrack=x,tr.VideoTrack=U,["isEl","isTextNode","createEl","hasClass","addClass","removeClass","toggleClass","setAttributes","getAttributes","emptyEl","appendContent","insertContent"].forEach(function(e){tr[e]=function(){return h.warn("videojs."+e+"() is deprecated; use videojs.dom."+e+"() instead"),we[e].apply(null,arguments)}}),tr.computedStyle=w,tr.dom=we,tr.url=zt,tr.defineLazyProperty=An,tr.addLanguage("en",{"Non-Fullscreen":"Exit Fullscreen"});function ir(e,t){if(/^[a-z]+:/i.test(t))return t;/^data:/.test(e)&&(e=window.location&&window.location.href||"");var i="function"==typeof window.URL,n=/^\/\//.test(e),r=!window.location&&!/\/\//i.test(e);if(i?e=new window.URL(e,window.location||rr):/\/\//i.test(e)||(e=nr.buildAbsoluteURL(window.location&&window.location.href||"",e)),i){i=new URL(t,e);return r?i.href.slice(rr.length):n?i.href.slice(i.protocol.length):i.href}return nr.buildAbsoluteURL(e,t)}var nr=m(function(e,t){var i,a,n,r,s;i=/^((?:[a-zA-Z0-9+\-.]+:)?)(\/\/[^\/?#]*)?((?:[^\/?#]*\/)*[^;?#]*)?(;[^?#]*)?(\?[^#]*)?(#[^]*)?$/,a=/^([^\/?#]*)([^]*)$/,n=/(?:\/|^)\.(?=\/)/g,r=/(?:\/|^)\.\.\/(?!\.\.\/)[^\/]*(?=\/)/g,s={buildAbsoluteURL:function(e,t,i){if(i=i||{},e=e.trim(),!(t=t.trim())){if(!i.alwaysNormalize)return e;var n=s.parseURL(e);if(!n)throw new Error("Error trying to parse base URL.");return n.path=s.normalizePath(n.path),s.buildURLFromParts(n)}n=s.parseURL(t);if(!n)throw new Error("Error trying to parse relative URL.");if(n.scheme)return i.alwaysNormalize?(n.path=s.normalizePath(n.path),s.buildURLFromParts(n)):t;t=s.parseURL(e);if(!t)throw new Error("Error trying to parse base URL.");!t.netLoc&&t.path&&"/"!==t.path[0]&&(r=a.exec(t.path),t.netLoc=r[1],t.path=r[2]),t.netLoc&&!t.path&&(t.path="/");var r,e={scheme:t.scheme,netLoc:n.netLoc,path:null,params:n.params,query:n.query,fragment:n.fragment};return n.netLoc||(e.netLoc=t.netLoc,"/"!==n.path[0]&&(n.path?(r=(r=t.path).substring(0,r.lastIndexOf("/")+1)+n.path,e.path=s.normalizePath(r)):(e.path=t.path,n.params||(e.params=t.params,n.query||(e.query=t.query))))),null===e.path&&(e.path=i.alwaysNormalize?s.normalizePath(n.path):n.path),s.buildURLFromParts(e)},parseURL:function(e){e=i.exec(e);return e?{scheme:e[1]||"",netLoc:e[2]||"",path:e[3]||"",params:e[4]||"",query:e[5]||"",fragment:e[6]||""}:null},normalizePath:function(e){for(e=e.split("").reverse().join("").replace(n,"");e.length!==(e=e.replace(r,"")).length;);return e.split("").reverse().join("")},buildURLFromParts:function(e){return e.scheme+e.netLoc+e.path+e.params+e.query+e.fragment}},e.exports=s}),rr="http://example.com",ar=function(){function e(){this.listeners={}}var t=e.prototype;return t.on=function(e,t){this.listeners[e]||(this.listeners[e]=[]),this.listeners[e].push(t)},t.off=function(e,t){if(!this.listeners[e])return!1;t=this.listeners[e].indexOf(t);return this.listeners[e]=this.listeners[e].slice(0),this.listeners[e].splice(t,1),-1<t},t.trigger=function(e){var t=this.listeners[e];if(t)if(2===arguments.length)for(var i=t.length,n=0;n<i;++n)t[n].call(this,arguments[1]);else for(var r=Array.prototype.slice.call(arguments,1),a=t.length,s=0;s<a;++s)t[s].apply(this,r)},t.dispose=function(){this.listeners={}},t.pipe=function(t){this.on("data",function(e){t.push(e)})},e}(),sr=function(e){return window.atob?window.atob(e):Buffer.from(e,"base64").toString("binary")};function or(e){for(var t=sr(e),i=new Uint8Array(t.length),n=0;n<t.length;n++)i[n]=t.charCodeAt(n);return i}
 /*! @name m3u8-parser @version 4.7.0 @license Apache-2.0 */function ur(e){var t=/([0-9.]*)?@?([0-9.]*)?/.exec(e||""),e={};return t[1]&&(e.length=parseInt(t[1],10)),t[2]&&(e.offset=parseInt(t[2],10)),e}function lr(e){for(var t,i=e.split(new RegExp('(?:^|,)((?:[^=]*)=(?:"[^"]*"|[^,]*))')),n={},r=i.length;r--;)""!==i[r]&&((t=/([^=]*)=(.*)/.exec(i[r]).slice(1))[0]=t[0].replace(/^\s+|\s+$/g,""),t[1]=t[1].replace(/^\s+|\s+$/g,""),t[1]=t[1].replace(/^['"](.*)['"]$/g,"$1"),n[t[0]]=t[1]);return n}function cr(t){var i={};return Object.keys(t).forEach(function(e){i[e.toLowerCase().replace(/-(\w)/g,function(e){return e[1].toUpperCase()})]=t[e]}),i}function dr(e){var t,i,n,r,a=e.serverControl,s=e.targetDuration,o=e.partTargetDuration;a&&(t="#EXT-X-SERVER-CONTROL",i="holdBack",n="partHoldBack",r=s&&3*s,e=o&&2*o,s&&!a.hasOwnProperty(i)&&(a[i]=r,this.trigger("info",{message:t+" defaulting HOLD-BACK to targetDuration * 3 ("+r+")."})),r&&a[i]<r&&(this.trigger("warn",{message:t+" clamping HOLD-BACK ("+a[i]+") to targetDuration * 3 ("+r+")"}),a[i]=r),o&&!a.hasOwnProperty(n)&&(a[n]=3*o,this.trigger("info",{message:t+" defaulting PART-HOLD-BACK to partTargetDuration * 3 ("+a[n]+")."})),o&&a[n]<e&&(this.trigger("warn",{message:t+" clamping PART-HOLD-BACK ("+a[n]+") to partTargetDuration * 2 ("+e+")."}),a[n]=e))}function hr(e){return e&&e.replace(/avc1\.(\d+)\.(\d+)/i,function(e,t,i){return"avc1."+("00"+Number(t).toString(16)).slice(-2)+"00"+("00"+Number(i).toString(16)).slice(-2)})}function pr(e){var e=(e=void 0===e?"":e).split(","),a=[];return e.forEach(function(n){var r;n=n.trim(),Er.forEach(function(e){var t,i=wr[e].exec(n.toLowerCase());!i||i.length<=1||(r=e,t=n.substring(0,i[1].length),i=n.replace(t,""),a.push({type:t,details:i,mediaType:e}))}),r||a.push({type:n,details:"",mediaType:"unknown"})}),a}function fr(e){return wr.audio.test((e=void 0===e?"":e).trim().toLowerCase())}function mr(e){if(e&&"string"==typeof e){var t=e.toLowerCase().split(",").map(function(e){return hr(e.trim())}),i="video";1===t.length&&fr(t[0])?i="audio":1===t.length&&(n=t[0],wr.text.test((n=void 0===n?"":n).trim().toLowerCase()))&&(i="application");var n="mp4";return t.every(function(e){return wr.mp4.test(e)})?n="mp4":t.every(function(e){return wr.webm.test(e)})?n="webm":t.every(function(e){return wr.ogg.test(e)})&&(n="ogg"),i+"/"+n+';codecs="'+e+'"'}}function gr(e){return void 0===e&&(e=""),window.MediaSource&&window.MediaSource.isTypeSupported&&window.MediaSource.isTypeSupported(mr(e))||!1}function yr(e){return(e=void 0===e?"":e).toLowerCase().split(",").every(function(e){e=e.trim();for(var t=0;t<kr.length;t++)if(wr["muxer"+kr[t]].test(e))return!0;return!1})}function vr(e){return Ir.test(e)?"hls":xr.test(e)?"dash":"application/vnd.videojs.vhs+json"===e?"vhs-json":null}var _r=function(t){function e(){var e=t.call(this)||this;return e.buffer="",e}return mt(e,t),e.prototype.push=function(e){var t;for(this.buffer+=e,t=this.buffer.indexOf("\n");-1<t;t=this.buffer.indexOf("\n"))this.trigger("data",this.buffer.substring(0,t)),this.buffer=this.buffer.substring(t+1)},e}(ar),br=String.fromCharCode(9),Tr=function(t){function e(){var e=t.call(this)||this;return e.customParsers=[],e.tagMappers=[],e}mt(e,t);var i=e.prototype;return i.push=function(i){var r,a,s=this;0!==(i=i.trim()).length&&("#"===i[0]?this.tagMappers.reduce(function(e,t){t=t(i);return t===i?e:e.concat([t])},[i]).forEach(function(e){for(var t,i,n=0;n<s.customParsers.length;n++)if(s.customParsers[n].call(s,e))return;if(0===e.indexOf("#EXT"))if(e=e.replace("\r",""),r=/^#EXTM3U/.exec(e))s.trigger("data",{type:"tag",tagType:"m3u"});else{if(r=/^#EXTINF:?([0-9\.]*)?,?(.*)?$/.exec(e))return a={type:"tag",tagType:"inf"},r[1]&&(a.duration=parseFloat(r[1])),r[2]&&(a.title=r[2]),void s.trigger("data",a);if(r=/^#EXT-X-TARGETDURATION:?([0-9.]*)?/.exec(e))return a={type:"tag",tagType:"targetduration"},r[1]&&(a.duration=parseInt(r[1],10)),void s.trigger("data",a);if(r=/^#EXT-X-VERSION:?([0-9.]*)?/.exec(e))return a={type:"tag",tagType:"version"},r[1]&&(a.version=parseInt(r[1],10)),void s.trigger("data",a);if(r=/^#EXT-X-MEDIA-SEQUENCE:?(\-?[0-9.]*)?/.exec(e))return a={type:"tag",tagType:"media-sequence"},r[1]&&(a.number=parseInt(r[1],10)),void s.trigger("data",a);if(r=/^#EXT-X-DISCONTINUITY-SEQUENCE:?(\-?[0-9.]*)?/.exec(e))return a={type:"tag",tagType:"discontinuity-sequence"},r[1]&&(a.number=parseInt(r[1],10)),void s.trigger("data",a);if(r=/^#EXT-X-PLAYLIST-TYPE:?(.*)?$/.exec(e))return a={type:"tag",tagType:"playlist-type"},r[1]&&(a.playlistType=r[1]),void s.trigger("data",a);if(r=/^#EXT-X-BYTERANGE:?(.*)?$/.exec(e))return a=g(ur(r[1]),{type:"tag",tagType:"byterange"}),void s.trigger("data",a);if(r=/^#EXT-X-ALLOW-CACHE:?(YES|NO)?/.exec(e))return a={type:"tag",tagType:"allow-cache"},r[1]&&(a.allowed=!/NO/.test(r[1])),void s.trigger("data",a);if(r=/^#EXT-X-MAP:?(.*)$/.exec(e))return a={type:"tag",tagType:"map"},r[1]&&((t=lr(r[1])).URI&&(a.uri=t.URI),t.BYTERANGE&&(a.byterange=ur(t.BYTERANGE))),void s.trigger("data",a);if(r=/^#EXT-X-STREAM-INF:?(.*)$/.exec(e))return a={type:"tag",tagType:"stream-inf"},r[1]&&(a.attributes=lr(r[1]),a.attributes.RESOLUTION&&(i={},(t=a.attributes.RESOLUTION.split("x"))[0]&&(i.width=parseInt(t[0],10)),t[1]&&(i.height=parseInt(t[1],10)),a.attributes.RESOLUTION=i),a.attributes.BANDWIDTH&&(a.attributes.BANDWIDTH=parseInt(a.attributes.BANDWIDTH,10)),a.attributes["PROGRAM-ID"]&&(a.attributes["PROGRAM-ID"]=parseInt(a.attributes["PROGRAM-ID"],10))),void s.trigger("data",a);if(r=/^#EXT-X-MEDIA:?(.*)$/.exec(e))return a={type:"tag",tagType:"media"},r[1]&&(a.attributes=lr(r[1])),void s.trigger("data",a);if(r=/^#EXT-X-ENDLIST/.exec(e))s.trigger("data",{type:"tag",tagType:"endlist"});else{if(!(r=/^#EXT-X-DISCONTINUITY/.exec(e)))return(r=/^#EXT-X-PROGRAM-DATE-TIME:?(.*)$/.exec(e))?(a={type:"tag",tagType:"program-date-time"},r[1]&&(a.dateTimeString=r[1],a.dateTimeObject=new Date(r[1])),void s.trigger("data",a)):(r=/^#EXT-X-KEY:?(.*)$/.exec(e))?(a={type:"tag",tagType:"key"},r[1]&&(a.attributes=lr(r[1]),a.attributes.IV&&("0x"===a.attributes.IV.substring(0,2).toLowerCase()&&(a.attributes.IV=a.attributes.IV.substring(2)),a.attributes.IV=a.attributes.IV.match(/.{8}/g),a.attributes.IV[0]=parseInt(a.attributes.IV[0],16),a.attributes.IV[1]=parseInt(a.attributes.IV[1],16),a.attributes.IV[2]=parseInt(a.attributes.IV[2],16),a.attributes.IV[3]=parseInt(a.attributes.IV[3],16),a.attributes.IV=new Uint32Array(a.attributes.IV))),void s.trigger("data",a)):(r=/^#EXT-X-START:?(.*)$/.exec(e))?(a={type:"tag",tagType:"start"},r[1]&&(a.attributes=lr(r[1]),a.attributes["TIME-OFFSET"]=parseFloat(a.attributes["TIME-OFFSET"]),a.attributes.PRECISE=/YES/.test(a.attributes.PRECISE)),void s.trigger("data",a)):(r=/^#EXT-X-CUE-OUT-CONT:?(.*)?$/.exec(e))?(a={type:"tag",tagType:"cue-out-cont"},r[1]?a.data=r[1]:a.data="",void s.trigger("data",a)):(r=/^#EXT-X-CUE-OUT:?(.*)?$/.exec(e))?(a={type:"tag",tagType:"cue-out"},r[1]?a.data=r[1]:a.data="",void s.trigger("data",a)):(r=/^#EXT-X-CUE-IN:?(.*)?$/.exec(e))?(a={type:"tag",tagType:"cue-in"},r[1]?a.data=r[1]:a.data="",void s.trigger("data",a)):(r=/^#EXT-X-SKIP:(.*)$/.exec(e))&&r[1]?((a={type:"tag",tagType:"skip"}).attributes=lr(r[1]),a.attributes.hasOwnProperty("SKIPPED-SEGMENTS")&&(a.attributes["SKIPPED-SEGMENTS"]=parseInt(a.attributes["SKIPPED-SEGMENTS"],10)),a.attributes.hasOwnProperty("RECENTLY-REMOVED-DATERANGES")&&(a.attributes["RECENTLY-REMOVED-DATERANGES"]=a.attributes["RECENTLY-REMOVED-DATERANGES"].split(br)),void s.trigger("data",a)):(r=/^#EXT-X-PART:(.*)$/.exec(e))&&r[1]?((a={type:"tag",tagType:"part"}).attributes=lr(r[1]),["DURATION"].forEach(function(e){a.attributes.hasOwnProperty(e)&&(a.attributes[e]=parseFloat(a.attributes[e]))}),["INDEPENDENT","GAP"].forEach(function(e){a.attributes.hasOwnProperty(e)&&(a.attributes[e]=/YES/.test(a.attributes[e]))}),a.attributes.hasOwnProperty("BYTERANGE")&&(a.attributes.byterange=ur(a.attributes.BYTERANGE)),void s.trigger("data",a)):(r=/^#EXT-X-SERVER-CONTROL:(.*)$/.exec(e))&&r[1]?((a={type:"tag",tagType:"server-control"}).attributes=lr(r[1]),["CAN-SKIP-UNTIL","PART-HOLD-BACK","HOLD-BACK"].forEach(function(e){a.attributes.hasOwnProperty(e)&&(a.attributes[e]=parseFloat(a.attributes[e]))}),["CAN-SKIP-DATERANGES","CAN-BLOCK-RELOAD"].forEach(function(e){a.attributes.hasOwnProperty(e)&&(a.attributes[e]=/YES/.test(a.attributes[e]))}),void s.trigger("data",a)):(r=/^#EXT-X-PART-INF:(.*)$/.exec(e))&&r[1]?((a={type:"tag",tagType:"part-inf"}).attributes=lr(r[1]),["PART-TARGET"].forEach(function(e){a.attributes.hasOwnProperty(e)&&(a.attributes[e]=parseFloat(a.attributes[e]))}),void s.trigger("data",a)):(r=/^#EXT-X-PRELOAD-HINT:(.*)$/.exec(e))&&r[1]?((a={type:"tag",tagType:"preload-hint"}).attributes=lr(r[1]),["BYTERANGE-START","BYTERANGE-LENGTH"].forEach(function(e){var t;a.attributes.hasOwnProperty(e)&&(a.attributes[e]=parseInt(a.attributes[e],10),t="BYTERANGE-LENGTH"===e?"length":"offset",a.attributes.byterange=a.attributes.byterange||{},a.attributes.byterange[t]=a.attributes[e],delete a.attributes[e])}),void s.trigger("data",a)):(r=/^#EXT-X-RENDITION-REPORT:(.*)$/.exec(e))&&r[1]?((a={type:"tag",tagType:"rendition-report"}).attributes=lr(r[1]),["LAST-MSN","LAST-PART"].forEach(function(e){a.attributes.hasOwnProperty(e)&&(a.attributes[e]=parseInt(a.attributes[e],10))}),void s.trigger("data",a)):void s.trigger("data",{type:"tag",data:e.slice(4)});s.trigger("data",{type:"tag",tagType:"discontinuity"})}}else s.trigger("data",{type:"comment",text:e.slice(1)})}):this.trigger("data",{type:"uri",uri:i}))},i.addParser=function(e){var t=this,i=e.expression,n=e.customType,r=e.dataParser,a=e.segment;"function"!=typeof r&&(r=function(e){return e}),this.customParsers.push(function(e){if(i.exec(e))return t.trigger("data",{type:"custom",data:r(e),customType:n,segment:a}),!0})},i.addTagMapper=function(e){var t=e.expression,i=e.map;this.tagMappers.push(function(e){return t.test(e)?i(e):e})},e}(ar),Sr=function(t){function e(){var e=t.call(this)||this;e.lineStream=new _r,e.parseStream=new Tr,e.lineStream.pipe(e.parseStream);var n,r,a=ft(e),o=[],u={},l=!1,c={AUDIO:{},VIDEO:{},"CLOSED-CAPTIONS":{},SUBTITLES:{}},d=0;e.manifest={allowCache:!0,discontinuityStarts:[],segments:[]};var h=0,p=0;return e.on("end",function(){u.uri||!u.parts&&!u.preloadHints||(!u.map&&n&&(u.map=n),!u.key&&r&&(u.key=r),u.timeline||"number"!=typeof d||(u.timeline=d),e.manifest.preloadSegment=u)}),e.parseStream.on("data",function(s){var t,i;({tag:function(){({version:function(){s.version&&(this.manifest.version=s.version)},"allow-cache":function(){this.manifest.allowCache=s.allowed,"allowed"in s||(this.trigger("info",{message:"defaulting allowCache to YES"}),this.manifest.allowCache=!0)},byterange:function(){var e={};"length"in s&&((u.byterange=e).length=s.length,"offset"in s||(s.offset=h)),"offset"in s&&((u.byterange=e).offset=s.offset),h=e.offset+e.length},endlist:function(){this.manifest.endList=!0},inf:function(){"mediaSequence"in this.manifest||(this.manifest.mediaSequence=0,this.trigger("info",{message:"defaulting media sequence to zero"})),"discontinuitySequence"in this.manifest||(this.manifest.discontinuitySequence=0,this.trigger("info",{message:"defaulting discontinuity sequence to zero"})),0<s.duration&&(u.duration=s.duration),0===s.duration&&(u.duration=.01,this.trigger("info",{message:"updating zero segment duration to a small value"})),this.manifest.segments=o},key:function(){if(s.attributes)if("NONE"!==s.attributes.METHOD)if(s.attributes.URI){if("com.apple.streamingkeydelivery"===s.attributes.KEYFORMAT)return this.manifest.contentProtection=this.manifest.contentProtection||{},void(this.manifest.contentProtection["com.apple.fps.1_0"]={attributes:s.attributes});if("urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"===s.attributes.KEYFORMAT)return-1===["SAMPLE-AES","SAMPLE-AES-CTR","SAMPLE-AES-CENC"].indexOf(s.attributes.METHOD)?void this.trigger("warn",{message:"invalid key method provided for Widevine"}):("SAMPLE-AES-CENC"===s.attributes.METHOD&&this.trigger("warn",{message:"SAMPLE-AES-CENC is deprecated, please use SAMPLE-AES-CTR instead"}),"data:text/plain;base64,"!==s.attributes.URI.substring(0,23)?void this.trigger("warn",{message:"invalid key URI provided for Widevine"}):s.attributes.KEYID&&"0x"===s.attributes.KEYID.substring(0,2)?(this.manifest.contentProtection=this.manifest.contentProtection||{},void(this.manifest.contentProtection["com.widevine.alpha"]={attributes:{schemeIdUri:s.attributes.KEYFORMAT,keyId:s.attributes.KEYID.substring(2)},pssh:or(s.attributes.URI.split(",")[1])})):void this.trigger("warn",{message:"invalid key ID provided for Widevine"}));s.attributes.METHOD||this.trigger("warn",{message:"defaulting key method to AES-128"}),r={method:s.attributes.METHOD||"AES-128",uri:s.attributes.URI},"undefined"!=typeof s.attributes.IV&&(r.iv=s.attributes.IV)}else this.trigger("warn",{message:"ignoring key declaration without URI"});else r=null;else this.trigger("warn",{message:"ignoring key declaration without attribute list"})},"media-sequence":function(){isFinite(s.number)?this.manifest.mediaSequence=s.number:this.trigger("warn",{message:"ignoring invalid media sequence: "+s.number})},"discontinuity-sequence":function(){isFinite(s.number)?(this.manifest.discontinuitySequence=s.number,d=s.number):this.trigger("warn",{message:"ignoring invalid discontinuity sequence: "+s.number})},"playlist-type":function(){/VOD|EVENT/.test(s.playlistType)?this.manifest.playlistType=s.playlistType:this.trigger("warn",{message:"ignoring unknown playlist type: "+s.playlist})},map:function(){n={},s.uri&&(n.uri=s.uri),s.byterange&&(n.byterange=s.byterange),r&&(n.key=r)},"stream-inf":function(){this.manifest.playlists=o,this.manifest.mediaGroups=this.manifest.mediaGroups||c,s.attributes?(u.attributes||(u.attributes={}),g(u.attributes,s.attributes)):this.trigger("warn",{message:"ignoring empty stream-inf attributes"})},media:function(){var e;this.manifest.mediaGroups=this.manifest.mediaGroups||c,s.attributes&&s.attributes.TYPE&&s.attributes["GROUP-ID"]&&s.attributes.NAME?((e=this.manifest.mediaGroups[s.attributes.TYPE])[s.attributes["GROUP-ID"]]=e[s.attributes["GROUP-ID"]]||{},t=e[s.attributes["GROUP-ID"]],(i={default:/yes/i.test(s.attributes.DEFAULT)}).default?i.autoselect=!0:i.autoselect=/yes/i.test(s.attributes.AUTOSELECT),s.attributes.LANGUAGE&&(i.language=s.attributes.LANGUAGE),s.attributes.URI&&(i.uri=s.attributes.URI),s.attributes["INSTREAM-ID"]&&(i.instreamId=s.attributes["INSTREAM-ID"]),s.attributes.CHARACTERISTICS&&(i.characteristics=s.attributes.CHARACTERISTICS),s.attributes.FORCED&&(i.forced=/yes/i.test(s.attributes.FORCED)),t[s.attributes.NAME]=i):this.trigger("warn",{message:"ignoring incomplete or missing media group"})},discontinuity:function(){d+=1,u.discontinuity=!0,this.manifest.discontinuityStarts.push(o.length)},"program-date-time":function(){"undefined"==typeof this.manifest.dateTimeString&&(this.manifest.dateTimeString=s.dateTimeString,this.manifest.dateTimeObject=s.dateTimeObject),u.dateTimeString=s.dateTimeString,u.dateTimeObject=s.dateTimeObject},targetduration:function(){!isFinite(s.duration)||s.duration<0?this.trigger("warn",{message:"ignoring invalid target duration: "+s.duration}):(this.manifest.targetDuration=s.duration,dr.call(this,this.manifest))},start:function(){s.attributes&&!isNaN(s.attributes["TIME-OFFSET"])?this.manifest.start={timeOffset:s.attributes["TIME-OFFSET"],precise:s.attributes.PRECISE}:this.trigger("warn",{message:"ignoring start declaration without appropriate attribute list"})},"cue-out":function(){u.cueOut=s.data},"cue-out-cont":function(){u.cueOutCont=s.data},"cue-in":function(){u.cueIn=s.data},skip:function(){this.manifest.skip=cr(s.attributes),this.warnOnMissingAttributes_("#EXT-X-SKIP",s.attributes,["SKIPPED-SEGMENTS"])},part:function(){var i=this;l=!0;var e=this.manifest.segments.length,t=cr(s.attributes);u.parts=u.parts||[],u.parts.push(t),t.byterange&&(t.byterange.hasOwnProperty("offset")||(t.byterange.offset=p),p=t.byterange.offset+t.byterange.length);var n=u.parts.length-1;this.warnOnMissingAttributes_("#EXT-X-PART #"+n+" for segment #"+e,s.attributes,["URI","DURATION"]),this.manifest.renditionReports&&this.manifest.renditionReports.forEach(function(e,t){e.hasOwnProperty("lastPart")||i.trigger("warn",{message:"#EXT-X-RENDITION-REPORT #"+t+" lacks required attribute(s): LAST-PART"})})},"server-control":function(){var e=this.manifest.serverControl=cr(s.attributes);e.hasOwnProperty("canBlockReload")||(e.canBlockReload=!1,this.trigger("info",{message:"#EXT-X-SERVER-CONTROL defaulting CAN-BLOCK-RELOAD to false"})),dr.call(this,this.manifest),e.canSkipDateranges&&!e.hasOwnProperty("canSkipUntil")&&this.trigger("warn",{message:"#EXT-X-SERVER-CONTROL lacks required attribute CAN-SKIP-UNTIL which is required when CAN-SKIP-DATERANGES is set"})},"preload-hint":function(){var e=this.manifest.segments.length,t=cr(s.attributes),i=t.type&&"PART"===t.type;u.preloadHints=u.preloadHints||[],u.preloadHints.push(t),t.byterange&&(t.byterange.hasOwnProperty("offset")||(t.byterange.offset=i?p:0,i&&(p=t.byterange.offset+t.byterange.length)));var n=u.preloadHints.length-1;if(this.warnOnMissingAttributes_("#EXT-X-PRELOAD-HINT #"+n+" for segment #"+e,s.attributes,["TYPE","URI"]),t.type)for(var r=0;r<u.preloadHints.length-1;r++){var a=u.preloadHints[r];a.type&&a.type===t.type&&this.trigger("warn",{message:"#EXT-X-PRELOAD-HINT #"+n+" for segment #"+e+" has the same TYPE "+t.type+" as preload hint #"+r})}},"rendition-report":function(){var e=cr(s.attributes);this.manifest.renditionReports=this.manifest.renditionReports||[],this.manifest.renditionReports.push(e);var t=this.manifest.renditionReports.length-1,e=["LAST-MSN","URI"];l&&e.push("LAST-PART"),this.warnOnMissingAttributes_("#EXT-X-RENDITION-REPORT #"+t,s.attributes,e)},"part-inf":function(){this.manifest.partInf=cr(s.attributes),this.warnOnMissingAttributes_("#EXT-X-PART-INF",s.attributes,["PART-TARGET"]),this.manifest.partInf.partTarget&&(this.manifest.partTargetDuration=this.manifest.partInf.partTarget),dr.call(this,this.manifest)}}[s.tagType]||function(){}).call(a)},uri:function(){u.uri=s.uri,o.push(u),!this.manifest.targetDuration||"duration"in u||(this.trigger("warn",{message:"defaulting segment duration to the target duration"}),u.duration=this.manifest.targetDuration),r&&(u.key=r),u.timeline=d,n&&(u.map=n),p=0,u={}},comment:function(){},custom:function(){s.segment?(u.custom=u.custom||{},u.custom[s.customType]=s.data):(this.manifest.custom=this.manifest.custom||{},this.manifest.custom[s.customType]=s.data)}})[s.type].call(a)}),e}mt(e,t);var i=e.prototype;return i.warnOnMissingAttributes_=function(e,t,i){var n=[];i.forEach(function(e){t.hasOwnProperty(e)||n.push(e)}),n.length&&this.trigger("warn",{message:e+" lacks required attribute(s): "+n.join(", ")})},i.push=function(e){this.lineStream.push(e)},i.end=function(){this.lineStream.push("\n"),this.trigger("end")},i.addParser=function(e){this.parseStream.addParser(e)},i.addTagMapper=function(e){this.parseStream.addTagMapper(e)},e}(ar),wr={mp4:/^(av0?1|avc0?[1234]|vp0?9|flac|opus|mp3|mp4a|mp4v|stpp.ttml.im1t)/,webm:/^(vp0?[89]|av0?1|opus|vorbis)/,ogg:/^(vp0?[89]|theora|flac|opus|vorbis)/,video:/^(av0?1|avc0?[1234]|vp0?[89]|hvc1|hev1|theora|mp4v)/,audio:/^(mp4a|flac|vorbis|opus|ac-[34]|ec-3|alac|mp3|speex|aac)/,text:/^(stpp.ttml.im1t)/,muxerVideo:/^(avc0?1)/,muxerAudio:/^(mp4a)/,muxerText:/a^/},Er=["video","audio","text"],kr=["Video","Audio","Text"],Cr="mp4a.40.2",Ir=/^(audio|video|application)\/(x-|vnd\.apple\.)?mpegurl/i,xr=/^application\/dash\+xml/i;function Ar(e,t){return(t=void 0===t?Object:t)&&"function"==typeof t.freeze?t.freeze(e):e}var Pr=Ar({HTML:"text/html",isHTML:function(e){return e===Pr.HTML},XML_APPLICATION:"application/xml",XML_TEXT:"text/xml",XML_XHTML_APPLICATION:"application/xhtml+xml",XML_SVG_IMAGE:"image/svg+xml"}),Lr=Ar({HTML:"http://www.w3.org/1999/xhtml",isHTML:function(e){return e===Lr.HTML},SVG:"http://www.w3.org/2000/svg",XML:"http://www.w3.org/XML/1998/namespace",XMLNS:"http://www.w3.org/2000/xmlns/"}),Dr={freeze:Ar,MIME_TYPE:Pr,NAMESPACE:Lr},Or=Dr.NAMESPACE;function Rr(e){return""!==e}function Mr(e,t){return e.hasOwnProperty(t)||(e[t]=!0),e}function Nr(e){if(!e)return[];e=(e=e)?e.split(/[\t\n\f\r ]+/).filter(Rr):[];return Object.keys(e.reduce(Mr,{}))}function Ur(e,t){for(var i in e)t[i]=e[i]}function Br(e,t){var i,n=e.prototype;n instanceof t||((i=function(){}).prototype=t.prototype,Ur(n,i=new i),e.prototype=n=i),n.constructor!=e&&(n.constructor=e)}var W={},Fr=W.ELEMENT_NODE=1,jr=W.ATTRIBUTE_NODE=2,Hr=W.TEXT_NODE=3,qr=W.CDATA_SECTION_NODE=4,Vr=W.ENTITY_REFERENCE_NODE=5,Wr=(W.ENTITY_NODE=6,W.PROCESSING_INSTRUCTION_NODE=7),Gr=W.COMMENT_NODE=8,zr=W.DOCUMENT_NODE=9,Xr=W.DOCUMENT_TYPE_NODE=10,Kr=W.DOCUMENT_FRAGMENT_NODE=11,x=(W.NOTATION_NODE=12,{}),Yr={};x.INDEX_SIZE_ERR=(Yr[1]="Index size error",1),x.DOMSTRING_SIZE_ERR=(Yr[2]="DOMString size error",2);x.HIERARCHY_REQUEST_ERR=(Yr[3]="Hierarchy request error",3);x.WRONG_DOCUMENT_ERR=(Yr[4]="Wrong document",4),x.INVALID_CHARACTER_ERR=(Yr[5]="Invalid character",5),x.NO_DATA_ALLOWED_ERR=(Yr[6]="No data allowed",6),x.NO_MODIFICATION_ALLOWED_ERR=(Yr[7]="No modification allowed",7);x.NOT_FOUND_ERR=(Yr[8]="Not found",8);x.NOT_SUPPORTED_ERR=(Yr[9]="Not supported",9);var Qr;x.INUSE_ATTRIBUTE_ERR=(Yr[10]="Attribute in use",10);function $r(e,t){var i;return t instanceof Error?i=t:(i=this,Error.call(this,Yr[e]),this.message=Yr[e],Error.captureStackTrace&&Error.captureStackTrace(this,$r)),i.code=e,t&&(this.message=this.message+": "+t),i}function Jr(){}function Zr(e,t){this._node=e,this._refresh=t,ea(this)}function ea(e){var t,i=e._node._inc||e._node.ownerDocument._inc;e._inc!=i&&(t=e._refresh(e._node),La(e,"length",t.length),Ur(t,e),e._inc=i)}function ta(){}function ia(e,t){for(var i=e.length;i--;)if(e[i]===t)return i}function na(e,t,i,n){n?t[ia(t,n)]=i:t[t.length++]=i,!e||(t=(i.ownerElement=e).ownerDocument)&&(n&&ca(t,e,n),e=e,i=i,(t=t)&&t._inc++,i.namespaceURI===Or.XMLNS&&(e._nsMap[i.prefix?i.localName:""]=i.value))}function ra(e,t,i){var n=ia(t,i);if(!(0<=n))throw $r(8,new Error(e.tagName+"@"+i));for(var r,a=t.length-1;n<a;)t[n]=t[++n];t.length=a,!e||(r=e.ownerDocument)&&(ca(r,e,i),i.ownerElement=null)}function aa(){}function sa(){}function oa(e){return("<"==e?"&lt;":">"==e&&"&gt;")||"&"==e&&"&amp;"||'"'==e&&"&quot;"||"&#"+e.charCodeAt()+";"}function ua(e,t){if(t(e))return 1;if(e=e.firstChild)do{if(ua(e,t))return 1}while(e=e.nextSibling)}function la(){}function ca(e,t,i){e&&e._inc++,i.namespaceURI===Or.XMLNS&&delete t._nsMap[i.prefix?i.localName:""]}function da(e,t,i){if(e&&e._inc){e._inc++;var n=t.childNodes;if(i)n[n.length++]=i;else{for(var r=t.firstChild,a=0;r;)r=(n[a++]=r).nextSibling;n.length=a}}}function ha(e,t){var i=t.previousSibling,n=t.nextSibling;return i?i.nextSibling=n:e.firstChild=n,n?n.previousSibling=i:e.lastChild=i,da(e.ownerDocument,e),t}function pa(e,t,i){var n=t.parentNode;if(n&&n.removeChild(t),t.nodeType===Kr){var r=t.firstChild;if(null==r)return t;var a=t.lastChild}else r=a=t;n=i?i.previousSibling:e.lastChild;for(r.previousSibling=n,a.nextSibling=i,n?n.nextSibling=r:e.firstChild=r,null==i?e.lastChild=a:i.previousSibling=a;r.parentNode=e,r!==a&&(r=r.nextSibling););return da(e.ownerDocument||e,e),t.nodeType==Kr&&(t.firstChild=t.lastChild=null),t}function fa(){this._nsMap={}}function ma(){}function ga(){}function ya(){}function va(){}function _a(){}function ba(){}function Ta(){}function Sa(){}function wa(){}function Ea(){}function ka(){}function Ca(){}function Ia(e,t){var i,n=[],r=9==this.nodeType&&this.documentElement||this,a=r.prefix,s=r.namespaceURI;return Pa(this,n,e,t,i=s&&null==a&&null==(a=r.lookupPrefix(s))?[{namespace:s,prefix:null}]:i),n.join("")}function xa(e,t,i){var n=e.prefix||"",r=e.namespaceURI;if(r&&("xml"!==n||r!==Or.XML)&&r!==Or.XMLNS){for(var a=i.length;a--;){var s=i[a];if(s.prefix===n)return s.namespace!==r}return 1}}function Aa(e,t,i){e.push(" ",t,'="',i.replace(/[<&"]/g,oa),'"')}function Pa(e,t,i,n,r){if(r=r||[],n){if(!(e=n(e)))return;if("string"==typeof e)return void t.push(e)}switch(e.nodeType){case Fr:var a=e.attributes,s=a.length,o=e.firstChild,u=e.tagName,l=u;if(!(i=Or.isHTML(e.namespaceURI)||i)&&!e.prefix&&e.namespaceURI){for(var c,d=0;d<a.length;d++)if("xmlns"===a.item(d).name){c=a.item(d).value;break}if(!c)for(var h=r.length-1;0<=h;h--)if(""===(p=r[h]).prefix&&p.namespace===e.namespaceURI){c=p.namespace;break}if(c!==e.namespaceURI)for(var p,h=r.length-1;0<=h;h--)if((p=r[h]).namespace===e.namespaceURI){p.prefix&&(l=p.prefix+":"+u);break}}t.push("<",l);for(var f=0;f<s;f++)"xmlns"==(m=a.item(f)).prefix?r.push({prefix:m.localName,namespace:m.value}):"xmlns"==m.nodeName&&r.push({prefix:"",namespace:m.value});for(var m,g,y,f=0;f<s;f++)xa(m=a.item(f),0,r)&&(Aa(t,(g=m.prefix||"")?"xmlns:"+g:"xmlns",y=m.namespaceURI),r.push({prefix:g,namespace:y})),Pa(m,t,i,n,r);if(u===l&&xa(e,0,r)&&(Aa(t,(g=e.prefix||"")?"xmlns:"+g:"xmlns",y=e.namespaceURI),r.push({prefix:g,namespace:y})),o||i&&!/^(?:meta|link|img|br|hr|input)$/i.test(u)){if(t.push(">"),i&&/^script$/i.test(u))for(;o;)o.data?t.push(o.data):Pa(o,t,i,n,r.slice()),o=o.nextSibling;else for(;o;)Pa(o,t,i,n,r.slice()),o=o.nextSibling;t.push("</",l,">")}else t.push("/>");return;case zr:case Kr:for(o=e.firstChild;o;)Pa(o,t,i,n,r.slice()),o=o.nextSibling;return;case jr:return Aa(t,e.name,e.value),0;case Hr:return t.push(e.data.replace(/[<&]/g,oa).replace(/]]>/g,"]]&gt;"));case qr:return t.push("<![CDATA[",e.data,"]]>");case Gr:return t.push("\x3c!--",e.data,"--\x3e");case Xr:var v=e.publicId,_=e.systemId;return t.push("<!DOCTYPE ",e.name),void(v?(t.push(" PUBLIC ",v),_&&"."!=_&&t.push(" ",_),t.push(">")):_&&"."!=_?t.push(" SYSTEM ",_,">"):((_=e.internalSubset)&&t.push(" [",_,"]"),t.push(">")));case Wr:return t.push("<?",e.target," ",e.data,"?>");case Vr:return t.push("&",e.nodeName,";");default:t.push("??",e.nodeName)}}function La(e,t,i){e[t]=i}x.INVALID_STATE_ERR=(Yr[11]="Invalid state",11),x.SYNTAX_ERR=(Yr[12]="Syntax error",12),x.INVALID_MODIFICATION_ERR=(Yr[13]="Invalid modification",13),x.NAMESPACE_ERR=(Yr[14]="Invalid namespace",14),x.INVALID_ACCESS_ERR=(Yr[15]="Invalid access",15),$r.prototype=Error.prototype,Ur(x,$r),Jr.prototype={length:0,item:function(e){return this[e]||null},toString:function(e,t){for(var i=[],n=0;n<this.length;n++)Pa(this[n],i,e,t);return i.join("")}},Zr.prototype.item=function(e){return ea(this),this[e]},Br(Zr,Jr),ta.prototype={length:0,item:Jr.prototype.item,getNamedItem:function(e){for(var t=this.length;t--;){var i=this[t];if(i.nodeName==e)return i}},setNamedItem:function(e){var t=e.ownerElement;if(t&&t!=this._ownerElement)throw new $r(10);t=this.getNamedItem(e.nodeName);return na(this._ownerElement,this,e,t),t},setNamedItemNS:function(e){var t=e.ownerElement;if(t&&t!=this._ownerElement)throw new $r(10);return t=this.getNamedItemNS(e.namespaceURI,e.localName),na(this._ownerElement,this,e,t),t},removeNamedItem:function(e){e=this.getNamedItem(e);return ra(this._ownerElement,this,e),e},removeNamedItemNS:function(e,t){t=this.getNamedItemNS(e,t);return ra(this._ownerElement,this,t),t},getNamedItemNS:function(e,t){for(var i=this.length;i--;){var n=this[i];if(n.localName==t&&n.namespaceURI==e)return n}return null}},aa.prototype={hasFeature:function(e,t){return!0},createDocument:function(e,t,i){var n=new la;return n.implementation=this,n.childNodes=new Jr,n.doctype=i||null,i&&n.appendChild(i),t&&(t=n.createElementNS(e,t),n.appendChild(t)),n},createDocumentType:function(e,t,i){var n=new ba;return n.name=e,n.nodeName=e,n.publicId=t||"",n.systemId=i||"",n}},sa.prototype={firstChild:null,lastChild:null,previousSibling:null,nextSibling:null,attributes:null,parentNode:null,childNodes:null,ownerDocument:null,nodeValue:null,namespaceURI:null,prefix:null,localName:null,insertBefore:function(e,t){return pa(this,e,t)},replaceChild:function(e,t){this.insertBefore(e,t),t&&this.removeChild(t)},removeChild:function(e){return ha(this,e)},appendChild:function(e){return this.insertBefore(e,null)},hasChildNodes:function(){return null!=this.firstChild},cloneNode:function(e){return function e(t,i,n){var r=new i.constructor;for(var a in i){var s=i[a];"object"!=typeof s&&s!=r[a]&&(r[a]=s)}i.childNodes&&(r.childNodes=new Jr);r.ownerDocument=t;switch(r.nodeType){case Fr:var o=i.attributes,u=r.attributes=new ta,l=o.length;u._ownerElement=r;for(var c=0;c<l;c++)r.setAttributeNode(e(t,o.item(c),!0));break;case jr:n=!0}if(n)for(var d=i.firstChild;d;)r.appendChild(e(t,d,n)),d=d.nextSibling;return r}(this.ownerDocument||this,this,e)},normalize:function(){for(var e=this.firstChild;e;){var t=e.nextSibling;t&&t.nodeType==Hr&&e.nodeType==Hr?(this.removeChild(t),e.appendData(t.data)):(e.normalize(),e=t)}},isSupported:function(e,t){return this.ownerDocument.implementation.hasFeature(e,t)},hasAttributes:function(){return 0<this.attributes.length},lookupPrefix:function(e){for(var t=this;t;){var i=t._nsMap;if(i)for(var n in i)if(i[n]==e)return n;t=t.nodeType==jr?t.ownerDocument:t.parentNode}return null},lookupNamespaceURI:function(e){for(var t=this;t;){var i=t._nsMap;if(i&&e in i)return i[e];t=t.nodeType==jr?t.ownerDocument:t.parentNode}return null},isDefaultNamespace:function(e){return null==this.lookupPrefix(e)}},Ur(W,sa),Ur(W,sa.prototype),la.prototype={nodeName:"#document",nodeType:zr,doctype:null,documentElement:null,_inc:1,insertBefore:function(e,t){if(e.nodeType!=Kr)return null==this.documentElement&&e.nodeType==Fr&&(this.documentElement=e),pa(this,e,t),e.ownerDocument=this,e;for(var i=e.firstChild;i;){var n=i.nextSibling;this.insertBefore(i,t),i=n}return e},removeChild:function(e){return this.documentElement==e&&(this.documentElement=null),ha(this,e)},importNode:function(e,t){return function e(t,i,n){var r;switch(i.nodeType){case Fr:(r=i.cloneNode(!1)).ownerDocument=t;case Kr:break;case jr:n=!0}r=r||i.cloneNode(!1);r.ownerDocument=t;r.parentNode=null;if(n)for(var a=i.firstChild;a;)r.appendChild(e(t,a,n)),a=a.nextSibling;return r}(this,e,t)},getElementById:function(t){var i=null;return ua(this.documentElement,function(e){if(e.nodeType==Fr&&e.getAttribute("id")==t)return i=e,!0}),i},getElementsByClassName:function(s){var o=Nr(s);return new Zr(this,function(r){var a=[];return 0<o.length&&ua(r.documentElement,function(e){var t,i,n;e===r||e.nodeType!==Fr||(t=e.getAttribute("class"))&&((i=s===t)||(t=Nr(t),i=o.every((n=t,function(e){return n&&-1!==n.indexOf(e)}))),i&&a.push(e))}),a})},createElement:function(e){var t=new fa;return t.ownerDocument=this,t.nodeName=e,t.tagName=e,t.localName=e,t.childNodes=new Jr,(t.attributes=new ta)._ownerElement=t},createDocumentFragment:function(){var e=new Ea;return e.ownerDocument=this,e.childNodes=new Jr,e},createTextNode:function(e){var t=new ya;return t.ownerDocument=this,t.appendData(e),t},createComment:function(e){var t=new va;return t.ownerDocument=this,t.appendData(e),t},createCDATASection:function(e){var t=new _a;return t.ownerDocument=this,t.appendData(e),t},createProcessingInstruction:function(e,t){var i=new ka;return i.ownerDocument=this,i.tagName=i.target=e,i.nodeValue=i.data=t,i},createAttribute:function(e){var t=new ma;return t.ownerDocument=this,t.name=e,t.nodeName=e,t.localName=e,t.specified=!0,t},createEntityReference:function(e){var t=new wa;return t.ownerDocument=this,t.nodeName=e,t},createElementNS:function(e,t){var i=new fa,n=t.split(":"),r=i.attributes=new ta;return i.childNodes=new Jr,i.ownerDocument=this,i.nodeName=t,i.tagName=t,i.namespaceURI=e,2==n.length?(i.prefix=n[0],i.localName=n[1]):i.localName=t,r._ownerElement=i},createAttributeNS:function(e,t){var i=new ma,n=t.split(":");return i.ownerDocument=this,i.nodeName=t,i.name=t,i.namespaceURI=e,i.specified=!0,2==n.length?(i.prefix=n[0],i.localName=n[1]):i.localName=t,i}},Br(la,sa),la.prototype.getElementsByTagName=(fa.prototype={nodeType:Fr,hasAttribute:function(e){return null!=this.getAttributeNode(e)},getAttribute:function(e){e=this.getAttributeNode(e);return e&&e.value||""},getAttributeNode:function(e){return this.attributes.getNamedItem(e)},setAttribute:function(e,t){e=this.ownerDocument.createAttribute(e);e.value=e.nodeValue=""+t,this.setAttributeNode(e)},removeAttribute:function(e){e=this.getAttributeNode(e);e&&this.removeAttributeNode(e)},appendChild:function(e){return e.nodeType===Kr?this.insertBefore(e,null):function(e,t){var i=t.parentNode;i&&(n=e.lastChild,i.removeChild(t),n=e.lastChild);var n=e.lastChild;return t.parentNode=e,t.previousSibling=n,t.nextSibling=null,n?n.nextSibling=t:e.firstChild=t,e.lastChild=t,da(e.ownerDocument,e,t),t}(this,e)},setAttributeNode:function(e){return this.attributes.setNamedItem(e)},setAttributeNodeNS:function(e){return this.attributes.setNamedItemNS(e)},removeAttributeNode:function(e){return this.attributes.removeNamedItem(e.nodeName)},removeAttributeNS:function(e,t){t=this.getAttributeNodeNS(e,t);t&&this.removeAttributeNode(t)},hasAttributeNS:function(e,t){return null!=this.getAttributeNodeNS(e,t)},getAttributeNS:function(e,t){t=this.getAttributeNodeNS(e,t);return t&&t.value||""},setAttributeNS:function(e,t,i){t=this.ownerDocument.createAttributeNS(e,t);t.value=t.nodeValue=""+i,this.setAttributeNode(t)},getAttributeNodeNS:function(e,t){return this.attributes.getNamedItemNS(e,t)},getElementsByTagName:function(n){return new Zr(this,function(t){var i=[];return ua(t,function(e){e===t||e.nodeType!=Fr||"*"!==n&&e.tagName!=n||i.push(e)}),i})},getElementsByTagNameNS:function(n,r){return new Zr(this,function(t){var i=[];return ua(t,function(e){e===t||e.nodeType!==Fr||"*"!==n&&e.namespaceURI!==n||"*"!==r&&e.localName!=r||i.push(e)}),i})}}).getElementsByTagName,la.prototype.getElementsByTagNameNS=fa.prototype.getElementsByTagNameNS,Br(fa,sa),ma.prototype.nodeType=jr,Br(ma,sa),ga.prototype={data:"",substringData:function(e,t){return this.data.substring(e,e+t)},appendData:function(e){e=this.data+e,this.nodeValue=this.data=e,this.length=e.length},insertData:function(e,t){this.replaceData(e,0,t)},appendChild:function(e){throw new Error(Yr[3])},deleteData:function(e,t){this.replaceData(e,t,"")},replaceData:function(e,t,i){var n=this.data.substring(0,e),t=this.data.substring(e+t);this.nodeValue=this.data=i=n+i+t,this.length=i.length}},Br(ga,sa),ya.prototype={nodeName:"#text",nodeType:Hr,splitText:function(e){var t=(i=this.data).substring(e),i=i.substring(0,e);this.data=this.nodeValue=i,this.length=i.length;t=this.ownerDocument.createTextNode(t);return this.parentNode&&this.parentNode.insertBefore(t,this.nextSibling),t}},Br(ya,ga),va.prototype={nodeName:"#comment",nodeType:Gr},Br(va,ga),_a.prototype={nodeName:"#cdata-section",nodeType:qr},Br(_a,ga),ba.prototype.nodeType=Xr,Br(ba,sa),Ta.prototype.nodeType=12,Br(Ta,sa),Sa.prototype.nodeType=6,Br(Sa,sa),wa.prototype.nodeType=Vr,Br(wa,sa),Ea.prototype.nodeName="#document-fragment",Ea.prototype.nodeType=Kr,Br(Ea,sa),ka.prototype.nodeType=Wr,Br(ka,sa),Ca.prototype.serializeToString=function(e,t,i){return Ia.call(e,t,i)},sa.prototype.toString=Ia;try{Object.defineProperty&&(Qr=function e(t){switch(t.nodeType){case Fr:case Kr:var i=[];for(t=t.firstChild;t;)7!==t.nodeType&&8!==t.nodeType&&i.push(e(t)),t=t.nextSibling;return i.join("");default:return t.nodeValue}},Object.defineProperty(Zr.prototype,"length",{get:function(){return ea(this),this.$$length}}),Object.defineProperty(sa.prototype,"textContent",{get:function(){return Qr(this)},set:function(e){switch(this.nodeType){case Fr:case Kr:for(;this.firstChild;)this.removeChild(this.firstChild);(e||String(e))&&this.appendChild(this.ownerDocument.createTextNode(e));break;default:this.data=e,this.value=e,this.nodeValue=e}}}),La=function(e,t,i){e["$$"+t]=i})}catch(e){}var U={DocumentType:ba,DOMException:$r,DOMImplementation:aa,Element:fa,Node:sa,NodeList:Jr,XMLSerializer:Ca},Da=m(function(e,t){var i=Dr.freeze;t.XML_ENTITIES=i({amp:"&",apos:"'",gt:">",lt:"<",quot:'"'}),t.HTML_ENTITIES=i({lt:"<",gt:">",amp:"&",quot:'"',apos:"'",Agrave:"À",Aacute:"Á",Acirc:"Â",Atilde:"Ã",Auml:"Ä",Aring:"Å",AElig:"Æ",Ccedil:"Ç",Egrave:"È",Eacute:"É",Ecirc:"Ê",Euml:"Ë",Igrave:"Ì",Iacute:"Í",Icirc:"Î",Iuml:"Ï",ETH:"Ð",Ntilde:"Ñ",Ograve:"Ò",Oacute:"Ó",Ocirc:"Ô",Otilde:"Õ",Ouml:"Ö",Oslash:"Ø",Ugrave:"Ù",Uacute:"Ú",Ucirc:"Û",Uuml:"Ü",Yacute:"Ý",THORN:"Þ",szlig:"ß",agrave:"à",aacute:"á",acirc:"â",atilde:"ã",auml:"ä",aring:"å",aelig:"æ",ccedil:"ç",egrave:"è",eacute:"é",ecirc:"ê",euml:"ë",igrave:"ì",iacute:"í",icirc:"î",iuml:"ï",eth:"ð",ntilde:"ñ",ograve:"ò",oacute:"ó",ocirc:"ô",otilde:"õ",ouml:"ö",oslash:"ø",ugrave:"ù",uacute:"ú",ucirc:"û",uuml:"ü",yacute:"ý",thorn:"þ",yuml:"ÿ",nbsp:" ",iexcl:"¡",cent:"¢",pound:"£",curren:"¤",yen:"¥",brvbar:"¦",sect:"§",uml:"¨",copy:"©",ordf:"ª",laquo:"«",not:"¬",shy:"­­",reg:"®",macr:"¯",deg:"°",plusmn:"±",sup2:"²",sup3:"³",acute:"´",micro:"µ",para:"¶",middot:"·",cedil:"¸",sup1:"¹",ordm:"º",raquo:"»",frac14:"¼",frac12:"½",frac34:"¾",iquest:"¿",times:"×",divide:"÷",forall:"∀",part:"∂",exist:"∃",empty:"∅",nabla:"∇",isin:"∈",notin:"∉",ni:"∋",prod:"∏",sum:"∑",minus:"−",lowast:"∗",radic:"√",prop:"∝",infin:"∞",ang:"∠",and:"∧",or:"∨",cap:"∩",cup:"∪",int:"∫",there4:"∴",sim:"∼",cong:"≅",asymp:"≈",ne:"≠",equiv:"≡",le:"≤",ge:"≥",sub:"⊂",sup:"⊃",nsub:"⊄",sube:"⊆",supe:"⊇",oplus:"⊕",otimes:"⊗",perp:"⊥",sdot:"⋅",Alpha:"Α",Beta:"Β",Gamma:"Γ",Delta:"Δ",Epsilon:"Ε",Zeta:"Ζ",Eta:"Η",Theta:"Θ",Iota:"Ι",Kappa:"Κ",Lambda:"Λ",Mu:"Μ",Nu:"Ν",Xi:"Ξ",Omicron:"Ο",Pi:"Π",Rho:"Ρ",Sigma:"Σ",Tau:"Τ",Upsilon:"Υ",Phi:"Φ",Chi:"Χ",Psi:"Ψ",Omega:"Ω",alpha:"α",beta:"β",gamma:"γ",delta:"δ",epsilon:"ε",zeta:"ζ",eta:"η",theta:"θ",iota:"ι",kappa:"κ",lambda:"λ",mu:"μ",nu:"ν",xi:"ξ",omicron:"ο",pi:"π",rho:"ρ",sigmaf:"ς",sigma:"σ",tau:"τ",upsilon:"υ",phi:"φ",chi:"χ",psi:"ψ",omega:"ω",thetasym:"ϑ",upsih:"ϒ",piv:"ϖ",OElig:"Œ",oelig:"œ",Scaron:"Š",scaron:"š",Yuml:"Ÿ",fnof:"ƒ",circ:"ˆ",tilde:"˜",ensp:" ",emsp:" ",thinsp:" ",zwnj:"‌",zwj:"‍",lrm:"‎",rlm:"‏",ndash:"–",mdash:"—",lsquo:"‘",rsquo:"’",sbquo:"‚",ldquo:"“",rdquo:"”",bdquo:"„",dagger:"†",Dagger:"‡",bull:"•",hellip:"…",permil:"‰",prime:"′",Prime:"″",lsaquo:"‹",rsaquo:"›",oline:"‾",euro:"€",trade:"™",larr:"←",uarr:"↑",rarr:"→",darr:"↓",harr:"↔",crarr:"↵",lceil:"⌈",rceil:"⌉",lfloor:"⌊",rfloor:"⌋",loz:"◊",spades:"♠",clubs:"♣",hearts:"♥",diams:"♦"}),t.entityMap=t.HTML_ENTITIES});Da.XML_ENTITIES,Da.HTML_ENTITIES,Da.entityMap;var Oa=Dr.NAMESPACE,zt=/[A-Z_a-z\xC0-\xD6\xD8-\xF6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]/,ar=new RegExp("[\\-\\.0-9"+zt.source.slice(1,-1)+"\\u00B7\\u0300-\\u036F\\u203F-\\u2040]"),Ra=new RegExp("^"+zt.source+ar.source+"*(?::"+zt.source+ar.source+"*)?$"),Ma=0,Na=1,Ua=2,Ba=3,Fa=4,ja=5,Ha=6,qa=7;function Va(e,t){this.message=e,this.locator=t,Error.captureStackTrace&&Error.captureStackTrace(this,Va)}function Wa(){}function Ga(e,t){return t.lineNumber=e.lineNumber,t.columnNumber=e.columnNumber,t}function za(e,t,i){for(var n=e.tagName,r=null,a=e.length;a--;){var s=e[a],o=s.qName,u=s.value,o=0<(c=o.indexOf(":"))?(l=s.prefix=o.slice(0,c),d=o.slice(c+1),"xmlns"===l&&d):(l=null,"xmlns"===(d=o)&&"");s.localName=d,!1!==o&&(null==r&&(r={},Xa(i,i={})),i[o]=r[o]=u,s.uri=Oa.XMLNS,t.startPrefixMapping(o,u))}for(var l,a=e.length;a--;)(l=(s=e[a]).prefix)&&("xml"===l&&(s.uri=Oa.XML),"xmlns"!==l&&(s.uri=i[l||""]));var c,d=0<(c=n.indexOf(":"))?(l=e.prefix=n.slice(0,c),e.localName=n.slice(c+1)):(l=null,e.localName=n),h=e.uri=i[l||""];if(t.startElement(h,d,n,e),!e.closed)return e.currentNSMap=i,e.localNSMap=r,1;if(t.endElement(h,d,n),r)for(l in r)t.endPrefixMapping(l)}function Xa(e,t){for(var i in e)t[i]=e[i]}function Ka(){this.attributeNames={}}(Va.prototype=new Error).name=Va.name,Wa.prototype={parse:function(e,t,i){var n=this.domBuilder;n.startDocument(),Xa(t,t={}),function(i,e,n,r,a){function s(e){var t=e.slice(1,-1);return t in n?n[t]:"#"===t.charAt(0)?65535<(t=parseInt(t.substr(1).replace("x","0x")))?(t-=65536,String.fromCharCode(55296+(t>>10),56320+(1023&t))):String.fromCharCode(t):(a.error("entity not found:"+e),e)}function t(e){var t;f<e&&(t=i.substring(f,e).replace(/&#?\w+;/g,s),d&&o(f),r.characters(t,0,e-f),f=e)}function o(e,t){for(;l<=e&&(t=c.exec(i));)u=t.index,l=u+t[0].length,d.lineNumber++;d.columnNumber=e-u+1}var u=0,l=0,c=/.*(?:\r\n?|\n)|.*$/g,d=r.locator,h=[{currentNSMap:e}],p={},f=0;for(;;){try{var m,g,y=i.indexOf("<",f);if(y<0)return i.substr(f).match(/^\s*$/)||(m=r.doc,g=m.createTextNode(i.substr(f)),m.appendChild(g),r.currentElement=g);switch(f<y&&t(y),i.charAt(y+1)){case"/":var v=i.indexOf(">",y+3),_=i.substring(y+2,v).replace(/[ \t\n\r]+$/g,""),b=h.pop();v<0?(_=i.substring(y+2).replace(/[\s<].*/,""),a.error("end tag name: "+_+" is not complete:"+b.tagName),v=y+1+_.length):_.match(/\s</)&&(_=_.replace(/[\s<].*/,""),a.error("end tag name: "+_+" maybe not complete"),v=y+1+_.length);var T=b.localNSMap,S=b.tagName==_;if(S||b.tagName&&b.tagName.toLowerCase()==_.toLowerCase()){if(r.endElement(b.uri,b.localName,_),T)for(var w in T)r.endPrefixMapping(w);S||a.fatalError("end tag name: "+_+" is not match the current start tagName:"+b.tagName)}else h.push(b);v++;break;case"?":d&&o(y),v=function(e,t,i){var n=e.indexOf("?>",t);if(n){t=e.substring(t,n).match(/^<\?(\S*)\s*([\s\S]*?)\s*$/);return t?(t[0].length,i.processingInstruction(t[1],t[2]),n+2):-1}return-1}(i,y,r);break;case"!":d&&o(y),v=function(e,t,i,n){{if("-"===e.charAt(t+2)){if("-"!==e.charAt(t+3))return-1;var r=e.indexOf("--\x3e",t+4);return t<r?(i.comment(e,t+4,r-t-4),r+3):(n.error("Unclosed comment"),-1)}if("CDATA["==e.substr(t+3,6)){r=e.indexOf("]]>",t+9);return i.startCDATA(),i.characters(e,t+9,r-t-9),i.endCDATA(),r+3}var a=function(e,t){var i,n=[],r=/'[^']+'|"[^"]+"|[^\s<>\/=]+=?|(\/?\s*>|<)/g;r.lastIndex=t,r.exec(e);for(;i=r.exec(e);)if(n.push(i),i[1])return n}(e,t),n=a.length;if(1<n&&/!doctype/i.test(a[0][0])){r=a[1][0],e=!1,t=!1;3<n&&(/^public$/i.test(a[2][0])?(e=a[3][0],t=4<n&&a[4][0]):/^system$/i.test(a[2][0])&&(t=a[3][0]));n=a[n-1];return i.startDTD(r,e,t),i.endDTD(),n.index+n[0].length}}return-1}(i,y,r,a);break;default:d&&o(y);var E=new Ka,k=h[h.length-1].currentNSMap,v=function(e,t,n,i,r,a){function s(e,t,i){n.attributeNames.hasOwnProperty(e)&&a.fatalError("Attribute "+e+" redefined"),n.addValue(e,t,i)}var o,u=++t,l=Ma;for(;;){var c=e.charAt(u);switch(c){case"=":if(l===Na)o=e.slice(t,u),l=Ba;else{if(l!==Ua)throw new Error("attribute equal must after attrName");l=Ba}break;case"'":case'"':if(l===Ba||l===Na){if(l===Na&&(a.warning('attribute value must after "="'),o=e.slice(t,u)),t=u+1,!(0<(u=e.indexOf(c,t))))throw new Error("attribute value no end '"+c+"' match");d=e.slice(t,u).replace(/&#?\w+;/g,r),s(o,d,t-1),l=ja}else{if(l!=Fa)throw new Error('attribute value must after "="');d=e.slice(t,u).replace(/&#?\w+;/g,r),s(o,d,t),a.warning('attribute "'+o+'" missed start quot('+c+")!!"),t=u+1,l=ja}break;case"/":switch(l){case Ma:n.setTagName(e.slice(t,u));case ja:case Ha:case qa:l=qa,n.closed=!0;case Fa:case Na:case Ua:break;default:throw new Error("attribute invalid close char('/')")}break;case"":return a.error("unexpected end of input"),l==Ma&&n.setTagName(e.slice(t,u)),u;case">":switch(l){case Ma:n.setTagName(e.slice(t,u));case ja:case Ha:case qa:break;case Fa:case Na:"/"===(d=e.slice(t,u)).slice(-1)&&(n.closed=!0,d=d.slice(0,-1));case Ua:l===Ua&&(d=o),l==Fa?(a.warning('attribute "'+d+'" missed quot(")!'),s(o,d.replace(/&#?\w+;/g,r),t)):(Oa.isHTML(i[""])&&d.match(/^(?:disabled|checked|selected)$/i)||a.warning('attribute "'+d+'" missed value!! "'+d+'" instead!!'),s(d,d,t));break;case Ba:throw new Error("attribute value missed!!")}return u;case"":c=" ";default:if(c<=" ")switch(l){case Ma:n.setTagName(e.slice(t,u)),l=Ha;break;case Na:o=e.slice(t,u),l=Ua;break;case Fa:var d=e.slice(t,u).replace(/&#?\w+;/g,r);a.warning('attribute "'+d+'" missed quot(")!!'),s(o,d,t);case ja:l=Ha}else switch(l){case Ua:n.tagName,Oa.isHTML(i[""])&&o.match(/^(?:disabled|checked|selected)$/i)||a.warning('attribute "'+o+'" missed value!! "'+o+'" instead2!!'),s(o,o,t),t=u,l=Na;break;case ja:a.warning('attribute space is required"'+o+'"!!');case Ha:l=Na,t=u;break;case Ba:l=Fa,t=u;break;case qa:throw new Error("elements closed character '/' and '>' must be connected to")}}u++}}(i,y,E,k,s,a),C=E.length;if(!E.closed&&function(e,t,i,n){var r=n[i];null==r&&((r=e.lastIndexOf("</"+i+">"))<t&&(r=e.lastIndexOf("</"+i)),n[i]=r);return r<t}(i,v,E.tagName,p)&&(E.closed=!0,n.nbsp||a.warning("unclosed xml attribute")),d&&C){for(var I=Ga(d,{}),x=0;x<C;x++){var A=E[x];o(A.offset),A.locator=Ga(d,{})}r.locator=I,za(E,r,k)&&h.push(E),r.locator=d}else za(E,r,k)&&h.push(E);Oa.isHTML(E.uri)&&!E.closed?v=function(e,t,i,n,r){if(/^(?:script|textarea)$/i.test(i)){var a=e.indexOf("</"+i+">",t),e=e.substring(t+1,a);if(/[&<]/.test(e))return/^script$/i.test(i)||(e=e.replace(/&#?\w+;/g,n)),r.characters(e,0,e.length),a}return t+1}(i,v,E.tagName,s,r):v++}}catch(e){if(e instanceof Va)throw e;a.error("element parse error: "+e),v=-1}f<v?f=v:t(Math.max(y,f)+1)}}(e,t,i,n,this.errorHandler),n.endDocument()}},Ka.prototype={setTagName:function(e){if(!Ra.test(e))throw new Error("invalid tagName:"+e);this.tagName=e},addValue:function(e,t,i){if(!Ra.test(e))throw new Error("invalid attribute:"+e);this.attributeNames[e]=this.length,this[this.length++]={qName:e,value:t,offset:i}},length:0,getLocalName:function(e){return this[e].localName},getLocator:function(e){return this[e].locator},getQName:function(e){return this[e].qName},getURI:function(e){return this[e].uri},getValue:function(e){return this[e].value}};var x={XMLReader:Wa,ParseError:Va},Ya=U.DOMImplementation,Qa=Dr.NAMESPACE,$a=x.ParseError,Ja=x.XMLReader;function Za(e){this.options=e||{locator:{}}}function es(){this.cdata=!1}function ts(e,t){t.lineNumber=e.lineNumber,t.columnNumber=e.columnNumber}function is(e){if(e)return"\n@"+(e.systemId||"")+"#[line:"+e.lineNumber+",col:"+e.columnNumber+"]"}function ns(e,t,i){return"string"==typeof e?e.substr(t,i):e.length>=t+i||t?new java.lang.String(e,t,i)+"":e}function rs(e,t){(e.currentElement||e.doc).appendChild(t)}Za.prototype.parseFromString=function(e,t){var i=this.options,n=new Ja,r=i.domBuilder||new es,a=i.errorHandler,s=i.locator,o=i.xmlns||{},u=/\/x?html?$/.test(t),t=u?Da.HTML_ENTITIES:Da.XML_ENTITIES;return s&&r.setDocumentLocator(s),n.errorHandler=function(n,e,r){if(!n){if(e instanceof es)return e;n=e}var a={},s=n instanceof Function;function t(t){var i=n[t];!i&&s&&(i=2==n.length?function(e){n(t,e)}:n),a[t]=i?function(e){i("[xmldom "+t+"]\t"+e+is(r))}:function(){}}return r=r||{},t("warning"),t("error"),t("fatalError"),a}(a,r,s),n.domBuilder=i.domBuilder||r,u&&(o[""]=Qa.HTML),o.xml=o.xml||Qa.XML,e&&"string"==typeof e?n.parse(e,o,t):n.errorHandler.error("invalid doc source"),r.doc},es.prototype={startDocument:function(){this.doc=(new Ya).createDocument(null,null,null),this.locator&&(this.doc.documentURI=this.locator.systemId)},startElement:function(e,t,i,n){var r=this.doc,a=r.createElementNS(e,i||t),s=n.length;rs(this,a),this.currentElement=a,this.locator&&ts(this.locator,a);for(var o=0;o<s;o++){var e=n.getURI(o),u=n.getValue(o),i=n.getQName(o),l=r.createAttributeNS(e,i);this.locator&&ts(n.getLocator(o),l),l.value=l.nodeValue=u,a.setAttributeNode(l)}},endElement:function(e,t,i){var n=this.currentElement;n.tagName,this.currentElement=n.parentNode},startPrefixMapping:function(e,t){},endPrefixMapping:function(e){},processingInstruction:function(e,t){t=this.doc.createProcessingInstruction(e,t);this.locator&&ts(this.locator,t),rs(this,t)},ignorableWhitespace:function(e,t,i){},characters:function(e,t,i){var n;(e=ns.apply(this,arguments))&&(n=this.cdata?this.doc.createCDATASection(e):this.doc.createTextNode(e),this.currentElement?this.currentElement.appendChild(n):/^\s*$/.test(e)&&this.doc.appendChild(n),this.locator&&ts(this.locator,n))},skippedEntity:function(e){},endDocument:function(){this.doc.normalize()},setDocumentLocator:function(e){(this.locator=e)&&(e.lineNumber=0)},comment:function(e,t,i){e=ns.apply(this,arguments);e=this.doc.createComment(e);this.locator&&ts(this.locator,e),rs(this,e)},startCDATA:function(){this.cdata=!0},endCDATA:function(){this.cdata=!1},startDTD:function(e,t,i){var n=this.doc.implementation;n&&n.createDocumentType&&(i=n.createDocumentType(e,t,i),this.locator&&ts(this.locator,i),rs(this,i),this.doc.doctype=i)},warning:function(e){},error:function(e){},fatalError:function(e){throw new $a(e,this.locator)}},"endDTD,startEntity,endEntity,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,resolveEntity,getExternalSubset,notationDecl,unparsedEntityDecl".replace(/\w+/g,function(e){es.prototype[e]=function(){return null}});function as(e){return!!e&&"object"==typeof e}function ss(){for(var e=arguments.length,t=new Array(e),i=0;i<e;i++)t[i]=arguments[i];return t.reduce(function(t,i){return"object"!=typeof i||Object.keys(i).forEach(function(e){Array.isArray(t[e])&&Array.isArray(i[e])?t[e]=t[e].concat(i[e]):as(t[e])&&as(i[e])?t[e]=ss(t[e],i[e]):t[e]=i[e]}),t},{})}function os(t){return Object.keys(t).map(function(e){return t[e]})}function us(e){return e.reduce(function(e,t){return e.concat(t)},[])}function ls(e){if(!e.length)return[];for(var t=[],i=0;i<e.length;i++)t.push(e[i]);return t}function cs(e,t){for(var i=0;i<e.length;i++)if(t(e[i]))return i;return-1}function ds(e){var t=e.baseUrl,i=void 0===(n=e.source)?"":n,n=void 0===(n=e.range)?"":n,e=void 0===(e=e.indexRange)?"":e,i={uri:i,resolvedUri:ir((void 0===t?"":t)||"",i)};return(n||e)&&(n=(n||e).split("-"),e=window.BigInt?window.BigInt(n[0]):parseInt(n[0],10),n=window.BigInt?window.BigInt(n[1]):parseInt(n[1],10),e<Number.MAX_SAFE_INTEGER&&"bigint"==typeof e&&(e=Number(e)),"bigint"==typeof(n="bigint"==typeof(n=n<Number.MAX_SAFE_INTEGER&&"bigint"==typeof n?Number(n):n)||"bigint"==typeof e?window.BigInt(n)-window.BigInt(e)+window.BigInt(1):n-e+1)&&n<Number.MAX_SAFE_INTEGER&&(n=Number(n)),i.byterange={length:n,offset:e}),i}function hs(e){return e&&"number"!=typeof e&&(e=parseInt(e,10)),isNaN(e)?null:e}function ps(e){var a,t=e.type,i=e.duration,n=e.timescale,r=void 0===n?1:n,s=e.periodDuration,o=e.sourceDuration,e=function(e,t){for(var i=[],n=e;n<t;n++)i.push(n);return i}((n=ho[t](e)).start,n.end).map((a=e,function(e){var t=a.duration,i=a.timescale,n=a.periodStart,r=a.startNumber;return{number:(void 0===r?1:r)+e,duration:t/(void 0===i?1:i),timeline:n,time:e*t}}));return"static"===t&&(e[t=e.length-1].duration=("number"==typeof s?s:o)-i/r*t),e}function fs(e){var t=e.baseUrl,i=void 0===(u=e.initialization)?{}:u,n=e.sourceDuration,r=void 0===(o=e.indexRange)?"":o,a=e.periodStart,s=e.presentationTime,o=void 0===(u=e.number)?0:u,u=e.duration;if(!t)throw new Error(uo);return i=ds({baseUrl:t,source:i.sourceURL,range:i.range}),(r=ds({baseUrl:t,source:t,indexRange:r})).map=i,u?(e=ps(e)).length&&(r.duration=e[0].duration,r.timeline=e[0].timeline):n&&(r.duration=n,r.timeline=a),r.presentationTime=s||a,r.number=o,[r]}function ms(e,t,i){for(var n=e.sidx.map||null,r=e.sidx.duration,a=e.timeline||0,s=(s=e.sidx.byterange).offset+s.length,o=t.timescale,u=t.references.filter(function(e){return 1!==e.referenceType}),l=[],c=e.endList?"static":"dynamic",d=e.sidx.timeline,h=d,p=e.mediaSequence||0,f="bigint"==typeof t.firstOffset?window.BigInt(s)+t.firstOffset:s+t.firstOffset,m=0;m<u.length;m++){var g=t.references[m],y=g.referencedSize,v=g.subsegmentDuration,g=void 0,g="bigint"==typeof f?f+window.BigInt(y)-window.BigInt(1):f+y-1,g=fs({baseUrl:i,timescale:o,timeline:a,periodStart:d,presentationTime:h,number:p,duration:v,sourceDuration:r,indexRange:f+"-"+g,type:c})[0];n&&(g.map=n),l.push(g),f+="bigint"==typeof f?window.BigInt(y):y,h+=v/o,p++}return e.segments=l,e}function gs(e){return i=function(e){return e.timeline},os(e.reduce(function(t,e){return e.forEach(function(e){t[i(e)]=e}),t},{})).sort(function(e,t){return e.timeline>t.timeline?1:-1});var i}function ys(e){var r,a,s=[];return r=e,a=function(e,t,i,n){s=s.concat(e.playlists||[])},po.forEach(function(e){for(var t in r.mediaGroups[e])for(var i in r.mediaGroups[e][t]){var n=r.mediaGroups[e][t][i];a(n,e,t,i)}}),s}function vs(e){var i=e.playlist,e=e.mediaSequence;i.mediaSequence=e,i.segments.forEach(function(e,t){e.number=i.mediaSequence+t})}function _s(e){var r,a,t=e.oldManifest,i=e.newManifest,n=t.playlists.concat(ys(t)),e=i.playlists.concat(ys(i));return i.timelineStarts=gs([t.timelineStarts,i.timelineStarts]),n={oldPlaylists:n,newPlaylists:e,timelineStarts:i.timelineStarts},r=n.oldPlaylists,e=n.newPlaylists,a=n.timelineStarts,e.forEach(function(t){t.discontinuitySequence=cs(a,function(e){return e.timeline===t.timeline});var e=function(e,t){for(var i=0;i<e.length;i++)if(e[i].attributes.NAME===t)return e[i];return null}(r,t.attributes.NAME);if(e&&!t.sidx){var i=t.segments[0],n=cs(e.segments,function(e){return Math.abs(e.presentationTime-i.presentationTime)<1/60});if(-1===n)return vs({playlist:t,mediaSequence:e.mediaSequence+e.segments.length}),t.segments[0].discontinuity=!0,t.discontinuityStarts.unshift(0),void((!e.segments.length&&t.timeline>e.timeline||e.segments.length&&t.timeline>e.segments[e.segments.length-1].timeline)&&t.discontinuitySequence--);e.segments[n].discontinuity&&!i.discontinuity&&(i.discontinuity=!0,t.discontinuityStarts.unshift(0),t.discontinuitySequence--),vs({playlist:t,mediaSequence:e.segments[n].number})}}),i}function bs(e){return e&&e.uri+"-"+(t=e.byterange,e="bigint"==typeof t.offset||"bigint"==typeof t.length?window.BigInt(t.offset)+window.BigInt(t.length)-window.BigInt(1):t.offset+t.length-1,t.offset+"-"+e);var t}function Ts(e){return os(e.reduce(function(e,t){var i,n=t.attributes.id+(t.attributes.lang||"");return e[n]?(t.segments&&(t.segments[0]&&(t.segments[0].discontinuity=!0),(i=e[n].segments).push.apply(i,t.segments)),t.attributes.contentProtection&&(e[n].attributes.contentProtection=t.attributes.contentProtection)):(e[n]=t,e[n].attributes.timelineStarts=[]),e[n].attributes.timelineStarts.push({start:t.attributes.periodStart,timeline:t.attributes.periodStart}),e},{})).map(function(e){var t,n;return e.discontinuityStarts=(t=e.segments||[],n="discontinuity",t.reduce(function(e,t,i){return t[n]&&e.push(i),e},[])),e})}function Ss(e,t){var i=bs(e.sidx);return(i=i&&t[i]&&t[i].sidx)&&ms(e,i,e.sidx.resolvedUri),e}function ws(e,h,p){var f;return void 0===h&&(h={}),void 0===p&&(p=!1),e=e.reduce(function(e,t){var i=t.attributes.role&&t.attributes.role.value||"",n=t.attributes.lang||"",r=t.attributes.label||"main";e[r=n&&!t.attributes.label?t.attributes.lang+(i?" ("+i+")":""):r]||(e[r]={language:n,autoselect:!0,default:"main"===i,playlists:[],uri:""});var a,s,o,u,l,c,d,u=Ss((s=p,o=(a=t).attributes,u=a.segments,l=a.sidx,c=a.mediaSequence,d=a.discontinuitySequence,n=a.discontinuityStarts,u={attributes:((a={NAME:o.id,BANDWIDTH:o.bandwidth,CODECS:o.codecs})["PROGRAM-ID"]=1,a),uri:"",endList:"static"===o.type,timeline:o.periodStart,resolvedUri:"",targetDuration:o.duration,discontinuitySequence:d,discontinuityStarts:n,timelineStarts:o.timelineStarts,mediaSequence:c,segments:u},o.contentProtection&&(u.contentProtection=o.contentProtection),l&&(u.sidx=l),s&&(u.attributes.AUDIO="audio",u.attributes.SUBTITLES="subs"),u),h);return e[r].playlists.push(u),"undefined"==typeof f&&"main"===i&&((f=t).default=!0),e},{}),f||(e[Object.keys(e)[0]].default=!0),e}function Es(e){var t=e.attributes,i=e.segments,n=e.sidx,r=e.discontinuityStarts,i={attributes:((e={NAME:t.id,AUDIO:"audio",SUBTITLES:"subs",RESOLUTION:{width:t.width,height:t.height},CODECS:t.codecs,BANDWIDTH:t.bandwidth})["PROGRAM-ID"]=1,e),uri:"",endList:"static"===t.type,timeline:t.periodStart,resolvedUri:"",targetDuration:t.duration,discontinuityStarts:r,timelineStarts:t.timelineStarts,segments:i};return t.contentProtection&&(i.contentProtection=t.contentProtection),n&&(i.sidx=n),i}function ks(e){return"video/mp4"===(e=e.attributes).mimeType||"video/webm"===e.mimeType||"video"===e.contentType}function Cs(e){return"audio/mp4"===(e=e.attributes).mimeType||"audio/webm"===e.mimeType||"audio"===e.contentType}function Is(e){return"text/vtt"===(e=e.attributes).mimeType||"text"===e.contentType}function xs(i){return i?Object.keys(i).reduce(function(e,t){t=i[t];return e.concat(t.playlists)},[]):[]}function As(e){var t=e.dashPlaylists,i=e.locations,n=void 0===(c=e.sidxMapping)?{}:c,r=e.previousManifest;if(!t.length)return{};var a=(d=t[0].attributes).sourceDuration,s=d.type,o=d.suggestedPresentationDelay,u=d.minimumUpdatePeriod,l=Ts(t.filter(ks)).map(Es),c=Ts(t.filter(Cs)),e=Ts(t.filter(Is)),d=t.map(function(e){return e.attributes.captionServices}).filter(Boolean),a={allowCache:!0,discontinuityStarts:[],segments:[],endList:!0,mediaGroups:((t={AUDIO:{},VIDEO:{}})["CLOSED-CAPTIONS"]={},t.SUBTITLES={},t),uri:"",duration:a,playlists:function(e,t){if(void 0===t&&(t={}),!Object.keys(t).length)return e;for(var i in e)e[i]=Ss(e[i],t);return e}(l,n)};0<=u&&(a.minimumUpdatePeriod=1e3*u),i&&(a.locations=i),"dynamic"===s&&(a.suggestedPresentationDelay=o);var h,p,o=0===a.playlists.length,o=c.length?ws(c,n,o):null,n=e.length?(void 0===(h=n)&&(h={}),e.reduce(function(e,t){var i=t.attributes.lang||"text";return e[i]||(e[i]={language:i,default:!1,autoselect:!1,playlists:[],uri:""}),e[i].playlists.push(Ss(function(e){var t=e.attributes,i=e.segments,n=e.mediaSequence,r=e.discontinuityStarts,a=e.discontinuitySequence;"undefined"==typeof i&&(i=[{uri:t.baseUrl,timeline:t.periodStart,resolvedUri:t.baseUrl||"",duration:t.sourceDuration,number:0}],t.duration=t.sourceDuration);(e={NAME:t.id,BANDWIDTH:t.bandwidth})["PROGRAM-ID"]=1;return t.codecs&&(e.CODECS=t.codecs),{attributes:e,uri:"",endList:"static"===t.type,timeline:t.periodStart,resolvedUri:t.baseUrl||"",targetDuration:t.duration,timelineStarts:t.timelineStarts,discontinuityStarts:r,discontinuitySequence:a,mediaSequence:n,segments:i}}(t),h)),e},{})):null,l=(e=l.concat(xs(o),xs(n))).map(function(e){return e.timelineStarts});return a.timelineStarts=gs(l),e=e,p=a.timelineStarts,e.forEach(function(t){t.mediaSequence=0,t.discontinuitySequence=cs(p,function(e){return e.timeline===t.timeline}),t.segments&&t.segments.forEach(function(e,t){e.number=t})}),o&&(a.mediaGroups.AUDIO.audio=o),n&&(a.mediaGroups.SUBTITLES.subs=n),d.length&&(a.mediaGroups["CLOSED-CAPTIONS"].cc=d.reduce(function(n,e){return e&&e.forEach(function(e){var t=e.channel,i=e.language;n[i]={autoselect:!1,default:!1,instreamId:t,language:i},e.hasOwnProperty("aspectRatio")&&(n[i].aspectRatio=e.aspectRatio),e.hasOwnProperty("easyReader")&&(n[i].easyReader=e.easyReader),e.hasOwnProperty("3D")&&(n[i]["3D"]=e["3D"])}),n},{})),r?_s({oldManifest:r,newManifest:a}):a}function Ps(e,t){for(var i,n,r,a,s,o,u=e.type,l=e.minimumUpdatePeriod,c=void 0===l?0:l,d=void 0===(l=e.media)?"":l,h=e.sourceDuration,p=void 0===(l=e.timescale)?1:l,f=void 0===(l=e.startNumber)?1:l,m=e.periodStart,g=[],y=-1,v=0;v<t.length;v++){var _=t[v],b=_.d,T=_.r||0,S=_.t||0;y<0&&(y=S),S&&y<S&&(y=S);var w,E=void 0;E=T<0?(w=v+1)===t.length?"dynamic"===u&&0<c&&0<d.indexOf("$Number$")?(i=y,n=b,_=o=s=a=r=void 0,r=(S=e).NOW,a=S.clientOffset,s=S.availabilityStartTime,o=S.timescale,_=S.periodStart,S=S.minimumUpdatePeriod,Math.ceil((((r+a)/1e3+(void 0===S?0:S)-(s+(void 0===_?0:_)))*(void 0===o?1:o)-i)/n)):(h*p-y)/b:(t[w].t-y)/b:T+1;for(var k=f+g.length+E,C=f+g.length;C<k;)g.push({number:C,duration:b/p,time:y,timeline:m}),y+=b,C++}return g}function Ls(e,t){return e.replace(fo,(r=t,function(e,t,i,n){if("$$"===e)return"$";if("undefined"==typeof r[t])return e;e=""+r[t];return"RepresentationID"===t||(n=i?parseInt(n,10):1)<=e.length?e:new Array(n-e.length+1).join("0")+e}));var r}function Ds(r,e){var a={RepresentationID:r.id,Bandwidth:r.bandwidth||0},t=void 0===(t=r.initialization)?{sourceURL:"",range:""}:t,s=ds({baseUrl:r.baseUrl,source:Ls(t.sourceURL,a),range:t.range});return(t=e,(e=r).duration||t?e.duration?ps(e):Ps(e,t):[{number:e.startNumber||1,duration:e.sourceDuration,time:0,timeline:e.periodStart}]).map(function(e){a.Number=e.number,a.Time=e.time;var t=Ls(r.media||"",a),i=r.timescale||1,n=r.presentationTimeOffset||0,i=r.periodStart+(e.time-n)/i;return{uri:t,timeline:e.timeline,duration:e.duration,resolvedUri:ir(r.baseUrl||"",t),map:s,number:e.number,presentationTime:i}})}function Os(r,e){var t=r.duration,i=void 0===(i=r.segmentUrls)?[]:i,a=r.periodStart;if(!t&&!e||t&&e)throw new Error(lo);var n,s=i.map(function(e){return i=e,e=(t=r).baseUrl,t=t.initialization,t=ds({baseUrl:e,source:(t=void 0===t?{}:t).sourceURL,range:t.range}),(i=ds({baseUrl:e,source:i.media,range:i.mediaRange})).map=t,i;var t,i});return t&&(n=ps(r)),(n=e?Ps(r,e):n).map(function(e,t){if(s[t]){var i=s[t],n=r.timescale||1,t=r.presentationTimeOffset||0;return i.timeline=e.timeline,i.duration=e.duration,i.number=e.number,i.presentationTime=a+(e.time-t)/n,i}}).filter(function(e){return e})}function Rs(e){var t,i=e.attributes,n=e.segmentInfo;n.template?(a=Ds,t=ss(i,n.template)):n.base?(a=fs,t=ss(i,n.base)):n.list&&(a=Os,t=ss(i,n.list));var r={attributes:i};if(!a)return r;var a,e=a(t,n.segmentTimeline);return t.duration?(i=t.duration,a=t.timescale,t.duration=i/(void 0===a?1:a)):e.length?t.duration=e.reduce(function(e,t){return Math.max(e,Math.ceil(t.duration))},0):t.duration=0,r.attributes=t,r.segments=e,n.base&&t.indexRange&&(r.sidx=e[0],r.segments=[]),r}function Ms(e,t){return ls(e.childNodes).filter(function(e){return e.tagName===t})}function Ns(e){return e.textContent.trim()}function Us(e){if(!(r=/P(?:(\d*)Y)?(?:(\d*)M)?(?:(\d*)D)?(?:T(?:(\d*)H)?(?:(\d*)M)?(?:([\d.]*)S)?)?/.exec(e)))return 0;var t=(a=r.slice(1))[0],i=a[1],n=a[2],e=a[3],r=a[4],a=a[5];return 31536e3*parseFloat(t||0)+2592e3*parseFloat(i||0)+86400*parseFloat(n||0)+3600*parseFloat(e||0)+60*parseFloat(r||0)+parseFloat(a||0)}function Bs(e){return e&&e.attributes?ls(e.attributes).reduce(function(e,t){var i=mo[t.name]||mo.DEFAULT;return e[t.name]=i(t.value),e},{}):{}}function Fs(e,i){return i.length?us(e.map(function(t){return i.map(function(e){return ir(t,Ns(e))})})):e}function js(e){var t=Ms(e,"SegmentTemplate")[0],i=Ms(e,"SegmentList")[0],n=i&&Ms(i,"SegmentURL").map(function(e){return ss({tag:"SegmentURL"},Bs(e))}),r=Ms(e,"SegmentBase")[0],e=(a=i||t)&&Ms(a,"SegmentTimeline")[0],a=(a=i||r||t)&&Ms(a,"Initialization")[0];(t=t&&Bs(t))&&a?t.initialization=a&&Bs(a):t&&t.initialization&&(t.initialization={sourceURL:t.initialization});var s={template:t,segmentTimeline:e&&Ms(e,"S").map(Bs),list:i&&ss(Bs(i),{segmentUrls:n,initialization:Bs(a)}),base:r&&ss(Bs(r),{initialization:Bs(a)})};return Object.keys(s).forEach(function(e){s[e]||delete s[e]}),s}function Hs(u,l,c){return function(e){var t=Bs(e),i=Fs(l,Ms(e,"BaseURL")),n=Ms(e,"Role")[0],n={role:Bs(n)},t=ss(u,t,n),n=Ms(e,"Accessibility")[0],n="urn:scte:dash:cc:cea-608:2015"===(n=Bs(n)).schemeIdUri?("string"!=typeof n.value?[]:n.value.split(";")).map(function(e){var t,i,n;return/^CC\d=/.test(n=e)?(i=(t=e.split("="))[0],n=t[1]):/^CC\d$/.test(e)&&(i=e),{channel:i,language:n}}):"urn:scte:dash:cc:cea-708:2015"===n.schemeIdUri?("string"!=typeof n.value?[]:n.value.split(";")).map(function(e){var t,i,n={channel:void 0,language:void 0,aspectRatio:1,easyReader:0,"3D":0};return/=/.test(e)?(t=(i=e.split("="))[0],i=void 0===(i=i[1])?"":i,n.channel=t,n.language=e,i.split(",").forEach(function(e){var t=e.split(":"),e=t[0],t=t[1];"lang"===e?n.language=t:"er"===e?n.easyReader=Number(t):"war"===e?n.aspectRatio=Number(t):"3D"===e&&(n["3D"]=Number(t))})):n.language=e,n.channel&&(n.channel="SERVICE"+n.channel),n}):void 0;n&&(t=ss(t,{captionServices:n}));n=Ms(e,"Label")[0];n&&n.childNodes.length&&(r=n.childNodes[0].nodeValue.trim(),t=ss(t,{label:r}));var r=Ms(e,"ContentProtection").reduce(function(e,t){var i=Bs(t),n=go[i.schemeIdUri];return n&&(e[n]={attributes:i},(t=Ms(t,"cenc:pssh")[0])&&(t=(t=Ns(t))&&or(t),e[n].pssh=t)),e},{});Object.keys(r).length&&(t=ss(t,{contentProtection:r}));var a,s,o,r=js(e),e=Ms(e,"Representation"),r=ss(c,r);return us(e.map((a=t,s=i,o=r,function(e){var t=Ms(e,"BaseURL"),t=Fs(s,t),i=ss(a,Bs(e)),n=js(e);return t.map(function(e){return{segmentInfo:ss(o,n),attributes:ss(i,{baseUrl:e})}})})))}}function qs(e,t){var i=t=void 0===t?{}:t,n=void 0===(a=i.manifestUri)?"":a,t=void 0===(r=i.NOW)?Date.now():r,r=void 0===(a=i.clientOffset)?0:a;if(!(i=Ms(e,"Period")).length)throw new Error(ao);var a=Ms(e,"Location"),s=Bs(e),e=Fs([n],Ms(e,"BaseURL"));s.type=s.type||"static",s.sourceDuration=s.mediaPresentationDuration||0,s.NOW=t,s.clientOffset=r,a.length&&(s.locations=a.map(Ns));var o,u,l=[];return i.forEach(function(e,t){var i,n=Bs(e),r=l[t-1];n.start=(i={attributes:n,priorPeriodAttributes:r?r.attributes:null,mpdType:s.type},t=i.attributes,r=i.priorPeriodAttributes,i=i.mpdType,"number"==typeof t.start?t.start:r&&"number"==typeof r.start&&"number"==typeof r.duration?r.start+r.duration:r||"static"!==i?null:0),l.push({node:e,attributes:n})}),{locations:s.locations,representationInfo:us(l.map((o=s,u=e,function(e,t){var i=Fs(u,Ms(e.node,"BaseURL")),n=ss(o,{periodStart:e.attributes.start});"number"==typeof e.attributes.duration&&(n.periodDuration=e.attributes.duration);var r=Ms(e.node,"AdaptationSet"),e=js(e.node);return us(r.map(Hs(n,i,e)))})))}}function Vs(e){if(""===e)throw new Error(so);var t,i,n=new ro;try{i=(t=n.parseFromString(e,"application/xml"))&&"MPD"===t.documentElement.tagName?t.documentElement:null}catch(e){}if(!i||i&&0<i.getElementsByTagName("parsererror").length)throw new Error(oo);return i}function Ws(e,t){void 0===t&&(t={});var i=qs(Vs(e),t),e=i.representationInfo.map(Rs);return As({dashPlaylists:e,locations:i.locations,sidxMapping:t.sidxMapping,previousManifest:t.previousManifest})}function Gs(e){return function(e){e=Ms(e,"UTCTiming")[0];if(!e)return null;var t=Bs(e);switch(t.schemeIdUri){case"urn:mpeg:dash:utc:http-head:2014":case"urn:mpeg:dash:utc:http-head:2012":t.method="HEAD";break;case"urn:mpeg:dash:utc:http-xsdate:2014":case"urn:mpeg:dash:utc:http-iso:2014":case"urn:mpeg:dash:utc:http-xsdate:2012":case"urn:mpeg:dash:utc:http-iso:2012":t.method="GET";break;case"urn:mpeg:dash:utc:direct:2014":case"urn:mpeg:dash:utc:direct:2012":t.method="DIRECT",t.value=Date.parse(t.value);break;case"urn:mpeg:dash:utc:http-ntp:2014":case"urn:mpeg:dash:utc:ntp:2014":case"urn:mpeg:dash:utc:sntp:2014":default:throw new Error(co)}return t}(Vs(e))}function zs(e){return e instanceof Uint8Array?e:(Array.isArray(e)||(t=e,ArrayBuffer.isView(t))||e instanceof ArrayBuffer||(e="number"!=typeof e||"number"==typeof e&&e!=e?0:[e]),new Uint8Array(e&&e.buffer||e,e&&e.byteOffset||0,e&&e.byteLength||0));var t}function Xs(e,t){var i=void 0!==(t=(void 0===t?{}:t).le)&&t;e=bo(e="bigint"!=typeof e&&"number"!=typeof e||"number"==typeof e&&e!=e?0:e);for(var n=(t=e,Math.ceil(t.toString(2).length/8)),r=new Uint8Array(new ArrayBuffer(n)),a=0;a<n;a++){var s=i?a:Math.abs(a+1-r.length);r[s]=Number(e/To[a]&bo(255)),e<0&&(r[s]=Math.abs(~r[s]),r[s]-=0===a?1:2)}return r}function Ks(e,t){if("string"!=typeof(e="string"!=typeof e&&e&&"function"==typeof e.toString?e.toString():e))return new Uint8Array;t||(e=unescape(encodeURIComponent(e)));for(var i=new Uint8Array(e.length),n=0;n<e.length;n++)i[n]=e.charCodeAt(n);return i}function Ys(i,e,t){var n=void 0===t?{}:t,r=void 0===(t=n.offset)?0:t,a=void 0===(n=n.mask)?[]:n;return i=zs(i),n=(e=zs(e)).every||Array.prototype.every,e.length&&i.length-r>=e.length&&n.call(e,function(e,t){return e===(a[t]?a[t]&i[r+t]:i[r+t])})}function Qs(e,t){return void 0===t&&(t=0),(e=zs(e)).length-t<10||!Ys(e,So,{offset:t})?t:Qs(e,t+=function(e,t){void 0===t&&(t=0);var i=(e=zs(e))[t+5],t=e[t+6]<<21|e[t+7]<<14|e[t+8]<<7|e[t+9];return(16&i)>>4?20+t:10+t}(e,t))}function $s(e){return"string"==typeof e?Ks(e):e}function Js(e,t,i){var n;void 0===i&&(i=!1),n=t,t=Array.isArray(n)?n.map($s):[$s(n)],e=zs(e);var r=[];if(!t.length)return r;for(var a=0;a<e.length;){var s=(e[a]<<24|e[a+1]<<16|e[a+2]<<8|e[a+3])>>>0,o=e.subarray(a+4,a+8);if(0==s)break;var u=a+s;if(u>e.length){if(i)break;u=e.length}s=e.subarray(a+8,u);Ys(o,t[0])&&(1===t.length?r.push(s):r.push.apply(r,Js(s,t.slice(1),i))),a=u}return r}function Zs(e,t,i,n){void 0===i&&(i=!0),void 0===n&&(n=!1);var r=function(e){for(var t=1,i=0;i<Eo.length&&!(e&Eo[i]);i++)t++;return t}(e[t]),a=e.subarray(t,t+r);return i&&((a=Array.prototype.slice.call(e,t,t+r))[0]^=Eo[r-1]),{length:r,value:function(n,e){var t=void 0===e?{}:e,e=t.signed,e=void 0!==e&&e,t=t.le,r=void 0!==t&&t;n=zs(n);t=r?"reduce":"reduceRight",t=(n[t]||Array.prototype[t]).call(n,function(e,t,i){i=r?i:Math.abs(i+1-n.length);return e+bo(t)*To[i]},bo(0));return!e||(e=To[n.length]/bo(2)-bo(1))<(t=bo(t))&&(t-=e,t-=e,t-=bo(2)),Number(t)}(a,{signed:n}),bytes:a}}function eo(e){return"string"==typeof e?e.match(/.{1,2}/g).map(eo):"number"==typeof e?Xs(e):e}function to(e,t,i){if(i>=t.length)return t.length;var n=Zs(t,i,!1);if(Ys(e.bytes,n.bytes))return i;var r=Zs(t,i+n.length);return to(e,t,i+r.length+r.value+n.length)}function io(e,t){var i;i=t,t=Array.isArray(i)?i.map(eo):[eo(i)],e=zs(e);var n=[];if(!t.length)return n;for(var r=0;r<e.length;){var a=Zs(e,r,!1),s=Zs(e,r+a.length),o=r+a.length+s.length;127===s.value&&(s.value=to(a,e,o),s.value!==e.length&&(s.value-=o));var u=o+s.value>e.length?e.length:o+s.value,u=e.subarray(o,u);Ys(t[0],a.bytes)&&(1===t.length?n.push(u):n=n.concat(io(u,t.slice(1)))),r+=a.length+s.length+u.length}return n}function no(e,t,i,n){void 0===n&&(n=1/0),e=zs(e),i=[].concat(i);for(var r,a=0,s=0;a<e.length&&(s<n||r);){var o=void 0;if(Ys(e.subarray(a),ko)?o=4:Ys(e.subarray(a),Co)&&(o=3),o){if(s++,r)return function(e){for(var t=[],i=1;i<e.length-2;)Ys(e.subarray(i,i+3),Io)&&(t.push(i+2),i++),i++;if(0===t.length)return e;for(var n=e.length-t.length,r=new Uint8Array(n),a=0,i=0;i<n;a++,i++)a===t[0]&&(a++,t.shift()),r[i]=e[a];return r}(e.subarray(r,a));var u=void 0;"h264"===t?u=31&e[a+o]:"h265"===t&&(u=e[a+o]>>1&63),-1!==i.indexOf(u)&&(r=a+o),a+=o+("h264"===t?1:2)}else a++}return e.subarray(0,0)}var ro={__DOMHandler:es,DOMParser:Za,DOMImplementation:U.DOMImplementation,XMLSerializer:U.XMLSerializer}.DOMParser,ao="INVALID_NUMBER_OF_PERIOD",so="DASH_EMPTY_MANIFEST",oo="DASH_INVALID_XML",uo="NO_BASE_URL",lo="SEGMENT_TIME_UNSPECIFIED",co="UNSUPPORTED_UTC_TIMING_SCHEME",ho={static:function(e){var t=e.duration,i=e.timescale,n=void 0===i?1:i,r=e.sourceDuration,i=e.periodDuration,e=hs(e.endNumber),n=t/n;return"number"==typeof e?{start:0,end:e}:"number"==typeof i?{start:0,end:i/n}:{start:0,end:r/n}},dynamic:function(e){var t=e.NOW,i=e.clientOffset,n=e.availabilityStartTime,r=e.timescale,a=void 0===r?1:r,s=e.duration,o=e.periodStart,u=void 0===o?0:o,r=e.minimumUpdatePeriod,o=void 0===r?0:r,r=e.timeShiftBufferDepth,r=void 0===r?1/0:r,e=hs(e.endNumber),i=(t+i)/1e3,u=n+u,o=Math.ceil((i+o-u)*a/s),r=Math.floor((i-u-r)*a/s),s=Math.floor((i-u)*a/s);return{start:Math.max(0,r),end:"number"==typeof e?e:Math.min(o,s)}}},po=["AUDIO","SUBTITLES"],fo=/\$([A-z]*)(?:(%0)([0-9]+)d)?\$/g,mo={mediaPresentationDuration:Us,availabilityStartTime:function(e){return/^\d+-\d+-\d+T\d+:\d+:\d+(\.\d+)?$/.test(e=e)&&(e+="Z"),Date.parse(e)/1e3},minimumUpdatePeriod:Us,suggestedPresentationDelay:Us,type:function(e){return e},timeShiftBufferDepth:Us,start:Us,width:function(e){return parseInt(e,10)},height:function(e){return parseInt(e,10)},bandwidth:function(e){return parseInt(e,10)},startNumber:function(e){return parseInt(e,10)},timescale:function(e){return parseInt(e,10)},presentationTimeOffset:function(e){return parseInt(e,10)},duration:function(e){var t=parseInt(e,10);return isNaN(t)?Us(e):t},d:function(e){return parseInt(e,10)},t:function(e){return parseInt(e,10)},r:function(e){return parseInt(e,10)},DEFAULT:function(e){return e}},go={"urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b":"org.w3.clearkey","urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed":"com.widevine.alpha","urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95":"com.microsoft.playready","urn:uuid:f239e769-efa3-4850-9c16-a903c6932efb":"com.adobe.primetime"},yo=Math.pow(2,32),vo=function(e){var t=new DataView(e.buffer,e.byteOffset,e.byteLength);return t.getBigUint64?(e=t.getBigUint64(0))<Number.MAX_SAFE_INTEGER?Number(e):e:t.getUint32(0)*yo+t.getUint32(4)},_o=function(e){var t=new DataView(e.buffer,e.byteOffset,e.byteLength),i={version:e[0],flags:new Uint8Array(e.subarray(1,4)),references:[],referenceId:t.getUint32(4),timescale:t.getUint32(8)},n=12;0===i.version?(i.earliestPresentationTime=t.getUint32(n),i.firstOffset=t.getUint32(n+4),n+=8):(i.earliestPresentationTime=vo(e.subarray(n)),i.firstOffset=vo(e.subarray(n+8)),n+=16);var r=t.getUint16(n+=2);for(n+=2;0<r;n+=12,r--)i.references.push({referenceType:(128&e[n])>>>7,referencedSize:2147483647&t.getUint32(n),subsegmentDuration:t.getUint32(n+4),startsWithSap:!!(128&e[n+8]),sapType:(112&e[n+8])>>>4,sapDeltaTime:268435455&t.getUint32(n+8)});return i},bo=window.BigInt||Number,To=[bo("0x1"),bo("0x100"),bo("0x10000"),bo("0x1000000"),bo("0x100000000"),bo("0x10000000000"),bo("0x1000000000000"),bo("0x100000000000000"),bo("0x10000000000000000")],So=zs([73,68,51]),wo={EBML:zs([26,69,223,163]),DocType:zs([66,130]),Segment:zs([24,83,128,103]),SegmentInfo:zs([21,73,169,102]),Tracks:zs([22,84,174,107]),Track:zs([174]),TrackNumber:zs([215]),DefaultDuration:zs([35,227,131]),TrackEntry:zs([174]),TrackType:zs([131]),FlagDefault:zs([136]),CodecID:zs([134]),CodecPrivate:zs([99,162]),VideoTrack:zs([224]),AudioTrack:zs([225]),Cluster:zs([31,67,182,117]),Timestamp:zs([231]),TimestampScale:zs([42,215,177]),BlockGroup:zs([160]),BlockDuration:zs([155]),Block:zs([161]),SimpleBlock:zs([163])},Eo=[128,64,32,16,8,4,2,1],ko=zs([0,0,0,1]),Co=zs([0,0,1]),Io=zs([0,0,3]),xo={webm:zs([119,101,98,109]),matroska:zs([109,97,116,114,111,115,107,97]),flac:zs([102,76,97,67]),ogg:zs([79,103,103,83]),ac3:zs([11,119]),riff:zs([82,73,70,70]),avi:zs([65,86,73]),wav:zs([87,65,86,69]),"3gp":zs([102,116,121,112,51,103]),mp4:zs([102,116,121,112]),fmp4:zs([115,116,121,112]),mov:zs([102,116,121,112,113,116]),moov:zs([109,111,111,118]),moof:zs([109,111,111,102])},Ao={aac:function(e){var t=Qs(e);return Ys(e,[255,16],{offset:t,mask:[255,22]})},mp3:function(e){var t=Qs(e);return Ys(e,[255,2],{offset:t,mask:[255,6]})},webm:function(e){e=io(e,[wo.EBML,wo.DocType])[0];return Ys(e,xo.webm)},mkv:function(e){e=io(e,[wo.EBML,wo.DocType])[0];return Ys(e,xo.matroska)},mp4:function(e){return!Ao["3gp"](e)&&!Ao.mov(e)&&(!(!Ys(e,xo.mp4,{offset:4})&&!Ys(e,xo.fmp4,{offset:4}))||(!(!Ys(e,xo.moof,{offset:4})&&!Ys(e,xo.moov,{offset:4}))||void 0))},mov:function(e){return Ys(e,xo.mov,{offset:4})},"3gp":function(e){return Ys(e,xo["3gp"],{offset:4})},ac3:function(e){var t=Qs(e);return Ys(e,xo.ac3,{offset:t})},ts:function(e){if(e.length<189&&1<=e.length)return 71===e[0];for(var t=0;t+188<e.length&&t<188;){if(71===e[t]&&71===e[t+188])return!0;t+=1}return!1},flac:function(e){var t=Qs(e);return Ys(e,xo.flac,{offset:t})},ogg:function(e){return Ys(e,xo.ogg)},avi:function(e){return Ys(e,xo.riff)&&Ys(e,xo.avi,{offset:8})},wav:function(e){return Ys(e,xo.riff)&&Ys(e,xo.wav,{offset:8})},h264:function(e){return no(e,"h264",7,3).length},h265:function(e){return no(e,"h265",[32,33],3).length}},Po=Object.keys(Ao).filter(function(e){return"ts"!==e&&"h264"!==e&&"h265"!==e}).concat(["ts","h264","h265"]);Po.forEach(function(e){var t=Ao[e];Ao[e]=function(e){return t(zs(e))}});function Lo(e){e=zs(e);for(var t=0;t<Po.length;t++){var i=Po[t];if(ll[i](e))return i}return""}function Do(e,t,i){return e&&i&&i.responseURL&&t!==i.responseURL?i.responseURL:t}function Oo(e){return tr.log.debug?tr.log.debug.bind(tr,"VHS:",e+" >"):function(){}}function Ro(e,t){var i,n=[];if(e&&e.length)for(i=0;i<e.length;i++)t(e.start(i),e.end(i))&&n.push([e.start(i),e.end(i)]);return tr.createTimeRanges(n)}function Mo(e,i){return Ro(e,function(e,t){return e-.1<=i&&i<=t+.1})}function No(e,t){return Ro(e,function(e){return t<=e-hl})}function Uo(e){var t=[];if(!e||!e.length)return"";for(var i=0;i<e.length;i++)t.push(e.start(i)+" => "+e.end(i));return t.join(", ")}function Bo(e){for(var t=[],i=0;i<e.length;i++)t.push({start:e.start(i),end:e.end(i)});return t}function Fo(e){if(e&&e.length&&e.end)return e.end(e.length-1)}function jo(e,t){var i=0;if(!e||!e.length)return i;for(var n=0;n<e.length;n++){var r=e.start(n),a=e.end(n);a<t||(i+=r<t&&t<=a?a-t:a-r)}return i}function Ho(t,e){if(!e.preload)return e.duration;var i=0;return(e.parts||[]).forEach(function(e){i+=e.duration}),(e.preloadHints||[]).forEach(function(e){"PART"===e.type&&(i+=t.partTargetDuration)}),i}function qo(e){return(e.segments||[]).reduce(function(i,n,r){return n.parts?n.parts.forEach(function(e,t){i.push({duration:e.duration,segmentIndex:r,partIndex:t,part:e,segment:n})}):i.push({duration:n.duration,segmentIndex:r,partIndex:null,segment:n,part:null}),i},[])}function Vo(e){return(e=e.segments&&e.segments.length&&e.segments[e.segments.length-1])&&e.parts||[]}function Wo(e){var t=e.preloadSegment;if(t){e=t.parts,t=(t.preloadHints||[]).reduce(function(e,t){return e+("PART"===t.type?1:0)},0);return t+=e&&e.length?e.length:0}}function Go(e,t){return t.endList?0:e&&e.suggestedPresentationDelay?e.suggestedPresentationDelay:(e=0<Vo(t).length)&&t.serverControl&&t.serverControl.partHoldBack?t.serverControl.partHoldBack:e&&t.partTargetDuration?3*t.partTargetDuration:t.serverControl&&t.serverControl.holdBack?t.serverControl.holdBack:t.targetDuration?3*t.targetDuration:0}function zo(e,t,i){if((t="undefined"==typeof t?e.mediaSequence+e.segments.length:t)<e.mediaSequence)return 0;var n=function(e,t){var i=0,n=t-e.mediaSequence,r=e.segments[n];if(r){if("undefined"!=typeof r.start)return{result:r.start,precise:!0};if("undefined"!=typeof r.end)return{result:r.end-r.duration,precise:!0}}for(;n--;){if("undefined"!=typeof(r=e.segments[n]).end)return{result:i+r.end,precise:!0};if(i+=Ho(e,r),"undefined"!=typeof r.start)return{result:i+r.start,precise:!0}}return{result:i,precise:!1}}(e,t);return n.precise?n.result:(t=function(e,t){for(var i,n=0,r=t-e.mediaSequence;r<e.segments.length;r++){if("undefined"!=typeof(i=e.segments[r]).start)return{result:i.start-n,precise:!0};if(n+=Ho(e,i),"undefined"!=typeof i.end)return{result:i.end-n,precise:!0}}return{result:-1,precise:!1}}(e,t)).precise?t.result:n.result+i}function Xo(e,t,i){if(!e)return 0;if("number"!=typeof i&&(i=0),"undefined"==typeof t){if(e.totalDuration)return e.totalDuration;if(!e.endList)return window.Infinity}return zo(e,t,i)}function Ko(e){var t=e.defaultDuration,i=e.durationList,n=e.startIndex,r=e.endIndex,a=0;if(r<n&&(n=(e=[r,n])[0],r=e[1]),n<0){for(var s=n;s<Math.min(0,r);s++)a+=t;n=0}for(var o=n;o<r;o++)a+=i[o].duration;return a}function Yo(e,t,i,n){return e&&e.segments?e.endList?Xo(e):null===t?null:(t=zo(e,e.mediaSequence+e.segments.length,t=t||0),i&&(t-=n="number"==typeof n?n:Go(null,e)),Math.max(0,t)):null}function Qo(e){return e.excludeUntil&&e.excludeUntil>Date.now()}function $o(e){return e.excludeUntil&&e.excludeUntil===1/0}function Jo(e){var t=Qo(e);return!e.disabled&&!t}function Zo(e,t){return t.attributes&&t.attributes[e]}function eu(e,t){if(1===e.playlists.length)return!0;var i=t.attributes.BANDWIDTH||Number.MAX_VALUE;return 0===e.playlists.filter(function(e){return!!Jo(e)&&(e.attributes.BANDWIDTH||0)<i}).length}function tu(e,t){return!(!e&&!t||!e&&t||e&&!t)&&(e===t||(!(!e.id||!t.id||e.id!==t.id)||(!(!e.resolvedUri||!t.resolvedUri||e.resolvedUri!==t.resolvedUri)||!(!e.uri||!t.uri||e.uri!==t.uri))))}function iu(e,t){var i,n=e&&e.mediaGroups&&e.mediaGroups.AUDIO||{},r=!1;for(i in n){for(var a in n[i])if(r=t(n[i][a]))break;if(r)break}return!!r}function nu(i){if(!i||!i.playlists||!i.playlists.length)return iu(i,function(e){return e.playlists&&e.playlists.length||e.uri});for(var e=0;e<i.playlists.length;e++){var t=function(e){var t=i.playlists[e],e=t.attributes&&t.attributes.CODECS;return e&&e.split(",").every(fr)||iu(i,function(e){return tu(t,e)})?"continue":{v:!1}}(e);if("continue"!==t&&"object"==typeof t)return t.v}return!0}function ru(e,t){return e+"-"+t}function au(r,a){r.mediaGroups&&["AUDIO","SUBTITLES"].forEach(function(e){if(r.mediaGroups[e])for(var t in r.mediaGroups[e])for(var i in r.mediaGroups[e][t]){var n=r.mediaGroups[e][t][i];a(n,e,t,i)}})}function su(e){var t=e.playlist,i=e.uri,e=e.id;t.id=e,t.playlistErrors_=0,i&&(t.uri=i),t.attributes=t.attributes||{}}function ou(o,e){o.uri=e;for(var t=0;t<o.playlists.length;t++)o.playlists[t].uri||(o.playlists[t].uri="placeholder-uri-"+t);var i,u=nu(o);au(o,function(e,t,i,n){var r="placeholder-uri-"+t+"-"+i+"-"+n;if(!e.playlists||!e.playlists.length){if(u&&"AUDIO"===t&&!e.uri)for(var a=0;a<o.playlists.length;a++){var s=o.playlists[a];if(s.attributes&&s.attributes.AUDIO&&s.attributes.AUDIO===i)return}e.playlists=[g({},e)]}e.playlists.forEach(function(e,t){var i=ru(t,r);e.uri?e.resolvedUri=e.resolvedUri||dl(o.uri,e.uri):(e.uri=0===t?r:i,e.resolvedUri=e.uri),e.id=e.id||i,e.attributes=e.attributes||{},o.playlists[e.id]=e,o.playlists[e.uri]=e})}),function(e){for(var t=e.playlists.length;t--;){var i=e.playlists[t];su({playlist:i,id:ru(t,i.uri)}),i.resolvedUri=dl(e.uri,i.uri),e.playlists[i.id]=i,(e.playlists[i.uri]=i).attributes.BANDWIDTH||ml.warn("Invalid playlist STREAM-INF detected. Missing BANDWIDTH attribute.")}}(o),au(i=o,function(e){e.uri&&(e.resolvedUri=dl(i.uri,e.uri))})}function uu(e,t,i){var n=e.slice(),r=t.slice();i=i||0;for(var a,s=[],o=0;o<r.length;o++){var u=n[o+i],l=r[o];u?(a=u.map||a,s.push(function(e,t){if(!e)return t;var i=gl(e,t);if(e.preloadHints&&!t.preloadHints&&delete i.preloadHints,e.parts&&!t.parts)delete i.parts;else if(e.parts&&t.parts)for(var n=0;n<t.parts.length;n++)e.parts&&e.parts[n]&&(i.parts[n]=gl(e.parts[n],t.parts[n]));return!e.skipped&&t.skipped&&(i.skipped=!1),e.preload&&!t.preload&&(i.preload=!1),i}(u,l))):(a&&!l.map&&(l.map=a),s.push(l))}return s}function lu(e,t){!e.resolvedUri&&e.uri&&(e.resolvedUri=dl(t,e.uri)),e.key&&!e.key.resolvedUri&&(e.key.resolvedUri=dl(t,e.key.uri)),e.map&&!e.map.resolvedUri&&(e.map.resolvedUri=dl(t,e.map.uri)),e.map&&e.map.key&&!e.map.key.resolvedUri&&(e.map.key.resolvedUri=dl(t,e.map.key.uri)),e.parts&&e.parts.length&&e.parts.forEach(function(e){e.resolvedUri||(e.resolvedUri=dl(t,e.uri))}),e.preloadHints&&e.preloadHints.length&&e.preloadHints.forEach(function(e){e.resolvedUri||(e.resolvedUri=dl(t,e.uri))})}function cu(e){var t=e.segments||[],i=e.preloadSegment;if(i&&i.parts&&i.parts.length){if(i.preloadHints)for(var n=0;n<i.preloadHints.length;n++)if("MAP"===i.preloadHints[n].type)return t;i.duration=e.targetDuration,i.preload=!0,t.push(i)}return t}function du(e,t){return e===t||e.segments&&t.segments&&e.segments.length===t.segments.length&&e.endList===t.endList&&e.mediaSequence===t.mediaSequence&&e.preloadSegment===t.preloadSegment}function hu(e,a,t){void 0===t&&(t=du);var i=gl(e,{}),n=i.playlists[a.id];if(!n)return null;if(t(n,a))return null;a.segments=cu(a);var r=gl(n,a);if(r.preloadSegment&&!a.preloadSegment&&delete r.preloadSegment,n.segments){if(a.skip){a.segments=a.segments||[];for(var s=0;s<a.skip.skippedSegments;s++)a.segments.unshift({skipped:!0})}r.segments=uu(n.segments,a.segments,a.mediaSequence-n.mediaSequence)}r.segments.forEach(function(e){lu(e,r.resolvedUri)});for(var o=0;o<i.playlists.length;o++)i.playlists[o].id===a.id&&(i.playlists[o]=r);return i.playlists[a.id]=r,i.playlists[a.uri]=r,au(e,function(e,t,i,n){if(e.playlists)for(var r=0;r<e.playlists.length;r++)a.id===e.playlists[r].id&&(e.playlists[r]=a)}),i}function pu(e,t){var i=e.segments||[],n=i[i.length-1],n=(i=n&&n.parts&&n.parts[n.parts.length-1])&&i.duration||n&&n.duration;return t&&n?1e3*n:500*(e.partTargetDuration||e.targetDuration||10)}function fu(e,t,i,n){var r="arraybuffer"===e.responseType?e.response:e.responseText;!t&&r&&(e.responseTime=Date.now(),e.roundTripTime=e.responseTime-e.requestTime,e.bytesReceived=r.byteLength||r.length,e.bandwidth||(e.bandwidth=Math.floor(e.bytesReceived/e.roundTripTime*8*1e3))),i.headers&&(e.responseHeaders=i.headers),t&&"ETIMEDOUT"===t.code&&(e.timedout=!0),n(t=!t&&!e.aborted&&200!==i.statusCode&&206!==i.statusCode&&0!==i.statusCode?new Error("XHR Failed with a response of: "+(e&&(r||e.responseText))):t,e)}function mu(){function a(e,i){e=_l({timeout:45e3},e);var t=a.beforeRequest||tr.Vhs.xhr.beforeRequest;!t||"function"!=typeof t||(t=t(e))&&(e=t);var n=(!0===tr.Vhs.xhr.original?vl:tr.Vhs.xhr)(e,function(e,t){return fu(n,e,t,i)}),r=n.abort;return n.abort=function(){return n.aborted=!0,r.apply(n,arguments)},n.uri=e.uri,n.requestTime=Date.now(),n}return a.original=!0,a}function gu(e){var t,i={};return e.byterange&&(i.Range=(t=e.byterange,e=t.offset,t="bigint"==typeof t.offset||"bigint"==typeof t.length?window.BigInt(t.offset)+window.BigInt(t.length)-window.BigInt(1):t.offset+t.length-1,"bytes="+e+"-"+t)),i}function yu(e,t){return e=e.toString(16),"00".substring(0,2-e.length)+e+(t%2?" ":"")}function vu(e){return 32<=e&&e<126?String.fromCharCode(e):"."}function _u(i){var n={};return Object.keys(i).forEach(function(e){var t=i[e];ArrayBuffer.isView(t)?n[e]={bytes:t.buffer,byteOffset:t.byteOffset,byteLength:t.byteLength}:n[e]=t}),n}function bu(e){var t=e.byterange||{length:1/0,offset:0};return[t.length,t.offset,e.resolvedUri].join(",")}function Tu(e){return e.resolvedUri}function Su(e){for(var t=Array.prototype.slice.call(e),i="",n=0;n<t.length/16;n++)i+=t.slice(16*n,16*n+16).map(yu).join("")+" "+t.slice(16*n,16*n+16).map(vu).join("")+"\n";return i}function wu(e){var t=e.playlist,i=e.time,n=void 0===i?void 0:i;if(!(i=e.callback))throw new Error("getProgramTime: callback must be provided");return t&&void 0!==n?(e=function(e,t){if(!t||!t.segments||0===t.segments.length)return null;for(var i,n=0,r=0;r<t.segments.length&&!(e<=(n=(i=t.segments[r]).videoTimingInfo?i.videoTimingInfo.transmuxedPresentationEnd:n+i.duration));r++);var a=t.segments[t.segments.length-1];if(a.videoTimingInfo&&a.videoTimingInfo.transmuxedPresentationEnd<e)return null;if(n<e){if(e>n+.25*a.duration)return null;i=a}return{segment:i,estimatedStart:i.videoTimingInfo?i.videoTimingInfo.transmuxedPresentationStart:n-i.duration,type:i.videoTimingInfo?"accurate":"estimate"}}(n,t))?"estimate"===e.type?i({message:"Accurate programTime could not be determined. Please seek to e.seekTime and try again",seekTime:e.estimatedStart}):(t={mediaSeconds:n},(e=function(e,t){if(!t.dateTimeObject)return null;var i=t.videoTimingInfo.transmuxerPrependedSeconds,i=e-(t.videoTimingInfo.transmuxedPresentationStart+i);return new Date(t.dateTimeObject.getTime()+1e3*i)}(n,e.segment))&&(t.programDateTime=e.toISOString()),i(null,t)):i({message:"valid programTime was not found"}):i({message:"getProgramTime: playlist and time must be provided"})}function Eu(e){var t=e.programTime,i=e.playlist,n=e.retryCount,r=void 0===n?2:n,a=e.seekTo,s=e.pauseAfterSeek,o=void 0===s||s,u=e.tech,l=e.callback;if(!l)throw new Error("seekToProgramTime: callback must be provided");return"undefined"!=typeof t&&i&&a?i.endList||u.hasStarted_?function(e){if(!e.segments||0===e.segments.length)return!1;for(var t=0;t<e.segments.length;t++)if(!e.segments[t].dateTimeObject)return!1;return!0}(i)?(n=function(e,t){var i;try{i=new Date(e)}catch(e){return null}if(!t||!t.segments||0===t.segments.length)return null;if(i<(r=t.segments[0]).dateTimeObject)return null;for(var n=0;n<t.segments.length-1;n++){var r=t.segments[n];if(i<t.segments[n+1].dateTimeObject)break}var a,s=t.segments[t.segments.length-1],e=s.dateTimeObject,a=s.videoTimingInfo?(a=s.videoTimingInfo).transmuxedPresentationEnd-a.transmuxedPresentationStart-a.transmuxerPrependedSeconds:s.duration+.25*s.duration;return new Date(e.getTime()+1e3*a)<i?null:{segment:r=e<i?s:r,estimatedStart:r.videoTimingInfo?r.videoTimingInfo.transmuxedPresentationStart:fl.duration(t,t.mediaSequence+t.segments.indexOf(r)),type:r.videoTimingInfo?"accurate":"estimate"}}(t,i))?(s=n.segment,e=function(e,t){var i;try{n=new Date(e),i=new Date(t)}catch(e){}var n=n.getTime();return(i.getTime()-n)/1e3}(s.dateTimeObject,t),"estimate"===n.type?0===r?l({message:t+" is not buffered yet. Try again"}):(a(n.estimatedStart+e),void u.one("seeked",function(){Eu({programTime:t,playlist:i,retryCount:r-1,seekTo:a,pauseAfterSeek:o,tech:u,callback:l})})):(e=s.start+e,u.one("seeked",function(){return l(null,u.currentTime())}),o&&u.pause(),void a(e))):l({message:t+" was not found in the stream"}):l({message:"programDateTime tags must be provided in the manifest "+i.resolvedUri}):l({message:"player must be playing a live stream to start buffering"}):l({message:"seekToProgramTime: programTime, seekTo and playlist must be provided"})}function ku(e,t){if(4===e.readyState)return t()}function Cu(e,t,r){function n(e,t,i,n){return t.abort(),o=!0,r(e,t,i,n)}function i(e,t){if(!o){if(e)return n(e,t,"",s);var i=t.responseText.substring(s&&s.byteLength||0,t.responseText.length);if(s=function(){for(var e=arguments.length,t=new Array(e),i=0;i<e;i++)t[i]=arguments[i];if((t=t.filter(function(e){return e&&(e.byteLength||e.length)&&"string"!=typeof e})).length<=1)return zs(t[0]);var n=t.reduce(function(e,t,i){return e+(t.byteLength||t.length)},0),r=new Uint8Array(n),a=0;return t.forEach(function(e){e=zs(e),r.set(e,a),a+=e.byteLength}),r}(s,Ks(i,!0)),a=a||Qs(s),s.length<10||a&&s.length<a+2)return ku(t,function(){return n(e,t,"",s)});i=Lo(s);return"ts"===i&&s.length<188||!i&&s.length<376?ku(t,function(){return n(e,t,"",s)}):n(null,t,i,s)}}var a,s=[],o=!1,u=t({uri:e,beforeSend:function(t){t.overrideMimeType("text/plain; charset=x-user-defined"),t.addEventListener("progress",function(e){return e.total,e.loaded,fu(t,null,{statusCode:t.status},i)})}},function(e,t){return fu(u,e,t,i)});return u}function Iu(e,t){if(!du(e,t))return!1;if(e.sidx&&t.sidx&&(e.sidx.offset!==t.sidx.offset||e.sidx.length!==t.sidx.length))return!1;if(!e.sidx&&t.sidx||e.sidx&&!t.sidx)return!1;if(e.segments&&!t.segments||!e.segments&&t.segments)return!1;if(!e.segments&&!t.segments)return!0;for(var i=0;i<e.segments.length;i++){var n=e.segments[i],r=t.segments[i];if(n.uri!==r.uri)return!1;if(n.byterange||r.byterange){n=n.byterange,r=r.byterange;if(n&&!r||!n&&r)return!1;if(n.offset!==r.offset||n.length!==r.length)return!1}}return!0}function xu(e,t){var i,n={};for(i in e){var r=e[i].sidx;if(r){var a=bs(r);if(!t[a])break;var s=t[a].sidxInfo;s=s,r=r,(Boolean(!s.map&&!r.map)||Boolean(s.map&&r.map&&s.map.byterange.offset===r.map.byterange.offset&&s.map.byterange.length===r.map.byterange.length))&&s.uri===r.uri&&s.byterange.offset===r.byterange.offset&&s.byterange.length===r.byterange.length&&(n[a]=t[a])}}return n}function Au(e){return e.on=e.addEventListener,e.off=e.removeEventListener,e}function Pu(i){var n=i.transmuxer,e=i.bytes,t=i.audioAppendStart,r=i.gopsToAlignWith,a=i.remux,s=i.onData,o=i.onTrackInfo,u=i.onAudioTimingInfo,l=i.onVideoTimingInfo,c=i.onVideoSegmentTimingInfo,d=i.onAudioSegmentTimingInfo,h=i.onId3,p=i.onCaptions,f=i.onDone,m=i.onEndedTimeline,g=i.onTransmuxerLog,y=i.isEndOfTimeline,v={buffer:[]},_=y;n.onmessage=function(e){var t;n.currentTransmux===i&&("data"===e.data.action&&function(e,t,i){var n=e.data.segment,r=n.type,a=n.initSegment,s=n.captions,o=n.captionStreams,u=n.metadata,l=n.videoFrameDtsTime,n=n.videoFramePtsTime;t.buffer.push({captions:s,captionStreams:o,metadata:u});e=e.data.segment.boxes||{data:e.data.segment.data},a={type:r,data:new Uint8Array(e.data,e.data.byteOffset,e.data.byteLength),initSegment:new Uint8Array(a.data,a.byteOffset,a.byteLength)};"undefined"!=typeof l&&(a.videoFrameDtsTime=l),"undefined"!=typeof n&&(a.videoFramePtsTime=n),i(a)}(e,v,s),"trackinfo"===e.data.action&&o(e.data.trackInfo),"gopInfo"===e.data.action&&(v.gopInfo=e.data.gopInfo),"audioTimingInfo"===e.data.action&&u(e.data.audioTimingInfo),"videoTimingInfo"===e.data.action&&l(e.data.videoTimingInfo),"videoSegmentTimingInfo"===e.data.action&&c(e.data.videoSegmentTimingInfo),"audioSegmentTimingInfo"===e.data.action&&d(e.data.audioSegmentTimingInfo),"id3Frame"===e.data.action&&h([e.data.id3Frame],e.data.id3Frame.dispatchType),"caption"===e.data.action&&p(e.data.caption),"endedtimeline"===e.data.action&&(_=!1,m()),"log"===e.data.action&&g(e.data.log),"transmuxed"===e.data.type&&(_||(n.onmessage=null,e=(t={transmuxedData:v,callback:f}).transmuxedData,t=t.callback,e.buffer=[],t(e),El(n))))},t&&n.postMessage({action:"setAudioAppendStart",appendStart:t}),Array.isArray(r)&&n.postMessage({action:"alignGopsWith",gopsToAlignWith:r}),"undefined"!=typeof a&&n.postMessage({action:"setRemux",remux:a}),e.byteLength&&(r=e instanceof ArrayBuffer?e:e.buffer,a=e instanceof ArrayBuffer?0:e.byteOffset,n.postMessage({action:"push",data:r,byteOffset:a,byteLength:e.byteLength},[r])),y&&n.postMessage({action:"endTimeline"}),n.postMessage({action:"flush"})}function Lu(e,t){e.postMessage({action:t}),El(e)}function Du(e,t){if(!t.currentTransmux)return t.currentTransmux=e,Lu(t,e),0;t.transmuxQueue.push(Lu.bind(null,t,e))}function Ou(e){if(!e.transmuxer.currentTransmux)return e.transmuxer.currentTransmux=e,void Pu(e);e.transmuxer.transmuxQueue.push(e)}function Ru(i){var n=i.transmuxer,r=i.endAction||i.action,a=i.callback,e=g({},i,{endAction:null,transmuxer:null,callback:null}),t=function e(t){t.data.action===r&&(n.removeEventListener("message",e),t.data.data&&(t.data.data=new Uint8Array(t.data.data,i.byteOffset||0,i.byteLength||t.data.data.byteLength),i.data&&(i.data=t.data.data)),a(t.data))};n.addEventListener("message",t),i.data?(t=i.data instanceof ArrayBuffer,e.byteOffset=t?0:i.data.byteOffset,e.byteLength=i.data.byteLength,t=[t?i.data:i.data.buffer],n.postMessage(e,t)):n.postMessage(e)}function Mu(e){e.forEach(function(e){e.abort()})}function Nu(e,t){return t.timedout?{status:t.status,message:"HLS request timed-out at URL: "+t.uri,code:xl,xhr:t}:t.aborted?{status:t.status,message:"HLS request aborted at URL: "+t.uri,code:Al,xhr:t}:e?{status:t.status,message:"HLS request errored at URL: "+t.uri,code:Il,xhr:t}:"arraybuffer"===t.responseType&&0===t.response.byteLength?{status:t.status,message:"Empty HLS response at URL: "+t.uri,code:Il,xhr:t}:null}function Uu(a,s,o){return function(e,t){var i=t.response,e=Nu(e,t);if(e)return o(e,a);if(16!==i.byteLength)return o({status:t.status,message:"Invalid HLS key at URL: "+t.uri,code:Il,xhr:t},a);for(var i=new DataView(i),n=new Uint32Array([i.getUint32(0),i.getUint32(4),i.getUint32(8),i.getUint32(12)]),r=0;r<s.length;r++)s[r].bytes=n;return o(null,a)}}function Bu(i,n){var e=Lo(i.map.bytes);if("mp4"!==e){var t=i.map.resolvedUri||i.map.uri;return n({internal:!0,message:"Found unsupported "+(e||"unknown")+" container for initialization segment at URL: "+t,code:Il})}Ru({action:"probeMp4Tracks",data:i.map.bytes,transmuxer:i.transmuxer,callback:function(e){var t=e.tracks,e=e.data;return i.map.bytes=e,t.forEach(function(e){i.map.tracks=i.map.tracks||{},i.map.tracks[e.type]||"number"==typeof(i.map.tracks[e.type]=e).id&&e.timescale&&(i.map.timescales=i.map.timescales||{},i.map.timescales[e.id]=e.timescale)}),n(null)}})}function Fu(e){var i=e.segment,n=e.finishProcessingFn,r=e.responseType;return function(e,t){e=Nu(e,t);if(e)return n(e,i);e="arraybuffer"!==r&&t.responseText?function(e){for(var t=new Uint8Array(new ArrayBuffer(e.length)),i=0;i<e.length;i++)t[i]=e.charCodeAt(i);return t.buffer}(t.responseText.substring(i.lastReachedChar||0)):t.response;return i.stats={bandwidth:(t=t).bandwidth,bytesReceived:t.bytesReceived||0,roundTripTime:t.roundTripTime||0},i.key?i.encryptedBytes=new Uint8Array(e):i.bytes=new Uint8Array(e),n(null,i)}}function ju(e){var i=e.segment,t=e.bytes,n=e.trackInfoFn,r=e.timingInfoFn,a=e.videoSegmentTimingInfoFn,s=e.audioSegmentTimingInfoFn,o=e.id3Fn,u=e.captionsFn,l=e.isEndOfTimeline,c=e.endedTimelineFn,d=e.dataFn,h=e.doneFn,p=e.onTransmuxerLog,e=i.map&&i.map.tracks||{},f=Boolean(e.audio&&e.video),m=r.bind(null,i,"audio","start"),g=r.bind(null,i,"audio","end"),y=r.bind(null,i,"video","start"),v=r.bind(null,i,"video","end");Ru({action:"probeTs",transmuxer:i.transmuxer,data:t,baseStartTime:i.baseStartTime,callback:function(e){i.bytes=t=e.data;e=e.result;e&&(n(i,{hasAudio:e.hasAudio,hasVideo:e.hasVideo,isMuxed:f}),n=null,e.hasAudio&&!f&&m(e.audioStart),e.hasVideo&&y(e.videoStart),y=m=null),Ou({bytes:t,transmuxer:i.transmuxer,audioAppendStart:i.audioAppendStart,gopsToAlignWith:i.gopsToAlignWith,remux:f,onData:function(e){e.type="combined"===e.type?"video":e.type,d(i,e)},onTrackInfo:function(e){n&&(f&&(e.isMuxed=!0),n(i,e))},onAudioTimingInfo:function(e){m&&"undefined"!=typeof e.start&&(m(e.start),m=null),g&&"undefined"!=typeof e.end&&g(e.end)},onVideoTimingInfo:function(e){y&&"undefined"!=typeof e.start&&(y(e.start),y=null),v&&"undefined"!=typeof e.end&&v(e.end)},onVideoSegmentTimingInfo:function(e){a(e)},onAudioSegmentTimingInfo:function(e){s(e)},onId3:function(e,t){o(i,e,t)},onCaptions:function(e){u(i,[e])},isEndOfTimeline:l,onEndedTimeline:function(){c()},onTransmuxerLog:p,onDone:function(e){h&&(e.type="combined"===e.type?"video":e.type,h(null,i,e))}})}})}function Hu(e){var i=e.segment,n=e.bytes,t=e.trackInfoFn,r=e.timingInfoFn,a=e.videoSegmentTimingInfoFn,s=e.audioSegmentTimingInfoFn,o=e.id3Fn,u=e.captionsFn,l=e.isEndOfTimeline,c=e.endedTimelineFn,d=e.dataFn,h=e.doneFn,p=e.onTransmuxerLog,f=new Uint8Array(n);if(0<Js(f,["moof"]).length){i.isFmp4=!0;var m=i.map.tracks,g={isFmp4:!0,hasVideo:!!m.video,hasAudio:!!m.audio};m.audio&&m.audio.codec&&"enca"!==m.audio.codec&&(g.audioCodec=m.audio.codec),m.video&&m.video.codec&&"encv"!==m.video.codec&&(g.videoCodec=m.video.codec),m.video&&m.audio&&(g.isMuxed=!0),t(i,g);var y=function(e){d(i,{data:f,type:g.hasAudio&&!g.isMuxed?"audio":"video"}),e&&e.length&&u(i,e),h(null,i,{})};Ru({action:"probeMp4StartTime",timescales:i.map.timescales,data:f,transmuxer:i.transmuxer,callback:function(e){var t=e.data,e=e.startTime;n=t.buffer,i.bytes=f=t,g.hasAudio&&!g.isMuxed&&r(i,"audio","start",e),g.hasVideo&&r(i,"video","start",e),m.video&&t.byteLength&&i.transmuxer?Ru({action:"pushMp4Captions",endAction:"mp4Captions",transmuxer:i.transmuxer,data:f,timescales:i.map.timescales,trackIds:[m.video.id],callback:function(e){n=e.data.buffer,i.bytes=f=e.data,e.logs.forEach(function(e){p(tr.mergeOptions(e,{stream:"mp4CaptionParser"}))}),y(e.captions)}}):y()}})}else if(i.transmuxer){if("undefined"==typeof i.container&&(i.container=Lo(f)),"ts"!==i.container&&"aac"!==i.container)return t(i,{hasAudio:!1,hasVideo:!1}),h(null,i,{}),0;ju({segment:i,bytes:n,trackInfoFn:t,timingInfoFn:r,videoSegmentTimingInfoFn:a,audioSegmentTimingInfoFn:s,id3Fn:o,captionsFn:u,isEndOfTimeline:l,endedTimelineFn:c,dataFn:d,doneFn:h,onTransmuxerLog:p})}else h(null,i,{})}function qu(e,i){var n=e.id,t=e.key,r=e.encryptedBytes,a=e.decryptionWorker,e=function e(t){t.data.source===n&&(a.removeEventListener("message",e),t=t.data.decrypted,i(new Uint8Array(t.bytes,t.byteOffset,t.byteLength)))};a.addEventListener("message",e),e=t.bytes.slice?t.bytes.slice():new Uint32Array(Array.prototype.slice.call(t.bytes)),a.postMessage(_u({source:n,encrypted:r,key:e,iv:t.iv}),[r.buffer,e.buffer])}function Vu(e){var i=e.activeXhrs,m=e.decryptionWorker,g=e.trackInfoFn,y=e.timingInfoFn,v=e.videoSegmentTimingInfoFn,_=e.audioSegmentTimingInfoFn,b=e.id3Fn,T=e.captionsFn,S=e.isEndOfTimeline,w=e.endedTimelineFn,E=e.dataFn,k=e.doneFn,C=e.onTransmuxerLog,n=0,r=!1;return function(e,f){if(!r){if(e)return r=!0,Mu(i),k(e,f);if((n+=1)===i.length){var t=function(){if(f.encryptedBytes)return t=(e={decryptionWorker:m,segment:f,trackInfoFn:g,timingInfoFn:y,videoSegmentTimingInfoFn:v,audioSegmentTimingInfoFn:_,id3Fn:b,captionsFn:T,isEndOfTimeline:S,endedTimelineFn:w,dataFn:E,doneFn:k,onTransmuxerLog:C}).decryptionWorker,i=e.segment,n=e.trackInfoFn,r=e.timingInfoFn,a=e.videoSegmentTimingInfoFn,s=e.audioSegmentTimingInfoFn,o=e.id3Fn,u=e.captionsFn,l=e.isEndOfTimeline,c=e.endedTimelineFn,d=e.dataFn,h=e.doneFn,p=e.onTransmuxerLog,void qu({id:i.requestId,key:i.key,encryptedBytes:i.encryptedBytes,decryptionWorker:t},function(e){i.bytes=e,Hu({segment:i,bytes:i.bytes,trackInfoFn:n,timingInfoFn:r,videoSegmentTimingInfoFn:a,audioSegmentTimingInfoFn:s,id3Fn:o,captionsFn:u,isEndOfTimeline:l,endedTimelineFn:c,dataFn:d,doneFn:h,onTransmuxerLog:p})});var e,t,i,n,r,a,s,o,u,l,c,d,h,p;Hu({segment:f,bytes:f.bytes,trackInfoFn:g,timingInfoFn:y,videoSegmentTimingInfoFn:v,audioSegmentTimingInfoFn:_,id3Fn:b,captionsFn:T,isEndOfTimeline:S,endedTimelineFn:w,dataFn:E,doneFn:k,onTransmuxerLog:C})};if(f.endOfAllRequests=Date.now(),f.map&&f.map.encryptedBytes&&!f.map.bytes)return qu({decryptionWorker:m,id:f.requestId+"-init",encryptedBytes:f.map.encryptedBytes,key:f.map.key},function(e){f.map.bytes=e,Bu(f,function(e){return e?(Mu(i),k(e,f)):void t()})});t()}}}}function Wu(e){var n=e.segment,r=e.progressFn;return e.trackInfoFn,e.timingInfoFn,e.videoSegmentTimingInfoFn,e.audioSegmentTimingInfoFn,e.id3Fn,e.captionsFn,e.isEndOfTimeline,e.endedTimelineFn,e.dataFn,function(e){var t,i=e.target;if(!i.aborted)return n.stats=tr.mergeOptions(n.stats,(i=(t=e).target,(i={bandwidth:1/0,bytesReceived:0,roundTripTime:Date.now()-i.requestTime||0}).bytesReceived=t.loaded,i.bandwidth=Math.floor(i.bytesReceived/i.roundTripTime*8*1e3),i)),!n.stats.firstBytesReceivedAt&&n.stats.bytesReceived&&(n.stats.firstBytesReceivedAt=Date.now()),r(e,n)}}function Gu(e){var t,i,n,r=e.xhr,a=e.xhrOptions,s=e.decryptionWorker,o=e.segment,u=e.abortFn,l=e.progressFn,c=e.trackInfoFn,d=e.timingInfoFn,h=e.videoSegmentTimingInfoFn,p=e.audioSegmentTimingInfoFn,f=e.id3Fn,m=e.captionsFn,g=e.isEndOfTimeline,y=e.endedTimelineFn,v=e.dataFn,_=e.doneFn,e=e.onTransmuxerLog,b=[],_=Vu({activeXhrs:b,decryptionWorker:s,trackInfoFn:c,timingInfoFn:d,videoSegmentTimingInfoFn:h,audioSegmentTimingInfoFn:p,id3Fn:f,captionsFn:m,isEndOfTimeline:g,endedTimelineFn:y,dataFn:v,doneFn:_,onTransmuxerLog:e});o.key&&!o.key.bytes&&(e=[o.key],o.map&&!o.map.bytes&&o.map.key&&o.map.key.resolvedUri===o.key.resolvedUri&&e.push(o.map.key),e=r(tr.mergeOptions(a,{uri:o.key.resolvedUri,responseType:"arraybuffer"}),Uu(o,e,_)),b.push(e)),o.map&&!o.map.bytes&&(!o.map.key||o.key&&o.key.resolvedUri===o.map.key.resolvedUri||(t=r(tr.mergeOptions(a,{uri:o.map.key.resolvedUri,responseType:"arraybuffer"}),Uu(o,[o.map.key],_)),b.push(t)),t=r(tr.mergeOptions(a,{uri:o.map.resolvedUri,responseType:"arraybuffer",headers:gu(o.map)}),(i=(t={segment:o,finishProcessingFn:_}).segment,n=t.finishProcessingFn,function(e,t){e=Nu(e,t);if(e)return n(e,i);e=new Uint8Array(t.response);if(i.map.key)return i.map.encryptedBytes=e,n(null,i);i.map.bytes=e,Bu(i,function(e){return e?(e.xhr=t,e.status=t.status,n(e,i)):void n(null,i)})})),b.push(t)),a=tr.mergeOptions(a,{uri:o.part&&o.part.resolvedUri||o.resolvedUri,responseType:"arraybuffer",headers:gu(o)}),(a=r(a,Fu({segment:o,finishProcessingFn:_,responseType:a.responseType}))).addEventListener("progress",Wu({segment:o,progressFn:l,trackInfoFn:c,timingInfoFn:d,videoSegmentTimingInfoFn:h,audioSegmentTimingInfoFn:p,id3Fn:f,captionsFn:m,isEndOfTimeline:g,endedTimelineFn:y,dataFn:v})),b.push(a);var T={};return b.forEach(function(e){var t,i;e.addEventListener("loadend",(t=(e={loadendState:T,abortFn:u}).loadendState,i=e.abortFn,function(e){e.target.aborted&&i&&!t.calledAbortFn&&(i(),t.calledAbortFn=!0)}))}),function(){return Mu(b)}}function zu(e,t){return t=t.attributes||{},e&&e.mediaGroups&&e.mediaGroups.AUDIO&&t.AUDIO&&e.mediaGroups.AUDIO[t.AUDIO]}function Xu(e){var n={};return e.forEach(function(e){var t=e.mediaType,i=e.type,e=e.details;n[t]=n[t]||[],n[t].push(hr(""+i+e))}),Object.keys(n).forEach(function(e){return 1<n[e].length?(Pl("multiple "+e+" codecs found as attributes: "+n[e].join(", ")+". Setting playlist codecs to null so that we wait for mux.js to probe segments for real codecs."),void(n[e]=null)):void(n[e]=n[e][0])}),n}function Ku(e){var t=0;return e.audio&&t++,e.video&&t++,t}function Yu(e,t){var i,n=t.attributes||{},r=Xu(function(e){e=e.attributes||{};if(e.CODECS)return pr(e.CODECS)}(t)||[]);return zu(e,t)&&!r.audio&&!function(e,t){if(!zu(e,t))return!0;var i,t=t.attributes||{},n=e.mediaGroups.AUDIO[t.AUDIO];for(i in n)if(!n[i].uri&&!n[i].playlists)return!0;return!1}(e,t)&&(i=Xu(function(e,t){if(!e.mediaGroups.AUDIO||!t)return null;var i,n=e.mediaGroups.AUDIO[t];if(!n)return null;for(i in n){var r=n[i];if(r.default&&r.playlists)return pr(r.playlists[0].attributes.CODECS)}return null}(e,n.AUDIO)||[])).audio&&(r.audio=i.audio),r}function Qu(e){if(e&&e.playlist){var t=e.playlist;return JSON.stringify({id:t.id,bandwidth:e.bandwidth,width:e.width,height:e.height,codecs:t.attributes&&t.attributes.CODECS||""})}}function $u(e,t){return(e=e&&window.getComputedStyle(e))?e[t]:""}function Ju(e,n){var r=e.slice();e.sort(function(e,t){var i=n(e,t);return 0===i?r.indexOf(e)-r.indexOf(t):i})}function Zu(e,t){var i,n;return(i=(i=e.attributes.BANDWIDTH?e.attributes.BANDWIDTH:i)||window.Number.MAX_VALUE)-(n=(n=t.attributes.BANDWIDTH?t.attributes.BANDWIDTH:n)||window.Number.MAX_VALUE)}function el(e,t,i,n,r,a){if(e){var s={bandwidth:t,width:i,height:n,limitRenditionByPlayerDimensions:r},o=e.playlists;fl.isAudioOnly(e)&&(o=a.getAudioTrackPlaylists_(),s.audioOnly=!0);var u=o.map(function(e){var t=e.attributes&&e.attributes.RESOLUTION&&e.attributes.RESOLUTION.width,i=e.attributes&&e.attributes.RESOLUTION&&e.attributes.RESOLUTION.height,n=e.attributes&&e.attributes.BANDWIDTH;return{bandwidth:n||window.Number.MAX_VALUE,width:t,height:i,playlist:e}});Ju(u,function(e,t){return e.bandwidth-t.bandwidth});var l=(u=u.filter(function(e){return!fl.isIncompatible(e.playlist)})).filter(function(e){return fl.isEnabled(e.playlist)}),e=(l=!l.length?u.filter(function(e){return!fl.isDisabled(e.playlist)}):l).filter(function(e){return e.bandwidth*Sl.BANDWIDTH_VARIANCE<t}),c=e[e.length-1],o=e.filter(function(e){return e.bandwidth===c.bandwidth})[0];if(!1===r){var d=o||l[0]||u[0];if(d&&d.playlist){r=o?"bandwidthBestRep":"sortedPlaylistReps";return l[0]&&(r="enabledPlaylistReps"),Ll("choosing "+Qu(d)+" using "+r+" with options",s),d.playlist}return Ll("could not choose a playlist with options",s),null}d=e.filter(function(e){return e.width&&e.height});Ju(d,function(e,t){return e.width-t.width});var h,p,f,e=d.filter(function(e){return e.width===i&&e.height===n}),c=e[e.length-1],e=e.filter(function(e){return e.bandwidth===c.bandwidth})[0];e||(p=(h=d.filter(function(e){return e.width>i||e.height>n})).filter(function(e){return e.width===h[0].width&&e.height===h[0].height}),c=p[p.length-1],p=p.filter(function(e){return e.bandwidth===c.bandwidth})[0]),a.experimentalLeastPixelDiffSelector&&(m=d.map(function(e){return e.pixelDiff=Math.abs(e.width-i)+Math.abs(e.height-n),e}),Ju(m,function(e,t){return e.pixelDiff===t.pixelDiff?t.bandwidth-e.bandwidth:e.pixelDiff-t.pixelDiff}),f=m[0]);var m=f||p||e||o||l[0]||u[0];if(m&&m.playlist){u="sortedPlaylistReps";return f?u="leastPixelDiffRep":p?u="resolutionPlusOneRep":e?u="resolutionBestRep":o?u="bandwidthBestRep":l[0]&&(u="enabledPlaylistReps"),Ll("choosing "+Qu(m)+" using "+u+" with options",s),m.playlist}return Ll("could not choose a playlist with options",s),null}}function tl(e){var t=e.inbandTextTracks,i=e.metadataArray,r=e.timestampOffset,n=e.videoDuration;if(i){var a=window.WebKitDataCue||window.VTTCue,s=t.metadataTrack_;if(s&&(i.forEach(function(e){var n=e.cueTime+r;!("number"!=typeof n||window.isNaN(n)||n<0)&&n<1/0&&e.frames.forEach(function(e){var t,i=new a(n,n,e.value||e.url||e.data||"");i.frame=e,i.value=e,t=i,Object.defineProperties(t.frame,{id:{get:function(){return tr.log.warn("cue.frame.id is deprecated. Use cue.value.key instead."),t.value.key}},value:{get:function(){return tr.log.warn("cue.frame.value is deprecated. Use cue.value.data instead."),t.value.data}},privateData:{get:function(){return tr.log.warn("cue.frame.privateData is deprecated. Use cue.value.data instead."),t.value.data}}}),s.addCue(i)})}),s.cues&&s.cues.length)){for(var o=s.cues,u=[],l=0;l<o.length;l++)o[l]&&u.push(o[l]);var c=u.reduce(function(e,t){var i=e[t.startTime]||[];return i.push(t),e[t.startTime]=i,e},{}),d=Object.keys(c).sort(function(e,t){return Number(e)-Number(t)});d.forEach(function(e,t){var e=c[e],i=Number(d[t+1])||n;e.forEach(function(e){e.endTime=i})})}}}function il(e,t,i){var n,r;if(i&&i.cues)for(n=i.cues.length;n--;)(r=i.cues[n]).startTime>=e&&r.endTime<=t&&i.removeCue(r)}function nl(e){return"number"==typeof e&&isFinite(e)}function rl(e){var t=e.startOfSegment,i=e.duration,n=e.segment,r=e.part,a=e.playlist,s=a.mediaSequence,o=a.id,u=a.segments,l=e.mediaIndex,c=e.partIndex,d=e.timeline,h=(void 0===u?[]:u).length-1,p="mediaIndex/partIndex increment";return e.getMediaInfoForTime?p="getMediaInfoForTime ("+e.getMediaInfoForTime+")":e.isSyncRequest&&(p="getSyncSegmentCandidate (isSyncRequest)"),e.independent&&(p+=" with independent "+e.independent),a="number"==typeof c,u=e.segment.uri?"segment":"pre-segment",e=a?Wo({preloadSegment:n})-1:0,u+" ["+(s+l)+"/"+(s+h)+"]"+(a?" part ["+c+"/"+e+"]":"")+" segment start/end ["+n.start+" => "+n.end+"]"+(a?" part start/end ["+r.start+" => "+r.end+"]":"")+" startOfSegment ["+t+"] duration ["+i+"] timeline ["+d+"] selected by ["+p+"] playlist ["+o+"]"}function al(e){return e+"TimingInfo"}function sl(e){var t=e.timelineChangeController,i=e.currentTimeline,n=e.segmentTimeline,r=e.loaderType,e=e.audioDisabled;if(i!==n){if("audio"===r){i=t.lastTimelineChange({type:"main"});return!i||i.to!==n}if("main"===r&&e){t=t.pendingTimelineChange({type:"audio"});return t&&t.to===n?!1:!0}}}function ol(e){var t=e.segmentDuration,e=e.maxDuration;return!!t&&Math.round(t)>e+hl}function ul(e,t){if("hls"!==t)return null;var n,r,i=(n={audioTimingInfo:e.audioTimingInfo,videoTimingInfo:e.videoTimingInfo},r=0,["video","audio"].forEach(function(e){var t,i=n[e+"TimingInfo"];i&&(e=i.start,i=i.end,"bigint"==typeof e||"bigint"==typeof i?t=window.BigInt(i)-window.BigInt(e):"number"==typeof e&&"number"==typeof i&&(t=i-e),"undefined"!=typeof t&&r<t&&(r=t))}),r="bigint"==typeof r&&r<Number.MAX_SAFE_INTEGER?Number(r):r);if(!i)return null;var a=e.playlist.targetDuration,s=ol({segmentDuration:i,maxDuration:2*a}),t=ol({segmentDuration:i,maxDuration:a}),a="Segment with index "+e.mediaIndex+" from playlist "+e.playlist.id+" has a duration of "+i+" when the reported duration is "+e.duration+" and the target duration is "+a+". For HLS content, a duration in excess of the target duration may result in playback issues. See the HLS specification section on EXT-X-TARGETDURATION for more details: https://tools.ietf.org/html/draft-pantos-http-live-streaming-23#section-4.3.3.1";return s||t?{severity:s?"warn":"info",message:a}:null}var ll=Ao,cl=9e4,dl=ir,hl=1/30,pl=tr.createTimeRange,fl={liveEdgeDelay:Go,duration:Xo,seekable:function(e,t,i){var n=t||0,i=Yo(e,t,!0,i);return null===i?pl():pl(n,i)},getMediaInfoForTime:function(e){for(var t=e.playlist,i=e.currentTime,n=e.startingSegmentIndex,r=e.startingPartIndex,a=e.startTime,s=e.experimentalExactManifestTimings,o=i-a,u=qo(t),l=0,c=0;c<u.length;c++){var d=u[c];if(n===d.segmentIndex&&("number"!=typeof r||"number"!=typeof d.partIndex||r===d.partIndex)){l=c;break}}if(o<0){if(0<l)for(var h=l-1;0<=h;h--){var p=u[h];if(o+=p.duration,s){if(o<0)continue}else if(o+hl<=0)continue;return{partIndex:p.partIndex,segmentIndex:p.segmentIndex,startTime:a-Ko({defaultDuration:t.targetDuration,durationList:u,startIndex:l,endIndex:h})}}return{partIndex:u[0]&&u[0].partIndex||null,segmentIndex:u[0]&&u[0].segmentIndex||0,startTime:i}}if(l<0){for(var f=l;f<0;f++)if((o-=t.targetDuration)<0)return{partIndex:u[0]&&u[0].partIndex||null,segmentIndex:u[0]&&u[0].segmentIndex||0,startTime:i};l=0}for(var m=l;m<u.length;m++){var g=u[m];if(o-=g.duration,s){if(0<o)continue}else if(0<=o-hl)continue;return{partIndex:g.partIndex,segmentIndex:g.segmentIndex,startTime:a+Ko({defaultDuration:t.targetDuration,durationList:u,startIndex:l,endIndex:m})}}return{segmentIndex:u[u.length-1].segmentIndex,partIndex:u[u.length-1].partIndex,startTime:i}},isEnabled:Jo,isDisabled:function(e){return e.disabled},isBlacklisted:Qo,isIncompatible:$o,playlistEnd:Yo,isAes:function(e){for(var t=0;t<e.segments.length;t++)if(e.segments[t].key)return!0;return!1},hasAttribute:Zo,estimateSegmentRequestTime:function(e,t,i,n){return Zo("BANDWIDTH",i)?(e*i.attributes.BANDWIDTH-8*(n=void 0===n?0:n))/t:NaN},isLowestEnabledRendition:eu,isAudioOnly:nu,playlistMatch:tu,segmentDurationWithParts:Ho},ml=tr.log,gl=tr.mergeOptions,W=tr.EventTarget,yl=function(a){function e(e,t,i){var n;if(void 0===i&&(i={}),n=a.call(this)||this,!e)throw new Error("A non-empty playlist URL or object is required");n.logger_=Oo("PlaylistLoader");var r=i.withCredentials,r=void 0!==r&&r,i=i.handleManifestRedirects,i=void 0!==i&&i;n.src=e,n.vhs_=t,n.withCredentials=r,n.handleManifestRedirects=i;t=t.options_;return n.customTagParsers=t&&t.customTagParsers||[],n.customTagMappers=t&&t.customTagMappers||[],n.experimentalLLHLS=t&&t.experimentalLLHLS||!1,tr.browser.IE_VERSION&&(n.experimentalLLHLS=!1),n.state="HAVE_NOTHING",n.handleMediaupdatetimeout_=n.handleMediaupdatetimeout_.bind(ft(n)),n.on("mediaupdatetimeout",n.handleMediaupdatetimeout_),n}mt(e,a);var t=e.prototype;return t.handleMediaupdatetimeout_=function(){var e,t,i=this;"HAVE_METADATA"===this.state&&(e=this.media(),t=dl(this.master.uri,e.uri),this.experimentalLLHLS&&(t=function(e,t){if(t.endList||!t.serverControl)return e;var i,n,r,a,s={};return t.serverControl.canBlockReload&&(r=t.preloadSegment,i=t.mediaSequence+t.segments.length,r&&(n=r.parts||[],-1<(r=Wo(t)-1)&&r!=n.length-1&&(s._HLS_part=r),(-1<r||n.length)&&i--),s._HLS_msn=i),t.serverControl&&t.serverControl.canSkipUntil&&(s._HLS_skip=t.serverControl.canSkipDateranges?"v2":"YES"),Object.keys(s).length&&(a=new window.URL(e),["_HLS_skip","_HLS_msn","_HLS_part"].forEach(function(e){s.hasOwnProperty(e)&&a.searchParams.set(e,s[e])}),e=a.toString()),e}(t,e)),this.state="HAVE_CURRENT_METADATA",this.request=this.vhs_.xhr({uri:t,withCredentials:this.withCredentials},function(e,t){if(i.request)return e?i.playlistRequestError(i.request,i.media(),"HAVE_METADATA"):void i.haveMetadata({playlistString:i.request.responseText,url:i.media().uri,id:i.media().id})}))},t.playlistRequestError=function(e,t,i){var n=t.uri,t=t.id;this.request=null,i&&(this.state=i),this.error={playlist:this.master.playlists[t],status:e.status,message:"HLS playlist request error at URL: "+n+".",responseText:e.responseText,code:500<=e.status?4:2},this.trigger("error")},t.parseManifest_=function(e){var t=this,i=e.url;return function(e){var t=e.onwarn,i=e.oninfo,n=e.manifestString,r=e.customTagParsers,a=void 0===r?[]:r,r=e.customTagMappers,r=void 0===r?[]:r,e=e.experimentalLLHLS,s=new Sr;t&&s.on("warn",t),i&&s.on("info",i),a.forEach(function(e){return s.addParser(e)}),r.forEach(function(e){return s.addTagMapper(e)}),s.push(n),s.end();var o=s.manifest;e||(["preloadSegment","skip","serverControl","renditionReports","partInf","partTargetDuration"].forEach(function(e){o.hasOwnProperty(e)&&delete o[e]}),o.segments&&o.segments.forEach(function(t){["parts","preloadHints"].forEach(function(e){t.hasOwnProperty(e)&&delete t[e]})})),o.targetDuration||(u=10,o.segments&&o.segments.length&&(u=o.segments.reduce(function(e,t){return Math.max(e,t.duration)},0)),t&&t("manifest has no targetDuration defaulting to "+u),o.targetDuration=u);var u=Vo(o);return u.length&&!o.partTargetDuration&&(u=u.reduce(function(e,t){return Math.max(e,t.duration)},0),t&&(t("manifest has no partTargetDuration defaulting to "+u),ml.error("LL-HLS manifest has parts but lacks required #EXT-X-PART-INF:PART-TARGET value. See https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis-09#section-4.4.3.7. Playback is not guaranteed.")),o.partTargetDuration=u),o}({onwarn:function(e){e=e.message;return t.logger_("m3u8-parser warn for "+i+": "+e)},oninfo:function(e){e=e.message;return t.logger_("m3u8-parser info for "+i+": "+e)},manifestString:e.manifestString,customTagParsers:this.customTagParsers,customTagMappers:this.customTagMappers,experimentalLLHLS:this.experimentalLLHLS})},t.haveMetadata=function(e){var t=e.playlistString,i=e.playlistObject,n=e.url,e=e.id;this.request=null,this.state="HAVE_METADATA";t=i||this.parseManifest_({url:n,manifestString:t});t.lastRequest=Date.now(),su({playlist:t,uri:n,id:e});n=hu(this.master,t);this.targetDuration=t.partTargetDuration||t.targetDuration,this.pendingMedia_=null,n?(this.master=n,this.media_=this.master.playlists[e]):this.trigger("playlistunchanged"),this.updateMediaUpdateTimeout_(pu(this.media(),!!n)),this.trigger("loadedplaylist")},t.dispose=function(){this.trigger("dispose"),this.stopRequest(),window.clearTimeout(this.mediaUpdateTimeout),window.clearTimeout(this.finalRenditionTimeout),this.off()},t.stopRequest=function(){var e;this.request&&(e=this.request,this.request=null,e.onreadystatechange=null,e.abort())},t.media=function(i,e){var n=this;if(!i)return this.media_;if("HAVE_NOTHING"===this.state)throw new Error("Cannot switch media playlist from "+this.state);if("string"==typeof i){if(!this.master.playlists[i])throw new Error("Unknown playlist URI: "+i);i=this.master.playlists[i]}if(window.clearTimeout(this.finalRenditionTimeout),e){var t=(i.partTargetDuration||i.targetDuration)/2*1e3||5e3;this.finalRenditionTimeout=window.setTimeout(this.media.bind(this,i,!1),t)}else{var r=this.state,e=!this.media_||i.id!==this.media_.id,t=this.master.playlists[i.id];if(t&&t.endList||i.endList&&i.segments.length)return this.request&&(this.request.onreadystatechange=null,this.request.abort(),this.request=null),this.state="HAVE_METADATA",this.media_=i,void(e&&(this.trigger("mediachanging"),"HAVE_MASTER"===r?this.trigger("loadedmetadata"):this.trigger("mediachange")));if(this.updateMediaUpdateTimeout_(pu(i,!0)),e){if(this.state="SWITCHING_MEDIA",this.request){if(i.resolvedUri===this.request.url)return;this.request.onreadystatechange=null,this.request.abort(),this.request=null}this.media_&&this.trigger("mediachanging"),this.pendingMedia_=i,this.request=this.vhs_.xhr({uri:i.resolvedUri,withCredentials:this.withCredentials},function(e,t){if(n.request){if(i.lastRequest=Date.now(),i.resolvedUri=Do(n.handleManifestRedirects,i.resolvedUri,t),e)return n.playlistRequestError(n.request,i,r);n.haveMetadata({playlistString:t.responseText,url:i.uri,id:i.id}),"HAVE_MASTER"===r?n.trigger("loadedmetadata"):n.trigger("mediachange")}})}}},t.pause=function(){this.mediaUpdateTimeout&&(window.clearTimeout(this.mediaUpdateTimeout),this.mediaUpdateTimeout=null),this.stopRequest(),"HAVE_NOTHING"===this.state&&(this.started=!1),"SWITCHING_MEDIA"===this.state?this.media_?this.state="HAVE_METADATA":this.state="HAVE_MASTER":"HAVE_CURRENT_METADATA"===this.state&&(this.state="HAVE_METADATA")},t.load=function(e){var t=this;this.mediaUpdateTimeout&&(window.clearTimeout(this.mediaUpdateTimeout),this.mediaUpdateTimeout=null);var i=this.media();e?(e=i?(i.partTargetDuration||i.targetDuration)/2*1e3:5e3,this.mediaUpdateTimeout=window.setTimeout(function(){t.mediaUpdateTimeout=null,t.load()},e)):this.started?i&&!i.endList?this.trigger("mediaupdatetimeout"):this.trigger("loadedplaylist"):this.start()},t.updateMediaUpdateTimeout_=function(e){var t=this;this.mediaUpdateTimeout&&(window.clearTimeout(this.mediaUpdateTimeout),this.mediaUpdateTimeout=null),this.media()&&!this.media().endList&&(this.mediaUpdateTimeout=window.setTimeout(function(){t.mediaUpdateTimeout=null,t.trigger("mediaupdatetimeout"),t.updateMediaUpdateTimeout_(e)},e))},t.start=function(){var i=this;if(this.started=!0,"object"==typeof this.src)return this.src.uri||(this.src.uri=window.location.href),this.src.resolvedUri=this.src.uri,void setTimeout(function(){i.setupInitialPlaylist(i.src)},0);this.request=this.vhs_.xhr({uri:this.src,withCredentials:this.withCredentials},function(e,t){if(i.request){if(i.request=null,e)return i.error={status:t.status,message:"HLS playlist request error at URL: "+i.src+".",responseText:t.responseText,code:2},"HAVE_NOTHING"===i.state&&(i.started=!1),i.trigger("error");i.src=Do(i.handleManifestRedirects,i.src,t);t=i.parseManifest_({manifestString:t.responseText,url:i.src});i.setupInitialPlaylist(t)}})},t.srcUri=function(){return"string"==typeof this.src?this.src:this.src.uri},t.setupInitialPlaylist=function(e){if(this.state="HAVE_MASTER",e.playlists)return this.master=e,ou(this.master,this.srcUri()),e.playlists.forEach(function(t){t.segments=cu(t),t.segments.forEach(function(e){lu(e,t.resolvedUri)})}),this.trigger("loadedplaylist"),void(this.request||this.media(this.master.playlists[0]));var t,i,n,r=this.srcUri()||window.location.href;this.master=(i=ru(0,t=r),(n={mediaGroups:{AUDIO:{},VIDEO:{},"CLOSED-CAPTIONS":{},SUBTITLES:{}},uri:window.location.href,resolvedUri:window.location.href,playlists:[{uri:t,id:i,resolvedUri:t,attributes:{}}]}).playlists[i]=n.playlists[0],n.playlists[t]=n.playlists[0],n),this.haveMetadata({playlistObject:e,url:r,id:this.master.playlists[0].id}),this.trigger("loadedmetadata")},e}(W),vl=tr.xhr,_l=tr.mergeOptions,zt=Object.freeze({__proto__:null,createTransferableMessage:_u,initSegmentId:bu,segmentKeyId:Tu,hexDump:Su,tagDump:function(e){e=e.bytes;return Su(e)},textRanges:function(e){for(var t,i,n="",r=0;r<e.length;r++)n+=(i=r,(t=e).start(i)+"-"+t.end(i)+" ");return n}}),ar=tr.EventTarget,bl=tr.mergeOptions,Tl=function(a){function e(e,t,i,n){var r;void 0===i&&(i={}),(r=a.call(this)||this).masterPlaylistLoader_=n||ft(r),n||(r.isMaster_=!0);n=i.withCredentials,n=void 0!==n&&n,i=i.handleManifestRedirects,i=void 0!==i&&i;if(r.vhs_=t,r.withCredentials=n,r.handleManifestRedirects=i,!e)throw new Error("A non-empty playlist URL or object is required");return r.on("minimumUpdatePeriod",function(){r.refreshXml_()}),r.on("mediaupdatetimeout",function(){r.refreshMedia_(r.media().id)}),r.state="HAVE_NOTHING",r.loadedPlaylists_={},r.logger_=Oo("DashPlaylistLoader"),r.isMaster_?(r.masterPlaylistLoader_.srcUrl=e,r.masterPlaylistLoader_.sidxMapping_={}):r.childPlaylist_=e,r}mt(e,a);var t=e.prototype;return t.requestErrored_=function(e,t,i){return!this.request||(this.request=null,e?(this.error="object"!=typeof e||e instanceof Error?{status:t.status,message:"DASH request error at URL: "+t.uri,response:t.response,code:2}:e,i&&(this.state=i),this.trigger("error"),!0):void 0)},t.addSidxSegments_=function(a,n,r){var s,o,u=this,l=a.sidx&&bs(a.sidx);a.sidx&&l&&!this.masterPlaylistLoader_.sidxMapping_[l]?(s=Do(this.handleManifestRedirects,a.sidx.resolvedUri),o=function(e,t){if(!u.requestErrored_(e,t,n)){var i,e=u.masterPlaylistLoader_.sidxMapping_;try{i=_o(zs(t.response).subarray(8))}catch(e){return void u.requestErrored_(e,t,n)}return e[l]={sidxInfo:a.sidx,sidx:i},ms(a,i,a.sidx.resolvedUri),r(!0)}},this.request=Cu(s,this.vhs_.xhr,function(e,t,i,n){if(e)return o(e,t);if(!i||"mp4"!==i)return o({status:t.status,message:"Unsupported "+(i||"unknown")+" container type for sidx segment at URL: "+s,response:"",playlist:a,internal:!0,blacklistDuration:1/0,code:2},t);var r=a.sidx.byterange,i=r.offset,r=r.length;if(n.length>=r+i)return o(e,{response:n.subarray(i,i+r),status:t.status,uri:t.uri});u.request=u.vhs_.xhr({uri:s,responseType:"arraybuffer",headers:gu({byterange:a.sidx.byterange})},o)})):this.mediaRequest_=window.setTimeout(function(){return r(!1)},0)},t.dispose=function(){this.trigger("dispose"),this.stopRequest(),this.loadedPlaylists_={},window.clearTimeout(this.minimumUpdatePeriodTimeout_),window.clearTimeout(this.mediaRequest_),window.clearTimeout(this.mediaUpdateTimeout),this.mediaUpdateTimeout=null,this.mediaRequest_=null,this.minimumUpdatePeriodTimeout_=null,this.masterPlaylistLoader_.createMupOnMedia_&&(this.off("loadedmetadata",this.masterPlaylistLoader_.createMupOnMedia_),this.masterPlaylistLoader_.createMupOnMedia_=null),this.off()},t.hasPendingRequest=function(){return this.request||this.mediaRequest_},t.stopRequest=function(){var e;this.request&&(e=this.request,this.request=null,e.onreadystatechange=null,e.abort())},t.media=function(t){var i=this;if(!t)return this.media_;if("HAVE_NOTHING"===this.state)throw new Error("Cannot switch media playlist from "+this.state);var n=this.state;if("string"==typeof t){if(!this.masterPlaylistLoader_.master.playlists[t])throw new Error("Unknown playlist URI: "+t);t=this.masterPlaylistLoader_.master.playlists[t]}var e=!this.media_||t.id!==this.media_.id;if(e&&this.loadedPlaylists_[t.id]&&this.loadedPlaylists_[t.id].endList)return this.state="HAVE_METADATA",this.media_=t,void(e&&(this.trigger("mediachanging"),this.trigger("mediachange")));e&&(this.media_&&this.trigger("mediachanging"),this.addSidxSegments_(t,n,function(e){i.haveMetadata({startingState:n,playlist:t})}))},t.haveMetadata=function(e){var t=e.startingState,e=e.playlist;this.state="HAVE_METADATA",this.loadedPlaylists_[e.id]=e,this.mediaRequest_=null,this.refreshMedia_(e.id),"HAVE_MASTER"===t?this.trigger("loadedmetadata"):this.trigger("mediachange")},t.pause=function(){this.masterPlaylistLoader_.createMupOnMedia_&&(this.off("loadedmetadata",this.masterPlaylistLoader_.createMupOnMedia_),this.masterPlaylistLoader_.createMupOnMedia_=null),this.stopRequest(),window.clearTimeout(this.mediaUpdateTimeout),this.mediaUpdateTimeout=null,this.isMaster_&&(window.clearTimeout(this.masterPlaylistLoader_.minimumUpdatePeriodTimeout_),this.masterPlaylistLoader_.minimumUpdatePeriodTimeout_=null),"HAVE_NOTHING"===this.state&&(this.started=!1)},t.load=function(e){var t=this;window.clearTimeout(this.mediaUpdateTimeout),this.mediaUpdateTimeout=null;var i=this.media();e?(e=i?i.targetDuration/2*1e3:5e3,this.mediaUpdateTimeout=window.setTimeout(function(){return t.load()},e)):this.started?i&&!i.endList?(this.isMaster_&&!this.minimumUpdatePeriodTimeout_&&(this.trigger("minimumUpdatePeriod"),this.updateMinimumUpdatePeriodTimeout_()),this.trigger("mediaupdatetimeout")):this.trigger("loadedplaylist"):this.start()},t.start=function(){var i=this;this.started=!0,this.isMaster_?this.requestMaster_(function(e,t){i.haveMaster_(),i.hasPendingRequest()||i.media_||i.media(i.masterPlaylistLoader_.master.playlists[0])}):this.mediaRequest_=window.setTimeout(function(){return i.haveMaster_()},0)},t.requestMaster_=function(n){var r=this;this.request=this.vhs_.xhr({uri:this.masterPlaylistLoader_.srcUrl,withCredentials:this.withCredentials},function(e,t){if(!r.requestErrored_(e,t)){var i=t.responseText!==r.masterPlaylistLoader_.masterXml_;return r.masterPlaylistLoader_.masterXml_=t.responseText,t.responseHeaders&&t.responseHeaders.date?r.masterLoaded_=Date.parse(t.responseHeaders.date):r.masterLoaded_=Date.now(),r.masterPlaylistLoader_.srcUrl=Do(r.handleManifestRedirects,r.masterPlaylistLoader_.srcUrl,t),i?(r.handleMaster_(),void r.syncClientServerClock_(function(){return n(t,i)})):n(t,i)}"HAVE_NOTHING"===r.state&&(r.started=!1)})},t.syncClientServerClock_=function(i){var n=this,r=Gs(this.masterPlaylistLoader_.masterXml_);return null===r?(this.masterPlaylistLoader_.clientOffset_=this.masterLoaded_-Date.now(),i()):"DIRECT"===r.method?(this.masterPlaylistLoader_.clientOffset_=r.value-Date.now(),i()):void(this.request=this.vhs_.xhr({uri:dl(this.masterPlaylistLoader_.srcUrl,r.value),method:r.method,withCredentials:this.withCredentials},function(e,t){if(n.request){if(e)return n.masterPlaylistLoader_.clientOffset_=n.masterLoaded_-Date.now(),i();t="HEAD"===r.method?t.responseHeaders&&t.responseHeaders.date?Date.parse(t.responseHeaders.date):n.masterLoaded_:Date.parse(t.responseText);n.masterPlaylistLoader_.clientOffset_=t-Date.now(),i()}}))},t.haveMaster_=function(){this.state="HAVE_MASTER",this.isMaster_?this.trigger("loadedplaylist"):this.media_||this.media(this.childPlaylist_)},t.handleMaster_=function(){this.mediaRequest_=null;var e,t,i,n,r=this.masterPlaylistLoader_.master,t=(a={masterXml:this.masterPlaylistLoader_.masterXml_,srcUrl:this.masterPlaylistLoader_.srcUrl,clientOffset:this.masterPlaylistLoader_.clientOffset_,sidxMapping:this.masterPlaylistLoader_.sidxMapping_,previousManifest:r},e=a.masterXml,t=a.srcUrl,i=a.clientOffset,n=a.sidxMapping,a=a.previousManifest,a=Ws(e,{manifestUri:t,clientOffset:i,sidxMapping:n,previousManifest:a}),ou(a,t),a);r&&(t=function(e,t,i){for(var a=!0,s=bl(e,{duration:t.duration,minimumUpdatePeriod:t.minimumUpdatePeriod,timelineStarts:t.timelineStarts}),n=0;n<t.playlists.length;n++){var r,o=t.playlists[n];o.sidx&&(r=bs(o.sidx),i&&i[r]&&i[r].sidx&&ms(o,i[r].sidx,o.sidx.resolvedUri));o=hu(s,o,Iu);o&&(s=o,a=!1)}return au(t,function(e,t,i,n){var r;e.playlists&&e.playlists.length&&(r=e.playlists[0].id,(e=hu(s,e.playlists[0],Iu))&&((s=e).mediaGroups[t][i][n].playlists[0]=s.playlists[r],a=!1))}),(a=t.minimumUpdatePeriod===e.minimumUpdatePeriod&&a)?null:s}(r,t,this.masterPlaylistLoader_.sidxMapping_)),this.masterPlaylistLoader_.master=t||r;var a=this.masterPlaylistLoader_.master.locations&&this.masterPlaylistLoader_.master.locations[0];return a&&a!==this.masterPlaylistLoader_.srcUrl&&(this.masterPlaylistLoader_.srcUrl=a),(!r||t&&t.minimumUpdatePeriod!==r.minimumUpdatePeriod)&&this.updateMinimumUpdatePeriodTimeout_(),Boolean(t)},t.updateMinimumUpdatePeriodTimeout_=function(){var e=this.masterPlaylistLoader_;e.createMupOnMedia_&&(e.off("loadedmetadata",e.createMupOnMedia_),e.createMupOnMedia_=null),e.minimumUpdatePeriodTimeout_&&(window.clearTimeout(e.minimumUpdatePeriodTimeout_),e.minimumUpdatePeriodTimeout_=null);var t=e.master&&e.master.minimumUpdatePeriod;0===t&&(e.media()?t=1e3*e.media().targetDuration:(e.createMupOnMedia_=e.updateMinimumUpdatePeriodTimeout_,e.one("loadedmetadata",e.createMupOnMedia_))),"number"!=typeof t||t<=0?t<0&&this.logger_("found invalid minimumUpdatePeriod of "+t+", not setting a timeout"):this.createMUPTimeout_(t)},t.createMUPTimeout_=function(e){var t=this.masterPlaylistLoader_;t.minimumUpdatePeriodTimeout_=window.setTimeout(function(){t.minimumUpdatePeriodTimeout_=null,t.trigger("minimumUpdatePeriod"),t.createMUPTimeout_(e)},e)},t.refreshXml_=function(){var i=this;this.requestMaster_(function(e,t){var r,a;t&&(i.media_&&(i.media_=i.masterPlaylistLoader_.master.playlists[i.media_.id]),i.masterPlaylistLoader_.sidxMapping_=(t=i.masterPlaylistLoader_.master,r=i.masterPlaylistLoader_.sidxMapping_,a=xu(t.playlists,r),au(t,function(e,t,i,n){e.playlists&&e.playlists.length&&(e=e.playlists,a=bl(a,xu(e,r)))}),a),i.addSidxSegments_(i.media(),i.state,function(e){i.refreshMedia_(i.media().id)}))})},t.refreshMedia_=function(e){var t=this;if(!e)throw new Error("refreshMedia_ must take a media id");this.media_&&this.isMaster_&&this.handleMaster_();var i=this.masterPlaylistLoader_.master.playlists,n=!this.media_||this.media_!==i[e];n?this.media_=i[e]:this.trigger("playlistunchanged"),this.mediaUpdateTimeout||function e(){t.media().endList||(t.mediaUpdateTimeout=window.setTimeout(function(){t.trigger("mediaupdatetimeout"),e()},pu(t.media(),Boolean(n))))}(),this.trigger("loadedplaylist")},e}(ar),Sl={GOAL_BUFFER_LENGTH:30,MAX_GOAL_BUFFER_LENGTH:60,BACK_BUFFER_LENGTH:30,GOAL_BUFFER_LENGTH_RATE:1,INITIAL_BANDWIDTH:4194304,BANDWIDTH_VARIANCE:1.2,BUFFER_LOW_WATER_LINE:0,MAX_BUFFER_LOW_WATER_LINE:30,EXPERIMENTAL_MAX_BUFFER_LOW_WATER_LINE:16,BUFFER_LOW_WATER_LINE_RATE:1,BUFFER_HIGH_WATER_LINE:30},x=function(n){return function(){var e=function(t){try{return URL.createObjectURL(new Blob([t],{type:"application/javascript"}))}catch(e){var i=new BlobBuilder;return i.append(t),URL.createObjectURL(i.getBlob())}}(n),t=Au(new Worker(e));t.objURL=e;var i=t.terminate;return t.on=t.addEventListener,t.off=t.removeEventListener,t.terminate=function(){return URL.revokeObjectURL(e),i.call(this)},t}},U=function(e){return"var browserWorkerPolyFill = "+Au.toString()+";\nbrowserWorkerPolyFill(self);\n"+e},W=function(e){return e.toString().replace(/^function.+?{/,"").slice(0,-1)},wl=x(U(W(function(){var e=function(){this.init=function(){var a={};this.on=function(e,t){a[e]||(a[e]=[]),a[e]=a[e].concat(t)},this.off=function(e,t){return!!a[e]&&(t=a[e].indexOf(t),a[e]=a[e].slice(),a[e].splice(t,1),-1<t)},this.trigger=function(e){var t,i,n,r=a[e];if(r)if(2===arguments.length)for(i=r.length,t=0;t<i;++t)r[t].call(this,arguments[1]);else{for(n=[],t=arguments.length,t=1;t<arguments.length;++t)n.push(arguments[t]);for(i=r.length,t=0;t<i;++t)r[t].apply(this,n)}},this.dispose=function(){a={}}}};e.prototype.pipe=function(t){return this.on("data",function(e){t.push(e)}),this.on("done",function(e){t.flush(e)}),this.on("partialdone",function(e){t.partialFlush(e)}),this.on("endedtimeline",function(e){t.endTimeline(e)}),this.on("reset",function(e){t.reset(e)}),t},e.prototype.push=function(e){this.trigger("data",e)},e.prototype.flush=function(e){this.trigger("done",e)},e.prototype.partialFlush=function(e){this.trigger("partialdone",e)},e.prototype.endTimeline=function(e){this.trigger("endedtimeline",e)},e.prototype.reset=function(e){this.trigger("reset",e)};var u,t,i,n,r,a,s,o,l,c,d,h,p,f,m,g,y,v,_,b,T,S,w,E,k,C,I,x,A,P,L,D,O,R,M,N,U,B,F,j=e,H=Math.pow(2,32),q={getUint64:function(e){var t=new DataView(e.buffer,e.byteOffset,e.byteLength);return t.getBigUint64?(e=t.getBigUint64(0))<Number.MAX_SAFE_INTEGER?Number(e):e:t.getUint32(0)*H+t.getUint32(4)},MAX_UINT32:H},V=q.MAX_UINT32;!function(){if(T={avc1:[],avcC:[],btrt:[],dinf:[],dref:[],esds:[],ftyp:[],hdlr:[],mdat:[],mdhd:[],mdia:[],mfhd:[],minf:[],moof:[],moov:[],mp4a:[],mvex:[],mvhd:[],pasp:[],sdtp:[],smhd:[],stbl:[],stco:[],stsc:[],stsd:[],stsz:[],stts:[],styp:[],tfdt:[],tfhd:[],traf:[],trak:[],trun:[],trex:[],tkhd:[],vmhd:[]},"undefined"!=typeof Uint8Array){for(var e in T)T.hasOwnProperty(e)&&(T[e]=[e.charCodeAt(0),e.charCodeAt(1),e.charCodeAt(2),e.charCodeAt(3)]);S=new Uint8Array(["i".charCodeAt(0),"s".charCodeAt(0),"o".charCodeAt(0),"m".charCodeAt(0)]),E=new Uint8Array(["a".charCodeAt(0),"v".charCodeAt(0),"c".charCodeAt(0),"1".charCodeAt(0)]),w=new Uint8Array([0,0,0,1]),k=new Uint8Array([0,0,0,0,0,0,0,0,118,105,100,101,0,0,0,0,0,0,0,0,0,0,0,0,86,105,100,101,111,72,97,110,100,108,101,114,0]),C=new Uint8Array([0,0,0,0,0,0,0,0,115,111,117,110,0,0,0,0,0,0,0,0,0,0,0,0,83,111,117,110,100,72,97,110,100,108,101,114,0]),I={video:k,audio:C},P=new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,12,117,114,108,32,0,0,0,1]),A=new Uint8Array([0,0,0,0,0,0,0,0]),L=new Uint8Array([0,0,0,0,0,0,0,0]),D=L,O=new Uint8Array([0,0,0,0,0,0,0,0,0,0,0,0]),R=L,x=new Uint8Array([0,0,0,1,0,0,0,0,0,0,0,0])}}(),u=function(e){for(var t,i=[],n=0,r=1;r<arguments.length;r++)i.push(arguments[r]);for(r=i.length;r--;)n+=i[r].byteLength;for(t=new Uint8Array(n+8),new DataView(t.buffer,t.byteOffset,t.byteLength).setUint32(0,t.byteLength),t.set(e,4),r=0,n=8;r<i.length;r++)t.set(i[r],n),n+=i[r].byteLength;return t},t=function(){return u(T.dinf,u(T.dref,P))},i=function(e){return u(T.esds,new Uint8Array([0,0,0,0,3,25,0,0,0,4,17,64,21,0,6,0,0,0,218,192,0,0,218,192,5,2,e.audioobjecttype<<3|e.samplingfrequencyindex>>>1,e.samplingfrequencyindex<<7|e.channelcount<<3,6,1,2]))},f=function(e){return u(T.hdlr,I[e])},p=function(e){var t=new Uint8Array([0,0,0,0,0,0,0,2,0,0,0,3,0,1,95,144,e.duration>>>24&255,e.duration>>>16&255,e.duration>>>8&255,255&e.duration,85,196,0,0]);return e.samplerate&&(t[12]=e.samplerate>>>24&255,t[13]=e.samplerate>>>16&255,t[14]=e.samplerate>>>8&255,t[15]=255&e.samplerate),u(T.mdhd,t)},h=function(e){return u(T.mdia,p(e),f(e.type),a(e))},r=function(e){return u(T.mfhd,new Uint8Array([0,0,0,0,(4278190080&e)>>24,(16711680&e)>>16,(65280&e)>>8,255&e]))},a=function(e){return u(T.minf,"video"===e.type?u(T.vmhd,x):u(T.smhd,A),t(),g(e))},We=function(e,t){for(var i=[],n=t.length;n--;)i[n]=v(t[n]);return u.apply(null,[T.moof,r(e)].concat(i))},s=function(e){for(var t=e.length,i=[];t--;)i[t]=c(e[t]);return u.apply(null,[T.moov,l(4294967295)].concat(i).concat(o(e)))},o=function(e){for(var t=e.length,i=[];t--;)i[t]=_(e[t]);return u.apply(null,[T.mvex].concat(i))},l=function(e){e=new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,2,0,1,95,144,(4278190080&e)>>24,(16711680&e)>>16,(65280&e)>>8,255&e,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255]);return u(T.mvhd,e)},m=function(e){for(var t,i=e.samples||[],n=new Uint8Array(4+i.length),r=0;r<i.length;r++)t=i[r].flags,n[r+4]=t.dependsOn<<4|t.isDependedOn<<2|t.hasRedundancy;return u(T.sdtp,n)},g=function(e){return u(T.stbl,y(e),u(T.stts,R),u(T.stsc,D),u(T.stsz,O),u(T.stco,L))},y=function(e){return u(T.stsd,new Uint8Array([0,0,0,0,0,0,0,1]),("video"===e.type?M:N)(e))},M=function(e){for(var t,i,n=e.sps||[],r=e.pps||[],a=[],s=[],o=0;o<n.length;o++)a.push((65280&n[o].byteLength)>>>8),a.push(255&n[o].byteLength),a=a.concat(Array.prototype.slice.call(n[o]));for(o=0;o<r.length;o++)s.push((65280&r[o].byteLength)>>>8),s.push(255&r[o].byteLength),s=s.concat(Array.prototype.slice.call(r[o]));return t=[T.avc1,new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,(65280&e.width)>>8,255&e.width,(65280&e.height)>>8,255&e.height,0,72,0,0,0,72,0,0,0,0,0,0,0,1,19,118,105,100,101,111,106,115,45,99,111,110,116,114,105,98,45,104,108,115,0,0,0,0,0,0,0,0,0,0,0,0,0,24,17,17]),u(T.avcC,new Uint8Array([1,e.profileIdc,e.profileCompatibility,e.levelIdc,255].concat([n.length],a,[r.length],s))),u(T.btrt,new Uint8Array([0,28,156,128,0,45,198,192,0,45,198,192]))],e.sarRatio&&(i=e.sarRatio[0],e=e.sarRatio[1],t.push(u(T.pasp,new Uint8Array([(4278190080&i)>>24,(16711680&i)>>16,(65280&i)>>8,255&i,(4278190080&e)>>24,(16711680&e)>>16,(65280&e)>>8,255&e])))),u.apply(null,t)},N=function(e){return u(T.mp4a,new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,(65280&e.channelcount)>>8,255&e.channelcount,(65280&e.samplesize)>>8,255&e.samplesize,0,0,0,0,(65280&e.samplerate)>>8,255&e.samplerate,0,0]),i(e))},d=function(e){e=new Uint8Array([0,0,0,7,0,0,0,0,0,0,0,0,(4278190080&e.id)>>24,(16711680&e.id)>>16,(65280&e.id)>>8,255&e.id,0,0,0,0,(4278190080&e.duration)>>24,(16711680&e.duration)>>16,(65280&e.duration)>>8,255&e.duration,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,(65280&e.width)>>8,255&e.width,0,0,(65280&e.height)>>8,255&e.height,0,0]);return u(T.tkhd,e)},v=function(e){var t,i=u(T.tfhd,new Uint8Array([0,0,0,58,(4278190080&e.id)>>24,(16711680&e.id)>>16,(65280&e.id)>>8,255&e.id,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0])),n=Math.floor(e.baseMediaDecodeTime/V),r=Math.floor(e.baseMediaDecodeTime%V),n=u(T.tfdt,new Uint8Array([1,0,0,0,n>>>24&255,n>>>16&255,n>>>8&255,255&n,r>>>24&255,r>>>16&255,r>>>8&255,255&r]));return"audio"===e.type?(t=b(e,92),u(T.traf,i,n,t)):(r=m(e),t=b(e,r.length+92),u(T.traf,i,n,t,r))},c=function(e){return e.duration=e.duration||4294967295,u(T.trak,d(e),h(e))},_=function(e){var t=new Uint8Array([0,0,0,0,(4278190080&e.id)>>24,(16711680&e.id)>>16,(65280&e.id)>>8,255&e.id,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1]);return"video"!==e.type&&(t[t.length-1]=0),u(T.trex,t)},U=function(e,t){var i=0,n=0,r=0,a=0;return e.length&&(void 0!==e[0].duration&&(i=1),void 0!==e[0].size&&(n=2),void 0!==e[0].flags&&(r=4),void 0!==e[0].compositionTimeOffset&&(a=8)),[0,0,i|n|r|a,1,(4278190080&e.length)>>>24,(16711680&e.length)>>>16,(65280&e.length)>>>8,255&e.length,(4278190080&t)>>>24,(16711680&t)>>>16,(65280&t)>>>8,255&t]},B=function(e,t){var i,n,r,a,s=e.samples||[];for(t+=20+16*s.length,t=U(s,t),(n=new Uint8Array(t.length+16*s.length)).set(t),i=t.length,a=0;a<s.length;a++)r=s[a],n[i++]=(4278190080&r.duration)>>>24,n[i++]=(16711680&r.duration)>>>16,n[i++]=(65280&r.duration)>>>8,n[i++]=255&r.duration,n[i++]=(4278190080&r.size)>>>24,n[i++]=(16711680&r.size)>>>16,n[i++]=(65280&r.size)>>>8,n[i++]=255&r.size,n[i++]=r.flags.isLeading<<2|r.flags.dependsOn,n[i++]=r.flags.isDependedOn<<6|r.flags.hasRedundancy<<4|r.flags.paddingValue<<1|r.flags.isNonSyncSample,n[i++]=61440&r.flags.degradationPriority,n[i++]=15&r.flags.degradationPriority,n[i++]=(4278190080&r.compositionTimeOffset)>>>24,n[i++]=(16711680&r.compositionTimeOffset)>>>16,n[i++]=(65280&r.compositionTimeOffset)>>>8,n[i++]=255&r.compositionTimeOffset;return u(T.trun,n)},F=function(e,t){var i,n,r,a,s=e.samples||[];for(t+=20+8*s.length,t=U(s,t),(i=new Uint8Array(t.length+8*s.length)).set(t),n=t.length,a=0;a<s.length;a++)r=s[a],i[n++]=(4278190080&r.duration)>>>24,i[n++]=(16711680&r.duration)>>>16,i[n++]=(65280&r.duration)>>>8,i[n++]=255&r.duration,i[n++]=(4278190080&r.size)>>>24,i[n++]=(16711680&r.size)>>>16,i[n++]=(65280&r.size)>>>8,i[n++]=255&r.size;return u(T.trun,i)},b=function(e,t){return("audio"===e.type?F:B)(e,t)};n=function(){return u(T.ftyp,S,w,S,E)};function W(e,t){var i={size:0,flags:{isLeading:0,dependsOn:1,isDependedOn:0,hasRedundancy:0,degradationPriority:0,isNonSyncSample:1}};return i.dataOffset=t,i.compositionTimeOffset=e.pts-e.dts,i.duration=e.duration,i.size=4*e.length,i.size+=e.byteLength,e.keyFrame&&(i.flags.dependsOn=2,i.flags.isNonSyncSample=0),i}function G(e){for(var t=[];e--;)t.push(0);return t}function z(){var e,i;return X||(e={96e3:[ie,[227,64],G(154),[56]],88200:[ie,[231],G(170),[56]],64e3:[ie,[248,192],G(240),[56]],48e3:[ie,[255,192],G(268),[55,148,128],G(54),[112]],44100:[ie,[255,192],G(268),[55,163,128],G(84),[112]],32e3:[ie,[255,192],G(268),[55,234],G(226),[112]],24e3:[ie,[255,192],G(268),[55,255,128],G(268),[111,112],G(126),[224]],16e3:[ie,[255,192],G(268),[55,255,128],G(268),[111,255],G(269),[223,108],G(195),[1,192]],12e3:[ne,G(268),[3,127,248],G(268),[6,255,240],G(268),[13,255,224],G(268),[27,253,128],G(259),[56]],11025:[ne,G(268),[3,127,248],G(268),[6,255,240],G(268),[13,255,224],G(268),[27,255,192],G(268),[55,175,128],G(108),[112]],8e3:[ne,G(268),[3,121,16],G(47),[7]]},i=e,X=Object.keys(i).reduce(function(e,t){return e[t]=new Uint8Array(i[t].reduce(function(e,t){return e.concat(t)},[])),e},{})),X}var X,K=function(e){return u(T.mdat,e)},Y=We,Q=function(e){var t=n(),i=s(e),e=new Uint8Array(t.byteLength+i.byteLength);return e.set(t),e.set(i,t.byteLength),e},$=function(e){var t,i,n=[],r=[];for(r.byteLength=0,r.nalCount=0,r.duration=0,t=n.byteLength=0;t<e.length;t++)"access_unit_delimiter_rbsp"===(i=e[t]).nalUnitType?(n.length&&(n.duration=i.dts-n.dts,r.byteLength+=n.byteLength,r.nalCount+=n.length,r.duration+=n.duration,r.push(n)),(n=[i]).byteLength=i.data.byteLength,n.pts=i.pts,n.dts=i.dts):("slice_layer_without_partitioning_rbsp_idr"===i.nalUnitType&&(n.keyFrame=!0),n.duration=i.dts-n.dts,n.byteLength+=i.data.byteLength,n.push(i));return r.length&&(!n.duration||n.duration<=0)&&(n.duration=r[r.length-1].duration),r.byteLength+=n.byteLength,r.nalCount+=n.length,r.duration+=n.duration,r.push(n),r},J=function(e){var t,i,n=[],r=[];for(n.byteLength=0,n.nalCount=0,n.duration=0,n.pts=e[0].pts,n.dts=e[0].dts,r.byteLength=0,r.nalCount=0,r.duration=0,r.pts=e[0].pts,r.dts=e[0].dts,t=0;t<e.length;t++)(i=e[t]).keyFrame?(n.length&&(r.push(n),r.byteLength+=n.byteLength,r.nalCount+=n.nalCount,r.duration+=n.duration),(n=[i]).nalCount=i.length,n.byteLength=i.byteLength,n.pts=i.pts,n.dts=i.dts,n.duration=i.duration):(n.duration+=i.duration,n.nalCount+=i.length,n.byteLength+=i.byteLength,n.push(i));return r.length&&n.duration<=0&&(n.duration=r[r.length-1].duration),r.byteLength+=n.byteLength,r.nalCount+=n.nalCount,r.duration+=n.duration,r.push(n),r},Z=function(e){var t;return!e[0][0].keyFrame&&1<e.length&&(t=e.shift(),e.byteLength-=t.byteLength,e.nalCount-=t.nalCount,e[0][0].dts=t.dts,e[0][0].pts=t.pts,e[0][0].duration+=t.duration),e},ee=function(e,t){for(var i,n,r,a=t||0,s=[],o=0;o<e.length;o++)for(n=e[o],i=0;i<n.length;i++)r=n[i],a+=(r=W(r,a)).size,s.push(r);return s},te=function(e){for(var t,i,n,r,a,s=0,o=e.byteLength,u=e.nalCount,l=new Uint8Array(o+4*u),c=new DataView(l.buffer),d=0;d<e.length;d++)for(n=e[d],t=0;t<n.length;t++)for(r=n[t],i=0;i<r.length;i++)a=r[i],c.setUint32(s,a.data.byteLength),l.set(a.data,s+=4),s+=a.data.byteLength;return l},ie=[33,16,5,32,164,27],ne=[33,65,108,84,1,2,4,8,168,2,4,8,17,191,252],re=function(e){return 9e4*e},ae=function(e,t){return e*t},se=function(e){return e/9e4},oe=function(e,t){return e/t},ue=9e4,le=re,ce=se,de=function(e,t){return re(oe(e,t))},he=function(e,t){return ae(se(e),t)},pe=function(e,t,i){return se(i?e:e-t)},fe=function(e,t,i,n){var r,a,s,o,u,l,c=0,d=0;if(t.length&&(r=de(e.baseMediaDecodeTime,e.samplerate),a=Math.ceil(ue/(e.samplerate/1024)),i&&n&&(s=r-Math.max(i,n),d=(c=Math.floor(s/a))*a),!(c<1||ue/2<d))){for(o=(o=z()[e.samplerate])||t[0].data,u=0;u<c;u++)l=t[0],t.splice(0,0,{data:o,dts:l.dts-a,pts:l.pts-a});return e.baseMediaDecodeTime-=Math.floor(he(d,e.samplerate)),d}},me=function(e,t,i){return t.minSegmentDts>=i?e:(t.minSegmentDts=1/0,e.filter(function(e){return e.dts>=i&&(t.minSegmentDts=Math.min(t.minSegmentDts,e.dts),t.minSegmentPts=t.minSegmentDts,!0)}))},ge=function(e){for(var t,i=[],n=0;n<e.length;n++)t=e[n],i.push({size:t.data.byteLength,duration:1024});return i},ye=function(e){for(var t,i=0,n=new Uint8Array(function(e){for(var t=0,i=0;i<e.length;i++)t+=e[i].data.byteLength;return t}(e)),r=0;r<e.length;r++)t=e[r],n.set(t.data,i),i+=t.data.byteLength;return n},ve=ue,_e=function(e){delete e.minSegmentDts,delete e.maxSegmentDts,delete e.minSegmentPts,delete e.maxSegmentPts},be=function(e,t){var i=e.minSegmentDts;return t||(i-=e.timelineStartInfo.dts),t=e.timelineStartInfo.baseMediaDecodeTime,t+=i,t=Math.max(0,t),"audio"===e.type&&(t*=e.samplerate/ve,t=Math.floor(t)),t},Te=function(e,t){"number"==typeof t.pts&&(void 0===e.timelineStartInfo.pts&&(e.timelineStartInfo.pts=t.pts),void 0===e.minSegmentPts?e.minSegmentPts=t.pts:e.minSegmentPts=Math.min(e.minSegmentPts,t.pts),void 0===e.maxSegmentPts?e.maxSegmentPts=t.pts:e.maxSegmentPts=Math.max(e.maxSegmentPts,t.pts)),"number"==typeof t.dts&&(void 0===e.timelineStartInfo.dts&&(e.timelineStartInfo.dts=t.dts),void 0===e.minSegmentDts?e.minSegmentDts=t.dts:e.minSegmentDts=Math.min(e.minSegmentDts,t.dts),void 0===e.maxSegmentDts?e.maxSegmentDts=t.dts:e.maxSegmentDts=Math.max(e.maxSegmentDts,t.dts))},Se=function(e){for(var t=0,i={payloadType:-1,payloadSize:0},n=0,r=0;t<e.byteLength&&128!==e[t];){for(;255===e[t];)n+=255,t++;for(n+=e[t++];255===e[t];)r+=255,t++;if(r+=e[t++],!i.payload&&4===n){if("GA94"===String.fromCharCode(e[t+3],e[t+4],e[t+5],e[t+6])){i.payloadType=n,i.payloadSize=r,i.payload=e.subarray(t,t+r);break}i.payload=void 0}t+=r,r=n=0}return i},we=function(e){return 181!==e.payload[0]||49!=(e.payload[1]<<8|e.payload[2])||"GA94"!==String.fromCharCode(e.payload[3],e.payload[4],e.payload[5],e.payload[6])||3!==e.payload[7]?null:e.payload.subarray(8,e.payload.length-1)},Ee=function(e,t){var i,n,r,a,s=[];if(!(64&t[0]))return s;for(n=31&t[0],i=0;i<n;i++)a={type:3&t[2+(r=3*i)],pts:e},4&t[2+r]&&(a.ccData=t[3+r]<<8|t[4+r],s.push(a));return s},ke=function(e){for(var t=e.byteLength,i=[],n=1;n<t-2;)0===e[n]&&0===e[n+1]&&3===e[n+2]?(i.push(n+2),n+=2):n++;if(0===i.length)return e;for(var r=t-i.length,a=new Uint8Array(r),s=0,n=0;n<r;s++,n++)s===i[0]&&(s++,i.shift()),a[n]=e[s];return a},Ce=4,Ie=function e(t){t=t||{},e.prototype.init.call(this),this.parse708captions_="boolean"!=typeof t.parse708captions||t.parse708captions,this.captionPackets_=[],this.ccStreams_=[new Ue(0,0),new Ue(0,1),new Ue(1,0),new Ue(1,1)],this.parse708captions_&&(this.cc708Stream_=new De({captionServices:t.captionServices})),this.reset(),this.ccStreams_.forEach(function(e){e.on("data",this.trigger.bind(this,"data")),e.on("partialdone",this.trigger.bind(this,"partialdone")),e.on("done",this.trigger.bind(this,"done"))},this),this.parse708captions_&&(this.cc708Stream_.on("data",this.trigger.bind(this,"data")),this.cc708Stream_.on("partialdone",this.trigger.bind(this,"partialdone")),this.cc708Stream_.on("done",this.trigger.bind(this,"done")))};(Ie.prototype=new j).push=function(e){var t,i;if("sei_rbsp"===e.nalUnitType&&(t=Se(e.escapedRBSP)).payload&&t.payloadType===Ce&&(i=we(t)))if(e.dts<this.latestDts_)this.ignoreNextEqualDts_=!0;else{if(e.dts===this.latestDts_&&this.ignoreNextEqualDts_)return this.numSameDts_--,void(this.numSameDts_||(this.ignoreNextEqualDts_=!1));i=Ee(e.pts,i),this.captionPackets_=this.captionPackets_.concat(i),this.latestDts_!==e.dts&&(this.numSameDts_=0),this.numSameDts_++,this.latestDts_=e.dts}},Ie.prototype.flushCCStreams=function(t){this.ccStreams_.forEach(function(e){return"flush"===t?e.flush():e.partialFlush()},this)},Ie.prototype.flushStream=function(e){this.captionPackets_.length&&(this.captionPackets_.forEach(function(e,t){e.presortIndex=t}),this.captionPackets_.sort(function(e,t){return e.pts===t.pts?e.presortIndex-t.presortIndex:e.pts-t.pts}),this.captionPackets_.forEach(function(e){e.type<2?this.dispatchCea608Packet(e):this.dispatchCea708Packet(e)},this),this.captionPackets_.length=0),this.flushCCStreams(e)},Ie.prototype.flush=function(){return this.flushStream("flush")},Ie.prototype.partialFlush=function(){return this.flushStream("partialFlush")},Ie.prototype.reset=function(){this.latestDts_=null,this.ignoreNextEqualDts_=!1,this.numSameDts_=0,this.activeCea608Channel_=[null,null],this.ccStreams_.forEach(function(e){e.reset()})},Ie.prototype.dispatchCea608Packet=function(e){this.setsTextOrXDSActive(e)?this.activeCea608Channel_[e.type]=null:this.setsChannel1Active(e)?this.activeCea608Channel_[e.type]=0:this.setsChannel2Active(e)&&(this.activeCea608Channel_[e.type]=1),null!==this.activeCea608Channel_[e.type]&&this.ccStreams_[(e.type<<1)+this.activeCea608Channel_[e.type]].push(e)},Ie.prototype.setsChannel1Active=function(e){return 4096==(30720&e.ccData)},Ie.prototype.setsChannel2Active=function(e){return 6144==(30720&e.ccData)},Ie.prototype.setsTextOrXDSActive=function(e){return 256==(28928&e.ccData)||4138==(30974&e.ccData)||6186==(30974&e.ccData)},Ie.prototype.dispatchCea708Packet=function(e){this.parse708captions_&&this.cc708Stream_.push(e)};function xe(e){return 32<=e&&e<=127||160<=e&&e<=255}function Ae(e){this.windowNum=e,this.reset()}var Pe={127:9834,4128:32,4129:160,4133:8230,4138:352,4140:338,4144:9608,4145:8216,4146:8217,4147:8220,4148:8221,4149:8226,4153:8482,4154:353,4156:339,4157:8480,4159:376,4214:8539,4215:8540,4216:8541,4217:8542,4218:9168,4219:9124,4220:9123,4221:9135,4222:9126,4223:9121,4256:12600};Ae.prototype.reset=function(){this.clearText(),this.pendingNewLine=!1,this.winAttr={},this.penAttr={},this.penLoc={},this.penColor={},this.visible=0,this.rowLock=0,this.columnLock=0,this.priority=0,this.relativePositioning=0,this.anchorVertical=0,this.anchorHorizontal=0,this.anchorPoint=0,this.rowCount=1,this.virtualRowCount=this.rowCount+1,this.columnCount=41,this.windowStyle=0,this.penStyle=0},Ae.prototype.getText=function(){return this.rows.join("\n")},Ae.prototype.clearText=function(){this.rows=[""],this.rowIdx=0},Ae.prototype.newLine=function(e){for(this.rows.length>=this.virtualRowCount&&"function"==typeof this.beforeRowOverflow&&this.beforeRowOverflow(e),0<this.rows.length&&(this.rows.push(""),this.rowIdx++);this.rows.length>this.virtualRowCount;)this.rows.shift(),this.rowIdx--},Ae.prototype.isEmpty=function(){return 0===this.rows.length||1===this.rows.length&&""===this.rows[0]},Ae.prototype.addText=function(e){this.rows[this.rowIdx]+=e},Ae.prototype.backspace=function(){var e;this.isEmpty()||(e=this.rows[this.rowIdx],this.rows[this.rowIdx]=e.substr(0,e.length-1))};function Le(e,t,i){this.serviceNum=e,this.text="",this.currentWindow=new Ae(-1),this.windows=[],this.stream=i,"string"==typeof t&&this.createTextDecoder(t)}Le.prototype.init=function(e,t){this.startPts=e;for(var i=0;i<8;i++)this.windows[i]=new Ae(i),"function"==typeof t&&(this.windows[i].beforeRowOverflow=t)},Le.prototype.setCurrentWindow=function(e){this.currentWindow=this.windows[e]},Le.prototype.createTextDecoder=function(t){if("undefined"==typeof TextDecoder)this.stream.trigger("log",{level:"warn",message:"The `encoding` option is unsupported without TextDecoder support"});else try{this.textDecoder_=new TextDecoder(t)}catch(e){this.stream.trigger("log",{level:"warn",message:"TextDecoder could not be created with "+t+" encoding. "+e})}};var De=function e(t){t=t||{},e.prototype.init.call(this);var i,n=this,r=t.captionServices||{},a={};Object.keys(r).forEach(function(e){i=r[e],/^SERVICE/.test(e)&&(a[e]=i.encoding)}),this.serviceEncodings=a,this.current708Packet=null,this.services={},this.push=function(e){(3===e.type||null===n.current708Packet)&&n.new708Packet(),n.add708Bytes(e)}};De.prototype=new j,De.prototype.new708Packet=function(){null!==this.current708Packet&&this.push708Packet(),this.current708Packet={data:[],ptsVals:[]}},De.prototype.add708Bytes=function(e){var t=e.ccData,i=t>>>8,t=255&t;this.current708Packet.ptsVals.push(e.pts),this.current708Packet.data.push(i),this.current708Packet.data.push(t)},De.prototype.push708Packet=function(){var e,t=this.current708Packet,i=t.data,n=null,r=0,a=i[r++];for(t.seq=a>>6,t.sizeCode=63&a;r<i.length;r++)e=31&(a=i[r++]),7===(n=a>>5)&&0<e&&(n=i[r++]),this.pushServiceBlock(n,r,e),0<e&&(r+=e-1)},De.prototype.pushServiceBlock=function(e,t,i){for(var n,r=t,a=this.current708Packet.data,s=(s=this.services[e])||this.initService(e,r);r<t+i&&r<a.length;r++)n=a[r],xe(n)?r=this.handleText(r,s):24===n?r=this.multiByteCharacter(r,s):16===n?r=this.extendedCommands(r,s):128<=n&&n<=135?r=this.setCurrentWindow(r,s):152<=n&&n<=159?r=this.defineWindow(r,s):136===n?r=this.clearWindows(r,s):140===n?r=this.deleteWindows(r,s):137===n?r=this.displayWindows(r,s):138===n?r=this.hideWindows(r,s):139===n?r=this.toggleWindows(r,s):151===n?r=this.setWindowAttributes(r,s):144===n?r=this.setPenAttributes(r,s):145===n?r=this.setPenColor(r,s):146===n?r=this.setPenLocation(r,s):143===n?s=this.reset(r,s):8===n?s.currentWindow.backspace():12===n?s.currentWindow.clearText():13===n?s.currentWindow.pendingNewLine=!0:14===n?s.currentWindow.clearText():141===n&&r++},De.prototype.extendedCommands=function(e,t){var i=this.current708Packet.data[++e];return e=xe(i)?this.handleText(e,t,{isExtended:!0}):e},De.prototype.getPts=function(e){return this.current708Packet.ptsVals[Math.floor(e/2)]},De.prototype.initService=function(t,e){var i,n="SERVICE"+t,r=this;return n in this.serviceEncodings&&(i=this.serviceEncodings[n]),this.services[t]=new Le(t,i,r),this.services[t].init(this.getPts(e),function(e){r.flushDisplayed(e,r.services[t])}),this.services[t]},De.prototype.handleText=function(e,t,i){var n,r=i&&i.isExtended,a=i&&i.isMultiByte,s=this.current708Packet.data,o=r?4096:0,u=s[e],i=s[e+1],s=t.currentWindow,l=t.textDecoder_&&!r?(a?(n=[u,i],e++):n=[u],t.textDecoder_.decode(new Uint8Array(n))):(l=Pe[u=o|u]||u,4096&u&&u===l?"":String.fromCharCode(l));return s.pendingNewLine&&!s.isEmpty()&&s.newLine(this.getPts(e)),s.pendingNewLine=!1,s.addText(l),e},De.prototype.multiByteCharacter=function(e,t){var i=this.current708Packet.data,n=i[e+1],i=i[e+2];return e=xe(n)&&xe(i)?this.handleText(++e,t,{isMultiByte:!0}):e},De.prototype.setCurrentWindow=function(e,t){var i=this.current708Packet.data[e];return t.setCurrentWindow(7&i),e},De.prototype.defineWindow=function(e,t){var i=this.current708Packet.data,n=i[e];t.setCurrentWindow(7&n);t=t.currentWindow,n=i[++e];return t.visible=(32&n)>>5,t.rowLock=(16&n)>>4,t.columnLock=(8&n)>>3,t.priority=7&n,n=i[++e],t.relativePositioning=(128&n)>>7,t.anchorVertical=127&n,n=i[++e],t.anchorHorizontal=n,n=i[++e],t.anchorPoint=(240&n)>>4,t.rowCount=15&n,n=i[++e],t.columnCount=63&n,n=i[++e],t.windowStyle=(56&n)>>3,t.penStyle=7&n,t.virtualRowCount=t.rowCount+1,e},De.prototype.setWindowAttributes=function(e,t){var i=this.current708Packet.data,n=i[e],t=t.currentWindow.winAttr,n=i[++e];return t.fillOpacity=(192&n)>>6,t.fillRed=(48&n)>>4,t.fillGreen=(12&n)>>2,t.fillBlue=3&n,n=i[++e],t.borderType=(192&n)>>6,t.borderRed=(48&n)>>4,t.borderGreen=(12&n)>>2,t.borderBlue=3&n,n=i[++e],t.borderType+=(128&n)>>5,t.wordWrap=(64&n)>>6,t.printDirection=(48&n)>>4,t.scrollDirection=(12&n)>>2,t.justify=3&n,n=i[++e],t.effectSpeed=(240&n)>>4,t.effectDirection=(12&n)>>2,t.displayEffect=3&n,e},De.prototype.flushDisplayed=function(e,t){for(var i=[],n=0;n<8;n++)t.windows[n].visible&&!t.windows[n].isEmpty()&&i.push(t.windows[n].getText());t.endPts=e,t.text=i.join("\n\n"),this.pushCaption(t),t.startPts=e},De.prototype.pushCaption=function(e){""!==e.text&&(this.trigger("data",{startPts:e.startPts,endPts:e.endPts,text:e.text,stream:"cc708_"+e.serviceNum}),e.text="",e.startPts=e.endPts)},De.prototype.displayWindows=function(e,t){var i=this.current708Packet.data[++e],n=this.getPts(e);this.flushDisplayed(n,t);for(var r=0;r<8;r++)i&1<<r&&(t.windows[r].visible=1);return e},De.prototype.hideWindows=function(e,t){var i=this.current708Packet.data[++e],n=this.getPts(e);this.flushDisplayed(n,t);for(var r=0;r<8;r++)i&1<<r&&(t.windows[r].visible=0);return e},De.prototype.toggleWindows=function(e,t){var i=this.current708Packet.data[++e],n=this.getPts(e);this.flushDisplayed(n,t);for(var r=0;r<8;r++)i&1<<r&&(t.windows[r].visible^=1);return e},De.prototype.clearWindows=function(e,t){var i=this.current708Packet.data[++e],n=this.getPts(e);this.flushDisplayed(n,t);for(var r=0;r<8;r++)i&1<<r&&t.windows[r].clearText();return e},De.prototype.deleteWindows=function(e,t){var i=this.current708Packet.data[++e],n=this.getPts(e);this.flushDisplayed(n,t);for(var r=0;r<8;r++)i&1<<r&&t.windows[r].reset();return e},De.prototype.setPenAttributes=function(e,t){var i=this.current708Packet.data,n=i[e],t=t.currentWindow.penAttr,n=i[++e];return t.textTag=(240&n)>>4,t.offset=(12&n)>>2,t.penSize=3&n,n=i[++e],t.italics=(128&n)>>7,t.underline=(64&n)>>6,t.edgeType=(56&n)>>3,t.fontStyle=7&n,e},De.prototype.setPenColor=function(e,t){var i=this.current708Packet.data,n=i[e],t=t.currentWindow.penColor,n=i[++e];return t.fgOpacity=(192&n)>>6,t.fgRed=(48&n)>>4,t.fgGreen=(12&n)>>2,t.fgBlue=3&n,n=i[++e],t.bgOpacity=(192&n)>>6,t.bgRed=(48&n)>>4,t.bgGreen=(12&n)>>2,t.bgBlue=3&n,n=i[++e],t.edgeRed=(48&n)>>4,t.edgeGreen=(12&n)>>2,t.edgeBlue=3&n,e},De.prototype.setPenLocation=function(e,t){var i=this.current708Packet.data,n=i[e],r=t.currentWindow.penLoc;return t.currentWindow.pendingNewLine=!0,n=i[++e],r.row=15&n,n=i[++e],r.column=63&n,e},De.prototype.reset=function(e,t){var i=this.getPts(e);return this.flushDisplayed(i,t),this.initService(t.serviceNum,e)};function Oe(e){return null===e?"":(e=Me[e]||e,String.fromCharCode(e))}function Re(){for(var e=[],t=15;t--;)e.push("");return e}var Me={42:225,92:233,94:237,95:243,96:250,123:231,124:247,125:209,126:241,127:9608,304:174,305:176,306:189,307:191,308:8482,309:162,310:163,311:9834,312:224,313:160,314:232,315:226,316:234,317:238,318:244,319:251,544:193,545:201,546:211,547:218,548:220,549:252,550:8216,551:161,552:42,553:39,554:8212,555:169,556:8480,557:8226,558:8220,559:8221,560:192,561:194,562:199,563:200,564:202,565:203,566:235,567:206,568:207,569:239,570:212,571:217,572:249,573:219,574:171,575:187,800:195,801:227,802:205,803:204,804:236,805:210,806:242,807:213,808:245,809:123,810:125,811:92,812:94,813:95,814:124,815:126,816:196,817:228,818:214,819:246,820:223,821:165,822:164,823:9474,824:197,825:229,826:216,827:248,828:9484,829:9488,830:9492,831:9496},Ne=[4352,4384,4608,4640,5376,5408,5632,5664,5888,5920,4096,4864,4896,5120,5152],Ue=function e(t,i){e.prototype.init.call(this),this.field_=t||0,this.dataChannel_=i||0,this.name_="CC"+(1+(this.field_<<1|this.dataChannel_)),this.setConstants(),this.reset(),this.push=function(e){var t,i,n,r,a=32639&e.ccData;a!==this.lastControlCode_?(4096==(61440&a)?this.lastControlCode_=a:a!==this.PADDING_&&(this.lastControlCode_=null),t=a>>>8,i=255&a,a===this.PADDING_||(a===this.RESUME_CAPTION_LOADING_?this.mode_="popOn":a===this.END_OF_CAPTION_?(this.mode_="popOn",this.clearFormatting(e.pts),this.flushDisplayed(e.pts),r=this.displayed_,this.displayed_=this.nonDisplayed_,this.nonDisplayed_=r,this.startPts_=e.pts):a===this.ROLL_UP_2_ROWS_?(this.rollUpRows_=2,this.setRollUp(e.pts)):a===this.ROLL_UP_3_ROWS_?(this.rollUpRows_=3,this.setRollUp(e.pts)):a===this.ROLL_UP_4_ROWS_?(this.rollUpRows_=4,this.setRollUp(e.pts)):a===this.CARRIAGE_RETURN_?(this.clearFormatting(e.pts),this.flushDisplayed(e.pts),this.shiftRowsUp_(),this.startPts_=e.pts):a===this.BACKSPACE_?"popOn"===this.mode_?this.nonDisplayed_[this.row_]=this.nonDisplayed_[this.row_].slice(0,-1):this.displayed_[this.row_]=this.displayed_[this.row_].slice(0,-1):a===this.ERASE_DISPLAYED_MEMORY_?(this.flushDisplayed(e.pts),this.displayed_=Re()):a===this.ERASE_NON_DISPLAYED_MEMORY_?this.nonDisplayed_=Re():a===this.RESUME_DIRECT_CAPTIONING_?("paintOn"!==this.mode_&&(this.flushDisplayed(e.pts),this.displayed_=Re()),this.mode_="paintOn",this.startPts_=e.pts):this.isSpecialCharacter(t,i)?(n=Oe((t=(3&t)<<8)|i),this[this.mode_](e.pts,n),this.column_++):this.isExtCharacter(t,i)?("popOn"===this.mode_?this.nonDisplayed_[this.row_]=this.nonDisplayed_[this.row_].slice(0,-1):this.displayed_[this.row_]=this.displayed_[this.row_].slice(0,-1),n=Oe((t=(3&t)<<8)|i),this[this.mode_](e.pts,n),this.column_++):this.isMidRowCode(t,i)?(this.clearFormatting(e.pts),this[this.mode_](e.pts," "),this.column_++,14==(14&i)&&this.addFormatting(e.pts,["i"]),1==(1&i)&&this.addFormatting(e.pts,["u"])):this.isOffsetControlCode(t,i)?this.column_+=3&i:this.isPAC(t,i)?(r=Ne.indexOf(7968&a),"rollUp"===this.mode_&&(r-this.rollUpRows_+1<0&&(r=this.rollUpRows_-1),this.setRollUp(e.pts,r)),r!==this.row_&&(this.clearFormatting(e.pts),this.row_=r),1&i&&-1===this.formatting_.indexOf("u")&&this.addFormatting(e.pts,["u"]),16==(16&a)&&(this.column_=4*((14&a)>>1)),this.isColorPAC(i)&&14==(14&i)&&this.addFormatting(e.pts,["i"])):this.isNormalChar(t)&&(0===i&&(i=null),n=Oe(t),n+=Oe(i),this[this.mode_](e.pts,n),this.column_+=n.length))):this.lastControlCode_=null}};Ue.prototype=new j,Ue.prototype.flushDisplayed=function(e){var t=this.displayed_.map(function(e,t){try{return e.trim()}catch(e){return this.trigger("log",{level:"warn",message:"Skipping a malformed 608 caption at index "+t+"."}),""}},this).join("\n").replace(/^\n+|\n+$/g,"");t.length&&this.trigger("data",{startPts:this.startPts_,endPts:e,text:t,stream:this.name_})},Ue.prototype.reset=function(){this.mode_="popOn",this.topRow_=0,this.startPts_=0,this.displayed_=Re(),this.nonDisplayed_=Re(),this.lastControlCode_=null,this.column_=0,this.row_=14,this.rollUpRows_=2,this.formatting_=[]},Ue.prototype.setConstants=function(){0===this.dataChannel_?(this.BASE_=16,this.EXT_=17,this.CONTROL_=(20|this.field_)<<8,this.OFFSET_=23):1===this.dataChannel_&&(this.BASE_=24,this.EXT_=25,this.CONTROL_=(28|this.field_)<<8,this.OFFSET_=31),this.PADDING_=0,this.RESUME_CAPTION_LOADING_=32|this.CONTROL_,this.END_OF_CAPTION_=47|this.CONTROL_,this.ROLL_UP_2_ROWS_=37|this.CONTROL_,this.ROLL_UP_3_ROWS_=38|this.CONTROL_,this.ROLL_UP_4_ROWS_=39|this.CONTROL_,this.CARRIAGE_RETURN_=45|this.CONTROL_,this.RESUME_DIRECT_CAPTIONING_=41|this.CONTROL_,this.BACKSPACE_=33|this.CONTROL_,this.ERASE_DISPLAYED_MEMORY_=44|this.CONTROL_,this.ERASE_NON_DISPLAYED_MEMORY_=46|this.CONTROL_},Ue.prototype.isSpecialCharacter=function(e,t){return e===this.EXT_&&48<=t&&t<=63},Ue.prototype.isExtCharacter=function(e,t){return(e===this.EXT_+1||e===this.EXT_+2)&&32<=t&&t<=63},Ue.prototype.isMidRowCode=function(e,t){return e===this.EXT_&&32<=t&&t<=47},Ue.prototype.isOffsetControlCode=function(e,t){return e===this.OFFSET_&&33<=t&&t<=35},Ue.prototype.isPAC=function(e,t){return e>=this.BASE_&&e<this.BASE_+8&&64<=t&&t<=127},Ue.prototype.isColorPAC=function(e){return 64<=e&&e<=79||96<=e&&e<=127},Ue.prototype.isNormalChar=function(e){return 32<=e&&e<=127},Ue.prototype.setRollUp=function(e,t){if("rollUp"!==this.mode_&&(this.row_=14,this.mode_="rollUp",this.flushDisplayed(e),this.nonDisplayed_=Re(),this.displayed_=Re()),void 0!==t&&t!==this.row_)for(var i=0;i<this.rollUpRows_;i++)this.displayed_[t-i]=this.displayed_[this.row_-i],this.displayed_[this.row_-i]="";void 0===t&&(t=this.row_),this.topRow_=t-this.rollUpRows_+1},Ue.prototype.addFormatting=function(e,t){this.formatting_=this.formatting_.concat(t);t=t.reduce(function(e,t){return e+"<"+t+">"},"");this[this.mode_](e,t)},Ue.prototype.clearFormatting=function(e){var t;this.formatting_.length&&(t=this.formatting_.reverse().reduce(function(e,t){return e+"</"+t+">"},""),this.formatting_=[],this[this.mode_](e,t))},Ue.prototype.popOn=function(e,t){var i=this.nonDisplayed_[this.row_];this.nonDisplayed_[this.row_]=i+=t},Ue.prototype.rollUp=function(e,t){var i=this.displayed_[this.row_];this.displayed_[this.row_]=i+=t},Ue.prototype.shiftRowsUp_=function(){for(var e=0;e<this.topRow_;e++)this.displayed_[e]="";for(e=this.row_+1;e<15;e++)this.displayed_[e]="";for(e=this.topRow_;e<this.row_;e++)this.displayed_[e]=this.displayed_[e+1];this.displayed_[this.row_]=""},Ue.prototype.paintOn=function(e,t){var i=this.displayed_[this.row_];this.displayed_[this.row_]=i+=t};function Be(e,t){var i=1;for(t<e&&(i=-1);4294967296<Math.abs(t-e);)e+=8589934592*i;return e}var Fe={CaptionStream:Ie,Cea608Stream:Ue,Cea708Stream:De},je={H264_STREAM_TYPE:27,ADTS_STREAM_TYPE:15,METADATA_STREAM_TYPE:21},e=function e(t){var i,n;e.prototype.init.call(this),this.type_=t||"shared",this.push=function(e){"shared"!==this.type_&&e.type!==this.type_||(void 0===n&&(n=e.dts),e.dts=Be(e.dts,n),e.pts=Be(e.pts,n),i=e.dts,this.trigger("data",e))},this.flush=function(){n=i,this.trigger("done")},this.endTimeline=function(){this.flush(),this.trigger("endedtimeline")},this.discontinuity=function(){i=n=void 0},this.reset=function(){this.discontinuity(),this.trigger("reset")}};e.prototype=new j;function He(e,t,i){for(var n="",r=t;r<i;r++)n+="%"+("00"+e[r].toString(16)).slice(-2);return n}function qe(e,t,i){return decodeURIComponent(He(e,t,i))}function Ve(e){return e[0]<<21|e[1]<<14|e[2]<<7|e[3]}var We=e,Ie=Be,Ge={TXXX:function(e){var t;if(3===e.data[0]){for(t=1;t<e.data.length;t++)if(0===e.data[t]){e.description=qe(e.data,1,t),e.value=qe(e.data,t+1,e.data.length).replace(/\0*$/,"");break}e.data=e.value}},WXXX:function(e){var t;if(3===e.data[0])for(t=1;t<e.data.length;t++)if(0===e.data[t]){e.description=qe(e.data,1,t),e.url=qe(e.data,t+1,e.data.length);break}},PRIV:function(e){for(var t,i=0;i<e.data.length;i++)if(0===e.data[i]){e.owner=(t=e.data,unescape(He(t,0,i)));break}e.privateData=e.data.subarray(i+1),e.data=e.privateData}},ze=function(e){var t,i={descriptor:e&&e.descriptor},u=0,l=[],c=0;if(ze.prototype.init.call(this),this.dispatchType=je.METADATA_STREAM_TYPE.toString(16),i.descriptor)for(t=0;t<i.descriptor.length;t++)this.dispatchType+=("00"+i.descriptor[t].toString(16)).slice(-2);this.push=function(e){var t,i,n,r,a,s,o;if("timed-metadata"===e.type)if(e.dataAlignmentIndicator&&(c=0,l.length=0),0===l.length&&(e.data.length<10||e.data[0]!=="I".charCodeAt(0)||e.data[1]!=="D".charCodeAt(0)||e.data[2]!=="3".charCodeAt(0)))this.trigger("log",{level:"warn",message:"Skipping unrecognized metadata packet"});else if(l.push(e),c+=e.data.byteLength,1===l.length&&(u=Ve(e.data.subarray(6,10)),u+=10),!(c<u)){for(t={data:new Uint8Array(u),frames:[],pts:l[0].pts,dts:l[0].dts},r=0;r<u;)t.data.set(l[0].data.subarray(0,u-r),r),r+=l[0].data.byteLength,c-=l[0].data.byteLength,l.shift();i=10,64&t.data[5]&&(i+=4,i+=Ve(t.data.subarray(10,14)),u-=Ve(t.data.subarray(16,20)));do{if((n=Ve(t.data.subarray(i+4,i+8)))<1)return void this.trigger("log",{level:"warn",message:"Malformed ID3 frame encountered. Skipping metadata parsing."})}while((o={id:String.fromCharCode(t.data[i],t.data[i+1],t.data[i+2],t.data[i+3]),data:t.data.subarray(i+10,i+n+10)}).key=o.id,Ge[o.id]&&(Ge[o.id](o),"com.apple.streaming.transportStreamTimestamp"===o.owner&&(s=(1&(a=o.data)[3])<<30|a[4]<<22|a[5]<<14|a[6]<<6|a[7]>>>2,s*=4,s+=3&a[7],o.timeStamp=s,void 0===t.pts&&void 0===t.dts&&(t.pts=o.timeStamp,t.dts=o.timeStamp),this.trigger("timestamp",o))),t.frames.push(o),i+=10,(i+=n)<u);this.trigger("data",t)}}};ze.prototype=new j;var Xe,Ke,e=ze,We=We,Ye=function(){var r=new Uint8Array(188),a=0;Ye.prototype.init.call(this),this.push=function(e){var t,i=0,n=188;for(a?((t=new Uint8Array(e.byteLength+a)).set(r.subarray(0,a)),t.set(e,a),a=0):t=e;n<t.byteLength;)71!==t[i]||71!==t[n]?(i++,n++):(this.trigger("data",t.subarray(i,n)),i+=188,n+=188);i<t.byteLength&&(r.set(t.subarray(i),0),a=t.byteLength-i)},this.flush=function(){188===a&&71===r[0]&&(this.trigger("data",r),a=0),this.trigger("done")},this.endTimeline=function(){this.flush(),this.trigger("endedtimeline")},this.reset=function(){a=0,this.trigger("reset")}};Ye.prototype=new j,(Xe=function(){var n,r,a,s;Xe.prototype.init.call(this),(s=this).packetsWaitingForPmt=[],this.programMapTable=void 0,n=function(e,t){var i=0;t.payloadUnitStartIndicator&&(i+=e[i]+1),("pat"===t.type?r:a)(e.subarray(i),t)},r=function(e,t){t.section_number=e[7],t.last_section_number=e[8],s.pmtPid=(31&e[10])<<8|e[11],t.pmtPid=s.pmtPid},a=function(e,t){var i,n;if(1&e[5]){for(s.programMapTable={video:null,audio:null,"timed-metadata":{}},i=3+((15&e[1])<<8|e[2])-4,n=12+((15&e[10])<<8|e[11]);n<i;){var r=e[n],a=(31&e[n+1])<<8|e[n+2];r===je.H264_STREAM_TYPE&&null===s.programMapTable.video?s.programMapTable.video=a:r===je.ADTS_STREAM_TYPE&&null===s.programMapTable.audio?s.programMapTable.audio=a:r===je.METADATA_STREAM_TYPE&&(s.programMapTable["timed-metadata"][a]=r),n+=5+((15&e[n+3])<<8|e[n+4])}t.programMapTable=s.programMapTable}},this.push=function(e){var t={},i=4;if(t.payloadUnitStartIndicator=!!(64&e[1]),t.pid=31&e[1],t.pid<<=8,t.pid|=e[2],1<(48&e[3])>>>4&&(i+=e[i]+1),0===t.pid)t.type="pat",n(e.subarray(i),t),this.trigger("data",t);else if(t.pid===this.pmtPid)for(t.type="pmt",n(e.subarray(i),t),this.trigger("data",t);this.packetsWaitingForPmt.length;)this.processPes_.apply(this,this.packetsWaitingForPmt.shift());else void 0===this.programMapTable?this.packetsWaitingForPmt.push([e,i,t]):this.processPes_(e,i,t)},this.processPes_=function(e,t,i){i.pid===this.programMapTable.video?i.streamType=je.H264_STREAM_TYPE:i.pid===this.programMapTable.audio?i.streamType=je.ADTS_STREAM_TYPE:i.streamType=this.programMapTable["timed-metadata"][i.pid],i.type="pes",i.data=e.subarray(t),this.trigger("data",i)}}).prototype=new j,Xe.STREAM_TYPES={h264:27,adts:15},(Ke=function(){function n(e,t,i){var n,r,a,s,o=new Uint8Array(e.size),u={type:t},l=0,c=0;if(e.data.length&&!(e.size<9)){for(u.trackId=e.data[0].pid,l=0;l<e.data.length;l++)n=e.data[l],o.set(n.data,c),c+=n.data.byteLength;a=u,s=(r=o)[0]<<16|r[1]<<8|r[2],a.data=new Uint8Array,1==s&&(a.packetLength=6+(r[4]<<8|r[5]),a.dataAlignmentIndicator=0!=(4&r[6]),192&(s=r[7])&&(a.pts=(14&r[9])<<27|(255&r[10])<<20|(254&r[11])<<12|(255&r[12])<<5|(254&r[13])>>>3,a.pts*=4,a.pts+=(6&r[13])>>>1,a.dts=a.pts,64&s&&(a.dts=(14&r[14])<<27|(255&r[15])<<20|(254&r[16])<<12|(255&r[17])<<5|(254&r[18])>>>3,a.dts*=4,a.dts+=(6&r[18])>>>1)),a.data=r.subarray(9+r[8])),t="video"===t||u.packetLength<=e.size,(i||t)&&(e.size=0,e.data.length=0),t&&d.trigger("data",u)}}var t,d=this,r=!1,a={data:[],size:0},s={data:[],size:0},o={data:[],size:0};Ke.prototype.init.call(this),this.push=function(i){({pat:function(){},pes:function(){var e,t;switch(i.streamType){case je.H264_STREAM_TYPE:e=a,t="video";break;case je.ADTS_STREAM_TYPE:e=s,t="audio";break;case je.METADATA_STREAM_TYPE:e=o,t="timed-metadata";break;default:return}i.payloadUnitStartIndicator&&n(e,t,!0),e.data.push(i),e.size+=i.data.byteLength},pmt:function(){var e={type:"metadata",tracks:[]};null!==(t=i.programMapTable).video&&e.tracks.push({timelineStartInfo:{baseMediaDecodeTime:0},id:+t.video,codec:"avc",type:"video"}),null!==t.audio&&e.tracks.push({timelineStartInfo:{baseMediaDecodeTime:0},id:+t.audio,codec:"adts",type:"audio"}),r=!0,d.trigger("data",e)}})[i.type]()},this.reset=function(){a.size=0,a.data.length=0,s.size=0,s.data.length=0,this.trigger("reset")},this.flushStreams_=function(){n(a,"video"),n(s,"audio"),n(o,"timed-metadata")},this.flush=function(){var e;!r&&t&&(e={type:"metadata",tracks:[]},null!==t.video&&e.tracks.push({timelineStartInfo:{baseMediaDecodeTime:0},id:+t.video,codec:"avc",type:"video"}),null!==t.audio&&e.tracks.push({timelineStartInfo:{baseMediaDecodeTime:0},id:+t.audio,codec:"adts",type:"audio"}),d.trigger("data",e)),r=!1,this.flushStreams_(),this.trigger("done")}}).prototype=new j;var Qe,$e={PAT_PID:0,MP2T_PACKET_LENGTH:188,TransportPacketStream:Ye,TransportParseStream:Xe,ElementaryStream:Ke,TimestampRolloverStream:We,CaptionStream:Fe.CaptionStream,Cea608Stream:Fe.Cea608Stream,Cea708Stream:Fe.Cea708Stream,MetadataStream:e};for(Qe in je)je.hasOwnProperty(Qe)&&($e[Qe]=je[Qe]);var Je=$e,Ze=ue,et=[96e3,88200,64e3,48e3,44100,32e3,24e3,22050,16e3,12e3,11025,8e3,7350],tt=function(u){var l,c=0;tt.prototype.init.call(this),this.skipWarn_=function(e,t){this.trigger("log",{level:"warn",message:"adts skiping bytes "+e+" to "+t+" in frame "+c+" outside syncword"})},this.push=function(e){var t,i,n,r,a,s,o=0;if(u||(c=0),"audio"===e.type){for(l&&l.length?(n=l,(l=new Uint8Array(n.byteLength+e.data.byteLength)).set(n),l.set(e.data,n.byteLength)):l=e.data;o+7<l.length;)if(255===l[o]&&240==(246&l[o+1])){if("number"==typeof s&&(this.skipWarn_(s,o),s=null),i=2*(1&~l[o+1]),t=(3&l[o+3])<<11|l[o+4]<<3|(224&l[o+5])>>5,a=(r=1024*(1+(3&l[o+6])))*Ze/et[(60&l[o+2])>>>2],l.byteLength-o<t)break;this.trigger("data",{pts:e.pts+c*a,dts:e.dts+c*a,sampleCount:r,audioobjecttype:1+(l[o+2]>>>6&3),channelcount:(1&l[o+2])<<2|(192&l[o+3])>>>6,samplerate:et[(60&l[o+2])>>>2],samplingfrequencyindex:(60&l[o+2])>>>2,samplesize:16,data:l.subarray(o+7+i,o+t)}),c++,o+=t}else"number"!=typeof s&&(s=o),o++;"number"==typeof s&&(this.skipWarn_(s,o),s=null),l=l.subarray(o)}},this.flush=function(){c=0,this.trigger("done")},this.reset=function(){l=void 0,this.trigger("reset")},this.endTimeline=function(){l=void 0,this.trigger("endedtimeline")}};tt.prototype=new j;var it,nt,rt=tt,at=function(n){var r=n.byteLength,a=0,s=0;this.length=function(){return 8*r},this.bitsAvailable=function(){return 8*r+s},this.loadWord=function(){var e=n.byteLength-r,t=new Uint8Array(4),i=Math.min(4,r);if(0===i)throw new Error("no bytes available");t.set(n.subarray(e,e+i)),a=new DataView(t.buffer).getUint32(0),s=8*i,r-=i},this.skipBits=function(e){var t;e<s||(e-=s,e-=8*(t=Math.floor(e/8)),r-=t,this.loadWord()),a<<=e,s-=e},this.readBits=function(e){var t=Math.min(s,e),i=a>>>32-t;return 0<(s-=t)?a<<=t:0<r&&this.loadWord(),0<(t=e-t)?i<<t|this.readBits(t):i},this.skipLeadingZeros=function(){for(var e=0;e<s;++e)if(0!=(a&2147483648>>>e))return a<<=e,s-=e,e;return this.loadWord(),e+this.skipLeadingZeros()},this.skipUnsignedExpGolomb=function(){this.skipBits(1+this.skipLeadingZeros())},this.skipExpGolomb=function(){this.skipBits(1+this.skipLeadingZeros())},this.readUnsignedExpGolomb=function(){var e=this.skipLeadingZeros();return this.readBits(e+1)-1},this.readExpGolomb=function(){var e=this.readUnsignedExpGolomb();return 1&e?1+e>>>1:-1*(e>>>1)},this.readBoolean=function(){return 1===this.readBits(1)},this.readUnsignedByte=function(){return this.readBits(8)},this.loadWord()},st=function(){var n,r,a=0;st.prototype.init.call(this),this.push=function(e){for(var t,i=(r=r?((t=new Uint8Array(r.byteLength+e.data.byteLength)).set(r),t.set(e.data,r.byteLength),t):e.data).byteLength;a<i-3;a++)if(1===r[a+2]){n=a+5;break}for(;n<i;)switch(r[n]){case 0:if(0!==r[n-1]){n+=2;break}if(0!==r[n-2]){n++;break}for(a+3!==n-2&&this.trigger("data",r.subarray(a+3,n-2));1!==r[++n]&&n<i;);a=n-2,n+=3;break;case 1:if(0!==r[n-1]||0!==r[n-2]){n+=3;break}this.trigger("data",r.subarray(a+3,n-2)),a=n-2,n+=3;break;default:n+=3}r=r.subarray(a),n-=a,a=0},this.reset=function(){r=null,a=0,this.trigger("reset")},this.flush=function(){r&&3<r.byteLength&&this.trigger("data",r.subarray(a+3)),r=null,a=0,this.trigger("done")},this.endTimeline=function(){this.flush(),this.trigger("endedtimeline")}};st.prototype=new j,nt={100:!0,110:!0,122:!0,244:!0,44:!0,83:!0,86:!0,118:!0,128:!0,138:!0,139:!0,134:!0},(it=function(){var i,n,r,a,s,o,m,t=new st;it.prototype.init.call(this),(i=this).push=function(e){"video"===e.type&&(n=e.trackId,r=e.pts,a=e.dts,t.push(e))},t.on("data",function(e){var t={trackId:n,pts:r,dts:a,data:e,nalUnitTypeCode:31&e[0]};switch(t.nalUnitTypeCode){case 5:t.nalUnitType="slice_layer_without_partitioning_rbsp_idr";break;case 6:t.nalUnitType="sei_rbsp",t.escapedRBSP=s(e.subarray(1));break;case 7:t.nalUnitType="seq_parameter_set_rbsp",t.escapedRBSP=s(e.subarray(1)),t.config=o(t.escapedRBSP);break;case 8:t.nalUnitType="pic_parameter_set_rbsp";break;case 9:t.nalUnitType="access_unit_delimiter_rbsp"}i.trigger("data",t)}),t.on("done",function(){i.trigger("done")}),t.on("partialdone",function(){i.trigger("partialdone")}),t.on("reset",function(){i.trigger("reset")}),t.on("endedtimeline",function(){i.trigger("endedtimeline")}),this.flush=function(){t.flush()},this.partialFlush=function(){t.partialFlush()},this.reset=function(){t.reset()},this.endTimeline=function(){t.endTimeline()},m=function(e,t){for(var i=8,n=8,r=0;r<e;r++)i=0===(n=0!==n?(i+t.readExpGolomb()+256)%256:n)?i:n},s=function(e){for(var t=e.byteLength,i=[],n=1;n<t-2;)0===e[n]&&0===e[n+1]&&3===e[n+2]?(i.push(n+2),n+=2):n++;if(0===i.length)return e;for(var r=t-i.length,a=new Uint8Array(r),s=0,n=0;n<r;s++,n++)s===i[0]&&(s++,i.shift()),a[n]=e[s];return a},o=function(e){var t,i,n,r,a,s=0,o=0,u=0,l=0,c=[1,1],d=new at(e),h=d.readUnsignedByte(),p=d.readUnsignedByte(),f=d.readUnsignedByte();if(d.skipUnsignedExpGolomb(),nt[h]&&(3===(i=d.readUnsignedExpGolomb())&&d.skipBits(1),d.skipUnsignedExpGolomb(),d.skipUnsignedExpGolomb(),d.skipBits(1),d.readBoolean()))for(r=3!==i?8:12,a=0;a<r;a++)d.readBoolean()&&m(a<6?16:64,d);if(d.skipUnsignedExpGolomb(),0===(n=d.readUnsignedExpGolomb()))d.readUnsignedExpGolomb();else if(1===n)for(d.skipBits(1),d.skipExpGolomb(),d.skipExpGolomb(),t=d.readUnsignedExpGolomb(),a=0;a<t;a++)d.skipExpGolomb();if(d.skipUnsignedExpGolomb(),d.skipBits(1),e=d.readUnsignedExpGolomb(),i=d.readUnsignedExpGolomb(),0===(n=d.readBits(1))&&d.skipBits(1),d.skipBits(1),d.readBoolean()&&(s=d.readUnsignedExpGolomb(),o=d.readUnsignedExpGolomb(),u=d.readUnsignedExpGolomb(),l=d.readUnsignedExpGolomb()),d.readBoolean()&&d.readBoolean()){switch(d.readUnsignedByte()){case 1:c=[1,1];break;case 2:c=[12,11];break;case 3:c=[10,11];break;case 4:c=[16,11];break;case 5:c=[40,33];break;case 6:c=[24,11];break;case 7:c=[20,11];break;case 8:c=[32,11];break;case 9:c=[80,33];break;case 10:c=[18,11];break;case 11:c=[15,11];break;case 12:c=[64,33];break;case 13:c=[160,99];break;case 14:c=[4,3];break;case 15:c=[3,2];break;case 16:c=[2,1];break;case 255:c=[d.readUnsignedByte()<<8|d.readUnsignedByte(),d.readUnsignedByte()<<8|d.readUnsignedByte()]}c&&(c[0],c[1])}return{profileIdc:h,levelIdc:f,profileCompatibility:p,width:16*(e+1)-2*s-2*o,height:(2-n)*(i+1)*16-2*u-2*l,sarRatio:c}}}).prototype=new j;function ot(e,t){var i=0<=(i=e[t+6]<<21|e[t+7]<<14|e[t+8]<<7|e[t+9])?i:0;return(16&e[t+5])>>4?i+20:i+10}function ut(e,t){return e.length-t<10||e[t]!=="I".charCodeAt(0)||e[t+1]!=="D".charCodeAt(0)||e[t+2]!=="3".charCodeAt(0)?t:ut(e,t+=ot(e,t))}function lt(e){return e[0]<<21|e[1]<<14|e[2]<<7|e[3]}var e={H264Stream:it,NalByteStream:st},ct=[96e3,88200,64e3,48e3,44100,32e3,24e3,22050,16e3,12e3,11025,8e3,7350],dt={isLikelyAacData:function(e){var t=ut(e,0);return e.length>=t+2&&255==(255&e[t])&&240==(240&e[t+1])&&16==(22&e[t+1])},parseId3TagSize:ot,parseAdtsSize:function(e,t){var i=(224&e[t+5])>>5,n=e[t+4]<<3;return 6144&e[t+3]|n|i},parseType:function(e,t){return e[t]==="I".charCodeAt(0)&&e[t+1]==="D".charCodeAt(0)&&e[t+2]==="3".charCodeAt(0)?"timed-metadata":!0&e[t]&&240==(240&e[t+1])?"audio":null},parseSampleRate:function(e){for(var t=0;t+5<e.length;){if(255===e[t]&&240==(246&e[t+1]))return ct[(60&e[t+2])>>>2];t++}return null},parseAacTimestamp:function(e){var t,i=10;64&e[5]&&(i+=4,i+=lt(e.subarray(10,14)));do{if((t=lt(e.subarray(i+4,i+8)))<1)return null;if("PRIV"===String.fromCharCode(e[i],e[i+1],e[i+2],e[i+3]))for(var n=e.subarray(i+10,i+t+10),r=0;r<n.byteLength;r++)if(0===n[r]){if("com.apple.streaming.transportStreamTimestamp"!==unescape(function(e,t,i){for(var n="",r=t;r<i;r++)n+="%"+("00"+e[r].toString(16)).slice(-2);return n}(n,0,r)))break;var a=n.subarray(r+1),s=(1&a[3])<<30|a[4]<<22|a[5]<<14|a[6]<<6|a[7]>>>2;return s*=4,s+=3&a[7]}}while(i+=10,(i+=t)<e.byteLength);return null}},ht=function(){var a=new Uint8Array,s=0;ht.prototype.init.call(this),this.setTimestamp=function(e){s=e},this.push=function(e){var t,i,n=0,r=0;for(a.length?(i=a.length,(a=new Uint8Array(e.byteLength+i)).set(a.subarray(0,i)),a.set(e,i)):a=e;3<=a.length-r;)if(a[r]!=="I".charCodeAt(0)||a[r+1]!=="D".charCodeAt(0)||a[r+2]!=="3".charCodeAt(0))if(255!=(255&a[r])||240!=(240&a[r+1]))r++;else{if(a.length-r<7)break;if(r+(n=dt.parseAdtsSize(a,r))>a.length)break;t={type:"audio",data:a.subarray(r,r+n),pts:s,dts:s},this.trigger("data",t),r+=n}else{if(a.length-r<10)break;if(r+(n=dt.parseId3TagSize(a,r))>a.length)break;t={type:"timed-metadata",data:a.subarray(r,r+n)},this.trigger("data",t),r+=n}e=a.length-r,a=0<e?a.subarray(r):new Uint8Array},this.reset=function(){a=new Uint8Array,this.trigger("reset")},this.endTimeline=function(){a=new Uint8Array,this.trigger("endedtimeline")}};ht.prototype=new j;function pt(e,t){t.stream=e,this.trigger("log",t)}function ft(e,t){for(var i=Object.keys(t),n=0;n<i.length;n++){var r=i[n];"headOfPipeline"!==r&&t[r].on&&t[r].on("log",pt.bind(e,r))}}function mt(e,t){var i;if(e.length===t.length){for(i=0;i<e.length;i++)if(e[i]!==t[i])return;return 1}}function gt(e,t,i,n,r,a){return{start:{dts:e,pts:e+(i-t)},end:{dts:e+(n-t),pts:e+(r-i)},prependedContentDuration:a,baseMediaDecodeTime:e}}var yt,vt,_t,bt=ht,Tt=["audioobjecttype","channelcount","samplerate","samplingfrequencyindex","samplesize"],St=["width","height","profileIdc","levelIdc","profileCompatibility","sarRatio"],wt=e.H264Stream,Et=dt.isLikelyAacData,kt=ue,Ct=function(a,s){var o=[],u=0,l=0,c=1/0,d=(s=s||{}).firstSequenceNumber||0;Ct.prototype.init.call(this),this.push=function(t){Te(a,t),a&&Tt.forEach(function(e){a[e]=t[e]}),o.push(t)},this.setEarliestDts=function(e){u=e},this.setVideoBaseMediaDecodeTime=function(e){c=e},this.setAudioAppendStart=function(e){l=e},this.flush=function(){var e,t,i,n,r;0!==o.length&&(e=me(o,a,u),a.baseMediaDecodeTime=be(a,s.keepOriginalTimestamps),r=fe(a,e,l,c),a.samples=ge(e),t=K(ye(e)),o=[],n=Y(d,[a]),i=new Uint8Array(n.byteLength+t.byteLength),d++,i.set(n),i.set(t,n.byteLength),_e(a),n=Math.ceil(1024*kt/a.samplerate),e.length&&(n=e.length*n,this.trigger("segmentTimingInfo",gt(de(a.baseMediaDecodeTime,a.samplerate),e[0].dts,e[0].pts,e[0].dts+n,e[0].pts+n,r||0)),this.trigger("timingInfo",{start:e[0].pts,end:e[0].pts+n})),this.trigger("data",{track:a,boxes:i})),this.trigger("done","AudioSegmentStream")},this.reset=function(){_e(a),o=[],this.trigger("reset")}};Ct.prototype=new j,(yt=function(s,a){var t,i,o=[],l=[],u=(a=a||{}).firstSequenceNumber||0;yt.prototype.init.call(this),delete s.minPTS,this.gopCache_=[],this.push=function(e){Te(s,e),"seq_parameter_set_rbsp"!==e.nalUnitType||t||(t=e.config,s.sps=[e.data],St.forEach(function(e){s[e]=t[e]},this)),"pic_parameter_set_rbsp"!==e.nalUnitType||i||(i=e.data,s.pps=[e.data]),o.push(e)},this.flush=function(){for(var e,t,i,n=0;o.length&&"access_unit_delimiter_rbsp"!==o[0].nalUnitType;)o.shift();if(0===o.length)return this.resetStream_(),void this.trigger("done","VideoSegmentStream");if(e=$(o),(t=J(e))[0][0].keyFrame||((i=this.getGopForFusion_(o[0],s))?(n=i.duration,t.unshift(i),t.byteLength+=i.byteLength,t.nalCount+=i.nalCount,t.pts=i.pts,t.dts=i.dts,t.duration+=i.duration):t=Z(t)),l.length){var r=a.alignGopsAtEnd?this.alignGopsAtEnd_(t):this.alignGopsAtStart_(t);if(!r)return this.gopCache_.unshift({gop:t.pop(),pps:s.pps,sps:s.sps}),this.gopCache_.length=Math.min(6,this.gopCache_.length),o=[],this.resetStream_(),void this.trigger("done","VideoSegmentStream");_e(s),t=r}Te(s,t),s.samples=ee(t),e=K(te(t)),s.baseMediaDecodeTime=be(s,a.keepOriginalTimestamps),this.trigger("processedGopsInfo",t.map(function(e){return{pts:e.pts,dts:e.dts,byteLength:e.byteLength}})),i=t[0],r=t[t.length-1],this.trigger("segmentTimingInfo",gt(s.baseMediaDecodeTime,i.dts,i.pts,r.dts+r.duration,r.pts+r.duration,n)),this.trigger("timingInfo",{start:t[0].pts,end:t[t.length-1].pts+t[t.length-1].duration}),this.gopCache_.unshift({gop:t.pop(),pps:s.pps,sps:s.sps}),this.gopCache_.length=Math.min(6,this.gopCache_.length),o=[],this.trigger("baseMediaDecodeTime",s.baseMediaDecodeTime),this.trigger("timelineStartInfo",s.timelineStartInfo),n=Y(u,[s]),t=new Uint8Array(n.byteLength+e.byteLength),u++,t.set(n),t.set(e,n.byteLength),this.trigger("data",{track:s,boxes:t}),this.resetStream_(),this.trigger("done","VideoSegmentStream")},this.reset=function(){this.resetStream_(),o=[],this.gopCache_.length=0,l.length=0,this.trigger("reset")},this.resetStream_=function(){_e(s),i=t=void 0},this.getGopForFusion_=function(e){for(var t,i,n,r=1/0,a=0;a<this.gopCache_.length;a++)i=(n=this.gopCache_[a]).gop,s.pps&&mt(s.pps[0],n.pps[0])&&s.sps&&mt(s.sps[0],n.sps[0])&&(i.dts<s.timelineStartInfo.dts||-1e4<=(i=e.dts-i.dts-i.duration)&&i<=45e3&&(!t||i<r)&&(t=n,r=i));return t?t.gop:null},this.alignGopsAtStart_=function(e){for(var t,i,n,r,a=e.byteLength,s=e.nalCount,o=e.duration,u=t=0;u<l.length&&t<e.length&&(i=l[u],n=e[t],i.pts!==n.pts);)n.pts>i.pts?u++:(t++,a-=n.byteLength,s-=n.nalCount,o-=n.duration);return 0===t?e:t===e.length?null:((r=e.slice(t)).byteLength=a,r.duration=o,r.nalCount=s,r.pts=r[0].pts,r.dts=r[0].dts,r)},this.alignGopsAtEnd_=function(e){for(var t,i,n=l.length-1,r=e.length-1,a=null,s=!1;0<=n&&0<=r;){if(t=l[n],i=e[r],t.pts===i.pts){s=!0;break}t.pts>i.pts?n--:(n===l.length-1&&(a=r),r--)}if(!s&&null===a)return null;if(0===(u=s?r:a))return e;var o=e.slice(u),u=o.reduce(function(e,t){return e.byteLength+=t.byteLength,e.duration+=t.duration,e.nalCount+=t.nalCount,e},{byteLength:0,duration:0,nalCount:0});return o.byteLength=u.byteLength,o.duration=u.duration,o.nalCount=u.nalCount,o.pts=o[0].pts,o.dts=o[0].dts,o},this.alignGopsWith=function(e){l=e}}).prototype=new j,(_t=function(e,t){this.numberOfTracks=0,this.metadataStream=t,"undefined"!=typeof(e=e||{}).remux?this.remuxTracks=!!e.remux:this.remuxTracks=!0,"boolean"==typeof e.keepOriginalTimestamps?this.keepOriginalTimestamps=e.keepOriginalTimestamps:this.keepOriginalTimestamps=!1,this.pendingTracks=[],this.videoTrack=null,this.pendingBoxes=[],this.pendingCaptions=[],this.pendingMetadata=[],this.pendingBytes=0,this.emittedTracks=0,_t.prototype.init.call(this),this.push=function(e){return e.text?this.pendingCaptions.push(e):e.frames?this.pendingMetadata.push(e):(this.pendingTracks.push(e.track),this.pendingBytes+=e.boxes.byteLength,"video"===e.track.type&&(this.videoTrack=e.track,this.pendingBoxes.push(e.boxes)),void("audio"===e.track.type&&(this.audioTrack=e.track,this.pendingBoxes.unshift(e.boxes))))}}).prototype=new j,_t.prototype.flush=function(e){var t,i,n,r=0,a={captions:[],captionStreams:{},metadata:[],info:{}},s=0;if(this.pendingTracks.length<this.numberOfTracks){if("VideoSegmentStream"!==e&&"AudioSegmentStream"!==e)return;if(this.remuxTracks)return;if(0===this.pendingTracks.length)return this.emittedTracks++,void(this.emittedTracks>=this.numberOfTracks&&(this.trigger("done"),this.emittedTracks=0))}if(this.videoTrack?(s=this.videoTrack.timelineStartInfo.pts,St.forEach(function(e){a.info[e]=this.videoTrack[e]},this)):this.audioTrack&&(s=this.audioTrack.timelineStartInfo.pts,Tt.forEach(function(e){a.info[e]=this.audioTrack[e]},this)),this.videoTrack||this.audioTrack){for(1===this.pendingTracks.length?a.type=this.pendingTracks[0].type:a.type="combined",this.emittedTracks+=this.pendingTracks.length,e=Q(this.pendingTracks),a.initSegment=new Uint8Array(e.byteLength),a.initSegment.set(e),a.data=new Uint8Array(this.pendingBytes),n=0;n<this.pendingBoxes.length;n++)a.data.set(this.pendingBoxes[n],r),r+=this.pendingBoxes[n].byteLength;for(n=0;n<this.pendingCaptions.length;n++)(t=this.pendingCaptions[n]).startTime=pe(t.startPts,s,this.keepOriginalTimestamps),t.endTime=pe(t.endPts,s,this.keepOriginalTimestamps),a.captionStreams[t.stream]=!0,a.captions.push(t);for(n=0;n<this.pendingMetadata.length;n++)(i=this.pendingMetadata[n]).cueTime=pe(i.pts,s,this.keepOriginalTimestamps),a.metadata.push(i);for(a.metadata.dispatchType=this.metadataStream.dispatchType,this.pendingTracks.length=0,this.videoTrack=null,this.pendingBoxes.length=0,this.pendingCaptions.length=0,this.pendingBytes=0,this.pendingMetadata.length=0,this.trigger("data",a),n=0;n<a.captions.length;n++)t=a.captions[n],this.trigger("caption",t);for(n=0;n<a.metadata.length;n++)i=a.metadata[n],this.trigger("id3Frame",i)}this.emittedTracks>=this.numberOfTracks&&(this.trigger("done"),this.emittedTracks=0)},_t.prototype.setRemux=function(e){this.remuxTracks=e},(vt=function(n){var r,a,s=this,i=!0;vt.prototype.init.call(this),this.baseMediaDecodeTime=(n=n||{}).baseMediaDecodeTime||0,this.transmuxPipeline_={},this.setupAacPipeline=function(){var t={};(this.transmuxPipeline_=t).type="aac",t.metadataStream=new Je.MetadataStream,t.aacStream=new bt,t.audioTimestampRolloverStream=new Je.TimestampRolloverStream("audio"),t.timedMetadataTimestampRolloverStream=new Je.TimestampRolloverStream("timed-metadata"),t.adtsStream=new rt,t.coalesceStream=new _t(n,t.metadataStream),t.headOfPipeline=t.aacStream,t.aacStream.pipe(t.audioTimestampRolloverStream).pipe(t.adtsStream),t.aacStream.pipe(t.timedMetadataTimestampRolloverStream).pipe(t.metadataStream).pipe(t.coalesceStream),t.metadataStream.on("timestamp",function(e){t.aacStream.setTimestamp(e.timeStamp)}),t.aacStream.on("data",function(e){"timed-metadata"!==e.type&&"audio"!==e.type||t.audioSegmentStream||(a=a||{timelineStartInfo:{baseMediaDecodeTime:s.baseMediaDecodeTime},codec:"adts",type:"audio"},t.coalesceStream.numberOfTracks++,t.audioSegmentStream=new Ct(a,n),t.audioSegmentStream.on("log",s.getLogTrigger_("audioSegmentStream")),t.audioSegmentStream.on("timingInfo",s.trigger.bind(s,"audioTimingInfo")),t.adtsStream.pipe(t.audioSegmentStream).pipe(t.coalesceStream),s.trigger("trackinfo",{hasAudio:!!a,hasVideo:!!r}))}),t.coalesceStream.on("data",this.trigger.bind(this,"data")),t.coalesceStream.on("done",this.trigger.bind(this,"done")),ft(this,t)},this.setupTsPipeline=function(){var i={};(this.transmuxPipeline_=i).type="ts",i.metadataStream=new Je.MetadataStream,i.packetStream=new Je.TransportPacketStream,i.parseStream=new Je.TransportParseStream,i.elementaryStream=new Je.ElementaryStream,i.timestampRolloverStream=new Je.TimestampRolloverStream,i.adtsStream=new rt,i.h264Stream=new wt,i.captionStream=new Je.CaptionStream(n),i.coalesceStream=new _t(n,i.metadataStream),i.headOfPipeline=i.packetStream,i.packetStream.pipe(i.parseStream).pipe(i.elementaryStream).pipe(i.timestampRolloverStream),i.timestampRolloverStream.pipe(i.h264Stream),i.timestampRolloverStream.pipe(i.adtsStream),i.timestampRolloverStream.pipe(i.metadataStream).pipe(i.coalesceStream),i.h264Stream.pipe(i.captionStream).pipe(i.coalesceStream),i.elementaryStream.on("data",function(e){var t;if("metadata"===e.type){for(t=e.tracks.length;t--;)r||"video"!==e.tracks[t].type?a||"audio"!==e.tracks[t].type||((a=e.tracks[t]).timelineStartInfo.baseMediaDecodeTime=s.baseMediaDecodeTime):(r=e.tracks[t]).timelineStartInfo.baseMediaDecodeTime=s.baseMediaDecodeTime;r&&!i.videoSegmentStream&&(i.coalesceStream.numberOfTracks++,i.videoSegmentStream=new yt(r,n),i.videoSegmentStream.on("log",s.getLogTrigger_("videoSegmentStream")),i.videoSegmentStream.on("timelineStartInfo",function(e){a&&!n.keepOriginalTimestamps&&(a.timelineStartInfo=e,i.audioSegmentStream.setEarliestDts(e.dts-s.baseMediaDecodeTime))}),i.videoSegmentStream.on("processedGopsInfo",s.trigger.bind(s,"gopInfo")),i.videoSegmentStream.on("segmentTimingInfo",s.trigger.bind(s,"videoSegmentTimingInfo")),i.videoSegmentStream.on("baseMediaDecodeTime",function(e){a&&i.audioSegmentStream.setVideoBaseMediaDecodeTime(e)}),i.videoSegmentStream.on("timingInfo",s.trigger.bind(s,"videoTimingInfo")),i.h264Stream.pipe(i.videoSegmentStream).pipe(i.coalesceStream)),a&&!i.audioSegmentStream&&(i.coalesceStream.numberOfTracks++,i.audioSegmentStream=new Ct(a,n),i.audioSegmentStream.on("log",s.getLogTrigger_("audioSegmentStream")),i.audioSegmentStream.on("timingInfo",s.trigger.bind(s,"audioTimingInfo")),i.audioSegmentStream.on("segmentTimingInfo",s.trigger.bind(s,"audioSegmentTimingInfo")),i.adtsStream.pipe(i.audioSegmentStream).pipe(i.coalesceStream)),s.trigger("trackinfo",{hasAudio:!!a,hasVideo:!!r})}}),i.coalesceStream.on("data",this.trigger.bind(this,"data")),i.coalesceStream.on("id3Frame",function(e){e.dispatchType=i.metadataStream.dispatchType,s.trigger("id3Frame",e)}),i.coalesceStream.on("caption",this.trigger.bind(this,"caption")),i.coalesceStream.on("done",this.trigger.bind(this,"done")),ft(this,i)},this.setBaseMediaDecodeTime=function(e){var t=this.transmuxPipeline_;n.keepOriginalTimestamps||(this.baseMediaDecodeTime=e),a&&(a.timelineStartInfo.dts=void 0,a.timelineStartInfo.pts=void 0,_e(a),t.audioTimestampRolloverStream&&t.audioTimestampRolloverStream.discontinuity()),r&&(t.videoSegmentStream&&(t.videoSegmentStream.gopCache_=[]),r.timelineStartInfo.dts=void 0,r.timelineStartInfo.pts=void 0,_e(r),t.captionStream.reset()),t.timestampRolloverStream&&t.timestampRolloverStream.discontinuity()},this.setAudioAppendStart=function(e){a&&this.transmuxPipeline_.audioSegmentStream.setAudioAppendStart(e)},this.setRemux=function(e){var t=this.transmuxPipeline_;n.remux=e,t&&t.coalesceStream&&t.coalesceStream.setRemux(e)},this.alignGopsWith=function(e){r&&this.transmuxPipeline_.videoSegmentStream&&this.transmuxPipeline_.videoSegmentStream.alignGopsWith(e)},this.getLogTrigger_=function(t){var i=this;return function(e){e.stream=t,i.trigger("log",e)}},this.push=function(e){var t;i&&((t=Et(e))&&"aac"!==this.transmuxPipeline_.type?this.setupAacPipeline():t||"ts"===this.transmuxPipeline_.type||this.setupTsPipeline(),i=!1),this.transmuxPipeline_.headOfPipeline.push(e)},this.flush=function(){i=!0,this.transmuxPipeline_.headOfPipeline.flush()},this.endTimeline=function(){this.transmuxPipeline_.headOfPipeline.endTimeline()},this.reset=function(){this.transmuxPipeline_.headOfPipeline&&this.transmuxPipeline_.headOfPipeline.reset()},this.resetCaptions=function(){this.transmuxPipeline_.captionStream&&this.transmuxPipeline_.captionStream.reset()}}).prototype=new j;function It(e,c){var i=Mt(e,["moof","traf"]),e=Mt(e,["mdat"]),d={},n=[];return e.forEach(function(e,t){t=i[t];n.push({mdat:e,traf:t})}),n.forEach(function(e){var t,i,n,r,a,s=e.mdat,o=e.traf,u=Mt(o,["tfhd"]),l=Ht(u[0]),e=l.trackId,u=Mt(o,["tfdt"]),u=0<u.length?Bt(u[0]).baseMediaDecodeTime:0,o=Mt(o,["trun"]);c===e&&0<o.length&&(o=o,t=u,i=(l=l).defaultSampleDuration||0,n=l.defaultSampleSize||0,r=l.trackId,a=[],o.forEach(function(e){e=jt(e).samples;e.forEach(function(e){void 0===e.duration&&(e.duration=i),void 0===e.size&&(e.size=n),e.trackId=r,e.dts=t,void 0===e.compositionTimeOffset&&(e.compositionTimeOffset=0),"bigint"==typeof t?(e.pts=t+qt.BigInt(e.compositionTimeOffset),t+=qt.BigInt(e.duration)):(e.pts=t+e.compositionTimeOffset,t+=e.duration)}),a=a.concat(e)}),s=function(e,t,i){for(var n,r,a=new DataView(e.buffer,e.byteOffset,e.byteLength),s={logs:[],seiNals:[]},o=0;o+4<e.length;o+=n)if(n=a.getUint32(o),o+=4,!(n<=0))switch(31&e[o]){case 6:var u=e.subarray(o+1,o+1+n),l=function(e,t){for(var i=e,n=0;n<t.length;n++){var r=t[n];if(i<r.size)return r;i-=r.size}return null}(o,t),u={nalUnitType:"sei_rbsp",size:n,data:u,escapedRBSP:Vt(u),trackId:i};if(l)u.pts=l.pts,u.dts=l.dts,r=l;else{if(!r){s.logs.push({level:"warn",message:"We've encountered a nal unit without data at "+o+" for trackId "+i+". See mux.js#223."});break}u.pts=r.pts,u.dts=r.dts}s.seiNals.push(u)}return s}(s,a,e),d[e]||(d[e]={seiNals:[],logs:[]}),d[e].seiNals=d[e].seiNals.concat(s.seiNals),d[e].logs=d[e].logs.concat(s.logs))}),d}function xt(e){var t=31&e[1];return t<<=8,t|=e[2]}function At(e){return!!(64&e[1])}function Pt(e){var t=0;return 1<(48&e[3])>>>4&&(t+=e[4]+1),t}function Lt(e){switch(e){case 5:return"slice_layer_without_partitioning_rbsp_idr";case 6:return"sei_rbsp";case 7:return"seq_parameter_set_rbsp";case 8:return"pic_parameter_set_rbsp";case 9:return"access_unit_delimiter_rbsp";default:return null}}var Dt={Transmuxer:vt,VideoSegmentStream:yt,AudioSegmentStream:Ct,AUDIO_PROPERTIES:Tt,VIDEO_PROPERTIES:St,generateSegmentTimingInfo:gt},e=function(e){return e>>>0},Ot=function(e){var t="";return t+=String.fromCharCode(e[0]),t+=String.fromCharCode(e[1]),t+=String.fromCharCode(e[2]),t+=String.fromCharCode(e[3])},Rt=e,Mt=function e(t,i){var n,r,a,s=[];if(!i.length)return null;for(n=0;n<t.byteLength;)r=Rt(t[n]<<24|t[n+1]<<16|t[n+2]<<8|t[n+3]),a=Ot(t.subarray(n+4,n+8)),r=1<r?n+r:t.byteLength,a===i[0]&&(1===i.length?s.push(t.subarray(n+8,r)):(a=e(t.subarray(n+8,r),i.slice(1))).length&&(s=s.concat(a))),n=r;return s},Nt=e,Ut=q.getUint64,Bt=function(e){var t={version:e[0],flags:new Uint8Array(e.subarray(1,4))};return 1===t.version?t.baseMediaDecodeTime=Ut(e.subarray(4)):t.baseMediaDecodeTime=Nt(e[4]<<24|e[5]<<16|e[6]<<8|e[7]),t},Ft=function(e){return{isLeading:(12&e[0])>>>2,dependsOn:3&e[0],isDependedOn:(192&e[1])>>>6,hasRedundancy:(48&e[1])>>>4,paddingValue:(14&e[1])>>>1,isNonSyncSample:1&e[1],degradationPriority:e[2]<<8|e[3]}},jt=function(e){var t,i={version:e[0],flags:new Uint8Array(e.subarray(1,4)),samples:[]},n=new DataView(e.buffer,e.byteOffset,e.byteLength),r=1&i.flags[2],a=4&i.flags[2],s=1&i.flags[1],o=2&i.flags[1],u=4&i.flags[1],l=8&i.flags[1],c=n.getUint32(4),d=8;for(r&&(i.dataOffset=n.getInt32(d),d+=4),a&&c&&(t={flags:Ft(e.subarray(d,d+4))},d+=4,s&&(t.duration=n.getUint32(d),d+=4),o&&(t.size=n.getUint32(d),d+=4),l&&(1===i.version?t.compositionTimeOffset=n.getInt32(d):t.compositionTimeOffset=n.getUint32(d),d+=4),i.samples.push(t),c--);c--;)t={},s&&(t.duration=n.getUint32(d),d+=4),o&&(t.size=n.getUint32(d),d+=4),u&&(t.flags=Ft(e.subarray(d,d+4)),d+=4),l&&(1===i.version?t.compositionTimeOffset=n.getInt32(d):t.compositionTimeOffset=n.getUint32(d),d+=4),i.samples.push(t);return i},Ht=function(e){var t=new DataView(e.buffer,e.byteOffset,e.byteLength),i={version:e[0],flags:new Uint8Array(e.subarray(1,4)),trackId:t.getUint32(4)},n=1&i.flags[2],r=2&i.flags[2],a=8&i.flags[2],s=16&i.flags[2],o=32&i.flags[2],u=65536&i.flags[0],l=131072&i.flags[0],e=8;return n&&(e+=4,i.baseDataOffset=t.getUint32(12),e+=4),r&&(i.sampleDescriptionIndex=t.getUint32(e),e+=4),a&&(i.defaultSampleDuration=t.getUint32(e),e+=4),s&&(i.defaultSampleSize=t.getUint32(e),e+=4),o&&(i.defaultSampleFlags=t.getUint32(e)),u&&(i.durationIsEmpty=!0),!n&&l&&(i.baseDataOffsetIsMoof=!0),i},j="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},j="undefined"!=typeof window?window:"undefined"!=typeof j?j:"undefined"!=typeof self?self:{},qt=j,Vt=ke,Wt=Fe.CaptionStream,Gt=function(){var t,r,a,s,o,i,n=!1;this.isInitialized=function(){return n},this.init=function(e){t=new Wt,n=!0,i=!!e&&e.isPartial,t.on("data",function(e){e.startTime=e.startPts/s,e.endTime=e.endPts/s,o.captions.push(e),o.captionStreams[e.stream]=!0}),t.on("log",function(e){o.logs.push(e)})},this.isNewInit=function(e,t){return!(e&&0===e.length||t&&"object"==typeof t&&0===Object.keys(t).length)&&(a!==e[0]||s!==t[a])},this.parse=function(e,t,i){if(!this.isInitialized())return null;if(!t||!i)return null;if(this.isNewInit(t,i))a=t[0],s=i[a];else if(null===a||!s)return r.push(e),null;for(;0<r.length;){var n=r.shift();this.parse(n,t,i)}return(e=function(e,t,i){if(null===t)return null;t=It(e,t)[t]||{};return{seiNals:t.seiNals,logs:t.logs,timescale:i}}(e,a,s))&&e.logs&&(o.logs=o.logs.concat(e.logs)),null!==e&&e.seiNals?(this.pushNals(e.seiNals),this.flushStream(),o):o.logs.length?{logs:o.logs,captions:[],captionStreams:[]}:null},this.pushNals=function(e){if(!this.isInitialized()||!e||0===e.length)return null;e.forEach(function(e){t.push(e)})},this.flushStream=function(){if(!this.isInitialized())return null;i?t.partialFlush():t.flush()},this.clearParsedCaptions=function(){o.captions=[],o.captionStreams={},o.logs=[]},this.resetCaptionStream=function(){if(!this.isInitialized())return null;t.reset()},this.clearAllCaptions=function(){this.clearParsedCaptions(),this.resetCaptionStream()},this.reset=function(){r=[],s=a=null,o?this.clearParsedCaptions():o={captions:[],captionStreams:{},logs:[]},this.resetCaptionStream()},this.reset()},zt=e,Xt=function(e){return("00"+e.toString(16)).slice(-2)},Kt=q.getUint64,j=function(e){return Mt(e,["moov","trak"]).reduce(function(e,t){var i,n,r=Mt(t,["tkhd"])[0];return r?(i=r[0],r=zt(r[n=0===i?12:20]<<24|r[1+n]<<16|r[2+n]<<8|r[3+n]),(t=Mt(t,["mdia","mdhd"])[0])?(i=t[0],e[r]=zt(t[n=0===i?12:20]<<24|t[1+n]<<16|t[2+n]<<8|t[3+n]),e):null):null},{})},ke=function(a,e){e=Mt(e,["moof","traf"]).reduce(function(e,t){var i,n=Mt(t,["tfhd"])[0],r=zt(n[4]<<24|n[5]<<16|n[6]<<8|n[7]),n=a[r]||9e4,r=Mt(t,["tfdt"])[0],t=new DataView(r.buffer,r.byteOffset,r.byteLength),t=1===r[0]?Kt(r.subarray(4,12)):t.getUint32(4);return"bigint"==typeof t?i=t/qt.BigInt(n):"number"!=typeof t||isNaN(t)||(i=t/n),e=(i=i<Number.MAX_SAFE_INTEGER?Number(i):i)<e?i:e},1/0);return"bigint"==typeof e||isFinite(e)?e:0},e=function(e){var e=Mt(e,["moov","trak"]),n=[];return e.forEach(function(e){var t=Mt(e,["mdia","hdlr"]),i=Mt(e,["tkhd"]);t.forEach(function(e,t){e=Ot(e.subarray(8,12)),t=i[t];"vide"===e&&(t=0===(t=new DataView(t.buffer,t.byteOffset,t.byteLength)).getUint8(0)?t.getUint32(12):t.getUint32(20),n.push(t))})}),n},Yt=function(e){var t=0===e[0]?12:20;return zt(e[t]<<24|e[1+t]<<16|e[2+t]<<8|e[3+t])},q=function(e){var e=Mt(e,["moov","trak"]),s=[];return e.forEach(function(e){var t={},i=Mt(e,["tkhd"])[0];i&&(i=(n=new DataView(i.buffer,i.byteOffset,i.byteLength)).getUint8(0),t.id=0===i?n.getUint32(12):n.getUint32(20));var n=Mt(e,["mdia","hdlr"])[0];n&&(a=Ot(n.subarray(8,12)),t.type="vide"===a?"video":"soun"===a?"audio":a);var r,a=Mt(e,["mdia","minf","stbl","stsd"])[0];a&&(a=a.subarray(8),t.codec=Ot(a.subarray(4,8)),(a=Mt(a,[t.codec])[0])&&(/^[asm]vc[1-9]$/i.test(t.codec)?(r=a.subarray(78),"avcC"===Ot(r.subarray(4,8))&&11<r.length?(t.codec+=".",t.codec+=Xt(r[9]),t.codec+=Xt(r[10]),t.codec+=Xt(r[11])):t.codec="avc1.4d400d"):/^mp4[a,v]$/i.test(t.codec)?(r=a.subarray(28),"esds"===Ot(r.subarray(4,8))&&20<r.length&&0!==r[19]?(t.codec+="."+Xt(r[19]),t.codec+="."+Xt(r[20]>>>2&63).replace(/^0/,"")):t.codec="mp4a.40.2"):t.codec=t.codec.toLowerCase()));e=Mt(e,["mdia","mdhd"])[0];e&&(t.timescale=Yt(e)),s.push(t)}),s},Qt=ke,$t=q,Jt=Ie,Zt={};Zt.ts={parseType:function(e,t){e=xt(e);return 0===e?"pat":e===t?"pmt":t?"pes":null},parsePat:function(e){var t=At(e),i=4+Pt(e);return t&&(i+=e[i]+1),(31&e[i+10])<<8|e[i+11]},parsePmt:function(e){var t={},i=At(e),n=4+Pt(e);if(i&&(n+=e[n]+1),1&e[n+5]){for(var r=3+((15&e[n+1])<<8|e[n+2])-4,a=12+((15&e[n+10])<<8|e[n+11]);a<r;){var s=n+a;t[(31&e[s+1])<<8|e[s+2]]=e[s],a+=5+((15&e[s+3])<<8|e[s+4])}return t}},parsePayloadUnitStartIndicator:At,parsePesType:function(e,t){switch(t[xt(e)]){case je.H264_STREAM_TYPE:return"video";case je.ADTS_STREAM_TYPE:return"audio";case je.METADATA_STREAM_TYPE:return"timed-metadata";default:return null}},parsePesTime:function(e){if(!At(e))return null;var t=4+Pt(e);if(t>=e.byteLength)return null;var i=null,n=e[t+7];return 192&n&&((i={}).pts=(14&e[t+9])<<27|(255&e[t+10])<<20|(254&e[t+11])<<12|(255&e[t+12])<<5|(254&e[t+13])>>>3,i.pts*=4,i.pts+=(6&e[t+13])>>>1,i.dts=i.pts,64&n&&(i.dts=(14&e[t+14])<<27|(255&e[t+15])<<20|(254&e[t+16])<<12|(255&e[t+17])<<5|(254&e[t+18])>>>3,i.dts*=4,i.dts+=(6&e[t+18])>>>1)),i},videoPacketContainsKeyFrame:function(e){for(var t=4+Pt(e),i=e.subarray(t),n=0,r=0,a=!1;r<i.byteLength-3;r++)if(1===i[r+2]){n=r+5;break}for(;n<i.byteLength;)switch(i[n]){case 0:if(0!==i[n-1]){n+=2;break}if(0!==i[n-2]){n++;break}for(r+3!==n-2&&"slice_layer_without_partitioning_rbsp_idr"===Lt(31&i[r+3])&&(a=!0);1!==i[++n]&&n<i.length;);r=n-2,n+=3;break;case 1:if(0!==i[n-1]||0!==i[n-2]){n+=3;break}"slice_layer_without_partitioning_rbsp_idr"===Lt(31&i[r+3])&&(a=!0),r=n-2,n+=3;break;default:n+=3}return i=i.subarray(r),n-=r,r=0,a=i&&3<i.byteLength&&"slice_layer_without_partitioning_rbsp_idr"===Lt(31&i[r+3])?!0:a}},Zt.aac=dt;function ei(e,t,i){for(var n,r,a,s,o=0,u=188,l=!1;u<=e.byteLength;)if(71!==e[o]||71!==e[u]&&u!==e.byteLength)o++,u++;else{if(n=e.subarray(o,u),"pes"===Zt.ts.parseType(n,t.pid)&&(r=Zt.ts.parsePesType(n,t.table),a=Zt.ts.parsePayloadUnitStartIndicator(n),"audio"===r&&a&&(s=Zt.ts.parsePesTime(n))&&(s.type="audio",i.audio.push(s),l=!0)),l)break;o+=188,u+=188}for(o=(u=e.byteLength)-188,l=!1;0<=o;)if(71!==e[o]||71!==e[u]&&u!==e.byteLength)o--,u--;else{if(n=e.subarray(o,u),"pes"===Zt.ts.parseType(n,t.pid)&&(r=Zt.ts.parsePesType(n,t.table),a=Zt.ts.parsePayloadUnitStartIndicator(n),"audio"===r&&a&&(s=Zt.ts.parsePesTime(n))&&(s.type="audio",i.audio.push(s),l=!0)),l)break;o-=188,u-=188}}function ti(e){var t,i={pid:null,table:null},n={};for(t in!function(e,t){for(var i,n=0,r=188;r<e.byteLength;)if(71!==e[n]||71!==e[r])n++,r++;else{switch(i=e.subarray(n,r),Zt.ts.parseType(i,t.pid)){case"pat":t.pid=Zt.ts.parsePat(i);break;case"pmt":var a=Zt.ts.parsePmt(i);t.table=t.table||{},Object.keys(a).forEach(function(e){t.table[e]=a[e]})}n+=188,r+=188}}(e,i),i.table)if(i.table.hasOwnProperty(t))switch(i.table[t]){case je.H264_STREAM_TYPE:n.video=[],function(e,t,i){for(var n,r,a,s,o,u,l,c,d=0,h=188,p=!1,f={data:[],size:0};h<e.byteLength;)if(71!==e[d]||71!==e[h])d++,h++;else{if(n=e.subarray(d,h),"pes"===Zt.ts.parseType(n,t.pid))if(r=Zt.ts.parsePesType(n,t.table),a=Zt.ts.parsePayloadUnitStartIndicator(n),"video"===r&&(a&&!p&&(s=Zt.ts.parsePesTime(n))&&(s.type="video",i.video.push(s),p=!0),!i.firstKeyFrame)){if(a&&0!==f.size){for(o=new Uint8Array(f.size),u=0;f.data.length;)l=f.data.shift(),o.set(l,u),u+=l.byteLength;!Zt.ts.videoPacketContainsKeyFrame(o)||(c=Zt.ts.parsePesTime(o))&&(i.firstKeyFrame=c,i.firstKeyFrame.type="video"),f.size=0}f.data.push(n),f.size+=n.byteLength}if(p&&i.firstKeyFrame)break;d+=188,h+=188}for(d=(h=e.byteLength)-188,p=!1;0<=d;)if(71!==e[d]||71!==e[h])d--,h--;else{if(n=e.subarray(d,h),"pes"===Zt.ts.parseType(n,t.pid)&&(r=Zt.ts.parsePesType(n,t.table),a=Zt.ts.parsePayloadUnitStartIndicator(n),"video"===r&&a&&(s=Zt.ts.parsePesTime(n))&&(s.type="video",i.video.push(s),p=!0)),p)break;d-=188,h-=188}}(e,i,n),0===n.video.length&&delete n.video;break;case je.ADTS_STREAM_TYPE:n.audio=[],ei(e,i,n),0===n.audio.length&&delete n.audio}return n}var ii=ue,ni=function(e,t){var i,n,r=(Zt.aac.isLikelyAacData(e)?function(e){for(var t,i=!1,n=0,r=null,a=null,s=0,o=0;3<=e.length-o;){switch(Zt.aac.parseType(e,o)){case"timed-metadata":if(e.length-o<10){i=!0;break}if((s=Zt.aac.parseId3TagSize(e,o))>e.length){i=!0;break}null===a&&(t=e.subarray(o,o+s),a=Zt.aac.parseAacTimestamp(t)),o+=s;break;case"audio":if(e.length-o<7){i=!0;break}if((s=Zt.aac.parseAdtsSize(e,o))>e.length){i=!0;break}null===r&&(t=e.subarray(o,o+s),r=Zt.aac.parseSampleRate(t)),n++,o+=s;break;default:o++}if(i)return null}if(null===r||null===a)return null;var u=ii/r;return{audio:[{type:"audio",dts:a,pts:a},{type:"audio",dts:a+1024*n*u,pts:a+1024*n*u}]}}:ti)(e);return r&&(r.audio||r.video)?(e=t,(t=r).audio&&t.audio.length&&("undefined"!=typeof(i=e)&&!isNaN(i)||(i=t.audio[0].dts),t.audio.forEach(function(e){e.dts=Jt(e.dts,i),e.pts=Jt(e.pts,i),e.dtsTime=e.dts/ii,e.ptsTime=e.pts/ii})),t.video&&t.video.length&&("undefined"!=typeof(n=e)&&!isNaN(n)||(n=t.video[0].dts),t.video.forEach(function(e){e.dts=Jt(e.dts,n),e.pts=Jt(e.pts,n),e.dtsTime=e.dts/ii,e.ptsTime=e.pts/ii}),t.firstKeyFrame&&((t=t.firstKeyFrame).dts=Jt(t.dts,n),t.pts=Jt(t.pts,n),t.dtsTime=t.dts/ii,t.ptsTime=t.pts/ii)),r):null},ri=function(){function e(e,t){this.options=t||{},this.self=e,this.init()}var t=e.prototype;return t.init=function(){var i,e;this.transmuxer&&this.transmuxer.dispose(),this.transmuxer=new Dt.Transmuxer(this.options),i=this.self,(e=this.transmuxer).on("data",function(e){var t=e.initSegment;e.initSegment={data:t.buffer,byteOffset:t.byteOffset,byteLength:t.byteLength};t=e.data;e.data=t.buffer,i.postMessage({action:"data",segment:e,byteOffset:t.byteOffset,byteLength:t.byteLength},[e.data])}),e.on("done",function(e){i.postMessage({action:"done"})}),e.on("gopInfo",function(e){i.postMessage({action:"gopInfo",gopInfo:e})}),e.on("videoSegmentTimingInfo",function(e){var t={start:{decode:ce(e.start.dts),presentation:ce(e.start.pts)},end:{decode:ce(e.end.dts),presentation:ce(e.end.pts)},baseMediaDecodeTime:ce(e.baseMediaDecodeTime)};e.prependedContentDuration&&(t.prependedContentDuration=ce(e.prependedContentDuration)),i.postMessage({action:"videoSegmentTimingInfo",videoSegmentTimingInfo:t})}),e.on("audioSegmentTimingInfo",function(e){var t={start:{decode:ce(e.start.dts),presentation:ce(e.start.pts)},end:{decode:ce(e.end.dts),presentation:ce(e.end.pts)},baseMediaDecodeTime:ce(e.baseMediaDecodeTime)};e.prependedContentDuration&&(t.prependedContentDuration=ce(e.prependedContentDuration)),i.postMessage({action:"audioSegmentTimingInfo",audioSegmentTimingInfo:t})}),e.on("id3Frame",function(e){i.postMessage({action:"id3Frame",id3Frame:e})}),e.on("caption",function(e){i.postMessage({action:"caption",caption:e})}),e.on("trackinfo",function(e){i.postMessage({action:"trackinfo",trackInfo:e})}),e.on("audioTimingInfo",function(e){i.postMessage({action:"audioTimingInfo",audioTimingInfo:{start:ce(e.start),end:ce(e.end)}})}),e.on("videoTimingInfo",function(e){i.postMessage({action:"videoTimingInfo",videoTimingInfo:{start:ce(e.start),end:ce(e.end)}})}),e.on("log",function(e){i.postMessage({action:"log",log:e})})},t.pushMp4Captions=function(e){this.captionParser||(this.captionParser=new Gt,this.captionParser.init());var t=new Uint8Array(e.data,e.byteOffset,e.byteLength),e=this.captionParser.parse(t,e.trackIds,e.timescales);this.self.postMessage({action:"mp4Captions",captions:e&&e.captions||[],logs:e&&e.logs||[],data:t.buffer},[t.buffer])},t.probeMp4StartTime=function(e){var t=e.timescales,e=e.data,t=Qt(t,e);this.self.postMessage({action:"probeMp4StartTime",startTime:t,data:e},[e.buffer])},t.probeMp4Tracks=function(e){var t=e.data,e=$t(t);this.self.postMessage({action:"probeMp4Tracks",tracks:e,data:t},[t.buffer])},t.probeTs=function(e){var t=e.data,i=e.baseStartTime,e="number"!=typeof i||isNaN(i)?void 0:i*ue,i=ni(t,e),e=null;i&&((e={hasVideo:i.video&&2===i.video.length||!1,hasAudio:i.audio&&2===i.audio.length||!1}).hasVideo&&(e.videoStart=i.video[0].ptsTime),e.hasAudio&&(e.audioStart=i.audio[0].ptsTime)),this.self.postMessage({action:"probeTs",result:e,data:t},[t.buffer])},t.clearAllMp4Captions=function(){this.captionParser&&this.captionParser.clearAllCaptions()},t.clearParsedMp4Captions=function(){this.captionParser&&this.captionParser.clearParsedCaptions()},t.push=function(e){e=new Uint8Array(e.data,e.byteOffset,e.byteLength);this.transmuxer.push(e)},t.reset=function(){this.transmuxer.reset()},t.setTimestampOffset=function(e){e=e.timestampOffset||0;this.transmuxer.setBaseMediaDecodeTime(Math.round(le(e)))},t.setAudioAppendStart=function(e){this.transmuxer.setAudioAppendStart(Math.ceil(le(e.appendStart)))},t.setRemux=function(e){this.transmuxer.setRemux(e.remux)},t.flush=function(e){this.transmuxer.flush(),self.postMessage({action:"done",type:"transmuxed"})},t.endTimeline=function(){this.transmuxer.endTimeline(),self.postMessage({action:"endedtimeline",type:"transmuxed"})},t.alignGopsWith=function(e){this.transmuxer.alignGopsWith(e.gopsToAlignWith.slice())},e}();self.onmessage=function(e){"init"===e.data.action&&e.data.options?this.messageHandlers=new ri(self,e.data.options):(this.messageHandlers||(this.messageHandlers=new ri(self)),e.data&&e.data.action&&"init"!==e.data.action&&this.messageHandlers[e.data.action]&&this.messageHandlers[e.data.action](e.data))}}))),El=function(e){e.currentTransmux=null,e.transmuxQueue.length&&(e.currentTransmux=e.transmuxQueue.shift(),"function"==typeof e.currentTransmux?e.currentTransmux():Pu(e.currentTransmux))},kl=function(e){Du("reset",e)},Cl=function(e){var t=new wl;t.currentTransmux=null,t.transmuxQueue=[];var i=t.terminate;return t.terminate=function(){return t.currentTransmux=null,t.transmuxQueue.length=0,i.call(t)},t.postMessage({action:"init",options:e}),t},Il=2,xl=-101,Al=-102,Pl=Oo("CodecUtils"),Ll=Oo("PlaylistSelector"),ar=function(){var e=this.useDevicePixelRatio&&window.devicePixelRatio||1;return el(this.playlists.master,this.systemBandwidth,parseInt($u(this.tech_.el(),"width"),10)*e,parseInt($u(this.tech_.el(),"height"),10)*e,this.limitRenditionByPlayerDimensions,this.masterPlaylistController_)},Dl=function(n){function e(e,t){var i=n.call(this)||this;if(!e)throw new TypeError("Initialization settings are required");if("function"!=typeof e.currentTime)throw new TypeError("No currentTime getter specified");if(!e.mediaSource)throw new TypeError("No MediaSource specified");return i.bandwidth=e.bandwidth,i.throughput={rate:0,count:0},i.roundTrip=NaN,i.resetStats_(),i.mediaIndex=null,i.partIndex=null,i.hasPlayed_=e.hasPlayed,i.currentTime_=e.currentTime,i.seekable_=e.seekable,i.seeking_=e.seeking,i.duration_=e.duration,i.mediaSource_=e.mediaSource,i.vhs_=e.vhs,i.loaderType_=e.loaderType,i.currentMediaInfo_=void 0,i.startingMediaInfo_=void 0,i.segmentMetadataTrack_=e.segmentMetadataTrack,i.goalBufferLength_=e.goalBufferLength,i.sourceType_=e.sourceType,i.sourceUpdater_=e.sourceUpdater,i.inbandTextTracks_=e.inbandTextTracks,i.state_="INIT",i.timelineChangeController_=e.timelineChangeController,i.shouldSaveSegmentTimingInfo_=!0,i.parse708captions_=e.parse708captions,i.captionServices_=e.captionServices,i.experimentalExactManifestTimings=e.experimentalExactManifestTimings,i.checkBufferTimeout_=null,i.error_=void 0,i.currentTimeline_=-1,i.pendingSegment_=null,i.xhrOptions_=null,i.pendingSegments_=[],i.audioDisabled_=!1,i.isPendingTimestampOffset_=!1,i.gopBuffer_=[],i.timeMapping_=0,i.safeAppend_=11<=tr.browser.IE_VERSION,i.appendInitSegment_={audio:!0,video:!0},i.playlistOfLastInitSegment_={audio:null,video:null},i.callQueue_=[],i.loadQueue_=[],i.metadataQueue_={id3:[],caption:[]},i.waitingOnRemove_=!1,i.quotaExceededErrorRetryTimeout_=null,i.activeInitSegmentId_=null,i.initSegments_={},i.cacheEncryptionKeys_=e.cacheEncryptionKeys,i.keyCache_={},i.decrypter_=e.decrypter,i.syncController_=e.syncController,i.syncPoint_={segmentIndex:0,time:0},i.transmuxer_=i.createTransmuxer_(),i.triggerSyncInfoUpdate_=function(){return i.trigger("syncinfoupdate")},i.syncController_.on("syncinfoupdate",i.triggerSyncInfoUpdate_),i.mediaSource_.addEventListener("sourceopen",function(){i.isEndOfStream_()||(i.ended_=!1)}),i.fetchAtBuffer_=!1,i.logger_=Oo("SegmentLoader["+i.loaderType_+"]"),Object.defineProperty(ft(i),"state",{get:function(){return this.state_},set:function(e){e!==this.state_&&(this.logger_(this.state_+" -> "+e),this.state_=e,this.trigger("statechange"))}}),i.sourceUpdater_.on("ready",function(){i.hasEnoughInfoToAppend_()&&i.processCallQueue_()}),"main"===i.loaderType_&&i.timelineChangeController_.on("pendingtimelinechange",function(){i.hasEnoughInfoToAppend_()&&i.processCallQueue_()}),"audio"===i.loaderType_&&i.timelineChangeController_.on("timelinechange",function(){i.hasEnoughInfoToLoad_()&&i.processLoadQueue_(),i.hasEnoughInfoToAppend_()&&i.processCallQueue_()}),i}mt(e,n);var t=e.prototype;return t.createTransmuxer_=function(){return Cl({remux:!1,alignGopsAtEnd:this.safeAppend_,keepOriginalTimestamps:!0,parse708captions:this.parse708captions_,captionServices:this.captionServices_})},t.resetStats_=function(){this.mediaBytesTransferred=0,this.mediaRequests=0,this.mediaRequestsAborted=0,this.mediaRequestsTimedout=0,this.mediaRequestsErrored=0,this.mediaTransferDuration=0,this.mediaSecondsLoaded=0,this.mediaAppends=0},t.dispose=function(){this.trigger("dispose"),this.state="DISPOSED",this.pause(),this.abort_(),this.transmuxer_&&this.transmuxer_.terminate(),this.resetStats_(),this.checkBufferTimeout_&&window.clearTimeout(this.checkBufferTimeout_),this.syncController_&&this.triggerSyncInfoUpdate_&&this.syncController_.off("syncinfoupdate",this.triggerSyncInfoUpdate_),this.off()},t.setAudio=function(e){this.audioDisabled_=!e,e?this.appendInitSegment_.audio=!0:this.sourceUpdater_.removeAudio(0,this.duration_())},t.abort=function(){"WAITING"===this.state?(this.abort_(),this.state="READY",this.paused()||this.monitorBuffer_()):this.pendingSegment_&&(this.pendingSegment_=null)},t.abort_=function(){this.pendingSegment_&&this.pendingSegment_.abortRequests&&this.pendingSegment_.abortRequests(),this.pendingSegment_=null,this.callQueue_=[],this.loadQueue_=[],this.metadataQueue_.id3=[],this.metadataQueue_.caption=[],this.timelineChangeController_.clearPendingTimelineChange(this.loaderType_),this.waitingOnRemove_=!1,window.clearTimeout(this.quotaExceededErrorRetryTimeout_),this.quotaExceededErrorRetryTimeout_=null},t.checkForAbort_=function(e){return"APPENDING"!==this.state||this.pendingSegment_?!this.pendingSegment_||this.pendingSegment_.requestId!==e:(this.state="READY",!0)},t.error=function(e){return"undefined"!=typeof e&&(this.logger_("error occurred:",e),this.error_=e),this.pendingSegment_=null,this.error_},t.endOfStream=function(){this.ended_=!0,this.transmuxer_&&kl(this.transmuxer_),this.gopBuffer_.length=0,this.pause(),this.trigger("ended")},t.buffered_=function(){var e=this.getMediaInfo_();if(!this.sourceUpdater_||!e)return tr.createTimeRanges();if("main"===this.loaderType_){var t=e.hasAudio,i=e.hasVideo,e=e.isMuxed;if(i&&t&&!this.audioDisabled_&&!e)return this.sourceUpdater_.buffered();if(i)return this.sourceUpdater_.videoBuffered()}return this.sourceUpdater_.audioBuffered()},t.initSegmentForMap=function(e,t){if(void 0===t&&(t=!1),!e)return null;var i=bu(e),n=this.initSegments_[i];return t&&!n&&e.bytes&&(this.initSegments_[i]=n={resolvedUri:e.resolvedUri,byterange:e.byterange,bytes:e.bytes,tracks:e.tracks,timescales:e.timescales}),n||e},t.segmentKey=function(e,t){if(void 0===t&&(t=!1),!e)return null;var i=Tu(e),n=this.keyCache_[i];this.cacheEncryptionKeys_&&t&&!n&&e.bytes&&(this.keyCache_[i]=n={resolvedUri:e.resolvedUri,bytes:e.bytes});e={resolvedUri:(n||e).resolvedUri};return n&&(e.bytes=n.bytes),e},t.couldBeginLoading_=function(){return this.playlist_&&!this.paused()},t.load=function(){if(this.monitorBuffer_(),this.playlist_)return"INIT"===this.state&&this.couldBeginLoading_()?this.init_():void(!this.couldBeginLoading_()||"READY"!==this.state&&"INIT"!==this.state||(this.state="READY"))},t.init_=function(){return this.state="READY",this.resetEverything(),this.monitorBuffer_()},t.playlist=function(e,t){if(void 0===t&&(t={}),e){var i=this.playlist_,n=this.pendingSegment_;this.playlist_=e,this.xhrOptions_=t,"INIT"===this.state&&(e.syncInfo={mediaSequence:e.mediaSequence,time:0},"main"===this.loaderType_&&this.syncController_.setDateTimeMappingForStart(e));var r=null;if(i&&(i.id?r=i.id:i.uri&&(r=i.uri)),this.logger_("playlist update ["+r+" => "+(e.id||e.uri)+"]"),this.trigger("syncinfoupdate"),"INIT"===this.state&&this.couldBeginLoading_())return this.init_();if(!i||i.uri!==e.uri)return null!==this.mediaIndex&&(e.endList?this.resyncLoader():this.resetLoader()),this.currentMediaInfo_=void 0,void this.trigger("playlistupdate");t=e.mediaSequence-i.mediaSequence;this.logger_("live window shift ["+t+"]"),null!==this.mediaIndex&&(this.mediaIndex-=t,this.mediaIndex<0?(this.mediaIndex=null,this.partIndex=null):(r=this.playlist_.segments[this.mediaIndex],!this.partIndex||r.parts&&r.parts.length&&r.parts[this.partIndex]||(r=this.mediaIndex,this.logger_("currently processing part (index "+this.partIndex+") no longer exists."),this.resetLoader(),this.mediaIndex=r))),n&&(n.mediaIndex-=t,n.mediaIndex<0?(n.mediaIndex=null,n.partIndex=null):(0<=n.mediaIndex&&(n.segment=e.segments[n.mediaIndex]),0<=n.partIndex&&n.segment.parts&&(n.part=n.segment.parts[n.partIndex]))),this.syncController_.saveExpiredSegmentInfo(i,e)}},t.pause=function(){this.checkBufferTimeout_&&(window.clearTimeout(this.checkBufferTimeout_),this.checkBufferTimeout_=null)},t.paused=function(){return null===this.checkBufferTimeout_},t.resetEverything=function(e){this.ended_=!1,this.appendInitSegment_={audio:!0,video:!0},this.resetLoader(),this.remove(0,1/0,e),this.transmuxer_&&(this.transmuxer_.postMessage({action:"clearAllMp4Captions"}),this.transmuxer_.postMessage({action:"reset"}))},t.resetLoader=function(){this.fetchAtBuffer_=!1,this.resyncLoader()},t.resyncLoader=function(){this.transmuxer_&&kl(this.transmuxer_),this.mediaIndex=null,this.partIndex=null,this.syncPoint_=null,this.isPendingTimestampOffset_=!1,this.callQueue_=[],this.loadQueue_=[],this.metadataQueue_.id3=[],this.metadataQueue_.caption=[],this.abort(),this.transmuxer_&&this.transmuxer_.postMessage({action:"clearParsedMp4Captions"})},t.remove=function(e,t,i,n){if(void 0===i&&(i=function(){}),void 0===n&&(n=!1),(t=t===1/0?this.duration_():t)<=e)this.logger_("skipping remove because end ${end} is <= start ${start}");else if(this.sourceUpdater_&&this.getMediaInfo_()){var r,a=1,s=function(){0===--a&&i()};for(r in!n&&this.audioDisabled_||(a++,this.sourceUpdater_.removeAudio(e,t,s)),!n&&"main"!==this.loaderType_||(this.gopBuffer_=function(e,t,i,n){for(var r=Math.ceil((t-n)*cl),a=Math.ceil((i-n)*cl),n=e.slice(),s=e.length;s--&&!(e[s].pts<=a););if(-1===s)return n;for(var o=s+1;o--&&!(e[o].pts<=r););return o=Math.max(o,0),n.splice(o,s-o+1),n}(this.gopBuffer_,e,t,this.timeMapping_),a++,this.sourceUpdater_.removeVideo(e,t,s)),this.inbandTextTracks_)il(e,t,this.inbandTextTracks_[r]);il(e,t,this.segmentMetadataTrack_),s()}else this.logger_("skipping remove because no source updater or starting media info")},t.monitorBuffer_=function(){this.checkBufferTimeout_&&window.clearTimeout(this.checkBufferTimeout_),this.checkBufferTimeout_=window.setTimeout(this.monitorBufferTick_.bind(this),1)},t.monitorBufferTick_=function(){"READY"===this.state&&this.fillBuffer_(),this.checkBufferTimeout_&&window.clearTimeout(this.checkBufferTimeout_),this.checkBufferTimeout_=window.setTimeout(this.monitorBufferTick_.bind(this),500)},t.fillBuffer_=function(){var e;this.sourceUpdater_.updating()||(e=this.chooseNextRequest_())&&("number"==typeof e.timestampOffset&&(this.isPendingTimestampOffset_=!1,this.timelineChangeController_.pendingTimelineChange({type:this.loaderType_,from:this.currentTimeline_,to:e.timeline})),this.loadSegment_(e))},t.isEndOfStream_=function(e,t,i){if(void 0===e&&(e=this.mediaIndex),void 0===t&&(t=this.playlist_),void 0===i&&(i=this.partIndex),!t||!this.mediaSource_)return!1;var n="number"==typeof e&&t.segments[e],e=e+1===t.segments.length,n=!n||!n.parts||i+1===n.parts.length;return t.endList&&"open"===this.mediaSource_.readyState&&e&&n},t.chooseNextRequest_=function(){var e=this.buffered_(),t=Fo(e)||0,i=jo(e,this.currentTime_()),n=!this.hasPlayed_()&&1<=i,r=i>=this.goalBufferLength_(),e=this.playlist_.segments;if(!e.length||n||r)return null;this.syncPoint_=this.syncPoint_||this.syncController_.getSyncPoint(this.playlist_,this.duration_(),this.currentTimeline_,this.currentTime_());var a,n={partIndex:null,mediaIndex:null,startOfSegment:null,playlist:this.playlist_,isSyncRequest:Boolean(!this.syncPoint_)};n.isSyncRequest?n.mediaIndex=function(e,t,i){t=t||[];for(var n=[],r=0,a=0;a<t.length;a++){var s=t[a];if(e===s.timeline&&(n.push(a),i<(r+=s.duration)))return a}return 0===n.length?0:n[n.length-1]}(this.currentTimeline_,e,t):null!==this.mediaIndex?(r=e[this.mediaIndex],a="number"==typeof this.partIndex?this.partIndex:-1,n.startOfSegment=r.end||t,r.parts&&r.parts[a+1]?(n.mediaIndex=this.mediaIndex,n.partIndex=a+1):n.mediaIndex=this.mediaIndex+1):(a=(o=fl.getMediaInfoForTime({experimentalExactManifestTimings:this.experimentalExactManifestTimings,playlist:this.playlist_,currentTime:this.fetchAtBuffer_?t:this.currentTime_(),startingPartIndex:this.syncPoint_.partIndex,startingSegmentIndex:this.syncPoint_.segmentIndex,startTime:this.syncPoint_.time})).segmentIndex,s=o.startTime,o=o.partIndex,n.getMediaInfoForTime=this.fetchAtBuffer_?"bufferedEnd "+t:"currentTime "+this.currentTime_(),n.mediaIndex=a,n.startOfSegment=s,n.partIndex=o);var s=e[n.mediaIndex],o=s&&"number"==typeof n.partIndex&&s.parts&&s.parts[n.partIndex];if(!s||"number"==typeof n.partIndex&&!o)return null;"number"!=typeof n.partIndex&&s.parts&&(n.partIndex=0,o=s.parts[0]),i||!o||o.independent||(0===n.partIndex?(o=(i=e[n.mediaIndex-1]).parts&&i.parts.length&&i.parts[i.parts.length-1])&&o.independent&&(--n.mediaIndex,n.partIndex=i.parts.length-1,n.independent="previous segment"):s.parts[n.partIndex-1].independent&&(--n.partIndex,n.independent="previous part"));s=this.mediaSource_&&"ended"===this.mediaSource_.readyState;return n.mediaIndex>=e.length-1&&s&&!this.seeking_()?null:this.generateSegmentInfo_(n)},t.generateSegmentInfo_=function(e){var t=e.independent,i=e.playlist,n=e.mediaIndex,r=e.startOfSegment,a=e.isSyncRequest,s=e.partIndex,o=e.forceTimestampOffset,u=e.getMediaInfoForTime,l=i.segments[n],e="number"==typeof s&&l.parts[s],t={requestId:"segment-loader-"+Math.random(),uri:e&&e.resolvedUri||l.resolvedUri,mediaIndex:n,partIndex:e?s:null,isSyncRequest:a,startOfSegment:r,playlist:i,bytes:null,encryptedBytes:null,timestampOffset:null,timeline:l.timeline,duration:e&&e.duration||l.duration,segment:l,part:e,byteLength:0,transmuxer:this.transmuxer_,getMediaInfoForTime:u,independent:t},o="undefined"!=typeof o?o:this.isPendingTimestampOffset_;t.timestampOffset=this.timestampOffsetForSegment_({segmentTimeline:l.timeline,currentTimeline:this.currentTimeline_,startOfSegment:r,buffered:this.buffered_(),overrideCheck:o});o=Fo(this.sourceUpdater_.audioBuffered());return"number"==typeof o&&(t.audioAppendStart=o-this.sourceUpdater_.audioTimestampOffset()),this.sourceUpdater_.videoBuffered().length&&(t.gopsToAlignWith=function(e,t,i){if("undefined"==typeof t||null===t||!e.length)return[];for(var n=Math.ceil((t-i+3)*cl),r=0;r<e.length&&!(e[r].pts>n);r++);return e.slice(r)}(this.gopBuffer_,this.currentTime_()-this.sourceUpdater_.videoTimestampOffset(),this.timeMapping_)),t},t.timestampOffsetForSegment_=function(e){return i=(t=e).segmentTimeline,n=t.currentTimeline,r=t.startOfSegment,e=t.buffered,t.overrideCheck||i!==n?!(i<n)&&e.length?e.end(e.length-1):r:null;var t,i,n,r},t.earlyAbortWhenNeeded_=function(e){var t,i,n,r,a,s,o,u,l,c,d,h,p;!this.vhs_.tech_.paused()&&this.xhrOptions_.timeout&&this.playlist_.attributes.BANDWIDTH&&(Date.now()-(e.firstBytesReceivedAt||Date.now())<1e3||(t=this.currentTime_(),r=e.bandwidth,a=this.pendingSegment_.duration,p=fl.estimateSegmentRequestTime(a,r,this.playlist_,e.bytesReceived),i=this.buffered_(),n=t,void 0===(e=this.vhs_.tech_.playbackRate())&&(e=1),p<=(e=((i.length?i.end(i.length-1):0)-n)/e-1)||(r={master:this.vhs_.playlists.master,currentTime:t,bandwidth:r,duration:this.duration_(),segmentDuration:a,timeUntilRebuffer:e,currentTimeline:this.currentTimeline_,syncController:this.syncController_},a=r.master,s=r.currentTime,o=r.bandwidth,u=r.duration,l=r.segmentDuration,c=r.timeUntilRebuffer,d=r.currentTimeline,h=r.syncController,a=(r=(a=!(a=(r=a.playlists.filter(function(e){return!fl.isIncompatible(e)})).filter(fl.isEnabled)).length?r.filter(function(e){return!fl.isDisabled(e)}):a).filter(fl.hasAttribute.bind(null,"BANDWIDTH")).map(function(e){var t=h.getSyncPoint(e,u,d,s)?1:2;return{playlist:e,rebufferingImpact:fl.estimateSegmentRequestTime(l,o,e)*t-c}})).filter(function(e){return e.rebufferingImpact<=0}),Ju(a,function(e,t){return Zu(t.playlist,e.playlist)}),(r=a.length?a[0]:(Ju(r,function(e,t){return e.rebufferingImpact-t.rebufferingImpact}),r[0]||null))&&(p=p-e-r.rebufferingImpact,!r.playlist||r.playlist.uri===this.playlist_.uri||p<(e<=hl?1:.5)||(this.bandwidth=r.playlist.attributes.BANDWIDTH*Sl.BANDWIDTH_VARIANCE+1,this.trigger("earlyabort"))))))},t.handleAbort_=function(e){this.logger_("Aborting "+rl(e)),this.mediaRequestsAborted+=1},t.handleProgress_=function(e,t){this.earlyAbortWhenNeeded_(t.stats),this.checkForAbort_(t.requestId)||this.trigger("progress")},t.handleTrackInfo_=function(e,t){this.earlyAbortWhenNeeded_(e.stats),this.checkForAbort_(e.requestId)||this.checkForIllegalMediaSwitch(t)||(function(e,t){if(!e&&!t||!e&&t||e&&!t)return!1;if(e===t)return!0;var i=Object.keys(e).sort(),n=Object.keys(t).sort();if(i.length!==n.length)return!1;for(var r=0;r<i.length;r++){var a=i[r];if(a!==n[r])return!1;if(e[a]!==t[a])return!1}return!0}(this.currentMediaInfo_,t=t||{})||(this.appendInitSegment_={audio:!0,video:!0},this.startingMediaInfo_=t,this.currentMediaInfo_=t,this.logger_("trackinfo update",t),this.trigger("trackinfo")),this.checkForAbort_(e.requestId)||(this.pendingSegment_.trackInfo=t,this.hasEnoughInfoToAppend_()&&this.processCallQueue_()))},t.handleTimingInfo_=function(e,t,i,n){var r;this.earlyAbortWhenNeeded_(e.stats),this.checkForAbort_(e.requestId)||((r=this.pendingSegment_)[e=al(t)]=r[e]||{},r[e][i]=n,this.logger_("timinginfo: "+t+" - "+i+" - "+n),this.hasEnoughInfoToAppend_()&&this.processCallQueue_())},t.handleCaptions_=function(e,t){var g,y,v=this;this.earlyAbortWhenNeeded_(e.stats),this.checkForAbort_(e.requestId)||(0!==t.length?this.pendingSegment_.hasAppendedData_?(g=null===this.sourceUpdater_.videoTimestampOffset()?this.sourceUpdater_.audioTimestampOffset():this.sourceUpdater_.videoTimestampOffset(),y={},t.forEach(function(e){y[e.stream]=y[e.stream]||{startTime:1/0,captions:[],endTime:0};var t=y[e.stream];t.startTime=Math.min(t.startTime,e.startTime+g),t.endTime=Math.max(t.endTime,e.endTime+g),t.captions.push(e)}),Object.keys(y).forEach(function(e){var t,i,n,r,a,s,o,u,l,c,d=y[e],h=d.startTime,p=d.endTime,f=d.captions,m=v.inbandTextTracks_;v.logger_("adding cues from "+h+" -> "+p+" for "+e),t=m,i=v.vhs_.tech_,t[n=e]||(i.trigger({type:"usage",name:"vhs-608"}),i.trigger({type:"usage",name:"hls-608"}),/^cc708_/.test(r=n)&&(r="SERVICE"+n.split("_")[1]),(o=i.textTracks().getTrackById(r))?t[n]=o:(s=a=n,d=!1,(o=(i.options_.vhs&&i.options_.vhs.captionServices||{})[r])&&(a=o.label,s=o.language,d=o.default),t[n]=i.addRemoteTextTrack({kind:"captions",id:r,default:d,label:a,language:s},!1).track)),il(h,p,m[e]),l=(f={captionArray:f,inbandTextTracks:m,timestampOffset:g}).inbandTextTracks,m=f.captionArray,c=f.timestampOffset,m&&(u=window.WebKitDataCue||window.VTTCue,m.forEach(function(e){var t=e.stream;l[t].addCue(new u(e.startTime+c,e.endTime+c,e.text))}))}),this.transmuxer_&&this.transmuxer_.postMessage({action:"clearParsedMp4Captions"})):this.metadataQueue_.caption.push(this.handleCaptions_.bind(this,e,t)):this.logger_("SegmentLoader received no captions from a caption event"))},t.handleId3_=function(e,t,i){var n,r,a,s;this.earlyAbortWhenNeeded_(e.stats),this.checkForAbort_(e.requestId)||(this.pendingSegment_.hasAppendedData_?(n=null===this.sourceUpdater_.videoTimestampOffset()?this.sourceUpdater_.audioTimestampOffset():this.sourceUpdater_.videoTimestampOffset(),r=this.inbandTextTracks_,a=i,s=this.vhs_.tech_,r.metadataTrack_||(r.metadataTrack_=s.addRemoteTextTrack({kind:"metadata",label:"Timed Metadata"},!1).track,r.metadataTrack_.inBandMetadataTrackDispatchType=a),tl({inbandTextTracks:this.inbandTextTracks_,metadataArray:t,timestampOffset:n,videoDuration:this.duration_()})):this.metadataQueue_.id3.push(this.handleId3_.bind(this,e,t,i)))},t.processMetadataQueue_=function(){this.metadataQueue_.id3.forEach(function(e){return e()}),this.metadataQueue_.caption.forEach(function(e){return e()}),this.metadataQueue_.id3=[],this.metadataQueue_.caption=[]},t.processCallQueue_=function(){var e=this.callQueue_;this.callQueue_=[],e.forEach(function(e){return e()})},t.processLoadQueue_=function(){var e=this.loadQueue_;this.loadQueue_=[],e.forEach(function(e){return e()})},t.hasEnoughInfoToLoad_=function(){if("audio"!==this.loaderType_)return!0;var e=this.pendingSegment_;return!!e&&(!this.getCurrentMediaInfo_()||!sl({timelineChangeController:this.timelineChangeController_,currentTimeline:this.currentTimeline_,segmentTimeline:e.timeline,loaderType:this.loaderType_,audioDisabled:this.audioDisabled_}))},t.getCurrentMediaInfo_=function(e){return(e=void 0===e?this.pendingSegment_:e)&&e.trackInfo||this.currentMediaInfo_},t.getMediaInfo_=function(e){return void 0===e&&(e=this.pendingSegment_),this.getCurrentMediaInfo_(e)||this.startingMediaInfo_},t.hasEnoughInfoToAppend_=function(){if(!this.sourceUpdater_.ready())return!1;if(this.waitingOnRemove_||this.quotaExceededErrorRetryTimeout_)return!1;var e=this.pendingSegment_,t=this.getCurrentMediaInfo_();if(!e||!t)return!1;var i=t.hasAudio,n=t.hasVideo,t=t.isMuxed;return!(n&&!e.videoTimingInfo)&&(!(i&&!this.audioDisabled_&&!t&&!e.audioTimingInfo)&&!sl({timelineChangeController:this.timelineChangeController_,currentTimeline:this.currentTimeline_,segmentTimeline:e.timeline,loaderType:this.loaderType_,audioDisabled:this.audioDisabled_}))},t.handleData_=function(e,t){if(this.earlyAbortWhenNeeded_(e.stats),!this.checkForAbort_(e.requestId))if(!this.callQueue_.length&&this.hasEnoughInfoToAppend_()){var i,n=this.pendingSegment_;if(this.setTimeMapping_(n.timeline),this.updateMediaSecondsLoaded_(n.part||n.segment),"closed"!==this.mediaSource_.readyState){if(e.map&&(e.map=this.initSegmentForMap(e.map,!0),n.segment.map=e.map),e.key&&this.segmentKey(e.key,!0),n.isFmp4=e.isFmp4,n.timingInfo=n.timingInfo||{},n.isFmp4?(this.trigger("fmp4"),n.timingInfo.start=n[al(t.type)].start):(i=this.getCurrentMediaInfo_(),(i="main"===this.loaderType_&&i&&i.hasVideo)&&(r=n.videoTimingInfo.start),n.timingInfo.start=this.trueSegmentStart_({currentStart:n.timingInfo.start,playlist:n.playlist,mediaIndex:n.mediaIndex,currentVideoTimestampOffset:this.sourceUpdater_.videoTimestampOffset(),useVideoTimingInfo:i,firstVideoFrameTimeForData:r,videoTimingInfo:n.videoTimingInfo,audioTimingInfo:n.audioTimingInfo})),this.updateAppendInitSegmentStatus(n,t.type),this.updateSourceBufferTimestampOffset_(n),n.isSyncRequest){this.updateTimingInfoEnd_(n),this.syncController_.saveSegmentTimingInfo({segmentInfo:n,shouldSaveTimelineMapping:"main"===this.loaderType_});var r=this.chooseNextRequest_();if(r.mediaIndex!==n.mediaIndex||r.partIndex!==n.partIndex)return void this.logger_("sync segment was incorrect, not appending");this.logger_("sync segment was correct, appending")}n.hasAppendedData_=!0,this.processMetadataQueue_(),this.appendData_(n,t)}}else this.callQueue_.push(this.handleData_.bind(this,e,t))},t.updateAppendInitSegmentStatus=function(e,t){"main"!==this.loaderType_||"number"!=typeof e.timestampOffset||e.changedTimestampOffset||(this.appendInitSegment_={audio:!0,video:!0}),this.playlistOfLastInitSegment_[t]!==e.playlist&&(this.appendInitSegment_[t]=!0)},t.getInitSegmentAndUpdateState_=function(e){var t=e.type,i=e.initSegment,n=e.map,r=e.playlist;if(n){e=bu(n);if(this.activeInitSegmentId_===e)return null;i=this.initSegmentForMap(n,!0).bytes,this.activeInitSegmentId_=e}return i&&this.appendInitSegment_[t]?(this.playlistOfLastInitSegment_[t]=r,this.appendInitSegment_[t]=!1,this.activeInitSegmentId_=null,i):null},t.handleQuotaExceededError_=function(e,t){var i=this,n=e.segmentInfo,r=e.type,a=e.bytes,s=this.sourceUpdater_.audioBuffered(),o=this.sourceUpdater_.videoBuffered();1<s.length&&this.logger_("On QUOTA_EXCEEDED_ERR, found gaps in the audio buffer: "+Bo(s).join(", ")),1<o.length&&this.logger_("On QUOTA_EXCEEDED_ERR, found gaps in the video buffer: "+Bo(o).join(", "));var u=s.length?s.start(0):0,l=s.length?s.end(s.length-1):0,c=o.length?o.start(0):0,e=o.length?o.end(o.length-1):0;if(l-u<=1&&e-c<=1)return this.logger_("On QUOTA_EXCEEDED_ERR, single segment too large to append to buffer, triggering an error. Appended byte length: "+a.byteLength+", audio buffer: "+Bo(s).join(", ")+", video buffer: "+Bo(o).join(", ")+", "),this.error({message:"Quota exceeded error with append of a single segment of content",excludeUntil:1/0}),void this.trigger("error");this.waitingOnRemove_=!0,this.callQueue_.push(this.appendToSourceBuffer_.bind(this,{segmentInfo:n,type:r,bytes:a}));a=this.currentTime_()-1;this.logger_("On QUOTA_EXCEEDED_ERR, removing audio/video from 0 to "+a),this.remove(0,a,function(){i.logger_("On QUOTA_EXCEEDED_ERR, retrying append in 1s"),i.waitingOnRemove_=!1,i.quotaExceededErrorRetryTimeout_=window.setTimeout(function(){i.logger_("On QUOTA_EXCEEDED_ERR, re-processing call queue"),i.quotaExceededErrorRetryTimeout_=null,i.processCallQueue_()},1e3)},!0)},t.handleAppendError_=function(e,t){var i=e.segmentInfo,n=e.type,e=e.bytes;t&&(22!==t.code?(this.logger_("Received non QUOTA_EXCEEDED_ERR on append",t),this.error(n+" append of "+e.length+"b failed for segment #"+i.mediaIndex+" in playlist "+i.playlist.id),this.trigger("appenderror")):this.handleQuotaExceededError_({segmentInfo:i,type:n,bytes:e}))},t.appendToSourceBuffer_=function(e){var t,i,n=e.segmentInfo,r=e.type,a=e.initSegment,s=e.data,o=e.bytes;o||(e=[s],s=s.byteLength,a&&(e.unshift(a),s+=a.byteLength),i=0,(e={bytes:s,segments:e}).bytes&&(t=new Uint8Array(e.bytes),e.segments.forEach(function(e){t.set(e,i),i+=e.byteLength})),o=t),this.sourceUpdater_.appendBuffer({segmentInfo:n,type:r,bytes:o},this.handleAppendError_.bind(this,{segmentInfo:n,type:r,bytes:o}))},t.handleSegmentTimingInfo_=function(e,t,i){this.pendingSegment_&&t===this.pendingSegment_.requestId&&((t=this.pendingSegment_.segment)[e=e+"TimingInfo"]||(t[e]={}),t[e].transmuxerPrependedSeconds=i.prependedContentDuration||0,t[e].transmuxedPresentationStart=i.start.presentation,t[e].transmuxedDecodeStart=i.start.decode,t[e].transmuxedPresentationEnd=i.end.presentation,t[e].transmuxedDecodeEnd=i.end.decode,t[e].baseMediaDecodeTime=i.baseMediaDecodeTime)},t.appendData_=function(e,t){var i=t.type,n=t.data;n&&n.byteLength&&("audio"===i&&this.audioDisabled_||(t=this.getInitSegmentAndUpdateState_({type:i,initSegment:t.initSegment,playlist:e.playlist,map:e.isFmp4?e.segment.map:null}),this.appendToSourceBuffer_({segmentInfo:e,type:i,initSegment:t,data:n})))},t.loadSegment_=function(t){var i=this;this.state="WAITING",this.pendingSegment_=t,this.trimBackBuffer_(t),"number"==typeof t.timestampOffset&&this.transmuxer_&&this.transmuxer_.postMessage({action:"clearAllMp4Captions"}),this.hasEnoughInfoToLoad_()?this.updateTransmuxerAndRequestSegment_(t):this.loadQueue_.push(function(){var e=g({},t,{forceTimestampOffset:!0});g(t,i.generateSegmentInfo_(e)),i.isPendingTimestampOffset_=!1,i.updateTransmuxerAndRequestSegment_(t)})},t.updateTransmuxerAndRequestSegment_=function(n){var r=this;this.shouldUpdateTransmuxerTimestampOffset_(n.timestampOffset)&&(this.gopBuffer_.length=0,n.gopsToAlignWith=[],this.timeMapping_=0,this.transmuxer_.postMessage({action:"reset"}),this.transmuxer_.postMessage({action:"setTimestampOffset",timestampOffset:n.timestampOffset}));var e=this.createSimplifiedSegmentObj_(n),t=this.isEndOfStream_(n.mediaIndex,n.playlist,n.partIndex),i=null!==this.mediaIndex,a=n.timeline!==this.currentTimeline_&&0<n.timeline,a=t||i&&a;this.logger_("Requesting "+rl(n)),e.map&&!e.map.bytes&&(this.logger_("going to request init segment."),this.appendInitSegment_={video:!0,audio:!0}),n.abortRequests=Gu({xhr:this.vhs_.xhr,xhrOptions:this.xhrOptions_,decryptionWorker:this.decrypter_,segment:e,abortFn:this.handleAbort_.bind(this,n),progressFn:this.handleProgress_.bind(this),trackInfoFn:this.handleTrackInfo_.bind(this),timingInfoFn:this.handleTimingInfo_.bind(this),videoSegmentTimingInfoFn:this.handleSegmentTimingInfo_.bind(this,"video",n.requestId),audioSegmentTimingInfoFn:this.handleSegmentTimingInfo_.bind(this,"audio",n.requestId),captionsFn:this.handleCaptions_.bind(this),isEndOfTimeline:a,endedTimelineFn:function(){r.logger_("received endedtimeline callback")},id3Fn:this.handleId3_.bind(this),dataFn:this.handleData_.bind(this),doneFn:this.segmentRequestFinished_.bind(this),onTransmuxerLog:function(e){var t=e.message,i=e.level,e=e.stream;r.logger_(rl(n)+" logged from transmuxer stream "+e+" as a "+i+": "+t)}})},t.trimBackBuffer_=function(e){var t,i,n,r,r=(t=this.seekable_(),i=this.currentTime_(),n=this.playlist_.targetDuration||10,r=i-Sl.BACK_BUFFER_LENGTH,t.length&&(r=Math.max(r,t.start(0))),Math.min(i-n,r));0<r&&this.remove(0,r)},t.createSimplifiedSegmentObj_=function(e){var t=e.segment,i=e.part,n={resolvedUri:(i||t).resolvedUri,byterange:(i||t).byterange,requestId:e.requestId,transmuxer:e.transmuxer,audioAppendStart:e.audioAppendStart,gopsToAlignWith:e.gopsToAlignWith,part:e.part},i=e.playlist.segments[e.mediaIndex-1];return i&&i.timeline===t.timeline&&(i.videoTimingInfo?n.baseStartTime=i.videoTimingInfo.transmuxedDecodeEnd:i.audioTimingInfo&&(n.baseStartTime=i.audioTimingInfo.transmuxedDecodeEnd)),t.key&&(e=t.key.iv||new Uint32Array([0,0,0,e.mediaIndex+e.playlist.mediaSequence]),n.key=this.segmentKey(t.key),n.key.iv=e),t.map&&(n.map=this.initSegmentForMap(t.map)),n},t.saveTransferStats_=function(e){this.mediaRequests+=1,e&&(this.mediaBytesTransferred+=e.bytesReceived,this.mediaTransferDuration+=e.roundTripTime)},t.saveBandwidthRelatedStats_=function(e,t){this.pendingSegment_.byteLength=t.bytesReceived,e<1/60?this.logger_("Ignoring segment's bandwidth because its duration of "+e+" is less than the min to record "+1/60):(this.bandwidth=t.bandwidth,this.roundTrip=t.roundTripTime)},t.handleTimeout_=function(){this.mediaRequestsTimedout+=1,this.bandwidth=1,this.roundTrip=NaN,this.trigger("bandwidthupdate")},t.segmentRequestFinished_=function(e,t,i){if(this.callQueue_.length)this.callQueue_.push(this.segmentRequestFinished_.bind(this,e,t,i));else if(this.saveTransferStats_(t.stats),this.pendingSegment_&&t.requestId===this.pendingSegment_.requestId){if(e)return this.pendingSegment_=null,this.state="READY",e.code===Al?void 0:(this.pause(),e.code===xl?void this.handleTimeout_():(this.mediaRequestsErrored+=1,this.error(e),void this.trigger("error")));e=this.pendingSegment_;this.saveBandwidthRelatedStats_(e.duration,t.stats),e.endOfAllRequests=t.endOfAllRequests,i.gopInfo&&(this.gopBuffer_=function(e,t,i){if(!t.length)return e;if(i)return t.slice();for(var n=t[0].pts,r=0;r<e.length&&!(e[r].pts>=n);r++);return e.slice(0,r).concat(t)}(this.gopBuffer_,i.gopInfo,this.safeAppend_)),this.state="APPENDING",this.trigger("appending"),this.waitForAppendsToComplete_(e)}},t.setTimeMapping_=function(e){e=this.syncController_.mappingForTimeline(e);null!==e&&(this.timeMapping_=e)},t.updateMediaSecondsLoaded_=function(e){"number"==typeof e.start&&"number"==typeof e.end?this.mediaSecondsLoaded+=e.end-e.start:this.mediaSecondsLoaded+=e.duration},t.shouldUpdateTransmuxerTimestampOffset_=function(e){return null!==e&&("main"===this.loaderType_&&e!==this.sourceUpdater_.videoTimestampOffset()||!this.audioDisabled_&&e!==this.sourceUpdater_.audioTimestampOffset())},t.trueSegmentStart_=function(e){var t=e.currentStart,i=e.playlist,n=e.mediaIndex,r=e.firstVideoFrameTimeForData,a=e.currentVideoTimestampOffset,s=e.useVideoTimingInfo,o=e.videoTimingInfo,e=e.audioTimingInfo;if("undefined"!=typeof t)return t;if(!s)return e.start;i=i.segments[n-1];return 0!==n&&i&&"undefined"!=typeof i.start&&i.end===r+a?o.start:r},t.waitForAppendsToComplete_=function(e){var t=this.getCurrentMediaInfo_(e);if(!t)return this.error({message:"No starting media returned, likely due to an unsupported media format.",blacklistDuration:1/0}),void this.trigger("error");var i=t.hasAudio,n=t.hasVideo,t=t.isMuxed,n="main"===this.loaderType_&&n,t=!this.audioDisabled_&&i&&!t;if(e.waitingOnAppends=0,!e.hasAppendedData_)return e.timingInfo||"number"!=typeof e.timestampOffset||(this.isPendingTimestampOffset_=!0),e.timingInfo={start:0},e.waitingOnAppends++,this.isPendingTimestampOffset_||(this.updateSourceBufferTimestampOffset_(e),this.processMetadataQueue_()),void this.checkAppendsDone_(e);n&&e.waitingOnAppends++,t&&e.waitingOnAppends++,n&&this.sourceUpdater_.videoQueueCallback(this.checkAppendsDone_.bind(this,e)),t&&this.sourceUpdater_.audioQueueCallback(this.checkAppendsDone_.bind(this,e))},t.checkAppendsDone_=function(e){this.checkForAbort_(e.requestId)||(e.waitingOnAppends--,0===e.waitingOnAppends&&this.handleAppendsDone_())},t.checkForIllegalMediaSwitch=function(e){var t,i,e=(t=this.loaderType_,i=this.getCurrentMediaInfo_(),e=e,"main"===t&&i&&e?e.hasAudio||e.hasVideo?i.hasVideo&&!e.hasVideo?"Only audio found in segment when we expected video. We can't switch to audio only from a stream that had video. To get rid of this message, please add codec information to the manifest.":!i.hasVideo&&e.hasVideo?"Video found in segment when we expected only audio. We can't switch to a stream with video from an audio only stream. To get rid of this message, please add codec information to the manifest.":null:"Neither audio nor video found in segment.":null);return!!e&&(this.error({message:e,blacklistDuration:1/0}),this.trigger("error"),!0)},t.updateSourceBufferTimestampOffset_=function(e){var t;null===e.timestampOffset||"number"!=typeof e.timingInfo.start||e.changedTimestampOffset||"main"!==this.loaderType_||(t=!1,e.timestampOffset-=e.timingInfo.start,e.changedTimestampOffset=!0,e.timestampOffset!==this.sourceUpdater_.videoTimestampOffset()&&(this.sourceUpdater_.videoTimestampOffset(e.timestampOffset),t=!0),e.timestampOffset!==this.sourceUpdater_.audioTimestampOffset()&&(this.sourceUpdater_.audioTimestampOffset(e.timestampOffset),t=!0),t&&this.trigger("timestampoffset"))},t.updateTimingInfoEnd_=function(e){e.timingInfo=e.timingInfo||{};var t=this.getMediaInfo_(),t="main"===this.loaderType_&&t&&t.hasVideo&&e.videoTimingInfo?e.videoTimingInfo:e.audioTimingInfo;t&&(e.timingInfo.end="number"==typeof t.end?t.end:t.start+e.duration)},t.handleAppendsDone_=function(){if(this.pendingSegment_&&this.trigger("appendsdone"),!this.pendingSegment_)return this.state="READY",void(this.paused()||this.monitorBuffer_());var e=this.pendingSegment_;this.updateTimingInfoEnd_(e),this.shouldSaveSegmentTimingInfo_&&this.syncController_.saveSegmentTimingInfo({segmentInfo:e,shouldSaveTimelineMapping:"main"===this.loaderType_});var t=ul(e,this.sourceType_);if(t&&("warn"===t.severity?tr.log.warn(t.message):this.logger_(t.message)),this.recordThroughput_(e),this.pendingSegment_=null,this.state="READY",!e.isSyncRequest||(this.trigger("syncinfoupdate"),e.hasAppendedData_)){this.logger_("Appended "+rl(e)),this.addSegmentMetadataCue_(e),this.fetchAtBuffer_=!0,this.currentTimeline_!==e.timeline&&(this.timelineChangeController_.lastTimelineChange({type:this.loaderType_,from:this.currentTimeline_,to:e.timeline}),"main"!==this.loaderType_||this.audioDisabled_||this.timelineChangeController_.lastTimelineChange({type:"audio",from:this.currentTimeline_,to:e.timeline})),this.currentTimeline_=e.timeline,this.trigger("syncinfoupdate");var i=e.segment,t=e.part,i=i.end&&this.currentTime_()-i.end>3*e.playlist.targetDuration,t=t&&t.end&&this.currentTime_()-t.end>3*e.playlist.partTargetDuration;if(i||t)return this.logger_("bad "+(i?"segment":"part")+" "+rl(e)),void this.resetEverything();null!==this.mediaIndex&&this.trigger("bandwidthupdate"),this.trigger("progress"),this.mediaIndex=e.mediaIndex,this.partIndex=e.partIndex,this.isEndOfStream_(e.mediaIndex,e.playlist,e.partIndex)&&this.endOfStream(),this.trigger("appended"),e.hasAppendedData_&&this.mediaAppends++,this.paused()||this.monitorBuffer_()}else this.logger_("Throwing away un-appended sync request "+rl(e))},t.recordThroughput_=function(e){var t,i;e.duration<1/60?this.logger_("Ignoring segment's throughput because its duration of "+e.duration+" is less than the min to record "+1/60):(t=this.throughput.rate,i=Date.now()-e.endOfAllRequests+1,i=Math.floor(e.byteLength/i*8*1e3),this.throughput.rate+=(i-t)/++this.throughput.count)},t.addSegmentMetadataCue_=function(e){var t,i,n,r;this.segmentMetadataTrack_&&(i=(t=e.segment).start,r=t.end,nl(i)&&nl(r)&&(il(i,r,this.segmentMetadataTrack_),n=window.WebKitDataCue||window.VTTCue,e={custom:t.custom,dateTimeObject:t.dateTimeObject,dateTimeString:t.dateTimeString,bandwidth:e.playlist.attributes.BANDWIDTH,resolution:e.playlist.attributes.RESOLUTION,codecs:e.playlist.attributes.CODECS,byteLength:e.byteLength,uri:e.uri,timeline:e.timeline,playlist:e.playlist.id,start:i,end:r},(r=new n(i,r,JSON.stringify(e))).value=e,this.segmentMetadataTrack_.addCue(r)))},e}(tr.EventTarget);function Ol(){}function Rl(e){return"string"!=typeof e?e:e.replace(/./,function(e){return e.toUpperCase()})}function Ml(e,t){var i=t[e+"Buffer"];return i&&i.updating||t.queuePending[e]}function Nl(e,t){if(0!==t.queue.length){var i=0,n=t.queue[i];if("mediaSource"!==n.type){if("mediaSource"!==e&&t.ready()&&"closed"!==t.mediaSource.readyState&&!Ml(e,t)){if(n.type!==e){if(null===(i=function(e,t){for(var i=0;i<t.length;i++){var n=t[i];if("mediaSource"===n.type)return null;if(n.type===e)return i}return null}(e,t.queue)))return;n=t.queue[i]}t.queue.splice(i,1),(t.queuePending[e]=n).action(e,t),n.doneFn||(t.queuePending[e]=null,Nl(e,t))}}else t.updating()||"closed"===t.mediaSource.readyState||(t.queue.shift(),n.action(t),n.doneFn&&n.doneFn(),Nl("audio",t),Nl("video",t))}}function Ul(e,t){var i=t[e+"Buffer"],n=Rl(e);i&&(i.removeEventListener("updateend",t["on"+n+"UpdateEnd_"]),i.removeEventListener("error",t["on"+n+"Error_"]),t.codecs[e]=null,t[e+"Buffer"]=null)}function Bl(e,t){return e&&t&&-1!==Array.prototype.indexOf.call(e.sourceBuffers,t)}function Fl(e){var t=e.type,i=e.sourceUpdater,n=e.action,r=e.doneFn,e=e.name;i.queue.push({type:t,action:n,doneFn:r,name:e}),Nl(t,i)}function jl(i,n){return function(e){var t;n.queuePending[i]&&(t=n.queuePending[i].doneFn,n.queuePending[i]=null,t&&t(n[i+"Error_"])),Nl(i,n)}}function Hl(e){return decodeURIComponent(escape(String.fromCharCode.apply(null,e)))}function ql(e,t){e.abort(),e.pause(),t&&t.activePlaylistLoader&&(t.activePlaylistLoader.pause(),t.activePlaylistLoader=null)}function Vl(e,t){(t.activePlaylistLoader=e).load()}function Wl(e,t){for(var i=0;i<e.length;i++){if(tu(t,e[i]))return!0;if(e[i].playlists&&Wl(e[i].playlists,t))return!0}return!1}function Gl(a){["AUDIO","SUBTITLES","CLOSED-CAPTIONS"].forEach(function(e){mc[e](e,a)});var e,s=a.mediaTypes,t=a.masterPlaylistLoader,i=a.tech,n=a.vhs,r=a.segmentLoaders,o=r.AUDIO,u=r.main;function l(){s.AUDIO.onTrackChanged(),i.trigger({type:"usage",name:"vhs-audio-change"}),i.trigger({type:"usage",name:"hls-audio-change"})}for(e in["AUDIO","SUBTITLES"].forEach(function(e){var u,l,o,c,t,i,d,h,n,r;s[e].activeGroup=(u=e,l=a,function(t){var e=l.masterPlaylistLoader,i=l.mediaTypes[u].groups,n=e.media();if(!n)return null;var r=null;n.attributes[u]&&(r=i[n.attributes[u]]);var a=Object.keys(i);if(!r)if("AUDIO"===u&&1<a.length&&nu(l.master))for(var s=0;s<a.length;s++){var o=i[a[s]];if(Wl(o,n)){r=o;break}}else i.main?r=i.main:1===a.length&&(r=i[a[0]]);return"undefined"==typeof t?r:null!==t&&r&&r.filter(function(e){return e.id===t.id})[0]||null}),s[e].activeTrack=gc[e](e,a),s[e].onGroupChanged=(o=e,c=a,function(){var e=c.segmentLoaders,t=e[o],i=e.main,n=c.mediaTypes[o],r=n.activeTrack(),a=n.getActiveGroup(),s=n.activePlaylistLoader,e=n.lastGroup_;a&&e&&a.id===e.id||(n.lastGroup_=a,n.lastTrack_=r,ql(t,n),a&&!a.isMasterPlaylist&&(a.playlistLoader?(t.resyncLoader(),Vl(a.playlistLoader,n)):s&&i.resetEverything()))}),s[e].onGroupChanging=(t=e,i=a,function(){var e=i.segmentLoaders[t];i.mediaTypes[t].lastGroup_=null,e.abort(),e.pause()}),s[e].onTrackChanged=(d=e,h=a,function(){var e=h.masterPlaylistLoader,t=h.segmentLoaders,i=t[d],n=t.main,r=h.mediaTypes[d],a=r.activeTrack(),s=r.getActiveGroup(),o=r.activePlaylistLoader,u=r.lastTrack_;if((!u||!a||u.id!==a.id)&&(r.lastGroup_=s,r.lastTrack_=a,ql(i,r),s)){if(s.isMasterPlaylist){if(!a||!u||a.id===u.id)return;var l=h.vhs.masterPlaylistController_,t=l.selectPlaylist();return l.media()===t?void 0:(r.logger_("track change. Switching master audio from "+u.id+" to "+a.id),e.pause(),n.resetEverything(),void l.fastQualityChange_(t))}if("AUDIO"===d){if(!s.playlistLoader)return n.setAudio(!0),void n.resetEverything();i.setAudio(!0),n.setAudio(!1)}o!==s.playlistLoader&&(i.track&&i.track(a),i.resetEverything()),Vl(s.playlistLoader,r)}}),s[e].getActiveGroup=(n=e,r=a.mediaTypes,function(){var e=r[n].activeTrack();return e?r[n].activeGroup(e):null})}),(r=s.AUDIO.activeGroup())&&(r=(r.filter(function(e){return e.default})[0]||r[0]).id,s.AUDIO.tracks[r].enabled=!0,s.AUDIO.onGroupChanged(),s.AUDIO.onTrackChanged(),s.AUDIO.getActiveGroup().playlistLoader?(u.setAudio(!1),o.setAudio(!0)):u.setAudio(!0)),t.on("mediachange",function(){["AUDIO","SUBTITLES"].forEach(function(e){return s[e].onGroupChanged()})}),t.on("mediachanging",function(){["AUDIO","SUBTITLES"].forEach(function(e){return s[e].onGroupChanging()})}),i.audioTracks().addEventListener("change",l),i.remoteTextTracks().addEventListener("change",s.SUBTITLES.onTrackChanged),n.on("dispose",function(){i.audioTracks().removeEventListener("change",l),i.remoteTextTracks().removeEventListener("change",s.SUBTITLES.onTrackChanged)}),i.clearTracks("audio"),s.AUDIO.tracks)i.audioTracks().addTrack(s.AUDIO.tracks[e])}function zl(e,t,i){var n,r,a,s,o=e.masterPlaylistController_,u=o[(e.options_.smoothQualityChange?"smooth":"fast")+"QualityChange_"].bind(o);t.attributes&&(n=t.attributes.RESOLUTION,this.width=n&&n.width,this.height=n&&n.height,this.bandwidth=t.attributes.BANDWIDTH),this.codecs=Yu(o.master(),t),this.playlist=t,this.id=i,this.enabled=(r=e.playlists,a=t.id,s=u,function(e){var t=r.master.playlists[a],i=$o(t),n=Jo(t);return"undefined"==typeof e?n:(e?delete t.disabled:t.disabled=!0,e===n||i||(s(),e?r.trigger("renditionenabled"):r.trigger("renditiondisabled")),e)})}function Xl(t,e){var i=0,n=0,r=tr.mergeOptions(Tc,e);function a(){n&&t.currentTime(n)}function s(e){null!=e&&(n=t.duration()!==1/0&&t.currentTime()||0,t.one("loadedmetadata",a),t.src(e),t.trigger({type:"usage",name:"vhs-error-reload"}),t.trigger({type:"usage",name:"hls-error-reload"}),t.play())}function o(){return Date.now()-i<1e3*r.errorInterval?(t.trigger({type:"usage",name:"vhs-error-reload-canceled"}),void t.trigger({type:"usage",name:"hls-error-reload-canceled"})):r.getSource&&"function"==typeof r.getSource?(i=Date.now(),r.getSource.call(t,s)):void tr.log.error("ERROR: reloadSourceOnError - The option getSource must be a function!")}function u(){t.off("loadedmetadata",a),t.off("error",o),t.off("dispose",u)}t.ready(function(){t.trigger({type:"usage",name:"vhs-error-reload-initialized"}),t.trigger({type:"usage",name:"hls-error-reload-initialized"})}),t.on("error",o),t.on("dispose",u),t.reloadSourceOnError=function(e){u(),Xl(t,e)}}var Kl,Yl=["video","audio"],Ql=function(n,r,a){return function(t,i){var e=i[t+"Buffer"];if(Bl(i.mediaSource,e)){i.logger_("Appending segment "+r.mediaIndex+"'s "+n.length+" bytes to "+t+"Buffer");try{e.appendBuffer(n)}catch(e){i.logger_("Error with code "+e.code+" "+(22===e.code?"(QUOTA_EXCEEDED_ERR) ":"")+"when appending segment "+r.mediaIndex+" to "+t+"Buffer"),i.queuePending[t]=null,a(e)}}}},$l=function(n,r){return function(t,i){var e=i[t+"Buffer"];if(Bl(i.mediaSource,e)){i.logger_("Removing "+n+" to "+r+" from "+t+"Buffer");try{e.remove(n,r)}catch(e){i.logger_("Remove "+n+" to "+r+" from "+t+"Buffer failed")}}}},Jl=function(n){return function(e,t){var i=t[e+"Buffer"];Bl(t.mediaSource,i)&&(t.logger_("Setting "+e+"timestampOffset to "+n),i.timestampOffset=n)}},Zl=function(i){return function(e,t){i()}},ec=function(t){return function(e){if("open"===e.mediaSource.readyState){e.logger_("Calling mediaSource endOfStream("+(t||"")+")");try{e.mediaSource.endOfStream(t)}catch(e){tr.log.warn("Failed to call media source endOfStream",e)}}}},tc=function(t){return function(e){e.logger_("Setting mediaSource duration to "+t);try{e.mediaSource.duration=t}catch(e){tr.log.warn("Failed to set media source duration",e)}}},ic=function(){return function(t,e){if("open"===e.mediaSource.readyState){var i=e[t+"Buffer"];if(Bl(e.mediaSource,i)){e.logger_("calling abort on "+t+"Buffer");try{i.abort()}catch(e){tr.log.warn("Failed to abort on "+t+"Buffer",e)}}}}},nc=function(n,r){return function(e){var t=Rl(n),i=mr(r);e.logger_("Adding "+n+"Buffer with codec "+r+" to mediaSource");i=e.mediaSource.addSourceBuffer(i);i.addEventListener("updateend",e["on"+t+"UpdateEnd_"]),i.addEventListener("error",e["on"+t+"Error_"]),e.codecs[n]=r,e[n+"Buffer"]=i}},rc=function(i){return function(e){var t=e[i+"Buffer"];if(Ul(i,e),Bl(e.mediaSource,t)){e.logger_("Removing "+i+"Buffer with codec "+e.codecs[i]+" from mediaSource");try{e.mediaSource.removeSourceBuffer(t)}catch(e){tr.log.warn("Failed to removeSourceBuffer "+i+"Buffer",e)}}}},ac=function(r){return function(e,t){var i=t[e+"Buffer"],n=mr(r);Bl(t.mediaSource,i)&&t.codecs[e]!==r&&(t.logger_("changing "+e+"Buffer codec from "+t.codecs[e]+" to "+r),i.changeType(n),t.codecs[e]=r)}},sc=function(i){function e(e){var t=i.call(this)||this;return t.mediaSource=e,t.sourceopenListener_=function(){return Nl("mediaSource",ft(t))},t.mediaSource.addEventListener("sourceopen",t.sourceopenListener_),t.logger_=Oo("SourceUpdater"),t.audioTimestampOffset_=0,t.videoTimestampOffset_=0,t.queue=[],t.queuePending={audio:null,video:null},t.delayedAudioAppendQueue_=[],t.videoAppendQueued_=!1,t.codecs={},t.onVideoUpdateEnd_=jl("video",ft(t)),t.onAudioUpdateEnd_=jl("audio",ft(t)),t.onVideoError_=function(e){t.videoError_=e},t.onAudioError_=function(e){t.audioError_=e},t.createdSourceBuffers_=!1,t.initializedEme_=!1,t.triggeredReady_=!1,t}mt(e,i);var t=e.prototype;return t.initializedEme=function(){this.initializedEme_=!0,this.triggerReady()},t.hasCreatedSourceBuffers=function(){return this.createdSourceBuffers_},t.hasInitializedAnyEme=function(){return this.initializedEme_},t.ready=function(){return this.hasCreatedSourceBuffers()&&this.hasInitializedAnyEme()},t.createSourceBuffers=function(e){this.hasCreatedSourceBuffers()||(this.addOrChangeSourceBuffers(e),this.createdSourceBuffers_=!0,this.trigger("createdsourcebuffers"),this.triggerReady())},t.triggerReady=function(){this.ready()&&!this.triggeredReady_&&(this.triggeredReady_=!0,this.trigger("ready"))},t.addSourceBuffer=function(e,t){Fl({type:"mediaSource",sourceUpdater:this,action:nc(e,t),name:"addSourceBuffer"})},t.abort=function(e){Fl({type:e,sourceUpdater:this,action:ic(e),name:"abort"})},t.removeSourceBuffer=function(e){this.canRemoveSourceBuffer()?Fl({type:"mediaSource",sourceUpdater:this,action:rc(e),name:"removeSourceBuffer"}):tr.log.error("removeSourceBuffer is not supported!")},t.canRemoveSourceBuffer=function(){return!tr.browser.IE_VERSION&&!tr.browser.IS_FIREFOX&&window.MediaSource&&window.MediaSource.prototype&&"function"==typeof window.MediaSource.prototype.removeSourceBuffer},e.canChangeType=function(){return window.SourceBuffer&&window.SourceBuffer.prototype&&"function"==typeof window.SourceBuffer.prototype.changeType},t.canChangeType=function(){return this.constructor.canChangeType()},t.changeType=function(e,t){this.canChangeType()?Fl({type:e,sourceUpdater:this,action:ac(t),name:"changeType"}):tr.log.error("changeType is not supported!")},t.addOrChangeSourceBuffers=function(i){var n=this;if(!i||"object"!=typeof i||0===Object.keys(i).length)throw new Error("Cannot addOrChangeSourceBuffers to undefined codecs");Object.keys(i).forEach(function(e){var t=i[e];if(!n.hasCreatedSourceBuffers())return n.addSourceBuffer(e,t);n.canChangeType()&&n.changeType(e,t)})},t.appendBuffer=function(e,t){var i=this,n=e.segmentInfo,r=e.type,a=e.bytes;if(this.processedAppend_=!0,"audio"===r&&this.videoBuffer&&!this.videoAppendQueued_)return this.delayedAudioAppendQueue_.push([e,t]),void this.logger_("delayed audio append of "+a.length+" until video append");Fl({type:r,sourceUpdater:this,action:Ql(a,n||{mediaIndex:-1},t),doneFn:t,name:"appendBuffer"}),"video"===r&&(this.videoAppendQueued_=!0,this.delayedAudioAppendQueue_.length&&(r=this.delayedAudioAppendQueue_.slice(),this.logger_("queuing delayed audio "+r.length+" appendBuffers"),this.delayedAudioAppendQueue_.length=0,r.forEach(function(e){i.appendBuffer.apply(i,e)})))},t.audioBuffered=function(){return Bl(this.mediaSource,this.audioBuffer)&&this.audioBuffer.buffered||tr.createTimeRange()},t.videoBuffered=function(){return Bl(this.mediaSource,this.videoBuffer)&&this.videoBuffer.buffered||tr.createTimeRange()},t.buffered=function(){var e=Bl(this.mediaSource,this.videoBuffer)?this.videoBuffer:null,t=Bl(this.mediaSource,this.audioBuffer)?this.audioBuffer:null;return t&&!e?this.audioBuffered():e&&!t?this.videoBuffered():function(e,t){var i=null,n=null,r=0,a=[],s=[];if(!(e&&e.length&&t&&t.length))return tr.createTimeRange();for(var o=e.length;o--;)a.push({time:e.start(o),type:"start"}),a.push({time:e.end(o),type:"end"});for(o=t.length;o--;)a.push({time:t.start(o),type:"start"}),a.push({time:t.end(o),type:"end"});for(a.sort(function(e,t){return e.time-t.time}),o=0;o<a.length;o++)"start"===a[o].type?2===++r&&(i=a[o].time):"end"===a[o].type&&1===--r&&(n=a[o].time),null!==i&&null!==n&&(s.push([i,n]),n=i=null);return tr.createTimeRanges(s)}(this.audioBuffered(),this.videoBuffered())},t.setDuration=function(e,t){void 0===t&&(t=Ol),Fl({type:"mediaSource",sourceUpdater:this,action:tc(e),name:"duration",doneFn:t})},t.endOfStream=function(e,t){void 0===t&&(t=Ol),Fl({type:"mediaSource",sourceUpdater:this,action:ec(e="string"!=typeof(e=void 0===e?null:e)?void 0:e),name:"endOfStream",doneFn:t})},t.removeAudio=function(e,t,i){void 0===i&&(i=Ol),this.audioBuffered().length&&0!==this.audioBuffered().end(0)?Fl({type:"audio",sourceUpdater:this,action:$l(e,t),doneFn:i,name:"remove"}):i()},t.removeVideo=function(e,t,i){void 0===i&&(i=Ol),this.videoBuffered().length&&0!==this.videoBuffered().end(0)?Fl({type:"video",sourceUpdater:this,action:$l(e,t),doneFn:i,name:"remove"}):i()},t.updating=function(){return!(!Ml("audio",this)&&!Ml("video",this))},t.audioTimestampOffset=function(e){return"undefined"!=typeof e&&this.audioBuffer&&this.audioTimestampOffset_!==e&&(Fl({type:"audio",sourceUpdater:this,action:Jl(e),name:"timestampOffset"}),this.audioTimestampOffset_=e),this.audioTimestampOffset_},t.videoTimestampOffset=function(e){return"undefined"!=typeof e&&this.videoBuffer&&this.videoTimestampOffset!==e&&(Fl({type:"video",sourceUpdater:this,action:Jl(e),name:"timestampOffset"}),this.videoTimestampOffset_=e),this.videoTimestampOffset_},t.audioQueueCallback=function(e){this.audioBuffer&&Fl({type:"audio",sourceUpdater:this,action:Zl(e),name:"callback"})},t.videoQueueCallback=function(e){this.videoBuffer&&Fl({type:"video",sourceUpdater:this,action:Zl(e),name:"callback"})},t.dispose=function(){var t=this;this.trigger("dispose"),Yl.forEach(function(e){t.abort(e),t.canRemoveSourceBuffer()?t.removeSourceBuffer(e):t[e+"QueueCallback"](function(){return Ul(e,t)})}),this.videoAppendQueued_=!1,this.delayedAudioAppendQueue_.length=0,this.sourceopenListener_&&this.mediaSource.removeEventListener("sourceopen",this.sourceopenListener_),this.off()},e}(tr.EventTarget),oc=new Uint8Array("\n\n".split("").map(function(e){return e.charCodeAt(0)})),uc=function(i){function e(e,t){return(t=i.call(this,e,t=void 0===t?{}:t)||this).mediaSource_=null,t.subtitlesTrack_=null,t.loaderType_="subtitle",t.featuresNativeTextTracks_=e.featuresNativeTextTracks,t.shouldSaveSegmentTimingInfo_=!1,t}mt(e,i);var t=e.prototype;return t.createTransmuxer_=function(){return null},t.buffered_=function(){if(!this.subtitlesTrack_||!this.subtitlesTrack_.cues||!this.subtitlesTrack_.cues.length)return tr.createTimeRanges();var e=this.subtitlesTrack_.cues,t=e[0].startTime,e=e[e.length-1].startTime;return tr.createTimeRanges([[t,e]])},t.initSegmentForMap=function(e,t){if(void 0===t&&(t=!1),!e)return null;var i=bu(e),n=this.initSegments_[i];return t&&!n&&e.bytes&&(t=oc.byteLength+e.bytes.byteLength,(t=new Uint8Array(t)).set(e.bytes),t.set(oc,e.bytes.byteLength),this.initSegments_[i]=n={resolvedUri:e.resolvedUri,byterange:e.byterange,bytes:t}),n||e},t.couldBeginLoading_=function(){return this.playlist_&&this.subtitlesTrack_&&!this.paused()},t.init_=function(){return this.state="READY",this.resetEverything(),this.monitorBuffer_()},t.track=function(e){return"undefined"==typeof e||(this.subtitlesTrack_=e,"INIT"===this.state&&this.couldBeginLoading_()&&this.init_()),this.subtitlesTrack_},t.remove=function(e,t){il(e,t,this.subtitlesTrack_)},t.fillBuffer_=function(){var e=this,t=this.chooseNextRequest_();if(t){if(null===this.syncController_.timestampOffsetForTimeline(t.timeline))return this.syncController_.one("timestampoffset",function(){e.state="READY",e.paused()||e.monitorBuffer_()}),void(this.state="WAITING_ON_TIMELINE");this.loadSegment_(t)}},t.timestampOffsetForSegment_=function(){return null},t.chooseNextRequest_=function(){return this.skipEmptySegments_(i.prototype.chooseNextRequest_.call(this))},t.skipEmptySegments_=function(e){for(;e&&e.segment.empty;){if(e.mediaIndex+1>=e.playlist.segments.length){e=null;break}e=this.generateSegmentInfo_({playlist:e.playlist,mediaIndex:e.mediaIndex+1,startOfSegment:e.startOfSegment+e.duration,isSyncRequest:e.isSyncRequest})}return e},t.stopForError=function(e){this.error(e),this.state="READY",this.pause(),this.trigger("error")},t.segmentRequestFinished_=function(e,t,i){var n=this;if(this.subtitlesTrack_){if(this.saveTransferStats_(t.stats),!this.pendingSegment_)return this.state="READY",void(this.mediaRequestsAborted+=1);if(e)return e.code===xl&&this.handleTimeout_(),e.code===Al?this.mediaRequestsAborted+=1:this.mediaRequestsErrored+=1,void this.stopForError(e);var r=this.pendingSegment_;this.saveBandwidthRelatedStats_(r.duration,t.stats),this.state="APPENDING",this.trigger("appending");var a=r.segment;if(a.map&&(a.map.bytes=t.map.bytes),r.bytes=t.bytes,"function"!=typeof window.WebVTT&&this.subtitlesTrack_&&this.subtitlesTrack_.tech_){var s=function(){n.subtitlesTrack_.tech_.off("vttjsloaded",o),n.stopForError({message:"Error loading vtt.js"})},o=function(){n.subtitlesTrack_.tech_.off("vttjserror",s),n.segmentRequestFinished_(e,t,i)};return this.state="WAITING_ON_VTTJS",this.subtitlesTrack_.tech_.one("vttjsloaded",o),void this.subtitlesTrack_.tech_.one("vttjserror",s)}a.requested=!0;try{this.parseVTTCues_(r)}catch(e){return void this.stopForError({message:e.message})}if(this.updateTimeMapping_(r,this.syncController_.timelines[r.timeline],this.playlist_),r.cues.length?r.timingInfo={start:r.cues[0].startTime,end:r.cues[r.cues.length-1].endTime}:r.timingInfo={start:r.startOfSegment,end:r.startOfSegment+r.duration},r.isSyncRequest)return this.trigger("syncinfoupdate"),this.pendingSegment_=null,void(this.state="READY");r.byteLength=r.bytes.byteLength,this.mediaSecondsLoaded+=a.duration,r.cues.forEach(function(e){n.subtitlesTrack_.addCue(n.featuresNativeTextTracks_?new window.VTTCue(e.startTime,e.endTime,e.text):e)}),function(t){var e=t.cues;if(e)for(var i=0;i<e.length;i++){for(var n=[],r=0,a=0;a<e.length;a++)e[i].startTime===e[a].startTime&&e[i].endTime===e[a].endTime&&e[i].text===e[a].text&&1<++r&&n.push(e[a]);n.length&&n.forEach(function(e){return t.removeCue(e)})}}(this.subtitlesTrack_),this.handleAppendsDone_()}else this.state="READY"},t.handleData_=function(){},t.updateTimingInfoEnd_=function(){},t.parseVTTCues_=function(t){var e=!1;"function"==typeof window.TextDecoder?i=new window.TextDecoder("utf8"):(i=window.WebVTT.StringDecoder(),e=!0);var i=new window.WebVTT.Parser(window,window.vttjs,i);t.cues=[],t.timestampmap={MPEGTS:0,LOCAL:0},i.oncue=t.cues.push.bind(t.cues),i.ontimestampmap=function(e){t.timestampmap=e},i.onparsingerror=function(e){tr.log.warn("Error encountered when parsing cues: "+e.message)},t.segment.map&&(n=t.segment.map.bytes,e&&(n=Hl(n)),i.parse(n));var n=t.bytes;e&&(n=Hl(n)),i.parse(n),i.flush()},t.updateTimeMapping_=function(e,t,i){var n,r,a=e.segment;t&&(e.cues.length?(r=e.timestampmap,n=r.MPEGTS/cl-r.LOCAL+t.mapping,e.cues.forEach(function(e){e.startTime+=n,e.endTime+=n}),i.syncInfo||(r=e.cues[0].startTime,t=e.cues[e.cues.length-1].startTime,i.syncInfo={mediaSequence:i.mediaSequence+e.mediaIndex,time:Math.min(r,t-a.duration)})):a.empty=!0)},e}(Dl),lc=[{name:"VOD",run:function(e,t,i,n,r){if(i===1/0)return null;return{time:0,segmentIndex:0,partIndex:null}}},{name:"ProgramDateTime",run:function(e,t,i,n,r){if(!Object.keys(e.timelineToDatetimeMappings).length)return null;var a=null,s=null,o=qo(t);r=r||0;for(var u=0;u<o.length;u++){var l=o[t.endList||0===r?u:o.length-(u+1)],c=l.segment,d=e.timelineToDatetimeMappings[c.timeline];if(d&&c.dateTimeObject){var h=c.dateTimeObject.getTime()/1e3+d;if(c.parts&&"number"==typeof l.partIndex)for(var p=0;p<l.partIndex;p++)h+=c.parts[p].duration;d=Math.abs(r-h);if(null!==s&&(0===d||s<d))break;s=d,a={time:h,segmentIndex:l.segmentIndex,partIndex:l.partIndex}}}return a}},{name:"Segment",run:function(e,t,i,n,r){var a=null,s=null;r=r||0;for(var o=qo(t),u=0;u<o.length;u++){var l=o[t.endList||0===r?u:o.length-(u+1)],c=l.segment,d=l.part&&l.part.start||c&&c.start;if(c.timeline===n&&"undefined"!=typeof d){c=Math.abs(r-d);if(null!==s&&s<c)break;(!a||null===s||c<=s)&&(s=c,a={time:d,segmentIndex:l.segmentIndex,partIndex:l.partIndex})}}return a}},{name:"Discontinuity",run:function(e,t,i,n,r){var a=null;if(r=r||0,t.discontinuityStarts&&t.discontinuityStarts.length)for(var s=null,o=0;o<t.discontinuityStarts.length;o++){var u=t.discontinuityStarts[o],l=t.discontinuitySequence+o+1,c=e.discontinuities[l];if(c){l=Math.abs(r-c.time);if(null!==s&&s<l)break;(!a||null===s||l<=s)&&(s=l,a={time:c.time,segmentIndex:u,partIndex:null})}}return a}},{name:"Playlist",run:function(e,t,i,n,r){return t.syncInfo?{time:t.syncInfo.time,segmentIndex:t.syncInfo.mediaSequence-t.mediaSequence,partIndex:null}:null}}],cc=function(i){function e(e){var t=i.call(this)||this;return t.timelines=[],t.discontinuities=[],t.timelineToDatetimeMappings={},t.logger_=Oo("SyncController"),t}mt(e,i);var t=e.prototype;return t.getSyncPoint=function(e,t,i,n){i=this.runStrategies_(e,t,i,n);return i.length?this.selectSyncPoint_(i,{key:"time",value:n}):null},t.getExpiredTime=function(e,t){if(!e||!e.segments)return null;t=this.runStrategies_(e,t,e.discontinuitySequence,0);if(!t.length)return null;t=this.selectSyncPoint_(t,{key:"segmentIndex",value:0});return 0<t.segmentIndex&&(t.time*=-1),Math.abs(t.time+Ko({defaultDuration:e.targetDuration,durationList:e.segments,startIndex:t.segmentIndex,endIndex:0}))},t.runStrategies_=function(e,t,i,n){for(var r=[],a=0;a<lc.length;a++){var s=lc[a],o=s.run(this,e,t,i,n);o&&(o.strategy=s.name,r.push({strategy:s.name,syncPoint:o}))}return r},t.selectSyncPoint_=function(e,t){for(var i=e[0].syncPoint,n=Math.abs(e[0].syncPoint[t.key]-t.value),r=e[0].strategy,a=1;a<e.length;a++){var s=Math.abs(e[a].syncPoint[t.key]-t.value);s<n&&(n=s,i=e[a].syncPoint,r=e[a].strategy)}return this.logger_("syncPoint for ["+t.key+": "+t.value+"] chosen with strategy ["+r+"]: [time:"+i.time+", segmentIndex:"+i.segmentIndex+("number"==typeof i.partIndex?",partIndex:"+i.partIndex:"")+"]"),i},t.saveExpiredSegmentInfo=function(e,t){var i=t.mediaSequence-e.mediaSequence;if(86400<i)tr.log.warn("Not saving expired segment info. Media sequence gap "+i+" is too large.");else for(var n=i-1;0<=n;n--){var r=e.segments[n];if(r&&"undefined"!=typeof r.start){t.syncInfo={mediaSequence:e.mediaSequence+n,time:r.start},this.logger_("playlist refresh sync: [time:"+t.syncInfo.time+", mediaSequence: "+t.syncInfo.mediaSequence+"]"),this.trigger("syncinfoupdate");break}}},t.setDateTimeMappingForStart=function(e){var t;this.timelineToDatetimeMappings={},e.segments&&e.segments.length&&e.segments[0].dateTimeObject&&(e=(t=e.segments[0]).dateTimeObject.getTime()/1e3,this.timelineToDatetimeMappings[t.timeline]=-e)},t.saveSegmentTimingInfo=function(e){var t=e.segmentInfo,i=e.shouldSaveTimelineMapping,n=this.calculateSegmentTimeMapping_(t,t.timingInfo,i),e=t.segment;n&&(this.saveDiscontinuitySyncInfo_(t),t.playlist.syncInfo||(t.playlist.syncInfo={mediaSequence:t.playlist.mediaSequence+t.mediaIndex,time:e.start}));t=e.dateTimeObject;e.discontinuity&&i&&t&&(this.timelineToDatetimeMappings[e.timeline]=-t.getTime()/1e3)},t.timestampOffsetForTimeline=function(e){return"undefined"==typeof this.timelines[e]?null:this.timelines[e].time},t.mappingForTimeline=function(e){return"undefined"==typeof this.timelines[e]?null:this.timelines[e].mapping},t.calculateSegmentTimeMapping_=function(e,t,i){var n,r,a=e.segment,s=e.part,o=this.timelines[e.timeline];if("number"==typeof e.timestampOffset)o={time:e.startOfSegment,mapping:e.startOfSegment-t.start},i&&(this.timelines[e.timeline]=o,this.trigger("timestampoffset"),this.logger_("time mapping for timeline "+e.timeline+": [time: "+o.time+"] [mapping: "+o.mapping+"]")),n=e.startOfSegment,r=t.end+o.mapping;else{if(!o)return!1;n=t.start+o.mapping,r=t.end+o.mapping}return s&&(s.start=n,s.end=r),(!a.start||n<a.start)&&(a.start=n),a.end=r,!0},t.saveDiscontinuitySyncInfo_=function(e){var t=e.playlist,i=e.segment;if(i.discontinuity)this.discontinuities[i.timeline]={time:i.start,accuracy:0};else if(t.discontinuityStarts&&t.discontinuityStarts.length)for(var n=0;n<t.discontinuityStarts.length;n++){var r,a=t.discontinuityStarts[n],s=t.discontinuitySequence+n+1,o=a-e.mediaIndex,u=Math.abs(o);(!this.discontinuities[s]||this.discontinuities[s].accuracy>u)&&(r=void 0,r=o<0?i.start-Ko({defaultDuration:t.targetDuration,durationList:t.segments,startIndex:e.mediaIndex,endIndex:a}):i.end+Ko({defaultDuration:t.targetDuration,durationList:t.segments,startIndex:e.mediaIndex+1,endIndex:a}),this.discontinuities[s]={time:r,accuracy:u})}},t.dispose=function(){this.trigger("dispose"),this.off()},e}(tr.EventTarget),dc=function(t){function e(){var e=t.call(this)||this;return e.pendingTimelineChanges_={},e.lastTimelineChanges_={},e}mt(e,t);var i=e.prototype;return i.clearPendingTimelineChange=function(e){this.pendingTimelineChanges_[e]=null,this.trigger("pendingtimelinechange")},i.pendingTimelineChange=function(e){var t=e.type,i=e.from,e=e.to;return"number"==typeof i&&"number"==typeof e&&(this.pendingTimelineChanges_[t]={type:t,from:i,to:e},this.trigger("pendingtimelinechange")),this.pendingTimelineChanges_[t]},i.lastTimelineChange=function(e){var t=e.type,i=e.from,e=e.to;return"number"==typeof i&&"number"==typeof e&&(this.lastTimelineChanges_[t]={type:t,from:i,to:e},delete this.pendingTimelineChanges_[t],this.trigger("timelinechange")),this.lastTimelineChanges_[t]},i.dispose=function(){this.trigger("dispose"),this.pendingTimelineChanges_={},this.lastTimelineChanges_={},this.off()},e}(tr.EventTarget),hc=x(U(W(function(){function e(e,t,i){return e(i={path:t,exports:{},require:function(e,t){return function(){throw new Error("Dynamic requires are not currently supported by @rollup/plugin-commonjs")}(null==t&&i.path)}},i.exports),i.exports}var t=e(function(e){function n(e,t){for(var i=0;i<t.length;i++){var n=t[i];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(e,n.key,n)}}e.exports=function(e,t,i){return t&&n(e.prototype,t),i&&n(e,i),e},e.exports.default=e.exports,e.exports.__esModule=!0}),i=e(function(i){function n(e,t){return i.exports=n=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e},i.exports.default=i.exports,i.exports.__esModule=!0,n(e,t)}i.exports=n,i.exports.default=i.exports,i.exports.__esModule=!0}),n=e(function(e){e.exports=function(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,i(e,t)},e.exports.default=e.exports,e.exports.__esModule=!0}),r=function(){function e(){this.listeners={}}var t=e.prototype;return t.on=function(e,t){this.listeners[e]||(this.listeners[e]=[]),this.listeners[e].push(t)},t.off=function(e,t){if(!this.listeners[e])return!1;t=this.listeners[e].indexOf(t);return this.listeners[e]=this.listeners[e].slice(0),this.listeners[e].splice(t,1),-1<t},t.trigger=function(e){var t=this.listeners[e];if(t)if(2===arguments.length)for(var i=t.length,n=0;n<i;++n)t[n].call(this,arguments[1]);else for(var r=Array.prototype.slice.call(arguments,1),a=t.length,s=0;s<a;++s)t[s].apply(this,r)},t.dispose=function(){this.listeners={}},t.pipe=function(t){this.on("data",function(e){t.push(e)})},e}();
 /*! @name aes-decrypter @version 3.1.2 @license Apache-2.0 */
 var c=null,m=function(){function e(e){var t,i,n;c=c||function(){for(var e,t,i,n,r,a,s,o=[[[],[],[],[],[]],[[],[],[],[],[]]],u=o[0],l=o[1],c=u[4],d=l[4],h=[],p=[],f=0;f<256;f++)p[(h[f]=f<<1^283*(f>>7))^f]=f;for(e=t=0;!c[e];e^=i||1,t=p[t]||1)for(s=16843009*h[n=h[i=h[d[c[e]=r=(r=t^t<<1^t<<2^t<<3^t<<4)>>8^255&r^99]=e]]]^65537*n^257*i^16843008*e,a=257*h[r]^16843008*r,f=0;f<4;f++)u[f][e]=a=a<<24^a>>>8,l[f][r]=s=s<<24^s>>>8;for(f=0;f<5;f++)u[f]=u[f].slice(0),l[f]=l[f].slice(0);return o}(),this._tables=[[c[0][0].slice(),c[0][1].slice(),c[0][2].slice(),c[0][3].slice(),c[0][4].slice()],[c[1][0].slice(),c[1][1].slice(),c[1][2].slice(),c[1][3].slice(),c[1][4].slice()]];var r=this._tables[0][4],a=this._tables[1],s=e.length,o=1;if(4!==s&&6!==s&&8!==s)throw new Error("Invalid aes key size");var u=e.slice(0),l=[];for(this._key=[u,l],t=s;t<4*s+28;t++)n=u[t-1],(t%s==0||8===s&&t%s==4)&&(n=r[n>>>24]<<24^r[n>>16&255]<<16^r[n>>8&255]<<8^r[255&n],t%s==0&&(n=n<<8^n>>>24^o<<24,o=o<<1^283*(o>>7))),u[t]=u[t-s]^n;for(i=0;t;i++,t--)n=u[3&i?t:t-4],l[i]=t<=4||i<4?n:a[0][r[n>>>24]]^a[1][r[n>>16&255]]^a[2][r[n>>8&255]]^a[3][r[255&n]]}return e.prototype.decrypt=function(e,t,i,n,r,a){for(var s,o,u,l=this._key[1],c=e^l[0],d=n^l[1],h=i^l[2],p=t^l[3],f=l.length/4-2,m=4,t=this._tables[1],g=t[0],y=t[1],v=t[2],_=t[3],b=t[4],T=0;T<f;T++)s=g[c>>>24]^y[d>>16&255]^v[h>>8&255]^_[255&p]^l[m],o=g[d>>>24]^y[h>>16&255]^v[p>>8&255]^_[255&c]^l[m+1],u=g[h>>>24]^y[p>>16&255]^v[c>>8&255]^_[255&d]^l[m+2],p=g[p>>>24]^y[c>>16&255]^v[d>>8&255]^_[255&h]^l[m+3],m+=4,c=s,d=o,h=u;for(T=0;T<4;T++)r[(3&-T)+a]=b[c>>>24]<<24^b[d>>16&255]<<16^b[h>>8&255]<<8^b[255&p]^l[m++],s=c,c=d,d=h,h=p,p=s},e}(),l=function(t){function e(){var e=t.call(this,r)||this;return e.jobs=[],e.delay=1,e.timeout_=null,e}n(e,t);var i=e.prototype;return i.processJob_=function(){this.jobs.shift()(),this.jobs.length?this.timeout_=setTimeout(this.processJob_.bind(this),this.delay):this.timeout_=null},i.push=function(e){this.jobs.push(e),this.timeout_||(this.timeout_=setTimeout(this.processJob_.bind(this),this.delay))},e}(r),g=function(e){return e<<24|(65280&e)<<8|(16711680&e)>>8|e>>>24},a=function(){function u(e,t,i,n){var r=u.STEP,a=new Int32Array(e.buffer),s=new Uint8Array(e.byteLength),o=0;for(this.asyncStream_=new l,this.asyncStream_.push(this.decryptChunk_(a.subarray(o,o+r),t,i,s)),o=r;o<a.length;o+=r)i=new Uint32Array([g(a[o-4]),g(a[o-3]),g(a[o-2]),g(a[o-1])]),this.asyncStream_.push(this.decryptChunk_(a.subarray(o,o+r),t,i,s));this.asyncStream_.push(function(){
 /*! @name pkcs7 @version 1.0.4 @license Apache-2.0 */
 var e;n(null,(e=s).subarray(0,e.byteLength-e[e.byteLength-1]))})}return u.prototype.decryptChunk_=function(t,i,n,r){return function(){var e=function(e,t,i){for(var n,r,a,s,o=new Int32Array(e.buffer,e.byteOffset,e.byteLength>>2),u=new m(Array.prototype.slice.call(t)),e=new Uint8Array(e.byteLength),l=new Int32Array(e.buffer),c=i[0],d=i[1],h=i[2],p=i[3],f=0;f<o.length;f+=4)n=g(o[f]),r=g(o[f+1]),a=g(o[f+2]),s=g(o[f+3]),u.decrypt(n,r,a,s,l,f),l[f]=g(l[f]^c),l[f+1]=g(l[f+1]^d),l[f+2]=g(l[f+2]^h),l[f+3]=g(l[f+3]^p),c=n,d=r,h=a,p=s;return e}(t,i,n);r.set(e,t.byteOffset)}},t(u,null,[{key:"STEP",get:function(){return 32e3}}]),u}();self.onmessage=function(e){var r=e.data,t=new Uint8Array(r.encrypted.bytes,r.encrypted.byteOffset,r.encrypted.byteLength),i=new Uint32Array(r.key.bytes,r.key.byteOffset,r.key.byteLength/4),e=new Uint32Array(r.iv.bytes,r.iv.byteOffset,r.iv.byteLength/4);new a(t,i,e,function(e,t){var i,n;self.postMessage((i={source:r.source,decrypted:t},n={},Object.keys(i).forEach(function(e){var t=i[e];ArrayBuffer.isView(t)?n[e]={bytes:t.buffer,byteOffset:t.byteOffset,byteLength:t.byteLength}:n[e]=t}),n),[t.buffer])})}}))),pc={AUDIO:function(s,o){return function(){var e=o.segmentLoaders[s],t=o.mediaTypes[s],i=o.blacklistCurrentPlaylist;ql(e,t);var n=t.activeTrack(),e=t.activeGroup(),e=(e.filter(function(e){return e.default})[0]||e[0]).id,r=t.tracks[e];if(n!==r){for(var a in tr.log.warn("Problem encountered loading the alternate audio track.Switching back to default."),t.tracks)t.tracks[a].enabled=t.tracks[a]===r;t.onTrackChanged()}else i({message:"Problem encountered loading the default audio track."})}},SUBTITLES:function(i,n){return function(){var e=n.segmentLoaders[i],t=n.mediaTypes[i];tr.log.warn("Problem encountered loading the subtitle track.Disabling subtitle track."),ql(e,t);e=t.activeTrack();e&&(e.mode="disabled"),t.onTrackChanged()}}},fc={AUDIO:function(e,t,i){var n,r,a;t&&(n=i.tech,r=i.requestOptions,a=i.segmentLoaders[e],t.on("loadedmetadata",function(){var e=t.media();a.playlist(e,r),(!n.paused()||e.endList&&"none"!==n.preload())&&a.load()}),t.on("loadedplaylist",function(){a.playlist(t.media(),r),n.paused()||a.load()}),t.on("error",pc[e](e,i)))},SUBTITLES:function(e,t,i){var n=i.tech,r=i.requestOptions,a=i.segmentLoaders[e],s=i.mediaTypes[e];t.on("loadedmetadata",function(){var e=t.media();a.playlist(e,r),a.track(s.activeTrack()),(!n.paused()||e.endList&&"none"!==n.preload())&&a.load()}),t.on("loadedplaylist",function(){a.playlist(t.media(),r),n.paused()||a.load()}),t.on("error",pc[e](e,i))}},mc={AUDIO:function(e,t){var i,n,r=t.vhs,a=t.sourceType,s=t.segmentLoaders[e],o=t.requestOptions,u=t.master.mediaGroups,l=t.mediaTypes[e],c=l.groups,d=l.tracks,h=l.logger_,p=t.masterPlaylistLoader,f=nu(p.master);for(i in u[e]&&0!==Object.keys(u[e]).length||(u[e]={main:{default:{default:!0}}},f&&(u[e].main.default.playlists=p.master.playlists)),u[e])for(var m in c[i]||(c[i]=[]),u[e][i]){var g=u[e][i][m],y=void 0,y=f?(h("AUDIO group '"+i+"' label '"+m+"' is a master playlist"),g.isMasterPlaylist=!0,null):"vhs-json"===a&&g.playlists?new yl(g.playlists[0],r,o):g.resolvedUri?new yl(g.resolvedUri,r,o):g.playlists&&"dash"===a?new Tl(g.playlists[0],r,o,p):null,g=tr.mergeOptions({id:m,playlistLoader:y},g);fc[e](e,g.playlistLoader,t),c[i].push(g),"undefined"==typeof d[m]&&(g=new tr.AudioTrack({id:m,kind:(n=void 0,n=(y=g).default?"main":"alternative",n=y.characteristics&&0<=y.characteristics.indexOf("public.accessibility.describes-video")?"main-desc":n),enabled:!1,language:g.language,default:g.default,label:m}),d[m]=g)}s.on("error",pc[e](e,t))},SUBTITLES:function(e,t){var i,n=t.tech,r=t.vhs,a=t.sourceType,s=t.segmentLoaders[e],o=t.requestOptions,u=t.master.mediaGroups,l=t.mediaTypes[e],c=l.groups,d=l.tracks,h=t.masterPlaylistLoader;for(i in u[e])for(var p in c[i]||(c[i]=[]),u[e][i])if(!u[e][i][p].forced){var f=u[e][i][p],m=void 0;if("hls"===a)m=new yl(f.resolvedUri,r,o);else if("dash"===a){if(!f.playlists.filter(function(e){return e.excludeUntil!==1/0}).length)return;m=new Tl(f.playlists[0],r,o,h)}else"vhs-json"===a&&(m=new yl(f.playlists?f.playlists[0]:f.resolvedUri,r,o));f=tr.mergeOptions({id:p,playlistLoader:m},f),fc[e](e,f.playlistLoader,t),c[i].push(f),"undefined"==typeof d[p]&&(f=n.addRemoteTextTrack({id:p,kind:"subtitles",default:f.default&&f.autoselect,language:f.language,label:p},!1).track,d[p]=f)}s.on("error",pc[e](e,t))},"CLOSED-CAPTIONS":function(e,t){var i,n=t.tech,r=t.master.mediaGroups,t=t.mediaTypes[e],a=t.groups,s=t.tracks;for(i in r[e])for(var o in a[i]||(a[i]=[]),r[e][i]){var u,l,c=r[e][i][o];/^(?:CC|SERVICE)/.test(c.instreamId)&&(void 0===(l=(u=n.options_.vhs&&n.options_.vhs.captionServices||{})[(l={label:o,language:c.language,instreamId:c.instreamId,default:c.default&&c.autoselect}).instreamId]?tr.mergeOptions(l,u[l.instreamId]):l).default&&delete l.default,a[i].push(tr.mergeOptions({id:o},c)),"undefined"==typeof s[o]&&(l=n.addRemoteTextTrack({id:l.instreamId,kind:"captions",default:l.default,language:l.language,label:l.label},!1).track,s[o]=l))}}},gc={AUDIO:function(i,n){return function(){var e,t=n.mediaTypes[i].tracks;for(e in t)if(t[e].enabled)return t[e];return null}},SUBTITLES:function(i,n){return function(){var e,t=n.mediaTypes[i].tracks;for(e in t)if("showing"===t[e].mode||"hidden"===t[e].mode)return t[e];return null}}},yc=["mediaRequests","mediaRequestsAborted","mediaRequestsTimedout","mediaRequestsErrored","mediaTransferDuration","mediaBytesTransferred","mediaAppends"],vc=function(v){function e(e){var t=v.call(this)||this,i=e.src,n=e.handleManifestRedirects,r=e.withCredentials,a=e.tech,s=e.bandwidth,o=e.externVhs,u=e.useCueTags,l=e.blacklistDuration,c=e.enableLowInitialPlaylist,d=e.sourceType,h=e.cacheEncryptionKeys,p=e.experimentalBufferBasedABR,f=e.experimentalLeastPixelDiffSelector,m=e.captionServices;if(!i)throw new Error("A non-empty playlist URL or JSON manifest string is required");var g,y=e.maxPlaylistRetries;null!==y&&"undefined"!=typeof y||(y=1/0),Kl=o,t.experimentalBufferBasedABR=Boolean(p),t.experimentalLeastPixelDiffSelector=Boolean(f),t.withCredentials=r,t.tech_=a,t.vhs_=a.vhs,t.sourceType_=d,t.useCueTags_=u,t.blacklistDuration=l,t.maxPlaylistRetries=y,t.enableLowInitialPlaylist=c,t.useCueTags_&&(t.cueTagsTrack_=t.tech_.addTextTrack("metadata","ad-cues"),t.cueTagsTrack_.inBandMetadataTrackDispatchType=""),t.requestOptions_={withCredentials:r,handleManifestRedirects:n,maxPlaylistRetries:y,timeout:null},t.on("error",t.pauseLoading),t.mediaTypes_=(g={},["AUDIO","SUBTITLES","CLOSED-CAPTIONS"].forEach(function(e){g[e]={groups:{},tracks:{},activePlaylistLoader:null,activeGroup:Ol,activeTrack:Ol,getActiveGroup:Ol,onGroupChanged:Ol,onTrackChanged:Ol,lastTrack_:null,logger_:Oo("MediaGroups["+e+"]")}}),g),t.mediaSource=new window.MediaSource,t.handleDurationChange_=t.handleDurationChange_.bind(ft(t)),t.handleSourceOpen_=t.handleSourceOpen_.bind(ft(t)),t.handleSourceEnded_=t.handleSourceEnded_.bind(ft(t)),t.mediaSource.addEventListener("durationchange",t.handleDurationChange_),t.mediaSource.addEventListener("sourceopen",t.handleSourceOpen_),t.mediaSource.addEventListener("sourceended",t.handleSourceEnded_),t.seekable_=tr.createTimeRanges(),t.hasPlayed_=!1,t.syncController_=new cc(e),t.segmentMetadataTrack_=a.addRemoteTextTrack({kind:"metadata",label:"segment-metadata"},!1).track,t.decrypter_=new hc,t.sourceUpdater_=new sc(t.mediaSource),t.inbandTextTracks_={},t.timelineChangeController_=new dc;h={vhs:t.vhs_,parse708captions:e.parse708captions,captionServices:m,mediaSource:t.mediaSource,currentTime:t.tech_.currentTime.bind(t.tech_),seekable:function(){return t.seekable()},seeking:function(){return t.tech_.seeking()},duration:function(){return t.duration()},hasPlayed:function(){return t.hasPlayed_},goalBufferLength:function(){return t.goalBufferLength()},bandwidth:s,syncController:t.syncController_,decrypter:t.decrypter_,sourceType:t.sourceType_,inbandTextTracks:t.inbandTextTracks_,cacheEncryptionKeys:h,sourceUpdater:t.sourceUpdater_,timelineChangeController:t.timelineChangeController_,experimentalExactManifestTimings:e.experimentalExactManifestTimings};t.masterPlaylistLoader_=new("dash"===t.sourceType_?Tl:yl)(i,t.vhs_,t.requestOptions_),t.setupMasterPlaylistLoaderListeners_(),t.mainSegmentLoader_=new Dl(tr.mergeOptions(h,{segmentMetadataTrack:t.segmentMetadataTrack_,loaderType:"main"}),e),t.audioSegmentLoader_=new Dl(tr.mergeOptions(h,{loaderType:"audio"}),e),t.subtitleSegmentLoader_=new uc(tr.mergeOptions(h,{loaderType:"vtt",featuresNativeTextTracks:t.tech_.featuresNativeTextTracks}),e),t.setupSegmentLoaderListeners_(),t.experimentalBufferBasedABR&&(t.masterPlaylistLoader_.one("loadedplaylist",function(){return t.startABRTimer_()}),t.tech_.on("pause",function(){return t.stopABRTimer_()}),t.tech_.on("play",function(){return t.startABRTimer_()})),yc.forEach(function(e){t[e+"_"]=function(e){return this.audioSegmentLoader_[e]+this.mainSegmentLoader_[e]}.bind(ft(t),e)}),t.logger_=Oo("MPC"),t.triggeredFmp4Usage=!1,"none"===t.tech_.preload()?(t.loadOnPlay_=function(){t.loadOnPlay_=null,t.masterPlaylistLoader_.load()},t.tech_.one("play",t.loadOnPlay_)):t.masterPlaylistLoader_.load(),t.timeToLoadedData__=-1,t.mainAppendsToLoadedData__=-1,t.audioAppendsToLoadedData__=-1;e="none"===t.tech_.preload()?"play":"loadstart";return t.tech_.one(e,function(){var e=Date.now();t.tech_.one("loadeddata",function(){t.timeToLoadedData__=Date.now()-e,t.mainAppendsToLoadedData__=t.mainSegmentLoader_.mediaAppends,t.audioAppendsToLoadedData__=t.audioSegmentLoader_.mediaAppends})}),t}mt(e,v);var t=e.prototype;return t.mainAppendsToLoadedData_=function(){return this.mainAppendsToLoadedData__},t.audioAppendsToLoadedData_=function(){return this.audioAppendsToLoadedData__},t.appendsToLoadedData_=function(){var e=this.mainAppendsToLoadedData_(),t=this.audioAppendsToLoadedData_();return-1===e||-1===t?-1:e+t},t.timeToLoadedData_=function(){return this.timeToLoadedData__},t.checkABR_=function(){var e=this.selectPlaylist();e&&this.shouldSwitchToMedia_(e)&&this.switchMedia_(e,"abr")},t.switchMedia_=function(e,t,i){var n=this.media(),r=n&&(n.id||n.uri),n=e.id||e.uri;r&&r!==n&&(this.logger_("switch media "+r+" -> "+n+" from "+t),this.tech_.trigger({type:"usage",name:"vhs-rendition-change-"+t})),this.masterPlaylistLoader_.media(e,i)},t.startABRTimer_=function(){var e=this;this.stopABRTimer_(),this.abrTimer_=window.setInterval(function(){return e.checkABR_()},250)},t.stopABRTimer_=function(){this.tech_.scrubbing&&this.tech_.scrubbing()||(window.clearInterval(this.abrTimer_),this.abrTimer_=null)},t.getAudioTrackPlaylists_=function(){var e=this.master(),t=e&&e.playlists||[];if(!e||!e.mediaGroups||!e.mediaGroups.AUDIO)return t;var i,n=e.mediaGroups.AUDIO,r=Object.keys(n);if(Object.keys(this.mediaTypes_.AUDIO.groups).length)i=this.mediaTypes_.AUDIO.activeTrack();else{var a,s=n.main||r.length&&n[r[0]];for(a in s)if(s[a].default){i={label:a};break}}if(!i)return t;var o,u=[];for(o in n)if(n[o][i.label]){var l=n[o][i.label];if(l.playlists&&l.playlists.length)u.push.apply(u,l.playlists);else if(l.uri)u.push(l);else if(e.playlists.length)for(var c=0;c<e.playlists.length;c++){var d=e.playlists[c];d.attributes&&d.attributes.AUDIO&&d.attributes.AUDIO===o&&u.push(d)}}return u.length?u:t},t.setupMasterPlaylistLoaderListeners_=function(){var i=this;this.masterPlaylistLoader_.on("loadedmetadata",function(){var e=i.masterPlaylistLoader_.media(),t=1.5*e.targetDuration*1e3;eu(i.masterPlaylistLoader_.master,i.masterPlaylistLoader_.media())?i.requestOptions_.timeout=0:i.requestOptions_.timeout=t,e.endList&&"none"!==i.tech_.preload()&&(i.mainSegmentLoader_.playlist(e,i.requestOptions_),i.mainSegmentLoader_.load()),Gl({sourceType:i.sourceType_,segmentLoaders:{AUDIO:i.audioSegmentLoader_,SUBTITLES:i.subtitleSegmentLoader_,main:i.mainSegmentLoader_},tech:i.tech_,requestOptions:i.requestOptions_,masterPlaylistLoader:i.masterPlaylistLoader_,vhs:i.vhs_,master:i.master(),mediaTypes:i.mediaTypes_,blacklistCurrentPlaylist:i.blacklistCurrentPlaylist.bind(i)}),i.triggerPresenceUsage_(i.master(),e),i.setupFirstPlay(),!i.mediaTypes_.AUDIO.activePlaylistLoader||i.mediaTypes_.AUDIO.activePlaylistLoader.media()?i.trigger("selectedinitialmedia"):i.mediaTypes_.AUDIO.activePlaylistLoader.one("loadedmetadata",function(){i.trigger("selectedinitialmedia")})}),this.masterPlaylistLoader_.on("loadedplaylist",function(){i.loadOnPlay_&&i.tech_.off("play",i.loadOnPlay_);var e,t=i.masterPlaylistLoader_.media();if(!t){if(i.excludeUnsupportedVariants_(),!(e=(e=i.enableLowInitialPlaylist?i.selectInitialPlaylist():e)||i.selectPlaylist())||!i.shouldSwitchToMedia_(e))return;if(i.initialMedia_=e,i.switchMedia_(i.initialMedia_,"initial"),!("vhs-json"===i.sourceType_&&i.initialMedia_.segments))return;t=i.initialMedia_}i.handleUpdatedMediaPlaylist(t)}),this.masterPlaylistLoader_.on("error",function(){i.blacklistCurrentPlaylist(i.masterPlaylistLoader_.error)}),this.masterPlaylistLoader_.on("mediachanging",function(){i.mainSegmentLoader_.abort(),i.mainSegmentLoader_.pause()}),this.masterPlaylistLoader_.on("mediachange",function(){var e=i.masterPlaylistLoader_.media(),t=1.5*e.targetDuration*1e3;eu(i.masterPlaylistLoader_.master,i.masterPlaylistLoader_.media())?i.requestOptions_.timeout=0:i.requestOptions_.timeout=t,i.mainSegmentLoader_.playlist(e,i.requestOptions_),i.mainSegmentLoader_.load(),i.tech_.trigger({type:"mediachange",bubbles:!0})}),this.masterPlaylistLoader_.on("playlistunchanged",function(){var e=i.masterPlaylistLoader_.media();"playlist-unchanged"!==e.lastExcludeReason_&&i.stuckAtPlaylistEnd_(e)&&(i.blacklistCurrentPlaylist({message:"Playlist no longer updating.",reason:"playlist-unchanged"}),i.tech_.trigger("playliststuck"))}),this.masterPlaylistLoader_.on("renditiondisabled",function(){i.tech_.trigger({type:"usage",name:"vhs-rendition-disabled"}),i.tech_.trigger({type:"usage",name:"hls-rendition-disabled"})}),this.masterPlaylistLoader_.on("renditionenabled",function(){i.tech_.trigger({type:"usage",name:"vhs-rendition-enabled"}),i.tech_.trigger({type:"usage",name:"hls-rendition-enabled"})})},t.handleUpdatedMediaPlaylist=function(e){this.useCueTags_&&this.updateAdCues_(e),this.mainSegmentLoader_.playlist(e,this.requestOptions_),this.updateDuration(!e.endList),this.tech_.paused()||(this.mainSegmentLoader_.load(),this.audioSegmentLoader_&&this.audioSegmentLoader_.load())},t.triggerPresenceUsage_=function(e,t){var i,n=e.mediaGroups||{},r=!0,e=Object.keys(n.AUDIO);for(i in n.AUDIO)for(var a in n.AUDIO[i])n.AUDIO[i][a].uri||(r=!1);r&&(this.tech_.trigger({type:"usage",name:"vhs-demuxed"}),this.tech_.trigger({type:"usage",name:"hls-demuxed"})),Object.keys(n.SUBTITLES).length&&(this.tech_.trigger({type:"usage",name:"vhs-webvtt"}),this.tech_.trigger({type:"usage",name:"hls-webvtt"})),Kl.Playlist.isAes(t)&&(this.tech_.trigger({type:"usage",name:"vhs-aes"}),this.tech_.trigger({type:"usage",name:"hls-aes"})),e.length&&1<Object.keys(n.AUDIO[e[0]]).length&&(this.tech_.trigger({type:"usage",name:"vhs-alternate-audio"}),this.tech_.trigger({type:"usage",name:"hls-alternate-audio"})),this.useCueTags_&&(this.tech_.trigger({type:"usage",name:"vhs-playlist-cue-tags"}),this.tech_.trigger({type:"usage",name:"hls-playlist-cue-tags"}))},t.shouldSwitchToMedia_=function(e){var t=this.masterPlaylistLoader_.media()||this.masterPlaylistLoader_.pendingMedia_,i=this.tech_.currentTime(),n=this.bufferLowWaterLine(),r=this.bufferHighWaterLine();return function(e){var t=e.currentPlaylist,i=e.buffered,n=e.currentTime,r=e.nextPlaylist,a=e.bufferLowWaterLine,s=e.bufferHighWaterLine,o=e.duration,u=e.experimentalBufferBasedABR,l=e.log;if(!r)return tr.log.warn("We received no playlist to switch to. Please check your stream."),!1;var c="allowing switch "+(t&&t.id||"null")+" -> "+r.id;if(!t)return l(c+" as current playlist is not set"),!0;if(r.id===t.id)return!1;e=Boolean(Mo(i,n).length);if(!t.endList)return e||"number"!=typeof t.partTargetDuration?(l(c+" as current playlist is live"),!0):(l("not "+c+" as current playlist is live llhls, but currentTime isn't in buffered."),!1);i=jo(i,n),n=u?Sl.EXPERIMENTAL_MAX_BUFFER_LOW_WATER_LINE:Sl.MAX_BUFFER_LOW_WATER_LINE;if(o<n)return l(c+" as duration < max low water line ("+o+" < "+n+")"),!0;n=r.attributes.BANDWIDTH,r=t.attributes.BANDWIDTH;if(n<r&&(!u||i<s)){t=c+" as next bandwidth < current bandwidth ("+n+" < "+r+")";return u&&(t+=" and forwardBuffer < bufferHighWaterLine ("+i+" < "+s+")"),l(t),!0}if((!u||r<n)&&a<=i){a=c+" as forwardBuffer >= bufferLowWaterLine ("+i+" >= "+a+")";return u&&(a+=" and next bandwidth > current bandwidth ("+n+" > "+r+")"),l(a),!0}return l("not "+c+" as no switching criteria met"),!1}({buffered:this.tech_.buffered(),currentTime:i,currentPlaylist:t,nextPlaylist:e,bufferLowWaterLine:n,bufferHighWaterLine:r,duration:this.duration(),experimentalBufferBasedABR:this.experimentalBufferBasedABR,log:this.logger_})},t.setupSegmentLoaderListeners_=function(){var t=this;this.experimentalBufferBasedABR||(this.mainSegmentLoader_.on("bandwidthupdate",function(){var e=t.selectPlaylist();t.shouldSwitchToMedia_(e)&&t.switchMedia_(e,"bandwidthupdate"),t.tech_.trigger("bandwidthupdate")}),this.mainSegmentLoader_.on("progress",function(){t.trigger("progress")})),this.mainSegmentLoader_.on("error",function(){t.blacklistCurrentPlaylist(t.mainSegmentLoader_.error())}),this.mainSegmentLoader_.on("appenderror",function(){t.error=t.mainSegmentLoader_.error_,t.trigger("error")}),this.mainSegmentLoader_.on("syncinfoupdate",function(){t.onSyncInfoUpdate_()}),this.mainSegmentLoader_.on("timestampoffset",function(){t.tech_.trigger({type:"usage",name:"vhs-timestamp-offset"}),t.tech_.trigger({type:"usage",name:"hls-timestamp-offset"})}),this.audioSegmentLoader_.on("syncinfoupdate",function(){t.onSyncInfoUpdate_()}),this.audioSegmentLoader_.on("appenderror",function(){t.error=t.audioSegmentLoader_.error_,t.trigger("error")}),this.mainSegmentLoader_.on("ended",function(){t.logger_("main segment loader ended"),t.onEndOfStream()}),this.mainSegmentLoader_.on("earlyabort",function(e){t.experimentalBufferBasedABR||(t.delegateLoaders_("all",["abort"]),t.blacklistCurrentPlaylist({message:"Aborted early because there isn't enough bandwidth to complete the request without rebuffering."},120))});function e(){if(!t.sourceUpdater_.hasCreatedSourceBuffers())return t.tryToCreateSourceBuffers_();var e=t.getCodecsOrExclude_();e&&t.sourceUpdater_.addOrChangeSourceBuffers(e)}this.mainSegmentLoader_.on("trackinfo",e),this.audioSegmentLoader_.on("trackinfo",e),this.mainSegmentLoader_.on("fmp4",function(){t.triggeredFmp4Usage||(t.tech_.trigger({type:"usage",name:"vhs-fmp4"}),t.tech_.trigger({type:"usage",name:"hls-fmp4"}),t.triggeredFmp4Usage=!0)}),this.audioSegmentLoader_.on("fmp4",function(){t.triggeredFmp4Usage||(t.tech_.trigger({type:"usage",name:"vhs-fmp4"}),t.tech_.trigger({type:"usage",name:"hls-fmp4"}),t.triggeredFmp4Usage=!0)}),this.audioSegmentLoader_.on("ended",function(){t.logger_("audioSegmentLoader ended"),t.onEndOfStream()})},t.mediaSecondsLoaded_=function(){return Math.max(this.audioSegmentLoader_.mediaSecondsLoaded+this.mainSegmentLoader_.mediaSecondsLoaded)},t.load=function(){this.mainSegmentLoader_.load(),this.mediaTypes_.AUDIO.activePlaylistLoader&&this.audioSegmentLoader_.load(),this.mediaTypes_.SUBTITLES.activePlaylistLoader&&this.subtitleSegmentLoader_.load()},t.smoothQualityChange_=function(e){void 0===e&&(e=this.selectPlaylist()),this.fastQualityChange_(e)},t.fastQualityChange_=function(e){var t=this;(e=void 0===e?this.selectPlaylist():e)!==this.masterPlaylistLoader_.media()?(this.switchMedia_(e,"fast-quality"),this.mainSegmentLoader_.resetEverything(function(){tr.browser.IE_VERSION||tr.browser.IS_EDGE?t.tech_.setCurrentTime(t.tech_.currentTime()+.04):t.tech_.setCurrentTime(t.tech_.currentTime())})):this.logger_("skipping fastQualityChange because new media is same as old")},t.play=function(){if(!this.setupFirstPlay()){this.tech_.ended()&&this.tech_.setCurrentTime(0),this.hasPlayed_&&this.load();var e=this.tech_.seekable();return this.tech_.duration()===1/0&&this.tech_.currentTime()<e.start(0)?this.tech_.setCurrentTime(e.end(e.length-1)):void 0}},t.setupFirstPlay=function(){var e=this,t=this.masterPlaylistLoader_.media();if(!t||this.tech_.paused()||this.hasPlayed_)return!1;if(!t.endList){var i=this.seekable();if(!i.length)return!1;if(tr.browser.IE_VERSION&&0===this.tech_.readyState())return this.tech_.one("loadedmetadata",function(){e.trigger("firstplay"),e.tech_.setCurrentTime(i.end(0)),e.hasPlayed_=!0}),!1;this.trigger("firstplay"),this.tech_.setCurrentTime(i.end(0))}return this.hasPlayed_=!0,this.load(),!0},t.handleSourceOpen_=function(){var e;this.tryToCreateSourceBuffers_(),!this.tech_.autoplay()||"undefined"!=typeof(e=this.tech_.play())&&"function"==typeof e.then&&e.then(null,function(e){}),this.trigger("sourceopen")},t.handleSourceEnded_=function(){var e,t;!this.inbandTextTracks_.metadataTrack_||(e=this.inbandTextTracks_.metadataTrack_.cues)&&e.length&&(t=this.duration(),e[e.length-1].endTime=isNaN(t)||Math.abs(t)===1/0?Number.MAX_VALUE:t)},t.handleDurationChange_=function(){this.tech_.trigger("durationchange")},t.onEndOfStream=function(){var e,t=this.mainSegmentLoader_.ended_;(t=this.mediaTypes_.AUDIO.activePlaylistLoader?((e=this.mainSegmentLoader_.getCurrentMediaInfo_())&&!e.hasVideo||t)&&this.audioSegmentLoader_.ended_:t)&&(this.stopABRTimer_(),this.sourceUpdater_.endOfStream())},t.stuckAtPlaylistEnd_=function(e){if(!this.seekable().length)return!1;var t=this.syncController_.getExpiredTime(e,this.duration());if(null===t)return!1;var i=Kl.Playlist.playlistEnd(e,t),e=this.tech_.currentTime(),t=this.tech_.buffered();if(!t.length)return i-e<=.1;t=t.end(t.length-1);return t-e<=.1&&i-t<=.1},t.blacklistCurrentPlaylist=function(e,t){var i=(e=void 0===e?{}:e).playlist||this.masterPlaylistLoader_.media();if(t=t||e.blacklistDuration||this.blacklistDuration,!i)return this.error=e,void("open"!==this.mediaSource.readyState?this.trigger("error"):this.sourceUpdater_.endOfStream("network"));i.playlistErrors_++;var n,r=this.masterPlaylistLoader_.master.playlists,a=r.filter(Jo),s=1===a.length&&a[0]===i;if(1===r.length&&t!==1/0)return tr.log.warn("Problem encountered with playlist "+i.id+". Trying again since it is the only playlist."),this.tech_.trigger("retryplaylist"),this.masterPlaylistLoader_.load(s);s&&(n=!1,r.forEach(function(e){var t;e===i||"undefined"!=typeof(t=e.excludeUntil)&&t!==1/0&&(n=!0,delete e.excludeUntil)}),n&&(tr.log.warn("Removing other playlists from the exclusion list because the last rendition is about to be excluded."),this.tech_.trigger("retryplaylist"))),a=i.playlistErrors_>this.maxPlaylistRetries?1/0:Date.now()+1e3*t,i.excludeUntil=a,e.reason&&(i.lastExcludeReason_=e.reason),this.tech_.trigger("blacklistplaylist"),this.tech_.trigger({type:"usage",name:"vhs-rendition-blacklisted"}),this.tech_.trigger({type:"usage",name:"hls-rendition-blacklisted"});r=this.selectPlaylist();if(!r)return this.error="Playback cannot continue. No available working or supported playlists.",void this.trigger("error");t=e.internal?this.logger_:tr.log.warn,a=e.message?" "+e.message:"";t((e.internal?"Internal problem":"Problem")+" encountered with playlist "+i.id+"."+a+" Switching to playlist "+r.id+"."),r.attributes.AUDIO!==i.attributes.AUDIO&&this.delegateLoaders_("audio",["abort","pause"]),r.attributes.SUBTITLES!==i.attributes.SUBTITLES&&this.delegateLoaders_("subtitle",["abort","pause"]),this.delegateLoaders_("main",["abort","pause"]);a=r.targetDuration/2*1e3||5e3,a="number"==typeof r.lastRequest&&Date.now()-r.lastRequest<=a;return this.switchMedia_(r,"exclude",s||a)},t.pauseLoading=function(){this.delegateLoaders_("all",["abort","pause"]),this.stopABRTimer_()},t.delegateLoaders_=function(i,e){var n=this,r=[],t="all"===i;!t&&"main"!==i||r.push(this.masterPlaylistLoader_);var a=[];!t&&"audio"!==i||a.push("AUDIO"),!t&&"subtitle"!==i||(a.push("CLOSED-CAPTIONS"),a.push("SUBTITLES")),a.forEach(function(e){e=n.mediaTypes_[e]&&n.mediaTypes_[e].activePlaylistLoader;e&&r.push(e)}),["main","audio","subtitle"].forEach(function(e){var t=n[e+"SegmentLoader_"];!t||i!==e&&"all"!==i||r.push(t)}),r.forEach(function(t){return e.forEach(function(e){"function"==typeof t[e]&&t[e]()})})},t.setCurrentTime=function(e){var t=Mo(this.tech_.buffered(),e);return this.masterPlaylistLoader_&&this.masterPlaylistLoader_.media()&&this.masterPlaylistLoader_.media().segments?t&&t.length?e:(this.mainSegmentLoader_.resetEverything(),this.mainSegmentLoader_.abort(),this.mediaTypes_.AUDIO.activePlaylistLoader&&(this.audioSegmentLoader_.resetEverything(),this.audioSegmentLoader_.abort()),this.mediaTypes_.SUBTITLES.activePlaylistLoader&&(this.subtitleSegmentLoader_.resetEverything(),this.subtitleSegmentLoader_.abort()),void this.load()):0},t.duration=function(){if(!this.masterPlaylistLoader_)return 0;var e=this.masterPlaylistLoader_.media();return e?e.endList?this.mediaSource?this.mediaSource.duration:Kl.Playlist.duration(e):1/0:0},t.seekable=function(){return this.seekable_},t.onSyncInfoUpdate_=function(){var e;if(this.masterPlaylistLoader_){var t=this.masterPlaylistLoader_.media();if(t){var i=this.syncController_.getExpiredTime(t,this.duration());if(null!==i){var n,r,a=this.masterPlaylistLoader_.master,s=Kl.Playlist.seekable(t,i,Kl.Playlist.liveEdgeDelay(a,t));if(0!==s.length){if(this.mediaTypes_.AUDIO.activePlaylistLoader){if(t=this.mediaTypes_.AUDIO.activePlaylistLoader.media(),null===(i=this.syncController_.getExpiredTime(t,this.duration())))return;if(0===(e=Kl.Playlist.seekable(t,i,Kl.Playlist.liveEdgeDelay(a,t))).length)return}this.seekable_&&this.seekable_.length&&(n=this.seekable_.end(0),r=this.seekable_.start(0)),!e||e.start(0)>s.end(0)||s.start(0)>e.end(0)?this.seekable_=s:this.seekable_=tr.createTimeRanges([[(e.start(0)>s.start(0)?e:s).start(0),(e.end(0)<s.end(0)?e:s).end(0)]]),this.seekable_&&this.seekable_.length&&this.seekable_.end(0)===n&&this.seekable_.start(0)===r||(this.logger_("seekable updated ["+Uo(this.seekable_)+"]"),this.tech_.trigger("seekablechanged"))}}}}},t.updateDuration=function(e){if(this.updateDuration_&&(this.mediaSource.removeEventListener("sourceopen",this.updateDuration_),this.updateDuration_=null),"open"!==this.mediaSource.readyState)return this.updateDuration_=this.updateDuration.bind(this,e),void this.mediaSource.addEventListener("sourceopen",this.updateDuration_);if(e){var t=this.seekable();return t.length?void((isNaN(this.mediaSource.duration)||this.mediaSource.duration<t.end(t.length-1))&&this.sourceUpdater_.setDuration(t.end(t.length-1))):void 0}e=this.tech_.buffered(),t=Kl.Playlist.duration(this.masterPlaylistLoader_.media());0<e.length&&(t=Math.max(t,e.end(e.length-1))),this.mediaSource.duration!==t&&this.sourceUpdater_.setDuration(t)},t.dispose=function(){var n=this;this.trigger("dispose"),this.decrypter_.terminate(),this.masterPlaylistLoader_.dispose(),this.mainSegmentLoader_.dispose(),this.loadOnPlay_&&this.tech_.off("play",this.loadOnPlay_),["AUDIO","SUBTITLES"].forEach(function(e){var t,i=n.mediaTypes_[e].groups;for(t in i)i[t].forEach(function(e){e.playlistLoader&&e.playlistLoader.dispose()})}),this.audioSegmentLoader_.dispose(),this.subtitleSegmentLoader_.dispose(),this.sourceUpdater_.dispose(),this.timelineChangeController_.dispose(),this.stopABRTimer_(),this.updateDuration_&&this.mediaSource.removeEventListener("sourceopen",this.updateDuration_),this.mediaSource.removeEventListener("durationchange",this.handleDurationChange_),this.mediaSource.removeEventListener("sourceopen",this.handleSourceOpen_),this.mediaSource.removeEventListener("sourceended",this.handleSourceEnded_),this.off()},t.master=function(){return this.masterPlaylistLoader_.master},t.media=function(){return this.masterPlaylistLoader_.media()||this.initialMedia_},t.areMediaTypesKnown_=function(){var e=!!this.mediaTypes_.AUDIO.activePlaylistLoader,t=!!this.mainSegmentLoader_.getCurrentMediaInfo_(),e=!e||!!this.audioSegmentLoader_.getCurrentMediaInfo_();return t&&e},t.getCodecsOrExclude_=function(){var n=this,r={main:this.mainSegmentLoader_.getCurrentMediaInfo_()||{},audio:this.audioSegmentLoader_.getCurrentMediaInfo_()||{}};r.video=r.main;var e=Yu(this.master(),this.media()),a={},t=!!this.mediaTypes_.AUDIO.activePlaylistLoader;if(r.main.hasVideo&&(a.video=e.video||r.main.videoCodec||"avc1.4d400d"),r.main.isMuxed&&(a.video+=","+(e.audio||r.main.audioCodec||Cr)),(r.main.hasAudio&&!r.main.isMuxed||r.audio.hasAudio||t)&&(a.audio=e.audio||r.main.audioCodec||r.audio.audioCodec||Cr,r.audio.isFmp4=(r.main.hasAudio&&!r.main.isMuxed?r.main:r.audio).isFmp4),a.audio||a.video){var s,i,o={};if(["video","audio"].forEach(function(e){var t,i;a.hasOwnProperty(e)&&(t=r[e].isFmp4,i=a[e],!(t?gr:yr)(i))&&(i=r[e].isFmp4?"browser":"muxer",o[i]=o[i]||[],o[i].push(a[e]),"audio"===e&&(s=i))}),t&&s&&this.media().attributes.AUDIO&&(i=this.media().attributes.AUDIO,this.master().playlists.forEach(function(e){(e.attributes&&e.attributes.AUDIO)===i&&e!==n.media()&&(e.excludeUntil=1/0)}),this.logger_("excluding audio group "+i+" as "+s+' does not support codec(s): "'+a.audio+'"')),!Object.keys(o).length){if(this.sourceUpdater_.hasCreatedSourceBuffers()&&!this.sourceUpdater_.canChangeType()){var u=[];if(["video","audio"].forEach(function(e){var t=(pr(n.sourceUpdater_.codecs[e]||"")[0]||{}).type,i=(pr(a[e]||"")[0]||{}).type;t&&i&&t.toLowerCase()!==i.toLowerCase()&&u.push('"'+n.sourceUpdater_.codecs[e]+'" -> "'+a[e]+'"')}),u.length)return void this.blacklistCurrentPlaylist({playlist:this.media(),message:"Codec switching not supported: "+u.join(", ")+".",blacklistDuration:1/0,internal:!0})}return a}t=Object.keys(o).reduce(function(e,t){return e&&(e+=", "),e+=t+' does not support codec(s): "'+o[t].join(",")+'"'},"")+".";this.blacklistCurrentPlaylist({playlist:this.media(),internal:!0,message:t,blacklistDuration:1/0})}else this.blacklistCurrentPlaylist({playlist:this.media(),message:"Could not determine codecs for playlist.",blacklistDuration:1/0})},t.tryToCreateSourceBuffers_=function(){var e;"open"!==this.mediaSource.readyState||this.sourceUpdater_.hasCreatedSourceBuffers()||!this.areMediaTypesKnown_()||(e=this.getCodecsOrExclude_())&&(this.sourceUpdater_.createSourceBuffers(e),e=[e.video,e.audio].filter(Boolean).join(","),this.excludeIncompatibleVariants_(e))},t.excludeUnsupportedVariants_=function(){var n=this,r=this.master().playlists,a=[];Object.keys(r).forEach(function(e){var t,i=r[e];-1===a.indexOf(i.id)&&(a.push(i.id),t=[],!(e=Yu(n.master,i)).audio||yr(e.audio)||gr(e.audio)||t.push("audio codec "+e.audio),!e.video||yr(e.video)||gr(e.video)||t.push("video codec "+e.video),e.text&&"stpp.ttml.im1t"===e.text&&t.push("text codec "+e.text),t.length&&(i.excludeUntil=1/0,n.logger_("excluding "+i.id+" for unsupported: "+t.join(", "))))})},t.excludeIncompatibleVariants_=function(e){var r=this,a=[],s=this.master().playlists,e=Xu(pr(e)),o=Ku(e),u=e.video&&pr(e.video)[0]||null,l=e.audio&&pr(e.audio)[0]||null;Object.keys(s).forEach(function(e){var t,i,n=s[e];-1===a.indexOf(n.id)&&n.excludeUntil!==1/0&&(a.push(n.id),t=[],i=Yu(r.masterPlaylistLoader_.master,n),e=Ku(i),(i.audio||i.video)&&(e!==o&&t.push('codec count "'+e+'" !== "'+o+'"'),r.sourceUpdater_.canChangeType()||(e=i.video&&pr(i.video)[0]||null,i=i.audio&&pr(i.audio)[0]||null,e&&u&&e.type.toLowerCase()!==u.type.toLowerCase()&&t.push('video codec "'+e.type+'" !== "'+u.type+'"'),i&&l&&i.type.toLowerCase()!==l.type.toLowerCase()&&t.push('audio codec "'+i.type+'" !== "'+l.type+'"')),t.length&&(n.excludeUntil=1/0,r.logger_("blacklisting "+n.id+": "+t.join(" && ")))))})},t.updateAdCues_=function(e){var t=0,i=this.seekable();i.length&&(t=i.start(0)),function(e,t,i){if(void 0===i&&(i=0),e.segments)for(var n=i,r=0;r<e.segments.length;r++){var a,s,o,u=e.segments[r];if(o=o||function(e,t){for(var i=e.cues,n=0;n<i.length;n++){var r=i[n];if(t>=r.adStartTime&&t<=r.adEndTime)return r}return null}(t,n+u.duration/2)){if("cueIn"in u){o.endTime=n,o.adEndTime=n,n+=u.duration,o=null;continue}if(n<o.endTime){n+=u.duration;continue}o.endTime+=u.duration}else"cueOut"in u&&((o=new window.VTTCue(n,n+u.duration,u.cueOut)).adStartTime=n,o.adEndTime=n+parseFloat(u.cueOut),t.addCue(o)),"cueOutCont"in u&&(a=(s=u.cueOutCont.split("/").map(parseFloat))[0],s=s[1],(o=new window.VTTCue(n,n+u.duration,"")).adStartTime=n-a,o.adEndTime=o.adStartTime+s,t.addCue(o));n+=u.duration}}(e,this.cueTagsTrack_,t)},t.goalBufferLength=function(){var e=this.tech_.currentTime(),t=Sl.GOAL_BUFFER_LENGTH,i=Sl.GOAL_BUFFER_LENGTH_RATE,n=Math.max(t,Sl.MAX_GOAL_BUFFER_LENGTH);return Math.min(t+e*i,n)},t.bufferLowWaterLine=function(){var e=this.tech_.currentTime(),t=Sl.BUFFER_LOW_WATER_LINE,i=Sl.BUFFER_LOW_WATER_LINE_RATE,n=Math.max(t,Sl.MAX_BUFFER_LOW_WATER_LINE),r=Math.max(t,Sl.EXPERIMENTAL_MAX_BUFFER_LOW_WATER_LINE);return Math.min(t+e*i,this.experimentalBufferBasedABR?r:n)},t.bufferHighWaterLine=function(){return Sl.BUFFER_HIGH_WATER_LINE},e}(tr.EventTarget),_c=["seeking","seeked","pause","playing","error"],bc=function(){function e(e){var i=this;this.masterPlaylistController_=e.masterPlaylistController,this.tech_=e.tech,this.seekable=e.seekable,this.allowSeeksWithinUnsafeLiveWindow=e.allowSeeksWithinUnsafeLiveWindow,this.liveRangeSafeTimeDelta=e.liveRangeSafeTimeDelta,this.media=e.media,this.consecutiveUpdates=0,this.lastRecordedTime=null,this.timer_=null,this.checkCurrentTimeTimeout_=null,this.logger_=Oo("PlaybackWatcher"),this.logger_("initialize");function t(){return i.monitorCurrentTime_()}function n(){return i.monitorCurrentTime_()}function r(){return i.techWaiting_()}function a(){return i.cancelTimer_()}var s=this.masterPlaylistController_,o=["main","subtitle","audio"],u={};o.forEach(function(e){u[e]={reset:function(){return i.resetSegmentDownloads_(e)},updateend:function(){return i.checkSegmentDownloads_(e)}},s[e+"SegmentLoader_"].on("appendsdone",u[e].updateend),s[e+"SegmentLoader_"].on("playlistupdate",u[e].reset),i.tech_.on(["seeked","seeking"],u[e].reset)});function l(t){["main","audio"].forEach(function(e){s[e+"SegmentLoader_"][t]("appended",i.seekingAppendCheck_)})}this.seekingAppendCheck_=function(){i.fixesBadSeeks_()&&(i.consecutiveUpdates=0,i.lastRecordedTime=i.tech_.currentTime(),l("off"))},this.clearSeekingAppendCheck_=function(){return l("off")},this.watchForBadSeeking_=function(){i.clearSeekingAppendCheck_(),l("on")},this.tech_.on("seeked",this.clearSeekingAppendCheck_),this.tech_.on("seeking",this.watchForBadSeeking_),this.tech_.on("waiting",r),this.tech_.on(_c,a),this.tech_.on("canplay",n),this.tech_.one("play",t),this.dispose=function(){i.clearSeekingAppendCheck_(),i.logger_("dispose"),i.tech_.off("waiting",r),i.tech_.off(_c,a),i.tech_.off("canplay",n),i.tech_.off("play",t),i.tech_.off("seeking",i.watchForBadSeeking_),i.tech_.off("seeked",i.clearSeekingAppendCheck_),o.forEach(function(e){s[e+"SegmentLoader_"].off("appendsdone",u[e].updateend),s[e+"SegmentLoader_"].off("playlistupdate",u[e].reset),i.tech_.off(["seeked","seeking"],u[e].reset)}),i.checkCurrentTimeTimeout_&&window.clearTimeout(i.checkCurrentTimeTimeout_),i.cancelTimer_()}}var t=e.prototype;return t.monitorCurrentTime_=function(){this.checkCurrentTime_(),this.checkCurrentTimeTimeout_&&window.clearTimeout(this.checkCurrentTimeTimeout_),this.checkCurrentTimeTimeout_=window.setTimeout(this.monitorCurrentTime_.bind(this),250)},t.resetSegmentDownloads_=function(e){var t=this.masterPlaylistController_[e+"SegmentLoader_"];0<this[e+"StalledDownloads_"]&&this.logger_("resetting possible stalled download count for "+e+" loader"),this[e+"StalledDownloads_"]=0,this[e+"Buffered_"]=t.buffered_()},t.checkSegmentDownloads_=function(e){var t=this.masterPlaylistController_,i=t[e+"SegmentLoader_"],n=i.buffered_(),r=function(e,t){if(e===t)return!1;if(!e&&t||!t&&e)return!0;if(e.length!==t.length)return!0;for(var i=0;i<e.length;i++)if(e.start(i)!==t.start(i)||e.end(i)!==t.end(i))return!0;return!1}(this[e+"Buffered_"],n);this[e+"Buffered_"]=n,r?this.resetSegmentDownloads_(e):(this[e+"StalledDownloads_"]++,this.logger_("found #"+this[e+"StalledDownloads_"]+" "+e+" appends that did not increase buffer (possible stalled download)",{playlistId:i.playlist_&&i.playlist_.id,buffered:Bo(n)}),this[e+"StalledDownloads_"]<10||(this.logger_(e+" loader stalled download exclusion"),this.resetSegmentDownloads_(e),this.tech_.trigger({type:"usage",name:"vhs-"+e+"-download-exclusion"}),"subtitle"!==e&&t.blacklistCurrentPlaylist({message:"Excessive "+e+" segment downloading detected."},1/0)))},t.checkCurrentTime_=function(){if(!this.tech_.paused()&&!this.tech_.seeking()){var e=this.tech_.currentTime(),t=this.tech_.buffered();if(this.lastRecordedTime===e&&(!t.length||e+.1>=t.end(t.length-1)))return this.techWaiting_();5<=this.consecutiveUpdates&&e===this.lastRecordedTime?(this.consecutiveUpdates++,this.waiting_()):e===this.lastRecordedTime?this.consecutiveUpdates++:(this.consecutiveUpdates=0,this.lastRecordedTime=e)}},t.cancelTimer_=function(){this.consecutiveUpdates=0,this.timer_&&(this.logger_("cancelTimer_"),clearTimeout(this.timer_)),this.timer_=null},t.fixesBadSeeks_=function(){if(!this.tech_.seeking())return!1;var e,t=this.seekable(),i=this.tech_.currentTime();if(this.afterSeekableWindow_(t,i,this.media(),this.allowSeeksWithinUnsafeLiveWindow)&&(e=t.end(t.length-1)),"undefined"!=typeof(e=this.beforeSeekableWindow_(t,i)?(a=t.start(0))+(a===t.end(0)?0:.1):e))return this.logger_("Trying to seek outside of seekable at time "+i+" with seekable range "+Uo(t)+". Seeking to "+e+"."),this.tech_.setCurrentTime(e),!0;for(var n=this.masterPlaylistController_.sourceUpdater_,r=this.tech_.buffered(),a=n.audioBuffer?n.audioBuffered():null,t=n.videoBuffer?n.videoBuffered():null,n=this.media(),s=n.partTargetDuration||2*(n.targetDuration-hl),o=[a,t],u=0;u<o.length;u++)if(o[u])if(jo(o[u],i)<s)return!1;r=No(r,i);return 0!==r.length&&(e=r.start(0)+.1,this.logger_("Buffered region starts ("+r.start(0)+")  just beyond seek point ("+i+"). Seeking to "+e+"."),this.tech_.setCurrentTime(e),!0)},t.waiting_=function(){var e,t;this.techWaiting_()||(e=this.tech_.currentTime(),t=this.tech_.buffered(),(t=Mo(t,e)).length&&e+3<=t.end(0)&&(this.cancelTimer_(),this.tech_.setCurrentTime(e),this.logger_("Stopped at "+e+" while inside a buffered region ["+t.start(0)+" -> "+t.end(0)+"]. Attempting to resume playback by seeking to the current time."),this.tech_.trigger({type:"usage",name:"vhs-unknown-waiting"}),this.tech_.trigger({type:"usage",name:"hls-unknown-waiting"})))},t.techWaiting_=function(){var e=this.seekable(),t=this.tech_.currentTime();if(this.tech_.seeking()||null!==this.timer_)return!0;if(this.beforeSeekableWindow_(e,t)){var i=e.end(e.length-1);return this.logger_("Fell out of live window at time "+t+". Seeking to live point (seekable end) "+i),this.cancelTimer_(),this.tech_.setCurrentTime(i),this.tech_.trigger({type:"usage",name:"vhs-live-resync"}),this.tech_.trigger({type:"usage",name:"hls-live-resync"}),!0}e=this.tech_.vhs.masterPlaylistController_.sourceUpdater_,i=this.tech_.buffered();if(this.videoUnderflow_({audioBuffered:e.audioBuffered(),videoBuffered:e.videoBuffered(),currentTime:t}))return this.cancelTimer_(),this.tech_.setCurrentTime(t),this.tech_.trigger({type:"usage",name:"vhs-video-underflow"}),this.tech_.trigger({type:"usage",name:"hls-video-underflow"}),!0;e=No(i,t);if(0<e.length){i=e.start(0)-t;return this.logger_("Stopped at "+t+", setting timer for "+i+", seeking to "+e.start(0)),this.cancelTimer_(),this.timer_=setTimeout(this.skipTheGap_.bind(this),1e3*i,t),!0}return!1},t.afterSeekableWindow_=function(e,t,i,n){if(void 0===n&&(n=!1),!e.length)return!1;var r=e.end(e.length-1)+.1;return(r=!i.endList&&n?e.end(e.length-1)+3*i.targetDuration:r)<t},t.beforeSeekableWindow_=function(e,t){return!!(e.length&&0<e.start(0)&&t<e.start(0)-this.liveRangeSafeTimeDelta)},t.videoUnderflow_=function(e){var t,i,n=e.videoBuffered,r=e.audioBuffered,a=e.currentTime;if(n)return n.length&&r.length?(i=Mo(n,a-3),e=Mo(n,a),(r=Mo(r,a)).length&&!e.length&&i.length&&(t={start:i.end(0),end:r.end(0)})):No(n,a).length||(t=this.gapFromVideoUnderflow_(n,a)),!!t&&(this.logger_("Encountered a gap in video from "+t.start+" to "+t.end+". Seeking to current time "+a),!0)},t.skipTheGap_=function(e){var t=this.tech_.buffered(),i=this.tech_.currentTime(),t=No(t,i);this.cancelTimer_(),0!==t.length&&i===e&&(this.logger_("skipTheGap_:","currentTime:",i,"scheduled currentTime:",e,"nextRange start:",t.start(0)),this.tech_.setCurrentTime(t.start(0)+hl),this.tech_.trigger({type:"usage",name:"vhs-gap-skip"}),this.tech_.trigger({type:"usage",name:"hls-gap-skip"}))},t.gapFromVideoUnderflow_=function(e,t){for(var i=function(e){if(e.length<2)return tr.createTimeRanges();for(var t=[],i=1;i<e.length;i++){var n=e.end(i-1),r=e.start(i);t.push([n,r])}return tr.createTimeRanges(t)}(e),n=0;n<i.length;n++){var r=i.start(n),a=i.end(n);if(t-r<4&&2<t-r)return{start:r,end:a}}return null},e}(),Tc={errorInterval:30,getSource:function(e){return e(this.tech({IWillNotUseThisInPlugins:!0}).currentSource_||this.currentSource())}},Sc={PlaylistLoader:yl,Playlist:fl,utils:zt,STANDARD_PLAYLIST_SELECTOR:ar,INITIAL_PLAYLIST_SELECTOR:function(){var t=this,e=this.playlists.master.playlists.filter(fl.isEnabled);return Ju(e,Zu),e.filter(function(e){return!!Yu(t.playlists.master,e).video})[0]||null},lastBandwidthSelector:ar,movingAverageBandwidthSelector:function(t){var i=-1,n=-1;if(t<0||1<t)throw new Error("Moving average bandwidth decay must be between 0 and 1.");return function(){var e=this.useDevicePixelRatio&&window.devicePixelRatio||1;return i<0&&(i=this.systemBandwidth,n=this.systemBandwidth),0<this.systemBandwidth&&this.systemBandwidth!==n&&(i=t*this.systemBandwidth+(1-t)*i,n=this.systemBandwidth),el(this.playlists.master,i,parseInt($u(this.tech_.el(),"width"),10)*e,parseInt($u(this.tech_.el(),"height"),10)*e,this.limitRenditionByPlayerDimensions,this.masterPlaylistController_)}},comparePlaylistBandwidth:Zu,comparePlaylistResolution:function(e,t){var i,n;return(i=(i=e.attributes.RESOLUTION&&e.attributes.RESOLUTION.width?e.attributes.RESOLUTION.width:i)||window.Number.MAX_VALUE)===(n=(n=t.attributes.RESOLUTION&&t.attributes.RESOLUTION.width?t.attributes.RESOLUTION.width:n)||window.Number.MAX_VALUE)&&e.attributes.BANDWIDTH&&t.attributes.BANDWIDTH?e.attributes.BANDWIDTH-t.attributes.BANDWIDTH:i-n},xhr:mu()};Object.keys(Sl).forEach(function(t){Object.defineProperty(Sc,t,{get:function(){return tr.log.warn("using Vhs."+t+" is UNSAFE be sure you know what you are doing"),Sl[t]},set:function(e){tr.log.warn("using Vhs."+t+" is UNSAFE be sure you know what you are doing"),"number"!=typeof e||e<0?tr.log.warn("value of Vhs."+t+" must be greater than or equal to 0"):Sl[t]=e}})});function wc(e,t){for(var i=t.media(),n=-1,r=0;r<e.length;r++)if(e[r].id===i.id){n=r;break}e.selectedIndex_=n,e.trigger({selectedIndex:n,type:"change"})}var Ec="videojs-vhs";Sc.canPlaySource=function(){return tr.log.warn("HLS is no longer a tech. Please remove it from your player's techOrder.")};function kc(e){var n=e.player,t=e.sourceKeySystems,i=e.audioMedia,e=e.mainPlaylists;if(!n.eme.initializeMediaKeys)return Promise.resolve();var r,e=(e=e=i?e.concat([i]):e,r=Object.keys(t),e.reduce(function(e,n){if(!n.contentProtection)return e;var t=r.reduce(function(e,t){var i=n.contentProtection[t];return i&&i.pssh&&(e[t]={pssh:i.pssh}),e},{});return Object.keys(t).length&&e.push(t),e},[])),a=[],s=[];return e.forEach(function(e){s.push(new Promise(function(e,t){n.tech_.one("keysessioncreated",e)})),a.push(new Promise(function(t,i){n.eme.initializeMediaKeys({keySystems:e},function(e){e?i(e):t()})}))}),Promise.race([Promise.all(a),Promise.race(s)])}function Cc(e){var t=e.player;return!!(e=function(e,t,i){if(!e)return e;var n={};t&&t.attributes&&t.attributes.CODECS&&(n=Xu(pr(t.attributes.CODECS))),i&&i.attributes&&i.attributes.CODECS&&(n.audio=i.attributes.CODECS);var r,a=mr(n.video),s=mr(n.audio),o={};for(r in e)o[r]={},s&&(o[r].audioContentType=s),a&&(o[r].videoContentType=a),t.contentProtection&&t.contentProtection[r]&&t.contentProtection[r].pssh&&(o[r].pssh=t.contentProtection[r].pssh),"string"==typeof e[r]&&(o[r].url=e[r]);return tr.mergeOptions(e,o)}(e.sourceKeySystems,e.media,e.audioMedia))&&(!((t.currentSource().keySystems=e)&&!t.eme)||(tr.log.warn("DRM encrypted source cannot be decrypted without a DRM plugin"),!1))}function Ic(){if(!window.localStorage)return null;var e=window.localStorage.getItem(Ec);if(!e)return null;try{return JSON.parse(e)}catch(e){return null}}Sc.supportsNativeHls=function(){if(!document||!document.createElement)return!1;var t=document.createElement("video");if(!tr.getTech("Html5").isSupported())return!1;return["application/vnd.apple.mpegurl","audio/mpegurl","audio/x-mpegurl","application/x-mpegurl","video/x-mpegurl","video/mpegurl","application/mpegurl"].some(function(e){return/maybe|probably/i.test(t.canPlayType(e))})}(),Sc.supportsNativeDash=!!(document&&document.createElement&&tr.getTech("Html5").isSupported())&&/maybe|probably/i.test(document.createElement("video").canPlayType("application/dash+xml")),Sc.supportsTypeNatively=function(e){return"hls"===e?Sc.supportsNativeHls:"dash"===e&&Sc.supportsNativeDash},Sc.isSupported=function(){return tr.log.warn("HLS is no longer a tech. Please remove it from your player's techOrder.")};var xc=function(r){function e(e,t,i){var n=r.call(this,t,tr.mergeOptions(i.hls,i.vhs))||this;if(i.hls&&Object.keys(i.hls).length&&tr.log.warn("Using hls options is deprecated. Use vhs instead."),"number"==typeof i.initialBandwidth&&(n.options_.bandwidth=i.initialBandwidth),n.logger_=Oo("VhsHandler"),t.options_&&t.options_.playerId&&((i=tr(t.options_.playerId)).hasOwnProperty("hls")||Object.defineProperty(i,"hls",{get:function(){return tr.log.warn("player.hls is deprecated. Use player.tech().vhs instead."),t.trigger({type:"usage",name:"hls-player-access"}),ft(n)},configurable:!0}),i.hasOwnProperty("vhs")||Object.defineProperty(i,"vhs",{get:function(){return tr.log.warn("player.vhs is deprecated. Use player.tech().vhs instead."),t.trigger({type:"usage",name:"vhs-player-access"}),ft(n)},configurable:!0}),i.hasOwnProperty("dash")||Object.defineProperty(i,"dash",{get:function(){return tr.log.warn("player.dash is deprecated. Use player.tech().vhs instead."),ft(n)},configurable:!0}),n.player_=i),n.tech_=t,n.source_=e,n.stats={},n.ignoreNextSeekingEvent_=!1,n.setOptions_(),n.options_.overrideNative&&t.overrideNativeAudioTracks&&t.overrideNativeVideoTracks)t.overrideNativeAudioTracks(!0),t.overrideNativeVideoTracks(!0);else if(n.options_.overrideNative&&(t.featuresNativeVideoTracks||t.featuresNativeAudioTracks))throw new Error("Overriding native HLS requires emulated tracks. See https://git.io/vMpjB");return n.on(document,["fullscreenchange","webkitfullscreenchange","mozfullscreenchange","MSFullscreenChange"],function(e){var t=document.fullscreenElement||document.webkitFullscreenElement||document.mozFullScreenElement||document.msFullscreenElement;t&&t.contains(n.tech_.el())?n.masterPlaylistController_.fastQualityChange_():n.masterPlaylistController_.checkABR_()}),n.on(n.tech_,"seeking",function(){this.ignoreNextSeekingEvent_?this.ignoreNextSeekingEvent_=!1:this.setCurrentTime(this.tech_.currentTime())}),n.on(n.tech_,"error",function(){this.tech_.error()&&this.masterPlaylistController_&&this.masterPlaylistController_.pauseLoading()}),n.on(n.tech_,"play",n.play),n}mt(e,r);var t=e.prototype;return t.setOptions_=function(){var e,t=this;this.options_.withCredentials=this.options_.withCredentials||!1,this.options_.handleManifestRedirects=!1!==this.options_.handleManifestRedirects,this.options_.limitRenditionByPlayerDimensions=!1!==this.options_.limitRenditionByPlayerDimensions,this.options_.useDevicePixelRatio=this.options_.useDevicePixelRatio||!1,this.options_.smoothQualityChange=this.options_.smoothQualityChange||!1,this.options_.useBandwidthFromLocalStorage="undefined"!=typeof this.source_.useBandwidthFromLocalStorage?this.source_.useBandwidthFromLocalStorage:this.options_.useBandwidthFromLocalStorage||!1,this.options_.useNetworkInformationApi=this.options_.useNetworkInformationApi||!1,this.options_.customTagParsers=this.options_.customTagParsers||[],this.options_.customTagMappers=this.options_.customTagMappers||[],this.options_.cacheEncryptionKeys=this.options_.cacheEncryptionKeys||!1,"number"!=typeof this.options_.blacklistDuration&&(this.options_.blacklistDuration=300),"number"!=typeof this.options_.bandwidth&&this.options_.useBandwidthFromLocalStorage&&((e=Ic())&&e.bandwidth&&(this.options_.bandwidth=e.bandwidth,this.tech_.trigger({type:"usage",name:"vhs-bandwidth-from-local-storage"}),this.tech_.trigger({type:"usage",name:"hls-bandwidth-from-local-storage"})),e&&e.throughput&&(this.options_.throughput=e.throughput,this.tech_.trigger({type:"usage",name:"vhs-throughput-from-local-storage"}),this.tech_.trigger({type:"usage",name:"hls-throughput-from-local-storage"}))),"number"!=typeof this.options_.bandwidth&&(this.options_.bandwidth=Sl.INITIAL_BANDWIDTH),this.options_.enableLowInitialPlaylist=this.options_.enableLowInitialPlaylist&&this.options_.bandwidth===Sl.INITIAL_BANDWIDTH,["withCredentials","useDevicePixelRatio","limitRenditionByPlayerDimensions","bandwidth","smoothQualityChange","customTagParsers","customTagMappers","handleManifestRedirects","cacheEncryptionKeys","playlistSelector","initialPlaylistSelector","experimentalBufferBasedABR","liveRangeSafeTimeDelta","experimentalLLHLS","useNetworkInformationApi","experimentalExactManifestTimings","experimentalLeastPixelDiffSelector"].forEach(function(e){"undefined"!=typeof t.source_[e]&&(t.options_[e]=t.source_[e])}),this.limitRenditionByPlayerDimensions=this.options_.limitRenditionByPlayerDimensions,this.useDevicePixelRatio=this.options_.useDevicePixelRatio},t.src=function(e,t){var n=this;e&&(this.setOptions_(),this.options_.src=0===(e=this.source_.src).toLowerCase().indexOf("data:application/vnd.videojs.vhs+json,")?JSON.parse(e.substring(e.indexOf(",")+1)):e,this.options_.tech=this.tech_,this.options_.externVhs=Sc,this.options_.sourceType=vr(t),this.options_.seekTo=function(e){n.tech_.setCurrentTime(e)},this.options_.smoothQualityChange&&tr.log.warn("smoothQualityChange is deprecated and will be removed in the next major version"),this.masterPlaylistController_=new vc(this.options_),t=tr.mergeOptions({liveRangeSafeTimeDelta:.1},this.options_,{seekable:function(){return n.seekable()},media:function(){return n.masterPlaylistController_.media()},masterPlaylistController:this.masterPlaylistController_}),this.playbackWatcher_=new bc(t),this.masterPlaylistController_.on("error",function(){var e=tr.players[n.tech_.options_.playerId],t=n.masterPlaylistController_.error;"object"!=typeof t||t.code?"string"==typeof t&&(t={message:t,code:3}):t.code=3,e.error(t)}),t=this.options_.experimentalBufferBasedABR?Sc.movingAverageBandwidthSelector(.55):Sc.STANDARD_PLAYLIST_SELECTOR,this.masterPlaylistController_.selectPlaylist=(this.selectPlaylist||t).bind(this),this.masterPlaylistController_.selectInitialPlaylist=Sc.INITIAL_PLAYLIST_SELECTOR.bind(this),this.playlists=this.masterPlaylistController_.masterPlaylistLoader_,this.mediaSource=this.masterPlaylistController_.mediaSource,Object.defineProperties(this,{selectPlaylist:{get:function(){return this.masterPlaylistController_.selectPlaylist},set:function(e){this.masterPlaylistController_.selectPlaylist=e.bind(this)}},throughput:{get:function(){return this.masterPlaylistController_.mainSegmentLoader_.throughput.rate},set:function(e){this.masterPlaylistController_.mainSegmentLoader_.throughput.rate=e,this.masterPlaylistController_.mainSegmentLoader_.throughput.count=1}},bandwidth:{get:function(){var e=this.masterPlaylistController_.mainSegmentLoader_.bandwidth,t=window.navigator.connection||window.navigator.mozConnection||window.navigator.webkitConnection;return e=this.options_.useNetworkInformationApi&&t?1e7<=(t=1e3*t.downlink*1e3)&&1e7<=e?Math.max(e,t):t:e},set:function(e){this.masterPlaylistController_.mainSegmentLoader_.bandwidth=e,this.masterPlaylistController_.mainSegmentLoader_.throughput={rate:0,count:0}}},systemBandwidth:{get:function(){var e=1/(this.bandwidth||1),t=0<this.throughput?1/this.throughput:0;return Math.floor(1/(e+t))},set:function(){tr.log.error('The "systemBandwidth" property is read-only')}}}),this.options_.bandwidth&&(this.bandwidth=this.options_.bandwidth),this.options_.throughput&&(this.throughput=this.options_.throughput),Object.defineProperties(this.stats,{bandwidth:{get:function(){return n.bandwidth||0},enumerable:!0},mediaRequests:{get:function(){return n.masterPlaylistController_.mediaRequests_()||0},enumerable:!0},mediaRequestsAborted:{get:function(){return n.masterPlaylistController_.mediaRequestsAborted_()||0},enumerable:!0},mediaRequestsTimedout:{get:function(){return n.masterPlaylistController_.mediaRequestsTimedout_()||0},enumerable:!0},mediaRequestsErrored:{get:function(){return n.masterPlaylistController_.mediaRequestsErrored_()||0},enumerable:!0},mediaTransferDuration:{get:function(){return n.masterPlaylistController_.mediaTransferDuration_()||0},enumerable:!0},mediaBytesTransferred:{get:function(){return n.masterPlaylistController_.mediaBytesTransferred_()||0},enumerable:!0},mediaSecondsLoaded:{get:function(){return n.masterPlaylistController_.mediaSecondsLoaded_()||0},enumerable:!0},mediaAppends:{get:function(){return n.masterPlaylistController_.mediaAppends_()||0},enumerable:!0},mainAppendsToLoadedData:{get:function(){return n.masterPlaylistController_.mainAppendsToLoadedData_()||0},enumerable:!0},audioAppendsToLoadedData:{get:function(){return n.masterPlaylistController_.audioAppendsToLoadedData_()||0},enumerable:!0},appendsToLoadedData:{get:function(){return n.masterPlaylistController_.appendsToLoadedData_()||0},enumerable:!0},timeToLoadedData:{get:function(){return n.masterPlaylistController_.timeToLoadedData_()||0},enumerable:!0},buffered:{get:function(){return Bo(n.tech_.buffered())},enumerable:!0},currentTime:{get:function(){return n.tech_.currentTime()},enumerable:!0},currentSource:{get:function(){return n.tech_.currentSource_},enumerable:!0},currentTech:{get:function(){return n.tech_.name_},enumerable:!0},duration:{get:function(){return n.tech_.duration()},enumerable:!0},master:{get:function(){return n.playlists.master},enumerable:!0},playerDimensions:{get:function(){return n.tech_.currentDimensions()},enumerable:!0},seekable:{get:function(){return Bo(n.tech_.seekable())},enumerable:!0},timestamp:{get:function(){return Date.now()},enumerable:!0},videoPlaybackQuality:{get:function(){return n.tech_.getVideoPlaybackQuality()},enumerable:!0}}),this.tech_.one("canplay",this.masterPlaylistController_.setupFirstPlay.bind(this.masterPlaylistController_)),this.tech_.on("bandwidthupdate",function(){n.options_.useBandwidthFromLocalStorage&&function(e){if(!window.localStorage)return;var t=(t=Ic())?tr.mergeOptions(t,e):e;try{window.localStorage.setItem(Ec,JSON.stringify(t))}catch(e){return}}({bandwidth:n.bandwidth,throughput:Math.round(n.throughput)})}),this.masterPlaylistController_.on("selectedinitialmedia",function(){var i;(i=n).representations=function(){var e=i.masterPlaylistController_.master(),e=nu(e)?i.masterPlaylistController_.getAudioTrackPlaylists_():e.playlists;return e?e.filter(function(e){return!$o(e)}).map(function(e,t){return new zl(i,e,e.id)}):[]}}),this.masterPlaylistController_.sourceUpdater_.on("createdsourcebuffers",function(){n.setupEme_()}),this.on(this.masterPlaylistController_,"progress",function(){this.tech_.trigger("progress")}),this.on(this.masterPlaylistController_,"firstplay",function(){this.ignoreNextSeekingEvent_=!0}),this.setupQualityLevels_(),this.tech_.el()&&(this.mediaSourceUrl_=window.URL.createObjectURL(this.masterPlaylistController_.mediaSource),this.tech_.src(this.mediaSourceUrl_)))},t.createKeySessions_=function(){var t=this,e=this.masterPlaylistController_.mediaTypes_.AUDIO.activePlaylistLoader;this.logger_("waiting for EME key session creation"),kc({player:this.player_,sourceKeySystems:this.source_.keySystems,audioMedia:e&&e.media(),mainPlaylists:this.playlists.master.playlists}).then(function(){t.logger_("created EME key session"),t.masterPlaylistController_.sourceUpdater_.initializedEme()}).catch(function(e){t.logger_("error while creating EME key session",e),t.player_.error({message:"Failed to initialize media keys for EME",code:3})})},t.handleWaitingForKey_=function(){this.logger_("waitingforkey fired, attempting to create any new key sessions"),this.createKeySessions_()},t.setupEme_=function(){var t=this,e=this.masterPlaylistController_.mediaTypes_.AUDIO.activePlaylistLoader,e=Cc({player:this.player_,sourceKeySystems:this.source_.keySystems,media:this.playlists.media(),audioMedia:e&&e.media()});this.player_.tech_.on("keystatuschange",function(e){"output-restricted"===e.status&&t.masterPlaylistController_.blacklistCurrentPlaylist({playlist:t.masterPlaylistController_.media(),message:"DRM keystatus changed to "+e.status+". Playlist will fail to play. Check for HDCP content.",blacklistDuration:1/0})}),this.handleWaitingForKey_=this.handleWaitingForKey_.bind(this),this.player_.tech_.on("waitingforkey",this.handleWaitingForKey_),11!==tr.browser.IE_VERSION&&e?this.createKeySessions_():this.masterPlaylistController_.sourceUpdater_.initializedEme()},t.setupQualityLevels_=function(){var i=this,e=tr.players[this.tech_.options_.playerId];e&&e.qualityLevels&&!this.qualityLevels_&&(this.qualityLevels_=e.qualityLevels(),this.masterPlaylistController_.on("selectedinitialmedia",function(){var t,e;t=i.qualityLevels_,(e=i).representations().forEach(function(e){t.addQualityLevel(e)}),wc(t,e.playlists)}),this.playlists.on("mediachange",function(){wc(i.qualityLevels_,i.playlists)}))},e.version=function(){return{"@videojs/http-streaming":"2.13.1","mux.js":"6.0.1","mpd-parser":"0.21.0","m3u8-parser":"4.7.0","aes-decrypter":"3.1.2"}},t.version=function(){return this.constructor.version()},t.canChangeType=function(){return sc.canChangeType()},t.play=function(){this.masterPlaylistController_.play()},t.setCurrentTime=function(e){this.masterPlaylistController_.setCurrentTime(e)},t.duration=function(){return this.masterPlaylistController_.duration()},t.seekable=function(){return this.masterPlaylistController_.seekable()},t.dispose=function(){this.playbackWatcher_&&this.playbackWatcher_.dispose(),this.masterPlaylistController_&&this.masterPlaylistController_.dispose(),this.qualityLevels_&&this.qualityLevels_.dispose(),this.player_&&(delete this.player_.vhs,delete this.player_.dash,delete this.player_.hls),this.tech_&&this.tech_.vhs&&delete this.tech_.vhs,this.tech_&&delete this.tech_.hls,this.mediaSourceUrl_&&window.URL.revokeObjectURL&&(window.URL.revokeObjectURL(this.mediaSourceUrl_),this.mediaSourceUrl_=null),this.tech_&&this.tech_.off("waitingforkey",this.handleWaitingForKey_),r.prototype.dispose.call(this)},t.convertToProgramTime=function(e,t){return wu({playlist:this.masterPlaylistController_.media(),time:e,callback:t})},t.seekToProgramTime=function(e,t,i,n){return void 0===i&&(i=!0),void 0===n&&(n=2),Eu({programTime:e,playlist:this.masterPlaylistController_.media(),retryCount:n,pauseAfterSeek:i,seekTo:this.options_.seekTo,tech:this.options_.tech,callback:t})},e}(tr.getComponent("Component")),Ac={name:"videojs-http-streaming",VERSION:"2.13.1",canHandleSource:function(e,t){t=tr.mergeOptions(tr.options,t=void 0===t?{}:t);return Ac.canPlayType(e.type,t)},handleSource:function(e,t,i){i=tr.mergeOptions(tr.options,i=void 0===i?{}:i);return t.vhs=new xc(e,t,i),tr.hasOwnProperty("hls")||Object.defineProperty(t,"hls",{get:function(){return tr.log.warn("player.tech().hls is deprecated. Use player.tech().vhs instead."),t.vhs},configurable:!0}),t.vhs.xhr=mu(),t.vhs.src(e.src,e.type),t.vhs},canPlayType:function(e,t){var i=tr.mergeOptions(tr.options,t=void 0===t?{}:t),t=i.vhs,t=(t=void 0===t?{}:t).overrideNative,t=void 0===t?!tr.browser.IS_ANY_SAFARI:t,i=i.hls,i=(i=void 0===i?{}:i).overrideNative,i=void 0!==i&&i,e=vr(e);return e&&(!Sc.supportsTypeNatively(e)||i||t)?"maybe":""}};return gr("avc1.4d400d,mp4a.40.2")&&tr.getTech("Html5").registerSourceHandler(Ac,0),tr.VhsHandler=xc,Object.defineProperty(tr,"HlsHandler",{get:function(){return tr.log.warn("videojs.HlsHandler is deprecated. Use videojs.VhsHandler instead."),xc},configurable:!0}),tr.VhsSourceHandler=Ac,Object.defineProperty(tr,"HlsSourceHandler",{get:function(){return tr.log.warn("videojs.HlsSourceHandler is deprecated. Use videojs.VhsSourceHandler instead."),Ac},configurable:!0}),tr.Vhs=Sc,Object.defineProperty(tr,"Hls",{get:function(){return tr.log.warn("videojs.Hls is deprecated. Use videojs.Vhs instead."),Sc},configurable:!0}),tr.use||(tr.registerComponent("Hls",Sc),tr.registerComponent("Vhs",Sc)),tr.options.vhs=tr.options.vhs||{},tr.options.hls=tr.options.hls||{},tr.getPlugin&&tr.getPlugin("reloadSourceOnError")||(tr.registerPlugin||tr.plugin)("reloadSourceOnError",function(e){Xl(this,e)}),tr});
 !function(){!function(a){var b=a&&a.videojs;b&&(b.CDN_VERSION="7.18.1")}(window)}();
// source --> https://eternize360.com.br/wp-content/plugins/wpvr-pro/admin/lib/videojs-vr/videojs-vr.js?ver=1 
/*! @name videojs-vr @version 1.7.2 @license Apache-2.0 */
(function (global, factory) {
	typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('global/window'), require('global/document'), require('video.js')) :
	typeof define === 'function' && define.amd ? define(['global/window', 'global/document', 'video.js'], factory) :
	(global = global || self, global.videojsVr = factory(global.window, global.document, global.videojs));
  }(this, (function (window$1, document$1, videojs) { 'use strict';
  
	window$1 = window$1 && window$1.hasOwnProperty('default') ? window$1['default'] : window$1;
	document$1 = document$1 && document$1.hasOwnProperty('default') ? document$1['default'] : document$1;
	videojs = videojs && videojs.hasOwnProperty('default') ? videojs['default'] : videojs;
  
	function _assertThisInitialized(self) {
	  if (self === void 0) {
		throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
	  }
  
	  return self;
	}
  
	var assertThisInitialized = _assertThisInitialized;
  
	function _inheritsLoose(subClass, superClass) {
	  subClass.prototype = Object.create(superClass.prototype);
	  subClass.prototype.constructor = subClass;
	  subClass.__proto__ = superClass;
	}
  
	var inheritsLoose = _inheritsLoose;
  
	var version = "1.7.2";
  
	var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
  
	function unwrapExports (x) {
		return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
	}
  
	function createCommonjsModule(fn, module) {
		return module = { exports: {} }, fn(module, module.exports), module.exports;
	}
  
	var webvrPolyfill = createCommonjsModule(function (module, exports) {
	/**
	 * @license
	 * webvr-polyfill
	 * Copyright (c) 2015-2017 Google
	 * Licensed under the Apache License, Version 2.0 (the "License");
	 * you may not use this file except in compliance with the License.
	 * You may obtain a copy of the License at
	 *
	 * http://www.apache.org/licenses/LICENSE-2.0
	 *
	 * Unless required by applicable law or agreed to in writing, software
	 * distributed under the License is distributed on an "AS IS" BASIS,
	 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	 * See the License for the specific language governing permissions and
	 * limitations under the License.
	 */
  
	/**
	 * @license
	 * cardboard-vr-display
	 * Copyright (c) 2015-2017 Google
	 * Licensed under the Apache License, Version 2.0 (the "License");
	 * you may not use this file except in compliance with the License.
	 * You may obtain a copy of the License at
	 *
	 * http://www.apache.org/licenses/LICENSE-2.0
	 *
	 * Unless required by applicable law or agreed to in writing, software
	 * distributed under the License is distributed on an "AS IS" BASIS,
	 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	 * See the License for the specific language governing permissions and
	 * limitations under the License.
	 */
  
	/**
	 * @license
	 * webvr-polyfill-dpdb 
	 * Copyright (c) 2017 Google
	 * Licensed under the Apache License, Version 2.0 (the "License");
	 * you may not use this file except in compliance with the License.
	 * You may obtain a copy of the License at
	 *
	 * http://www.apache.org/licenses/LICENSE-2.0
	 *
	 * Unless required by applicable law or agreed to in writing, software
	 * distributed under the License is distributed on an "AS IS" BASIS,
	 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	 * See the License for the specific language governing permissions and
	 * limitations under the License.
	 */
  
	/**
	 * @license
	 * wglu-preserve-state
	 * Copyright (c) 2016, Brandon Jones.
	 *
	 * Permission is hereby granted, free of charge, to any person obtaining a copy
	 * of this software and associated documentation files (the "Software"), to deal
	 * in the Software without restriction, including without limitation the rights
	 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	 * copies of the Software, and to permit persons to whom the Software is
	 * furnished to do so, subject to the following conditions:
	 *
	 * The above copyright notice and this permission notice shall be included in
	 * all copies or substantial portions of the Software.
	 *
	 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	 * THE SOFTWARE.
	 */
  
	/**
	 * @license
	 * nosleep.js
	 * Copyright (c) 2017, Rich Tibbett
	 *
	 * Permission is hereby granted, free of charge, to any person obtaining a copy
	 * of this software and associated documentation files (the "Software"), to deal
	 * in the Software without restriction, including without limitation the rights
	 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	 * copies of the Software, and to permit persons to whom the Software is
	 * furnished to do so, subject to the following conditions:
	 *
	 * The above copyright notice and this permission notice shall be included in
	 * all copies or substantial portions of the Software.
	 *
	 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	 * THE SOFTWARE.
	 */
  
	(function (global, factory) {
		 module.exports = factory() ;
	}(commonjsGlobal, (function () {
	var commonjsGlobal$1 = typeof window !== 'undefined' ? window : typeof commonjsGlobal !== 'undefined' ? commonjsGlobal : typeof self !== 'undefined' ? self : {};
  
  
  
	function unwrapExports (x) {
		return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
	}
  
	function createCommonjsModule(fn, module) {
		return module = { exports: {} }, fn(module, module.exports), module.exports;
	}
  
	var race = function race(promises) {
	  if (Promise.race) {
		return Promise.race(promises);
	  }
	  return new Promise(function (resolve, reject) {
		for (var i = 0; i < promises.length; i++) {
		  promises[i].then(resolve, reject);
		}
	  });
	};
  
	var isMobile = function isMobile() {
	  return (/Android/i.test(navigator.userAgent) || /iPhone|iPad|iPod/i.test(navigator.userAgent)
	  );
	};
	var copyArray = function copyArray(source, dest) {
	  for (var i = 0, n = source.length; i < n; i++) {
		dest[i] = source[i];
	  }
	};
	var extend = function extend(dest, src) {
	  for (var key in src) {
		if (src.hasOwnProperty(key)) {
		  dest[key] = src[key];
		}
	  }
	  return dest;
	};
  
	var cardboardVrDisplay = createCommonjsModule(function (module, exports) {
	/**
	 * @license
	 * cardboard-vr-display
	 * Copyright (c) 2015-2017 Google
	 * Licensed under the Apache License, Version 2.0 (the "License");
	 * you may not use this file except in compliance with the License.
	 * You may obtain a copy of the License at
	 *
	 * http://www.apache.org/licenses/LICENSE-2.0
	 *
	 * Unless required by applicable law or agreed to in writing, software
	 * distributed under the License is distributed on an "AS IS" BASIS,
	 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	 * See the License for the specific language governing permissions and
	 * limitations under the License.
	 */
	/**
	 * @license
	 * gl-preserve-state
	 * Copyright (c) 2016, Brandon Jones.
	 *
	 * Permission is hereby granted, free of charge, to any person obtaining a copy
	 * of this software and associated documentation files (the "Software"), to deal
	 * in the Software without restriction, including without limitation the rights
	 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	 * copies of the Software, and to permit persons to whom the Software is
	 * furnished to do so, subject to the following conditions:
	 *
	 * The above copyright notice and this permission notice shall be included in
	 * all copies or substantial portions of the Software.
	 *
	 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	 * THE SOFTWARE.
	 */
	/**
	 * @license
	 * webvr-polyfill-dpdb
	 * Copyright (c) 2015-2017 Google
	 * Licensed under the Apache License, Version 2.0 (the "License");
	 * you may not use this file except in compliance with the License.
	 * You may obtain a copy of the License at
	 *
	 * http://www.apache.org/licenses/LICENSE-2.0
	 *
	 * Unless required by applicable law or agreed to in writing, software
	 * distributed under the License is distributed on an "AS IS" BASIS,
	 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	 * See the License for the specific language governing permissions and
	 * limitations under the License.
	 */
	/**
	 * @license
	 * nosleep.js
	 * Copyright (c) 2017, Rich Tibbett
	 *
	 * Permission is hereby granted, free of charge, to any person obtaining a copy
	 * of this software and associated documentation files (the "Software"), to deal
	 * in the Software without restriction, including without limitation the rights
	 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	 * copies of the Software, and to permit persons to whom the Software is
	 * furnished to do so, subject to the following conditions:
	 *
	 * The above copyright notice and this permission notice shall be included in
	 * all copies or substantial portions of the Software.
	 *
	 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	 * THE SOFTWARE.
	 */
	(function (global, factory) {
		module.exports = factory();
	}(commonjsGlobal$1, (function () { var classCallCheck = function (instance, Constructor) {
	  if (!(instance instanceof Constructor)) {
		throw new TypeError("Cannot call a class as a function");
	  }
	};
	var createClass = function () {
	  function defineProperties(target, props) {
		for (var i = 0; i < props.length; i++) {
		  var descriptor = props[i];
		  descriptor.enumerable = descriptor.enumerable || false;
		  descriptor.configurable = true;
		  if ("value" in descriptor) descriptor.writable = true;
		  Object.defineProperty(target, descriptor.key, descriptor);
		}
	  }
	  return function (Constructor, protoProps, staticProps) {
		if (protoProps) defineProperties(Constructor.prototype, protoProps);
		if (staticProps) defineProperties(Constructor, staticProps);
		return Constructor;
	  };
	}();
	var slicedToArray = function () {
	  function sliceIterator(arr, i) {
		var _arr = [];
		var _n = true;
		var _d = false;
		var _e = undefined;
		try {
		  for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
			_arr.push(_s.value);
			if (i && _arr.length === i) break;
		  }
		} catch (err) {
		  _d = true;
		  _e = err;
		} finally {
		  try {
			if (!_n && _i["return"]) _i["return"]();
		  } finally {
			if (_d) throw _e;
		  }
		}
		return _arr;
	  }
	  return function (arr, i) {
		if (Array.isArray(arr)) {
		  return arr;
		} else if (Symbol.iterator in Object(arr)) {
		  return sliceIterator(arr, i);
		} else {
		  throw new TypeError("Invalid attempt to destructure non-iterable instance");
		}
	  };
	}();
	var MIN_TIMESTEP = 0.001;
	var MAX_TIMESTEP = 1;
	var base64 = function base64(mimeType, _base) {
	  return 'data:' + mimeType + ';base64,' + _base;
	};
	var lerp = function lerp(a, b, t) {
	  return a + (b - a) * t;
	};
	var isIOS = function () {
	  var isIOS = /iPad|iPhone|iPod/.test(navigator.platform);
	  return function () {
		return isIOS;
	  };
	}();
	var isWebViewAndroid = function () {
	  var isWebViewAndroid = navigator.userAgent.indexOf('Version') !== -1 && navigator.userAgent.indexOf('Android') !== -1 && navigator.userAgent.indexOf('Chrome') !== -1;
	  return function () {
		return isWebViewAndroid;
	  };
	}();
	var isSafari = function () {
	  var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
	  return function () {
		return isSafari;
	  };
	}();
	var isFirefoxAndroid = function () {
	  var isFirefoxAndroid = navigator.userAgent.indexOf('Firefox') !== -1 && navigator.userAgent.indexOf('Android') !== -1;
	  return function () {
		return isFirefoxAndroid;
	  };
	}();
	var getChromeVersion = function () {
	  var match = navigator.userAgent.match(/.*Chrome\/([0-9]+)/);
	  var value = match ? parseInt(match[1], 10) : null;
	  return function () {
		return value;
	  };
	}();
	var isChromeWithoutDeviceMotion = function () {
	  var value = false;
	  if (getChromeVersion() === 65) {
		var match = navigator.userAgent.match(/.*Chrome\/([0-9\.]*)/);
		if (match) {
		  var _match$1$split = match[1].split('.'),
			  _match$1$split2 = slicedToArray(_match$1$split, 4),
			  major = _match$1$split2[0],
			  minor = _match$1$split2[1],
			  branch = _match$1$split2[2],
			  build = _match$1$split2[3];
		  value = parseInt(branch, 10) === 3325 && parseInt(build, 10) < 148;
		}
	  }
	  return function () {
		return value;
	  };
	}();
	var isR7 = function () {
	  var isR7 = navigator.userAgent.indexOf('R7 Build') !== -1;
	  return function () {
		return isR7;
	  };
	}();
	var isLandscapeMode = function isLandscapeMode() {
	  var rtn = window.orientation == 90 || window.orientation == -90;
	  return isR7() ? !rtn : rtn;
	};
	var isTimestampDeltaValid = function isTimestampDeltaValid(timestampDeltaS) {
	  if (isNaN(timestampDeltaS)) {
		return false;
	  }
	  if (timestampDeltaS <= MIN_TIMESTEP) {
		return false;
	  }
	  if (timestampDeltaS > MAX_TIMESTEP) {
		return false;
	  }
	  return true;
	};
	var getScreenWidth = function getScreenWidth() {
	  return Math.max(window.screen.width, window.screen.height) * window.devicePixelRatio;
	};
	var getScreenHeight = function getScreenHeight() {
	  return Math.min(window.screen.width, window.screen.height) * window.devicePixelRatio;
	};
	var requestFullscreen = function requestFullscreen(element) {
	  if (isWebViewAndroid()) {
		return false;
	  }
	  if (element.requestFullscreen) {
		element.requestFullscreen();
	  } else if (element.webkitRequestFullscreen) {
		element.webkitRequestFullscreen();
	  } else if (element.mozRequestFullScreen) {
		element.mozRequestFullScreen();
	  } else if (element.msRequestFullscreen) {
		element.msRequestFullscreen();
	  } else {
		return false;
	  }
	  return true;
	};
	var exitFullscreen = function exitFullscreen() {
	  if (document.exitFullscreen) {
		document.exitFullscreen();
	  } else if (document.webkitExitFullscreen) {
		document.webkitExitFullscreen();
	  } else if (document.mozCancelFullScreen) {
		document.mozCancelFullScreen();
	  } else if (document.msExitFullscreen) {
		document.msExitFullscreen();
	  } else {
		return false;
	  }
	  return true;
	};
	var getFullscreenElement = function getFullscreenElement() {
	  return document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement;
	};
	var linkProgram = function linkProgram(gl, vertexSource, fragmentSource, attribLocationMap) {
	  var vertexShader = gl.createShader(gl.VERTEX_SHADER);
	  gl.shaderSource(vertexShader, vertexSource);
	  gl.compileShader(vertexShader);
	  var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
	  gl.shaderSource(fragmentShader, fragmentSource);
	  gl.compileShader(fragmentShader);
	  var program = gl.createProgram();
	  gl.attachShader(program, vertexShader);
	  gl.attachShader(program, fragmentShader);
	  for (var attribName in attribLocationMap) {
		gl.bindAttribLocation(program, attribLocationMap[attribName], attribName);
	  }gl.linkProgram(program);
	  gl.deleteShader(vertexShader);
	  gl.deleteShader(fragmentShader);
	  return program;
	};
	var getProgramUniforms = function getProgramUniforms(gl, program) {
	  var uniforms = {};
	  var uniformCount = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
	  var uniformName = '';
	  for (var i = 0; i < uniformCount; i++) {
		var uniformInfo = gl.getActiveUniform(program, i);
		uniformName = uniformInfo.name.replace('[0]', '');
		uniforms[uniformName] = gl.getUniformLocation(program, uniformName);
	  }
	  return uniforms;
	};
	var orthoMatrix = function orthoMatrix(out, left, right, bottom, top, near, far) {
	  var lr = 1 / (left - right),
		  bt = 1 / (bottom - top),
		  nf = 1 / (near - far);
	  out[0] = -2 * lr;
	  out[1] = 0;
	  out[2] = 0;
	  out[3] = 0;
	  out[4] = 0;
	  out[5] = -2 * bt;
	  out[6] = 0;
	  out[7] = 0;
	  out[8] = 0;
	  out[9] = 0;
	  out[10] = 2 * nf;
	  out[11] = 0;
	  out[12] = (left + right) * lr;
	  out[13] = (top + bottom) * bt;
	  out[14] = (far + near) * nf;
	  out[15] = 1;
	  return out;
	};
	var isMobile = function isMobile() {
	  var check = false;
	  (function (a) {
		if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4))) check = true;
	  })(navigator.userAgent || navigator.vendor || window.opera);
	  return check;
	};
	var extend = function extend(dest, src) {
	  for (var key in src) {
		if (src.hasOwnProperty(key)) {
		  dest[key] = src[key];
		}
	  }
	  return dest;
	};
	var safariCssSizeWorkaround = function safariCssSizeWorkaround(canvas) {
	  if (isIOS()) {
		var width = canvas.style.width;
		var height = canvas.style.height;
		canvas.style.width = parseInt(width) + 1 + 'px';
		canvas.style.height = parseInt(height) + 'px';
		setTimeout(function () {
		  canvas.style.width = width;
		  canvas.style.height = height;
		}, 100);
	  }
	  window.canvas = canvas;
	};
	var frameDataFromPose = function () {
	  var piOver180 = Math.PI / 180.0;
	  var rad45 = Math.PI * 0.25;
	  function mat4_perspectiveFromFieldOfView(out, fov, near, far) {
		var upTan = Math.tan(fov ? fov.upDegrees * piOver180 : rad45),
			downTan = Math.tan(fov ? fov.downDegrees * piOver180 : rad45),
			leftTan = Math.tan(fov ? fov.leftDegrees * piOver180 : rad45),
			rightTan = Math.tan(fov ? fov.rightDegrees * piOver180 : rad45),
			xScale = 2.0 / (leftTan + rightTan),
			yScale = 2.0 / (upTan + downTan);
		out[0] = xScale;
		out[1] = 0.0;
		out[2] = 0.0;
		out[3] = 0.0;
		out[4] = 0.0;
		out[5] = yScale;
		out[6] = 0.0;
		out[7] = 0.0;
		out[8] = -((leftTan - rightTan) * xScale * 0.5);
		out[9] = (upTan - downTan) * yScale * 0.5;
		out[10] = far / (near - far);
		out[11] = -1.0;
		out[12] = 0.0;
		out[13] = 0.0;
		out[14] = far * near / (near - far);
		out[15] = 0.0;
		return out;
	  }
	  function mat4_fromRotationTranslation(out, q, v) {
		var x = q[0],
			y = q[1],
			z = q[2],
			w = q[3],
			x2 = x + x,
			y2 = y + y,
			z2 = z + z,
			xx = x * x2,
			xy = x * y2,
			xz = x * z2,
			yy = y * y2,
			yz = y * z2,
			zz = z * z2,
			wx = w * x2,
			wy = w * y2,
			wz = w * z2;
		out[0] = 1 - (yy + zz);
		out[1] = xy + wz;
		out[2] = xz - wy;
		out[3] = 0;
		out[4] = xy - wz;
		out[5] = 1 - (xx + zz);
		out[6] = yz + wx;
		out[7] = 0;
		out[8] = xz + wy;
		out[9] = yz - wx;
		out[10] = 1 - (xx + yy);
		out[11] = 0;
		out[12] = v[0];
		out[13] = v[1];
		out[14] = v[2];
		out[15] = 1;
		return out;
	  }
	  function mat4_translate(out, a, v) {
		var x = v[0],
			y = v[1],
			z = v[2],
			a00,
			a01,
			a02,
			a03,
			a10,
			a11,
			a12,
			a13,
			a20,
			a21,
			a22,
			a23;
		if (a === out) {
		  out[12] = a[0] * x + a[4] * y + a[8] * z + a[12];
		  out[13] = a[1] * x + a[5] * y + a[9] * z + a[13];
		  out[14] = a[2] * x + a[6] * y + a[10] * z + a[14];
		  out[15] = a[3] * x + a[7] * y + a[11] * z + a[15];
		} else {
		  a00 = a[0];a01 = a[1];a02 = a[2];a03 = a[3];
		  a10 = a[4];a11 = a[5];a12 = a[6];a13 = a[7];
		  a20 = a[8];a21 = a[9];a22 = a[10];a23 = a[11];
		  out[0] = a00;out[1] = a01;out[2] = a02;out[3] = a03;
		  out[4] = a10;out[5] = a11;out[6] = a12;out[7] = a13;
		  out[8] = a20;out[9] = a21;out[10] = a22;out[11] = a23;
		  out[12] = a00 * x + a10 * y + a20 * z + a[12];
		  out[13] = a01 * x + a11 * y + a21 * z + a[13];
		  out[14] = a02 * x + a12 * y + a22 * z + a[14];
		  out[15] = a03 * x + a13 * y + a23 * z + a[15];
		}
		return out;
	  }
	  function mat4_invert(out, a) {
		var a00 = a[0],
			a01 = a[1],
			a02 = a[2],
			a03 = a[3],
			a10 = a[4],
			a11 = a[5],
			a12 = a[6],
			a13 = a[7],
			a20 = a[8],
			a21 = a[9],
			a22 = a[10],
			a23 = a[11],
			a30 = a[12],
			a31 = a[13],
			a32 = a[14],
			a33 = a[15],
			b00 = a00 * a11 - a01 * a10,
			b01 = a00 * a12 - a02 * a10,
			b02 = a00 * a13 - a03 * a10,
			b03 = a01 * a12 - a02 * a11,
			b04 = a01 * a13 - a03 * a11,
			b05 = a02 * a13 - a03 * a12,
			b06 = a20 * a31 - a21 * a30,
			b07 = a20 * a32 - a22 * a30,
			b08 = a20 * a33 - a23 * a30,
			b09 = a21 * a32 - a22 * a31,
			b10 = a21 * a33 - a23 * a31,
			b11 = a22 * a33 - a23 * a32,
		det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
		if (!det) {
		  return null;
		}
		det = 1.0 / det;
		out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
		out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
		out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
		out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det;
		out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
		out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
		out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
		out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det;
		out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
		out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
		out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
		out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det;
		out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det;
		out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det;
		out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det;
		out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det;
		return out;
	  }
	  var defaultOrientation = new Float32Array([0, 0, 0, 1]);
	  var defaultPosition = new Float32Array([0, 0, 0]);
	  function updateEyeMatrices(projection, view, pose, fov, offset, vrDisplay) {
		mat4_perspectiveFromFieldOfView(projection, fov || null, vrDisplay.depthNear, vrDisplay.depthFar);
		var orientation = pose.orientation || defaultOrientation;
		var position = pose.position || defaultPosition;
		mat4_fromRotationTranslation(view, orientation, position);
		if (offset) mat4_translate(view, view, offset);
		mat4_invert(view, view);
	  }
	  return function (frameData, pose, vrDisplay) {
		if (!frameData || !pose) return false;
		frameData.pose = pose;
		frameData.timestamp = pose.timestamp;
		updateEyeMatrices(frameData.leftProjectionMatrix, frameData.leftViewMatrix, pose, vrDisplay._getFieldOfView("left"), vrDisplay._getEyeOffset("left"), vrDisplay);
		updateEyeMatrices(frameData.rightProjectionMatrix, frameData.rightViewMatrix, pose, vrDisplay._getFieldOfView("right"), vrDisplay._getEyeOffset("right"), vrDisplay);
		return true;
	  };
	}();
	var isInsideCrossOriginIFrame = function isInsideCrossOriginIFrame() {
	  var isFramed = window.self !== window.top;
	  var refOrigin = getOriginFromUrl(document.referrer);
	  var thisOrigin = getOriginFromUrl(window.location.href);
	  return isFramed && refOrigin !== thisOrigin;
	};
	var getOriginFromUrl = function getOriginFromUrl(url) {
	  var domainIdx;
	  var protoSepIdx = url.indexOf("://");
	  if (protoSepIdx !== -1) {
		domainIdx = protoSepIdx + 3;
	  } else {
		domainIdx = 0;
	  }
	  var domainEndIdx = url.indexOf('/', domainIdx);
	  if (domainEndIdx === -1) {
		domainEndIdx = url.length;
	  }
	  return url.substring(0, domainEndIdx);
	};
	var getQuaternionAngle = function getQuaternionAngle(quat) {
	  if (quat.w > 1) {
		console.warn('getQuaternionAngle: w > 1');
		return 0;
	  }
	  var angle = 2 * Math.acos(quat.w);
	  return angle;
	};
	var warnOnce = function () {
	  var observedWarnings = {};
	  return function (key, message) {
		if (observedWarnings[key] === undefined) {
		  console.warn('webvr-polyfill: ' + message);
		  observedWarnings[key] = true;
		}
	  };
	}();
	var deprecateWarning = function deprecateWarning(deprecated, suggested) {
	  var alternative = suggested ? 'Please use ' + suggested + ' instead.' : '';
	  warnOnce(deprecated, deprecated + ' has been deprecated. ' + 'This may not work on native WebVR displays. ' + alternative);
	};
	function WGLUPreserveGLState(gl, bindings, callback) {
	  if (!bindings) {
		callback(gl);
		return;
	  }
	  var boundValues = [];
	  var activeTexture = null;
	  for (var i = 0; i < bindings.length; ++i) {
		var binding = bindings[i];
		switch (binding) {
		  case gl.TEXTURE_BINDING_2D:
		  case gl.TEXTURE_BINDING_CUBE_MAP:
			var textureUnit = bindings[++i];
			if (textureUnit < gl.TEXTURE0 || textureUnit > gl.TEXTURE31) {
			  console.error("TEXTURE_BINDING_2D or TEXTURE_BINDING_CUBE_MAP must be followed by a valid texture unit");
			  boundValues.push(null, null);
			  break;
			}
			if (!activeTexture) {
			  activeTexture = gl.getParameter(gl.ACTIVE_TEXTURE);
			}
			gl.activeTexture(textureUnit);
			boundValues.push(gl.getParameter(binding), null);
			break;
		  case gl.ACTIVE_TEXTURE:
			activeTexture = gl.getParameter(gl.ACTIVE_TEXTURE);
			boundValues.push(null);
			break;
		  default:
			boundValues.push(gl.getParameter(binding));
			break;
		}
	  }
	  callback(gl);
	  for (var i = 0; i < bindings.length; ++i) {
		var binding = bindings[i];
		var boundValue = boundValues[i];
		switch (binding) {
		  case gl.ACTIVE_TEXTURE:
			break;
		  case gl.ARRAY_BUFFER_BINDING:
			gl.bindBuffer(gl.ARRAY_BUFFER, boundValue);
			break;
		  case gl.COLOR_CLEAR_VALUE:
			gl.clearColor(boundValue[0], boundValue[1], boundValue[2], boundValue[3]);
			break;
		  case gl.COLOR_WRITEMASK:
			gl.colorMask(boundValue[0], boundValue[1], boundValue[2], boundValue[3]);
			break;
		  case gl.CURRENT_PROGRAM:
			gl.useProgram(boundValue);
			break;
		  case gl.ELEMENT_ARRAY_BUFFER_BINDING:
			gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, boundValue);
			break;
		  case gl.FRAMEBUFFER_BINDING:
			gl.bindFramebuffer(gl.FRAMEBUFFER, boundValue);
			break;
		  case gl.RENDERBUFFER_BINDING:
			gl.bindRenderbuffer(gl.RENDERBUFFER, boundValue);
			break;
		  case gl.TEXTURE_BINDING_2D:
			var textureUnit = bindings[++i];
			if (textureUnit < gl.TEXTURE0 || textureUnit > gl.TEXTURE31)
			  break;
			gl.activeTexture(textureUnit);
			gl.bindTexture(gl.TEXTURE_2D, boundValue);
			break;
		  case gl.TEXTURE_BINDING_CUBE_MAP:
			var textureUnit = bindings[++i];
			if (textureUnit < gl.TEXTURE0 || textureUnit > gl.TEXTURE31)
			  break;
			gl.activeTexture(textureUnit);
			gl.bindTexture(gl.TEXTURE_CUBE_MAP, boundValue);
			break;
		  case gl.VIEWPORT:
			gl.viewport(boundValue[0], boundValue[1], boundValue[2], boundValue[3]);
			break;
		  case gl.BLEND:
		  case gl.CULL_FACE:
		  case gl.DEPTH_TEST:
		  case gl.SCISSOR_TEST:
		  case gl.STENCIL_TEST:
			if (boundValue) {
			  gl.enable(binding);
			} else {
			  gl.disable(binding);
			}
			break;
		  default:
			console.log("No GL restore behavior for 0x" + binding.toString(16));
			break;
		}
		if (activeTexture) {
		  gl.activeTexture(activeTexture);
		}
	  }
	}
	var glPreserveState = WGLUPreserveGLState;
	var distortionVS = ['attribute vec2 position;', 'attribute vec3 texCoord;', 'varying vec2 vTexCoord;', 'uniform vec4 viewportOffsetScale[2];', 'void main() {', '  vec4 viewport = viewportOffsetScale[int(texCoord.z)];', '  vTexCoord = (texCoord.xy * viewport.zw) + viewport.xy;', '  gl_Position = vec4( position, 1.0, 1.0 );', '}'].join('\n');
	var distortionFS = ['precision mediump float;', 'uniform sampler2D diffuse;', 'varying vec2 vTexCoord;', 'void main() {', '  gl_FragColor = texture2D(diffuse, vTexCoord);', '}'].join('\n');
	function CardboardDistorter(gl, cardboardUI, bufferScale, dirtySubmitFrameBindings) {
	  this.gl = gl;
	  this.cardboardUI = cardboardUI;
	  this.bufferScale = bufferScale;
	  this.dirtySubmitFrameBindings = dirtySubmitFrameBindings;
	  this.ctxAttribs = gl.getContextAttributes();
	  this.meshWidth = 20;
	  this.meshHeight = 20;
	  this.bufferWidth = gl.drawingBufferWidth;
	  this.bufferHeight = gl.drawingBufferHeight;
	  this.realBindFramebuffer = gl.bindFramebuffer;
	  this.realEnable = gl.enable;
	  this.realDisable = gl.disable;
	  this.realColorMask = gl.colorMask;
	  this.realClearColor = gl.clearColor;
	  this.realViewport = gl.viewport;
	  if (!isIOS()) {
		this.realCanvasWidth = Object.getOwnPropertyDescriptor(gl.canvas.__proto__, 'width');
		this.realCanvasHeight = Object.getOwnPropertyDescriptor(gl.canvas.__proto__, 'height');
	  }
	  this.isPatched = false;
	  this.lastBoundFramebuffer = null;
	  this.cullFace = false;
	  this.depthTest = false;
	  this.blend = false;
	  this.scissorTest = false;
	  this.stencilTest = false;
	  this.viewport = [0, 0, 0, 0];
	  this.colorMask = [true, true, true, true];
	  this.clearColor = [0, 0, 0, 0];
	  this.attribs = {
		position: 0,
		texCoord: 1
	  };
	  this.program = linkProgram(gl, distortionVS, distortionFS, this.attribs);
	  this.uniforms = getProgramUniforms(gl, this.program);
	  this.viewportOffsetScale = new Float32Array(8);
	  this.setTextureBounds();
	  this.vertexBuffer = gl.createBuffer();
	  this.indexBuffer = gl.createBuffer();
	  this.indexCount = 0;
	  this.renderTarget = gl.createTexture();
	  this.framebuffer = gl.createFramebuffer();
	  this.depthStencilBuffer = null;
	  this.depthBuffer = null;
	  this.stencilBuffer = null;
	  if (this.ctxAttribs.depth && this.ctxAttribs.stencil) {
		this.depthStencilBuffer = gl.createRenderbuffer();
	  } else if (this.ctxAttribs.depth) {
		this.depthBuffer = gl.createRenderbuffer();
	  } else if (this.ctxAttribs.stencil) {
		this.stencilBuffer = gl.createRenderbuffer();
	  }
	  this.patch();
	  this.onResize();
	}
	CardboardDistorter.prototype.destroy = function () {
	  var gl = this.gl;
	  this.unpatch();
	  gl.deleteProgram(this.program);
	  gl.deleteBuffer(this.vertexBuffer);
	  gl.deleteBuffer(this.indexBuffer);
	  gl.deleteTexture(this.renderTarget);
	  gl.deleteFramebuffer(this.framebuffer);
	  if (this.depthStencilBuffer) {
		gl.deleteRenderbuffer(this.depthStencilBuffer);
	  }
	  if (this.depthBuffer) {
		gl.deleteRenderbuffer(this.depthBuffer);
	  }
	  if (this.stencilBuffer) {
		gl.deleteRenderbuffer(this.stencilBuffer);
	  }
	  if (this.cardboardUI) {
		this.cardboardUI.destroy();
	  }
	};
	CardboardDistorter.prototype.onResize = function () {
	  var gl = this.gl;
	  var self = this;
	  var glState = [gl.RENDERBUFFER_BINDING, gl.TEXTURE_BINDING_2D, gl.TEXTURE0];
	  glPreserveState(gl, glState, function (gl) {
		self.realBindFramebuffer.call(gl, gl.FRAMEBUFFER, null);
		if (self.scissorTest) {
		  self.realDisable.call(gl, gl.SCISSOR_TEST);
		}
		self.realColorMask.call(gl, true, true, true, true);
		self.realViewport.call(gl, 0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
		self.realClearColor.call(gl, 0, 0, 0, 1);
		gl.clear(gl.COLOR_BUFFER_BIT);
		self.realBindFramebuffer.call(gl, gl.FRAMEBUFFER, self.framebuffer);
		gl.bindTexture(gl.TEXTURE_2D, self.renderTarget);
		gl.texImage2D(gl.TEXTURE_2D, 0, self.ctxAttribs.alpha ? gl.RGBA : gl.RGB, self.bufferWidth, self.bufferHeight, 0, self.ctxAttribs.alpha ? gl.RGBA : gl.RGB, gl.UNSIGNED_BYTE, null);
		gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
		gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
		gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
		gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
		gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, self.renderTarget, 0);
		if (self.ctxAttribs.depth && self.ctxAttribs.stencil) {
		  gl.bindRenderbuffer(gl.RENDERBUFFER, self.depthStencilBuffer);
		  gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, self.bufferWidth, self.bufferHeight);
		  gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, self.depthStencilBuffer);
		} else if (self.ctxAttribs.depth) {
		  gl.bindRenderbuffer(gl.RENDERBUFFER, self.depthBuffer);
		  gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, self.bufferWidth, self.bufferHeight);
		  gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, self.depthBuffer);
		} else if (self.ctxAttribs.stencil) {
		  gl.bindRenderbuffer(gl.RENDERBUFFER, self.stencilBuffer);
		  gl.renderbufferStorage(gl.RENDERBUFFER, gl.STENCIL_INDEX8, self.bufferWidth, self.bufferHeight);
		  gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, self.stencilBuffer);
		}
		if (!gl.checkFramebufferStatus(gl.FRAMEBUFFER) === gl.FRAMEBUFFER_COMPLETE) {
		  console.error('Framebuffer incomplete!');
		}
		self.realBindFramebuffer.call(gl, gl.FRAMEBUFFER, self.lastBoundFramebuffer);
		if (self.scissorTest) {
		  self.realEnable.call(gl, gl.SCISSOR_TEST);
		}
		self.realColorMask.apply(gl, self.colorMask);
		self.realViewport.apply(gl, self.viewport);
		self.realClearColor.apply(gl, self.clearColor);
	  });
	  if (this.cardboardUI) {
		this.cardboardUI.onResize();
	  }
	};
	CardboardDistorter.prototype.patch = function () {
	  if (this.isPatched) {
		return;
	  }
	  var self = this;
	  var canvas = this.gl.canvas;
	  var gl = this.gl;
	  if (!isIOS()) {
		canvas.width = getScreenWidth() * this.bufferScale;
		canvas.height = getScreenHeight() * this.bufferScale;
		Object.defineProperty(canvas, 'width', {
		  configurable: true,
		  enumerable: true,
		  get: function get() {
			return self.bufferWidth;
		  },
		  set: function set(value) {
			self.bufferWidth = value;
			self.realCanvasWidth.set.call(canvas, value);
			self.onResize();
		  }
		});
		Object.defineProperty(canvas, 'height', {
		  configurable: true,
		  enumerable: true,
		  get: function get() {
			return self.bufferHeight;
		  },
		  set: function set(value) {
			self.bufferHeight = value;
			self.realCanvasHeight.set.call(canvas, value);
			self.onResize();
		  }
		});
	  }
	  this.lastBoundFramebuffer = gl.getParameter(gl.FRAMEBUFFER_BINDING);
	  if (this.lastBoundFramebuffer == null) {
		this.lastBoundFramebuffer = this.framebuffer;
		this.gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
	  }
	  this.gl.bindFramebuffer = function (target, framebuffer) {
		self.lastBoundFramebuffer = framebuffer ? framebuffer : self.framebuffer;
		self.realBindFramebuffer.call(gl, target, self.lastBoundFramebuffer);
	  };
	  this.cullFace = gl.getParameter(gl.CULL_FACE);
	  this.depthTest = gl.getParameter(gl.DEPTH_TEST);
	  this.blend = gl.getParameter(gl.BLEND);
	  this.scissorTest = gl.getParameter(gl.SCISSOR_TEST);
	  this.stencilTest = gl.getParameter(gl.STENCIL_TEST);
	  gl.enable = function (pname) {
		switch (pname) {
		  case gl.CULL_FACE:
			self.cullFace = true;break;
		  case gl.DEPTH_TEST:
			self.depthTest = true;break;
		  case gl.BLEND:
			self.blend = true;break;
		  case gl.SCISSOR_TEST:
			self.scissorTest = true;break;
		  case gl.STENCIL_TEST:
			self.stencilTest = true;break;
		}
		self.realEnable.call(gl, pname);
	  };
	  gl.disable = function (pname) {
		switch (pname) {
		  case gl.CULL_FACE:
			self.cullFace = false;break;
		  case gl.DEPTH_TEST:
			self.depthTest = false;break;
		  case gl.BLEND:
			self.blend = false;break;
		  case gl.SCISSOR_TEST:
			self.scissorTest = false;break;
		  case gl.STENCIL_TEST:
			self.stencilTest = false;break;
		}
		self.realDisable.call(gl, pname);
	  };
	  this.colorMask = gl.getParameter(gl.COLOR_WRITEMASK);
	  gl.colorMask = function (r, g, b, a) {
		self.colorMask[0] = r;
		self.colorMask[1] = g;
		self.colorMask[2] = b;
		self.colorMask[3] = a;
		self.realColorMask.call(gl, r, g, b, a);
	  };
	  this.clearColor = gl.getParameter(gl.COLOR_CLEAR_VALUE);
	  gl.clearColor = function (r, g, b, a) {
		self.clearColor[0] = r;
		self.clearColor[1] = g;
		self.clearColor[2] = b;
		self.clearColor[3] = a;
		self.realClearColor.call(gl, r, g, b, a);
	  };
	  this.viewport = gl.getParameter(gl.VIEWPORT);
	  gl.viewport = function (x, y, w, h) {
		self.viewport[0] = x;
		self.viewport[1] = y;
		self.viewport[2] = w;
		self.viewport[3] = h;
		self.realViewport.call(gl, x, y, w, h);
	  };
	  this.isPatched = true;
	  safariCssSizeWorkaround(canvas);
	};
	CardboardDistorter.prototype.unpatch = function () {
	  if (!this.isPatched) {
		return;
	  }
	  var gl = this.gl;
	  var canvas = this.gl.canvas;
	  if (!isIOS()) {
		Object.defineProperty(canvas, 'width', this.realCanvasWidth);
		Object.defineProperty(canvas, 'height', this.realCanvasHeight);
	  }
	  canvas.width = this.bufferWidth;
	  canvas.height = this.bufferHeight;
	  gl.bindFramebuffer = this.realBindFramebuffer;
	  gl.enable = this.realEnable;
	  gl.disable = this.realDisable;
	  gl.colorMask = this.realColorMask;
	  gl.clearColor = this.realClearColor;
	  gl.viewport = this.realViewport;
	  if (this.lastBoundFramebuffer == this.framebuffer) {
		gl.bindFramebuffer(gl.FRAMEBUFFER, null);
	  }
	  this.isPatched = false;
	  setTimeout(function () {
		safariCssSizeWorkaround(canvas);
	  }, 1);
	};
	CardboardDistorter.prototype.setTextureBounds = function (leftBounds, rightBounds) {
	  if (!leftBounds) {
		leftBounds = [0, 0, 0.5, 1];
	  }
	  if (!rightBounds) {
		rightBounds = [0.5, 0, 0.5, 1];
	  }
	  this.viewportOffsetScale[0] = leftBounds[0];
	  this.viewportOffsetScale[1] = leftBounds[1];
	  this.viewportOffsetScale[2] = leftBounds[2];
	  this.viewportOffsetScale[3] = leftBounds[3];
	  this.viewportOffsetScale[4] = rightBounds[0];
	  this.viewportOffsetScale[5] = rightBounds[1];
	  this.viewportOffsetScale[6] = rightBounds[2];
	  this.viewportOffsetScale[7] = rightBounds[3];
	};
	CardboardDistorter.prototype.submitFrame = function () {
	  var gl = this.gl;
	  var self = this;
	  var glState = [];
	  if (!this.dirtySubmitFrameBindings) {
		glState.push(gl.CURRENT_PROGRAM, gl.ARRAY_BUFFER_BINDING, gl.ELEMENT_ARRAY_BUFFER_BINDING, gl.TEXTURE_BINDING_2D, gl.TEXTURE0);
	  }
	  glPreserveState(gl, glState, function (gl) {
		self.realBindFramebuffer.call(gl, gl.FRAMEBUFFER, null);
		if (self.cullFace) {
		  self.realDisable.call(gl, gl.CULL_FACE);
		}
		if (self.depthTest) {
		  self.realDisable.call(gl, gl.DEPTH_TEST);
		}
		if (self.blend) {
		  self.realDisable.call(gl, gl.BLEND);
		}
		if (self.scissorTest) {
		  self.realDisable.call(gl, gl.SCISSOR_TEST);
		}
		if (self.stencilTest) {
		  self.realDisable.call(gl, gl.STENCIL_TEST);
		}
		self.realColorMask.call(gl, true, true, true, true);
		self.realViewport.call(gl, 0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
		if (self.ctxAttribs.alpha || isIOS()) {
		  self.realClearColor.call(gl, 0, 0, 0, 1);
		  gl.clear(gl.COLOR_BUFFER_BIT);
		}
		gl.useProgram(self.program);
		gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, self.indexBuffer);
		gl.bindBuffer(gl.ARRAY_BUFFER, self.vertexBuffer);
		gl.enableVertexAttribArray(self.attribs.position);
		gl.enableVertexAttribArray(self.attribs.texCoord);
		gl.vertexAttribPointer(self.attribs.position, 2, gl.FLOAT, false, 20, 0);
		gl.vertexAttribPointer(self.attribs.texCoord, 3, gl.FLOAT, false, 20, 8);
		gl.activeTexture(gl.TEXTURE0);
		gl.uniform1i(self.uniforms.diffuse, 0);
		gl.bindTexture(gl.TEXTURE_2D, self.renderTarget);
		gl.uniform4fv(self.uniforms.viewportOffsetScale, self.viewportOffsetScale);
		gl.drawElements(gl.TRIANGLES, self.indexCount, gl.UNSIGNED_SHORT, 0);
		if (self.cardboardUI) {
		  self.cardboardUI.renderNoState();
		}
		self.realBindFramebuffer.call(self.gl, gl.FRAMEBUFFER, self.framebuffer);
		if (!self.ctxAttribs.preserveDrawingBuffer) {
		  self.realClearColor.call(gl, 0, 0, 0, 0);
		  gl.clear(gl.COLOR_BUFFER_BIT);
		}
		if (!self.dirtySubmitFrameBindings) {
		  self.realBindFramebuffer.call(gl, gl.FRAMEBUFFER, self.lastBoundFramebuffer);
		}
		if (self.cullFace) {
		  self.realEnable.call(gl, gl.CULL_FACE);
		}
		if (self.depthTest) {
		  self.realEnable.call(gl, gl.DEPTH_TEST);
		}
		if (self.blend) {
		  self.realEnable.call(gl, gl.BLEND);
		}
		if (self.scissorTest) {
		  self.realEnable.call(gl, gl.SCISSOR_TEST);
		}
		if (self.stencilTest) {
		  self.realEnable.call(gl, gl.STENCIL_TEST);
		}
		self.realColorMask.apply(gl, self.colorMask);
		self.realViewport.apply(gl, self.viewport);
		if (self.ctxAttribs.alpha || !self.ctxAttribs.preserveDrawingBuffer) {
		  self.realClearColor.apply(gl, self.clearColor);
		}
	  });
	  if (isIOS()) {
		var canvas = gl.canvas;
		if (canvas.width != self.bufferWidth || canvas.height != self.bufferHeight) {
		  self.bufferWidth = canvas.width;
		  self.bufferHeight = canvas.height;
		  self.onResize();
		}
	  }
	};
	CardboardDistorter.prototype.updateDeviceInfo = function (deviceInfo) {
	  var gl = this.gl;
	  var self = this;
	  var glState = [gl.ARRAY_BUFFER_BINDING, gl.ELEMENT_ARRAY_BUFFER_BINDING];
	  glPreserveState(gl, glState, function (gl) {
		var vertices = self.computeMeshVertices_(self.meshWidth, self.meshHeight, deviceInfo);
		gl.bindBuffer(gl.ARRAY_BUFFER, self.vertexBuffer);
		gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
		if (!self.indexCount) {
		  var indices = self.computeMeshIndices_(self.meshWidth, self.meshHeight);
		  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, self.indexBuffer);
		  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
		  self.indexCount = indices.length;
		}
	  });
	};
	CardboardDistorter.prototype.computeMeshVertices_ = function (width, height, deviceInfo) {
	  var vertices = new Float32Array(2 * width * height * 5);
	  var lensFrustum = deviceInfo.getLeftEyeVisibleTanAngles();
	  var noLensFrustum = deviceInfo.getLeftEyeNoLensTanAngles();
	  var viewport = deviceInfo.getLeftEyeVisibleScreenRect(noLensFrustum);
	  var vidx = 0;
	  for (var e = 0; e < 2; e++) {
		for (var j = 0; j < height; j++) {
		  for (var i = 0; i < width; i++, vidx++) {
			var u = i / (width - 1);
			var v = j / (height - 1);
			var s = u;
			var t = v;
			var x = lerp(lensFrustum[0], lensFrustum[2], u);
			var y = lerp(lensFrustum[3], lensFrustum[1], v);
			var d = Math.sqrt(x * x + y * y);
			var r = deviceInfo.distortion.distortInverse(d);
			var p = x * r / d;
			var q = y * r / d;
			u = (p - noLensFrustum[0]) / (noLensFrustum[2] - noLensFrustum[0]);
			v = (q - noLensFrustum[3]) / (noLensFrustum[1] - noLensFrustum[3]);
			u = (viewport.x + u * viewport.width - 0.5) * 2.0;
			v = (viewport.y + v * viewport.height - 0.5) * 2.0;
			vertices[vidx * 5 + 0] = u;
			vertices[vidx * 5 + 1] = v;
			vertices[vidx * 5 + 2] = s;
			vertices[vidx * 5 + 3] = t;
			vertices[vidx * 5 + 4] = e;
		  }
		}
		var w = lensFrustum[2] - lensFrustum[0];
		lensFrustum[0] = -(w + lensFrustum[0]);
		lensFrustum[2] = w - lensFrustum[2];
		w = noLensFrustum[2] - noLensFrustum[0];
		noLensFrustum[0] = -(w + noLensFrustum[0]);
		noLensFrustum[2] = w - noLensFrustum[2];
		viewport.x = 1 - (viewport.x + viewport.width);
	  }
	  return vertices;
	};
	CardboardDistorter.prototype.computeMeshIndices_ = function (width, height) {
	  var indices = new Uint16Array(2 * (width - 1) * (height - 1) * 6);
	  var halfwidth = width / 2;
	  var halfheight = height / 2;
	  var vidx = 0;
	  var iidx = 0;
	  for (var e = 0; e < 2; e++) {
		for (var j = 0; j < height; j++) {
		  for (var i = 0; i < width; i++, vidx++) {
			if (i == 0 || j == 0) continue;
			if (i <= halfwidth == j <= halfheight) {
			  indices[iidx++] = vidx;
			  indices[iidx++] = vidx - width - 1;
			  indices[iidx++] = vidx - width;
			  indices[iidx++] = vidx - width - 1;
			  indices[iidx++] = vidx;
			  indices[iidx++] = vidx - 1;
			} else {
			  indices[iidx++] = vidx - 1;
			  indices[iidx++] = vidx - width;
			  indices[iidx++] = vidx;
			  indices[iidx++] = vidx - width;
			  indices[iidx++] = vidx - 1;
			  indices[iidx++] = vidx - width - 1;
			}
		  }
		}
	  }
	  return indices;
	};
	CardboardDistorter.prototype.getOwnPropertyDescriptor_ = function (proto, attrName) {
	  var descriptor = Object.getOwnPropertyDescriptor(proto, attrName);
	  if (descriptor.get === undefined || descriptor.set === undefined) {
		descriptor.configurable = true;
		descriptor.enumerable = true;
		descriptor.get = function () {
		  return this.getAttribute(attrName);
		};
		descriptor.set = function (val) {
		  this.setAttribute(attrName, val);
		};
	  }
	  return descriptor;
	};
	var uiVS = ['attribute vec2 position;', 'uniform mat4 projectionMat;', 'void main() {', '  gl_Position = projectionMat * vec4( position, -1.0, 1.0 );', '}'].join('\n');
	var uiFS = ['precision mediump float;', 'uniform vec4 color;', 'void main() {', '  gl_FragColor = color;', '}'].join('\n');
	var DEG2RAD = Math.PI / 180.0;
	var kAnglePerGearSection = 60;
	var kOuterRimEndAngle = 12;
	var kInnerRimBeginAngle = 20;
	var kOuterRadius = 1;
	var kMiddleRadius = 0.75;
	var kInnerRadius = 0.3125;
	var kCenterLineThicknessDp = 4;
	var kButtonWidthDp = 28;
	var kTouchSlopFactor = 1.5;
	function CardboardUI(gl) {
	  this.gl = gl;
	  this.attribs = {
		position: 0
	  };
	  this.program = linkProgram(gl, uiVS, uiFS, this.attribs);
	  this.uniforms = getProgramUniforms(gl, this.program);
	  this.vertexBuffer = gl.createBuffer();
	  this.gearOffset = 0;
	  this.gearVertexCount = 0;
	  this.arrowOffset = 0;
	  this.arrowVertexCount = 0;
	  this.projMat = new Float32Array(16);
	  this.listener = null;
	  this.onResize();
	}
	CardboardUI.prototype.destroy = function () {
	  var gl = this.gl;
	  if (this.listener) {
		gl.canvas.removeEventListener('click', this.listener, false);
	  }
	  gl.deleteProgram(this.program);
	  gl.deleteBuffer(this.vertexBuffer);
	};
	CardboardUI.prototype.listen = function (optionsCallback, backCallback) {
	  var canvas = this.gl.canvas;
	  this.listener = function (event) {
		var midline = canvas.clientWidth / 2;
		var buttonSize = kButtonWidthDp * kTouchSlopFactor;
		if (event.clientX > midline - buttonSize && event.clientX < midline + buttonSize && event.clientY > canvas.clientHeight - buttonSize) {
		  optionsCallback(event);
		}
		else if (event.clientX < buttonSize && event.clientY < buttonSize) {
			backCallback(event);
		  }
	  };
	  canvas.addEventListener('click', this.listener, false);
	};
	CardboardUI.prototype.onResize = function () {
	  var gl = this.gl;
	  var self = this;
	  var glState = [gl.ARRAY_BUFFER_BINDING];
	  glPreserveState(gl, glState, function (gl) {
		var vertices = [];
		var midline = gl.drawingBufferWidth / 2;
		var physicalPixels = Math.max(screen.width, screen.height) * window.devicePixelRatio;
		var scalingRatio = gl.drawingBufferWidth / physicalPixels;
		var dps = scalingRatio * window.devicePixelRatio;
		var lineWidth = kCenterLineThicknessDp * dps / 2;
		var buttonSize = kButtonWidthDp * kTouchSlopFactor * dps;
		var buttonScale = kButtonWidthDp * dps / 2;
		var buttonBorder = (kButtonWidthDp * kTouchSlopFactor - kButtonWidthDp) * dps;
		vertices.push(midline - lineWidth, buttonSize);
		vertices.push(midline - lineWidth, gl.drawingBufferHeight);
		vertices.push(midline + lineWidth, buttonSize);
		vertices.push(midline + lineWidth, gl.drawingBufferHeight);
		self.gearOffset = vertices.length / 2;
		function addGearSegment(theta, r) {
		  var angle = (90 - theta) * DEG2RAD;
		  var x = Math.cos(angle);
		  var y = Math.sin(angle);
		  vertices.push(kInnerRadius * x * buttonScale + midline, kInnerRadius * y * buttonScale + buttonScale);
		  vertices.push(r * x * buttonScale + midline, r * y * buttonScale + buttonScale);
		}
		for (var i = 0; i <= 6; i++) {
		  var segmentTheta = i * kAnglePerGearSection;
		  addGearSegment(segmentTheta, kOuterRadius);
		  addGearSegment(segmentTheta + kOuterRimEndAngle, kOuterRadius);
		  addGearSegment(segmentTheta + kInnerRimBeginAngle, kMiddleRadius);
		  addGearSegment(segmentTheta + (kAnglePerGearSection - kInnerRimBeginAngle), kMiddleRadius);
		  addGearSegment(segmentTheta + (kAnglePerGearSection - kOuterRimEndAngle), kOuterRadius);
		}
		self.gearVertexCount = vertices.length / 2 - self.gearOffset;
		self.arrowOffset = vertices.length / 2;
		function addArrowVertex(x, y) {
		  vertices.push(buttonBorder + x, gl.drawingBufferHeight - buttonBorder - y);
		}
		var angledLineWidth = lineWidth / Math.sin(45 * DEG2RAD);
		addArrowVertex(0, buttonScale);
		addArrowVertex(buttonScale, 0);
		addArrowVertex(buttonScale + angledLineWidth, angledLineWidth);
		addArrowVertex(angledLineWidth, buttonScale + angledLineWidth);
		addArrowVertex(angledLineWidth, buttonScale - angledLineWidth);
		addArrowVertex(0, buttonScale);
		addArrowVertex(buttonScale, buttonScale * 2);
		addArrowVertex(buttonScale + angledLineWidth, buttonScale * 2 - angledLineWidth);
		addArrowVertex(angledLineWidth, buttonScale - angledLineWidth);
		addArrowVertex(0, buttonScale);
		addArrowVertex(angledLineWidth, buttonScale - lineWidth);
		addArrowVertex(kButtonWidthDp * dps, buttonScale - lineWidth);
		addArrowVertex(angledLineWidth, buttonScale + lineWidth);
		addArrowVertex(kButtonWidthDp * dps, buttonScale + lineWidth);
		self.arrowVertexCount = vertices.length / 2 - self.arrowOffset;
		gl.bindBuffer(gl.ARRAY_BUFFER, self.vertexBuffer);
		gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
	  });
	};
	CardboardUI.prototype.render = function () {
	  var gl = this.gl;
	  var self = this;
	  var glState = [gl.CULL_FACE, gl.DEPTH_TEST, gl.BLEND, gl.SCISSOR_TEST, gl.STENCIL_TEST, gl.COLOR_WRITEMASK, gl.VIEWPORT, gl.CURRENT_PROGRAM, gl.ARRAY_BUFFER_BINDING];
	  glPreserveState(gl, glState, function (gl) {
		gl.disable(gl.CULL_FACE);
		gl.disable(gl.DEPTH_TEST);
		gl.disable(gl.BLEND);
		gl.disable(gl.SCISSOR_TEST);
		gl.disable(gl.STENCIL_TEST);
		gl.colorMask(true, true, true, true);
		gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
		self.renderNoState();
	  });
	};
	CardboardUI.prototype.renderNoState = function () {
	  var gl = this.gl;
	  gl.useProgram(this.program);
	  gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
	  gl.enableVertexAttribArray(this.attribs.position);
	  gl.vertexAttribPointer(this.attribs.position, 2, gl.FLOAT, false, 8, 0);
	  gl.uniform4f(this.uniforms.color, 1.0, 1.0, 1.0, 1.0);
	  orthoMatrix(this.projMat, 0, gl.drawingBufferWidth, 0, gl.drawingBufferHeight, 0.1, 1024.0);
	  gl.uniformMatrix4fv(this.uniforms.projectionMat, false, this.projMat);
	  gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
	  gl.drawArrays(gl.TRIANGLE_STRIP, this.gearOffset, this.gearVertexCount);
	  gl.drawArrays(gl.TRIANGLE_STRIP, this.arrowOffset, this.arrowVertexCount);
	};
	function Distortion(coefficients) {
	  this.coefficients = coefficients;
	}
	Distortion.prototype.distortInverse = function (radius) {
	  var r0 = 0;
	  var r1 = 1;
	  var dr0 = radius - this.distort(r0);
	  while (Math.abs(r1 - r0) > 0.0001             ) {
		var dr1 = radius - this.distort(r1);
		var r2 = r1 - dr1 * ((r1 - r0) / (dr1 - dr0));
		r0 = r1;
		r1 = r2;
		dr0 = dr1;
	  }
	  return r1;
	};
	Distortion.prototype.distort = function (radius) {
	  var r2 = radius * radius;
	  var ret = 0;
	  for (var i = 0; i < this.coefficients.length; i++) {
		ret = r2 * (ret + this.coefficients[i]);
	  }
	  return (ret + 1) * radius;
	};
	var degToRad = Math.PI / 180;
	var radToDeg = 180 / Math.PI;
	var Vector3 = function Vector3(x, y, z) {
	  this.x = x || 0;
	  this.y = y || 0;
	  this.z = z || 0;
	};
	Vector3.prototype = {
	  constructor: Vector3,
	  set: function set(x, y, z) {
		this.x = x;
		this.y = y;
		this.z = z;
		return this;
	  },
	  copy: function copy(v) {
		this.x = v.x;
		this.y = v.y;
		this.z = v.z;
		return this;
	  },
	  length: function length() {
		return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
	  },
	  normalize: function normalize() {
		var scalar = this.length();
		if (scalar !== 0) {
		  var invScalar = 1 / scalar;
		  this.multiplyScalar(invScalar);
		} else {
		  this.x = 0;
		  this.y = 0;
		  this.z = 0;
		}
		return this;
	  },
	  multiplyScalar: function multiplyScalar(scalar) {
		this.x *= scalar;
		this.y *= scalar;
		this.z *= scalar;
	  },
	  applyQuaternion: function applyQuaternion(q) {
		var x = this.x;
		var y = this.y;
		var z = this.z;
		var qx = q.x;
		var qy = q.y;
		var qz = q.z;
		var qw = q.w;
		var ix = qw * x + qy * z - qz * y;
		var iy = qw * y + qz * x - qx * z;
		var iz = qw * z + qx * y - qy * x;
		var iw = -qx * x - qy * y - qz * z;
		this.x = ix * qw + iw * -qx + iy * -qz - iz * -qy;
		this.y = iy * qw + iw * -qy + iz * -qx - ix * -qz;
		this.z = iz * qw + iw * -qz + ix * -qy - iy * -qx;
		return this;
	  },
	  dot: function dot(v) {
		return this.x * v.x + this.y * v.y + this.z * v.z;
	  },
	  crossVectors: function crossVectors(a, b) {
		var ax = a.x,
			ay = a.y,
			az = a.z;
		var bx = b.x,
			by = b.y,
			bz = b.z;
		this.x = ay * bz - az * by;
		this.y = az * bx - ax * bz;
		this.z = ax * by - ay * bx;
		return this;
	  }
	};
	var Quaternion = function Quaternion(x, y, z, w) {
	  this.x = x || 0;
	  this.y = y || 0;
	  this.z = z || 0;
	  this.w = w !== undefined ? w : 1;
	};
	Quaternion.prototype = {
	  constructor: Quaternion,
	  set: function set(x, y, z, w) {
		this.x = x;
		this.y = y;
		this.z = z;
		this.w = w;
		return this;
	  },
	  copy: function copy(quaternion) {
		this.x = quaternion.x;
		this.y = quaternion.y;
		this.z = quaternion.z;
		this.w = quaternion.w;
		return this;
	  },
	  setFromEulerXYZ: function setFromEulerXYZ(x, y, z) {
		var c1 = Math.cos(x / 2);
		var c2 = Math.cos(y / 2);
		var c3 = Math.cos(z / 2);
		var s1 = Math.sin(x / 2);
		var s2 = Math.sin(y / 2);
		var s3 = Math.sin(z / 2);
		this.x = s1 * c2 * c3 + c1 * s2 * s3;
		this.y = c1 * s2 * c3 - s1 * c2 * s3;
		this.z = c1 * c2 * s3 + s1 * s2 * c3;
		this.w = c1 * c2 * c3 - s1 * s2 * s3;
		return this;
	  },
	  setFromEulerYXZ: function setFromEulerYXZ(x, y, z) {
		var c1 = Math.cos(x / 2);
		var c2 = Math.cos(y / 2);
		var c3 = Math.cos(z / 2);
		var s1 = Math.sin(x / 2);
		var s2 = Math.sin(y / 2);
		var s3 = Math.sin(z / 2);
		this.x = s1 * c2 * c3 + c1 * s2 * s3;
		this.y = c1 * s2 * c3 - s1 * c2 * s3;
		this.z = c1 * c2 * s3 - s1 * s2 * c3;
		this.w = c1 * c2 * c3 + s1 * s2 * s3;
		return this;
	  },
	  setFromAxisAngle: function setFromAxisAngle(axis, angle) {
		var halfAngle = angle / 2,
			s = Math.sin(halfAngle);
		this.x = axis.x * s;
		this.y = axis.y * s;
		this.z = axis.z * s;
		this.w = Math.cos(halfAngle);
		return this;
	  },
	  multiply: function multiply(q) {
		return this.multiplyQuaternions(this, q);
	  },
	  multiplyQuaternions: function multiplyQuaternions(a, b) {
		var qax = a.x,
			qay = a.y,
			qaz = a.z,
			qaw = a.w;
		var qbx = b.x,
			qby = b.y,
			qbz = b.z,
			qbw = b.w;
		this.x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby;
		this.y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz;
		this.z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx;
		this.w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz;
		return this;
	  },
	  inverse: function inverse() {
		this.x *= -1;
		this.y *= -1;
		this.z *= -1;
		this.normalize();
		return this;
	  },
	  normalize: function normalize() {
		var l = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);
		if (l === 0) {
		  this.x = 0;
		  this.y = 0;
		  this.z = 0;
		  this.w = 1;
		} else {
		  l = 1 / l;
		  this.x = this.x * l;
		  this.y = this.y * l;
		  this.z = this.z * l;
		  this.w = this.w * l;
		}
		return this;
	  },
	  slerp: function slerp(qb, t) {
		if (t === 0) return this;
		if (t === 1) return this.copy(qb);
		var x = this.x,
			y = this.y,
			z = this.z,
			w = this.w;
		var cosHalfTheta = w * qb.w + x * qb.x + y * qb.y + z * qb.z;
		if (cosHalfTheta < 0) {
		  this.w = -qb.w;
		  this.x = -qb.x;
		  this.y = -qb.y;
		  this.z = -qb.z;
		  cosHalfTheta = -cosHalfTheta;
		} else {
		  this.copy(qb);
		}
		if (cosHalfTheta >= 1.0) {
		  this.w = w;
		  this.x = x;
		  this.y = y;
		  this.z = z;
		  return this;
		}
		var halfTheta = Math.acos(cosHalfTheta);
		var sinHalfTheta = Math.sqrt(1.0 - cosHalfTheta * cosHalfTheta);
		if (Math.abs(sinHalfTheta) < 0.001) {
		  this.w = 0.5 * (w + this.w);
		  this.x = 0.5 * (x + this.x);
		  this.y = 0.5 * (y + this.y);
		  this.z = 0.5 * (z + this.z);
		  return this;
		}
		var ratioA = Math.sin((1 - t) * halfTheta) / sinHalfTheta,
			ratioB = Math.sin(t * halfTheta) / sinHalfTheta;
		this.w = w * ratioA + this.w * ratioB;
		this.x = x * ratioA + this.x * ratioB;
		this.y = y * ratioA + this.y * ratioB;
		this.z = z * ratioA + this.z * ratioB;
		return this;
	  },
	  setFromUnitVectors: function () {
		var v1, r;
		var EPS = 0.000001;
		return function (vFrom, vTo) {
		  if (v1 === undefined) v1 = new Vector3();
		  r = vFrom.dot(vTo) + 1;
		  if (r < EPS) {
			r = 0;
			if (Math.abs(vFrom.x) > Math.abs(vFrom.z)) {
			  v1.set(-vFrom.y, vFrom.x, 0);
			} else {
			  v1.set(0, -vFrom.z, vFrom.y);
			}
		  } else {
			v1.crossVectors(vFrom, vTo);
		  }
		  this.x = v1.x;
		  this.y = v1.y;
		  this.z = v1.z;
		  this.w = r;
		  this.normalize();
		  return this;
		};
	  }()
	};
	function Device(params) {
	  this.width = params.width || getScreenWidth();
	  this.height = params.height || getScreenHeight();
	  this.widthMeters = params.widthMeters;
	  this.heightMeters = params.heightMeters;
	  this.bevelMeters = params.bevelMeters;
	}
	var DEFAULT_ANDROID = new Device({
	  widthMeters: 0.110,
	  heightMeters: 0.062,
	  bevelMeters: 0.004
	});
	var DEFAULT_IOS = new Device({
	  widthMeters: 0.1038,
	  heightMeters: 0.0584,
	  bevelMeters: 0.004
	});
	var Viewers = {
	  CardboardV1: new CardboardViewer({
		id: 'CardboardV1',
		label: 'Cardboard I/O 2014',
		fov: 40,
		interLensDistance: 0.060,
		baselineLensDistance: 0.035,
		screenLensDistance: 0.042,
		distortionCoefficients: [0.441, 0.156],
		inverseCoefficients: [-0.4410035, 0.42756155, -0.4804439, 0.5460139, -0.58821183, 0.5733938, -0.48303202, 0.33299083, -0.17573841, 0.0651772, -0.01488963, 0.001559834]
	  }),
	  CardboardV2: new CardboardViewer({
		id: 'CardboardV2',
		label: 'Cardboard I/O 2015',
		fov: 60,
		interLensDistance: 0.064,
		baselineLensDistance: 0.035,
		screenLensDistance: 0.039,
		distortionCoefficients: [0.34, 0.55],
		inverseCoefficients: [-0.33836704, -0.18162185, 0.862655, -1.2462051, 1.0560602, -0.58208317, 0.21609078, -0.05444823, 0.009177956, -9.904169E-4, 6.183535E-5, -1.6981803E-6]
	  })
	};
	function DeviceInfo(deviceParams, additionalViewers) {
	  this.viewer = Viewers.CardboardV2;
	  this.updateDeviceParams(deviceParams);
	  this.distortion = new Distortion(this.viewer.distortionCoefficients);
	  for (var i = 0; i < additionalViewers.length; i++) {
		var viewer = additionalViewers[i];
		Viewers[viewer.id] = new CardboardViewer(viewer);
	  }
	}
	DeviceInfo.prototype.updateDeviceParams = function (deviceParams) {
	  this.device = this.determineDevice_(deviceParams) || this.device;
	};
	DeviceInfo.prototype.getDevice = function () {
	  return this.device;
	};
	DeviceInfo.prototype.setViewer = function (viewer) {
	  this.viewer = viewer;
	  this.distortion = new Distortion(this.viewer.distortionCoefficients);
	};
	DeviceInfo.prototype.determineDevice_ = function (deviceParams) {
	  if (!deviceParams) {
		if (isIOS()) {
		  console.warn('Using fallback iOS device measurements.');
		  return DEFAULT_IOS;
		} else {
		  console.warn('Using fallback Android device measurements.');
		  return DEFAULT_ANDROID;
		}
	  }
	  var METERS_PER_INCH = 0.0254;
	  var metersPerPixelX = METERS_PER_INCH / deviceParams.xdpi;
	  var metersPerPixelY = METERS_PER_INCH / deviceParams.ydpi;
	  var width = getScreenWidth();
	  var height = getScreenHeight();
	  return new Device({
		widthMeters: metersPerPixelX * width,
		heightMeters: metersPerPixelY * height,
		bevelMeters: deviceParams.bevelMm * 0.001
	  });
	};
	DeviceInfo.prototype.getDistortedFieldOfViewLeftEye = function () {
	  var viewer = this.viewer;
	  var device = this.device;
	  var distortion = this.distortion;
	  var eyeToScreenDistance = viewer.screenLensDistance;
	  var outerDist = (device.widthMeters - viewer.interLensDistance) / 2;
	  var innerDist = viewer.interLensDistance / 2;
	  var bottomDist = viewer.baselineLensDistance - device.bevelMeters;
	  var topDist = device.heightMeters - bottomDist;
	  var outerAngle = radToDeg * Math.atan(distortion.distort(outerDist / eyeToScreenDistance));
	  var innerAngle = radToDeg * Math.atan(distortion.distort(innerDist / eyeToScreenDistance));
	  var bottomAngle = radToDeg * Math.atan(distortion.distort(bottomDist / eyeToScreenDistance));
	  var topAngle = radToDeg * Math.atan(distortion.distort(topDist / eyeToScreenDistance));
	  return {
		leftDegrees: Math.min(outerAngle, viewer.fov),
		rightDegrees: Math.min(innerAngle, viewer.fov),
		downDegrees: Math.min(bottomAngle, viewer.fov),
		upDegrees: Math.min(topAngle, viewer.fov)
	  };
	};
	DeviceInfo.prototype.getLeftEyeVisibleTanAngles = function () {
	  var viewer = this.viewer;
	  var device = this.device;
	  var distortion = this.distortion;
	  var fovLeft = Math.tan(-degToRad * viewer.fov);
	  var fovTop = Math.tan(degToRad * viewer.fov);
	  var fovRight = Math.tan(degToRad * viewer.fov);
	  var fovBottom = Math.tan(-degToRad * viewer.fov);
	  var halfWidth = device.widthMeters / 4;
	  var halfHeight = device.heightMeters / 2;
	  var verticalLensOffset = viewer.baselineLensDistance - device.bevelMeters - halfHeight;
	  var centerX = viewer.interLensDistance / 2 - halfWidth;
	  var centerY = -verticalLensOffset;
	  var centerZ = viewer.screenLensDistance;
	  var screenLeft = distortion.distort((centerX - halfWidth) / centerZ);
	  var screenTop = distortion.distort((centerY + halfHeight) / centerZ);
	  var screenRight = distortion.distort((centerX + halfWidth) / centerZ);
	  var screenBottom = distortion.distort((centerY - halfHeight) / centerZ);
	  var result = new Float32Array(4);
	  result[0] = Math.max(fovLeft, screenLeft);
	  result[1] = Math.min(fovTop, screenTop);
	  result[2] = Math.min(fovRight, screenRight);
	  result[3] = Math.max(fovBottom, screenBottom);
	  return result;
	};
	DeviceInfo.prototype.getLeftEyeNoLensTanAngles = function () {
	  var viewer = this.viewer;
	  var device = this.device;
	  var distortion = this.distortion;
	  var result = new Float32Array(4);
	  var fovLeft = distortion.distortInverse(Math.tan(-degToRad * viewer.fov));
	  var fovTop = distortion.distortInverse(Math.tan(degToRad * viewer.fov));
	  var fovRight = distortion.distortInverse(Math.tan(degToRad * viewer.fov));
	  var fovBottom = distortion.distortInverse(Math.tan(-degToRad * viewer.fov));
	  var halfWidth = device.widthMeters / 4;
	  var halfHeight = device.heightMeters / 2;
	  var verticalLensOffset = viewer.baselineLensDistance - device.bevelMeters - halfHeight;
	  var centerX = viewer.interLensDistance / 2 - halfWidth;
	  var centerY = -verticalLensOffset;
	  var centerZ = viewer.screenLensDistance;
	  var screenLeft = (centerX - halfWidth) / centerZ;
	  var screenTop = (centerY + halfHeight) / centerZ;
	  var screenRight = (centerX + halfWidth) / centerZ;
	  var screenBottom = (centerY - halfHeight) / centerZ;
	  result[0] = Math.max(fovLeft, screenLeft);
	  result[1] = Math.min(fovTop, screenTop);
	  result[2] = Math.min(fovRight, screenRight);
	  result[3] = Math.max(fovBottom, screenBottom);
	  return result;
	};
	DeviceInfo.prototype.getLeftEyeVisibleScreenRect = function (undistortedFrustum) {
	  var viewer = this.viewer;
	  var device = this.device;
	  var dist = viewer.screenLensDistance;
	  var eyeX = (device.widthMeters - viewer.interLensDistance) / 2;
	  var eyeY = viewer.baselineLensDistance - device.bevelMeters;
	  var left = (undistortedFrustum[0] * dist + eyeX) / device.widthMeters;
	  var top = (undistortedFrustum[1] * dist + eyeY) / device.heightMeters;
	  var right = (undistortedFrustum[2] * dist + eyeX) / device.widthMeters;
	  var bottom = (undistortedFrustum[3] * dist + eyeY) / device.heightMeters;
	  return {
		x: left,
		y: bottom,
		width: right - left,
		height: top - bottom
	  };
	};
	DeviceInfo.prototype.getFieldOfViewLeftEye = function (opt_isUndistorted) {
	  return opt_isUndistorted ? this.getUndistortedFieldOfViewLeftEye() : this.getDistortedFieldOfViewLeftEye();
	};
	DeviceInfo.prototype.getFieldOfViewRightEye = function (opt_isUndistorted) {
	  var fov = this.getFieldOfViewLeftEye(opt_isUndistorted);
	  return {
		leftDegrees: fov.rightDegrees,
		rightDegrees: fov.leftDegrees,
		upDegrees: fov.upDegrees,
		downDegrees: fov.downDegrees
	  };
	};
	DeviceInfo.prototype.getUndistortedFieldOfViewLeftEye = function () {
	  var p = this.getUndistortedParams_();
	  return {
		leftDegrees: radToDeg * Math.atan(p.outerDist),
		rightDegrees: radToDeg * Math.atan(p.innerDist),
		downDegrees: radToDeg * Math.atan(p.bottomDist),
		upDegrees: radToDeg * Math.atan(p.topDist)
	  };
	};
	DeviceInfo.prototype.getUndistortedViewportLeftEye = function () {
	  var p = this.getUndistortedParams_();
	  var viewer = this.viewer;
	  var device = this.device;
	  var eyeToScreenDistance = viewer.screenLensDistance;
	  var screenWidth = device.widthMeters / eyeToScreenDistance;
	  var screenHeight = device.heightMeters / eyeToScreenDistance;
	  var xPxPerTanAngle = device.width / screenWidth;
	  var yPxPerTanAngle = device.height / screenHeight;
	  var x = Math.round((p.eyePosX - p.outerDist) * xPxPerTanAngle);
	  var y = Math.round((p.eyePosY - p.bottomDist) * yPxPerTanAngle);
	  return {
		x: x,
		y: y,
		width: Math.round((p.eyePosX + p.innerDist) * xPxPerTanAngle) - x,
		height: Math.round((p.eyePosY + p.topDist) * yPxPerTanAngle) - y
	  };
	};
	DeviceInfo.prototype.getUndistortedParams_ = function () {
	  var viewer = this.viewer;
	  var device = this.device;
	  var distortion = this.distortion;
	  var eyeToScreenDistance = viewer.screenLensDistance;
	  var halfLensDistance = viewer.interLensDistance / 2 / eyeToScreenDistance;
	  var screenWidth = device.widthMeters / eyeToScreenDistance;
	  var screenHeight = device.heightMeters / eyeToScreenDistance;
	  var eyePosX = screenWidth / 2 - halfLensDistance;
	  var eyePosY = (viewer.baselineLensDistance - device.bevelMeters) / eyeToScreenDistance;
	  var maxFov = viewer.fov;
	  var viewerMax = distortion.distortInverse(Math.tan(degToRad * maxFov));
	  var outerDist = Math.min(eyePosX, viewerMax);
	  var innerDist = Math.min(halfLensDistance, viewerMax);
	  var bottomDist = Math.min(eyePosY, viewerMax);
	  var topDist = Math.min(screenHeight - eyePosY, viewerMax);
	  return {
		outerDist: outerDist,
		innerDist: innerDist,
		topDist: topDist,
		bottomDist: bottomDist,
		eyePosX: eyePosX,
		eyePosY: eyePosY
	  };
	};
	function CardboardViewer(params) {
	  this.id = params.id;
	  this.label = params.label;
	  this.fov = params.fov;
	  this.interLensDistance = params.interLensDistance;
	  this.baselineLensDistance = params.baselineLensDistance;
	  this.screenLensDistance = params.screenLensDistance;
	  this.distortionCoefficients = params.distortionCoefficients;
	  this.inverseCoefficients = params.inverseCoefficients;
	}
	DeviceInfo.Viewers = Viewers;
	var format = 1;
	var last_updated = "2018-02-20T22:55:10Z";
	var devices = [{"type":"android","rules":[{"mdmh":"asus/*/Nexus 7/*"},{"ua":"Nexus 7"}],"dpi":[320.8,323],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"asus/*/ASUS_Z00AD/*"},{"ua":"ASUS_Z00AD"}],"dpi":[403,404.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Google/*/Pixel XL/*"},{"ua":"Pixel XL"}],"dpi":[537.9,533],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Google/*/Pixel/*"},{"ua":"Pixel"}],"dpi":[432.6,436.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"HTC/*/HTC6435LVW/*"},{"ua":"HTC6435LVW"}],"dpi":[449.7,443.3],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"HTC/*/HTC One XL/*"},{"ua":"HTC One XL"}],"dpi":[315.3,314.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"htc/*/Nexus 9/*"},{"ua":"Nexus 9"}],"dpi":289,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"HTC/*/HTC One M9/*"},{"ua":"HTC One M9"}],"dpi":[442.5,443.3],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"HTC/*/HTC One_M8/*"},{"ua":"HTC One_M8"}],"dpi":[449.7,447.4],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"HTC/*/HTC One/*"},{"ua":"HTC One"}],"dpi":472.8,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Huawei/*/Nexus 6P/*"},{"ua":"Nexus 6P"}],"dpi":[515.1,518],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LENOVO/*/Lenovo PB2-690Y/*"},{"ua":"Lenovo PB2-690Y"}],"dpi":[457.2,454.713],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"LGE/*/Nexus 5X/*"},{"ua":"Nexus 5X"}],"dpi":[422,419.9],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LGE/*/LGMS345/*"},{"ua":"LGMS345"}],"dpi":[221.7,219.1],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"LGE/*/LG-D800/*"},{"ua":"LG-D800"}],"dpi":[422,424.1],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"LGE/*/LG-D850/*"},{"ua":"LG-D850"}],"dpi":[537.9,541.9],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"LGE/*/VS985 4G/*"},{"ua":"VS985 4G"}],"dpi":[537.9,535.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LGE/*/Nexus 5/*"},{"ua":"Nexus 5 B"}],"dpi":[442.4,444.8],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LGE/*/Nexus 4/*"},{"ua":"Nexus 4"}],"dpi":[319.8,318.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LGE/*/LG-P769/*"},{"ua":"LG-P769"}],"dpi":[240.6,247.5],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LGE/*/LGMS323/*"},{"ua":"LGMS323"}],"dpi":[206.6,204.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"LGE/*/LGLS996/*"},{"ua":"LGLS996"}],"dpi":[403.4,401.5],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Micromax/*/4560MMX/*"},{"ua":"4560MMX"}],"dpi":[240,219.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Micromax/*/A250/*"},{"ua":"Micromax A250"}],"dpi":[480,446.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Micromax/*/Micromax AQ4501/*"},{"ua":"Micromax AQ4501"}],"dpi":240,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"motorola/*/G5/*"},{"ua":"Moto G (5) Plus"}],"dpi":[403.4,403],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/DROID RAZR/*"},{"ua":"DROID RAZR"}],"dpi":[368.1,256.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/XT830C/*"},{"ua":"XT830C"}],"dpi":[254,255.9],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/XT1021/*"},{"ua":"XT1021"}],"dpi":[254,256.7],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"motorola/*/XT1023/*"},{"ua":"XT1023"}],"dpi":[254,256.7],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"motorola/*/XT1028/*"},{"ua":"XT1028"}],"dpi":[326.6,327.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/XT1034/*"},{"ua":"XT1034"}],"dpi":[326.6,328.4],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"motorola/*/XT1053/*"},{"ua":"XT1053"}],"dpi":[315.3,316.1],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/XT1562/*"},{"ua":"XT1562"}],"dpi":[403.4,402.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/Nexus 6/*"},{"ua":"Nexus 6 B"}],"dpi":[494.3,489.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/XT1063/*"},{"ua":"XT1063"}],"dpi":[295,296.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/XT1064/*"},{"ua":"XT1064"}],"dpi":[295,295.6],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"motorola/*/XT1092/*"},{"ua":"XT1092"}],"dpi":[422,424.1],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"motorola/*/XT1095/*"},{"ua":"XT1095"}],"dpi":[422,423.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"motorola/*/G4/*"},{"ua":"Moto G (4)"}],"dpi":401,"bw":4,"ac":1000},{"type":"android","rules":[{"mdmh":"OnePlus/*/A0001/*"},{"ua":"A0001"}],"dpi":[403.4,401],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"OnePlus/*/ONE E1005/*"},{"ua":"ONE E1005"}],"dpi":[442.4,441.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"OnePlus/*/ONE A2005/*"},{"ua":"ONE A2005"}],"dpi":[391.9,405.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"OnePlus/*/ONEPLUS A5000/*"},{"ua":"ONEPLUS A5000 "}],"dpi":[403.411,399.737],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"OnePlus/*/ONE A5010/*"},{"ua":"ONEPLUS A5010"}],"dpi":[403,400],"bw":2,"ac":1000},{"type":"android","rules":[{"mdmh":"OPPO/*/X909/*"},{"ua":"X909"}],"dpi":[442.4,444.1],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9082/*"},{"ua":"GT-I9082"}],"dpi":[184.7,185.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G360P/*"},{"ua":"SM-G360P"}],"dpi":[196.7,205.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/Nexus S/*"},{"ua":"Nexus S"}],"dpi":[234.5,229.8],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9300/*"},{"ua":"GT-I9300"}],"dpi":[304.8,303.9],"bw":5,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-T230NU/*"},{"ua":"SM-T230NU"}],"dpi":216,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SGH-T399/*"},{"ua":"SGH-T399"}],"dpi":[217.7,231.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SGH-M919/*"},{"ua":"SGH-M919"}],"dpi":[440.8,437.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-N9005/*"},{"ua":"SM-N9005"}],"dpi":[386.4,387],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SAMSUNG-SM-N900A/*"},{"ua":"SAMSUNG-SM-N900A"}],"dpi":[386.4,387.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9500/*"},{"ua":"GT-I9500"}],"dpi":[442.5,443.3],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9505/*"},{"ua":"GT-I9505"}],"dpi":439.4,"bw":4,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G900F/*"},{"ua":"SM-G900F"}],"dpi":[415.6,431.6],"bw":5,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G900M/*"},{"ua":"SM-G900M"}],"dpi":[415.6,431.6],"bw":5,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G800F/*"},{"ua":"SM-G800F"}],"dpi":326.8,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G906S/*"},{"ua":"SM-G906S"}],"dpi":[562.7,572.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9300/*"},{"ua":"GT-I9300"}],"dpi":[306.7,304.8],"bw":5,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-T535/*"},{"ua":"SM-T535"}],"dpi":[142.6,136.4],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-N920C/*"},{"ua":"SM-N920C"}],"dpi":[515.1,518.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-N920P/*"},{"ua":"SM-N920P"}],"dpi":[386.3655,390.144],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-N920W8/*"},{"ua":"SM-N920W8"}],"dpi":[515.1,518.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9300I/*"},{"ua":"GT-I9300I"}],"dpi":[304.8,305.8],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-I9195/*"},{"ua":"GT-I9195"}],"dpi":[249.4,256.7],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SPH-L520/*"},{"ua":"SPH-L520"}],"dpi":[249.4,255.9],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SAMSUNG-SGH-I717/*"},{"ua":"SAMSUNG-SGH-I717"}],"dpi":285.8,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SPH-D710/*"},{"ua":"SPH-D710"}],"dpi":[217.7,204.2],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/GT-N7100/*"},{"ua":"GT-N7100"}],"dpi":265.1,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SCH-I605/*"},{"ua":"SCH-I605"}],"dpi":265.1,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/Galaxy Nexus/*"},{"ua":"Galaxy Nexus"}],"dpi":[315.3,314.2],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-N910H/*"},{"ua":"SM-N910H"}],"dpi":[515.1,518],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-N910C/*"},{"ua":"SM-N910C"}],"dpi":[515.2,520.2],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G130M/*"},{"ua":"SM-G130M"}],"dpi":[165.9,164.8],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G928I/*"},{"ua":"SM-G928I"}],"dpi":[515.1,518.4],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G920F/*"},{"ua":"SM-G920F"}],"dpi":580.6,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G920P/*"},{"ua":"SM-G920P"}],"dpi":[522.5,577],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G925F/*"},{"ua":"SM-G925F"}],"dpi":580.6,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G925V/*"},{"ua":"SM-G925V"}],"dpi":[522.5,576.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G930F/*"},{"ua":"SM-G930F"}],"dpi":576.6,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G935F/*"},{"ua":"SM-G935F"}],"dpi":533,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G950F/*"},{"ua":"SM-G950F"}],"dpi":[562.707,565.293],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"samsung/*/SM-G955U/*"},{"ua":"SM-G955U"}],"dpi":[522.514,525.762],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"Sony/*/C6903/*"},{"ua":"C6903"}],"dpi":[442.5,443.3],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"Sony/*/D6653/*"},{"ua":"D6653"}],"dpi":[428.6,427.6],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Sony/*/E6653/*"},{"ua":"E6653"}],"dpi":[428.6,425.7],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Sony/*/E6853/*"},{"ua":"E6853"}],"dpi":[403.4,401.9],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Sony/*/SGP321/*"},{"ua":"SGP321"}],"dpi":[224.7,224.1],"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"TCT/*/ALCATEL ONE TOUCH Fierce/*"},{"ua":"ALCATEL ONE TOUCH Fierce"}],"dpi":[240,247.5],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"THL/*/thl 5000/*"},{"ua":"thl 5000"}],"dpi":[480,443.3],"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"Fly/*/IQ4412/*"},{"ua":"IQ4412"}],"dpi":307.9,"bw":3,"ac":1000},{"type":"android","rules":[{"mdmh":"ZTE/*/ZTE Blade L2/*"},{"ua":"ZTE Blade L2"}],"dpi":240,"bw":3,"ac":500},{"type":"android","rules":[{"mdmh":"BENEVE/*/VR518/*"},{"ua":"VR518"}],"dpi":480,"bw":3,"ac":500},{"type":"ios","rules":[{"res":[640,960]}],"dpi":[325.1,328.4],"bw":4,"ac":1000},{"type":"ios","rules":[{"res":[640,1136]}],"dpi":[317.1,320.2],"bw":3,"ac":1000},{"type":"ios","rules":[{"res":[750,1334]}],"dpi":326.4,"bw":4,"ac":1000},{"type":"ios","rules":[{"res":[1242,2208]}],"dpi":[453.6,458.4],"bw":4,"ac":1000},{"type":"ios","rules":[{"res":[1125,2001]}],"dpi":[410.9,415.4],"bw":4,"ac":1000},{"type":"ios","rules":[{"res":[1125,2436]}],"dpi":458,"bw":4,"ac":1000}];
	var DPDB_CACHE = {
		format: format,
		last_updated: last_updated,
		devices: devices
	};
	function Dpdb(url, onDeviceParamsUpdated) {
	  this.dpdb = DPDB_CACHE;
	  this.recalculateDeviceParams_();
	  if (url) {
		this.onDeviceParamsUpdated = onDeviceParamsUpdated;
		var xhr = new XMLHttpRequest();
		var obj = this;
		xhr.open('GET', url, true);
		xhr.addEventListener('load', function () {
		  obj.loading = false;
		  if (xhr.status >= 200 && xhr.status <= 299) {
			obj.dpdb = JSON.parse(xhr.response);
			obj.recalculateDeviceParams_();
		  } else {
			console.error('Error loading online DPDB!');
		  }
		});
		xhr.send();
	  }
	}
	Dpdb.prototype.getDeviceParams = function () {
	  return this.deviceParams;
	};
	Dpdb.prototype.recalculateDeviceParams_ = function () {
	  var newDeviceParams = this.calcDeviceParams_();
	  if (newDeviceParams) {
		this.deviceParams = newDeviceParams;
		if (this.onDeviceParamsUpdated) {
		  this.onDeviceParamsUpdated(this.deviceParams);
		}
	  } else {
		console.error('Failed to recalculate device parameters.');
	  }
	};
	Dpdb.prototype.calcDeviceParams_ = function () {
	  var db = this.dpdb;
	  if (!db) {
		console.error('DPDB not available.');
		return null;
	  }
	  if (db.format != 1) {
		console.error('DPDB has unexpected format version.');
		return null;
	  }
	  if (!db.devices || !db.devices.length) {
		console.error('DPDB does not have a devices section.');
		return null;
	  }
	  var userAgent = navigator.userAgent || navigator.vendor || window.opera;
	  var width = getScreenWidth();
	  var height = getScreenHeight();
	  if (!db.devices) {
		console.error('DPDB has no devices section.');
		return null;
	  }
	  for (var i = 0; i < db.devices.length; i++) {
		var device = db.devices[i];
		if (!device.rules) {
		  console.warn('Device[' + i + '] has no rules section.');
		  continue;
		}
		if (device.type != 'ios' && device.type != 'android') {
		  console.warn('Device[' + i + '] has invalid type.');
		  continue;
		}
		if (isIOS() != (device.type == 'ios')) continue;
		var matched = false;
		for (var j = 0; j < device.rules.length; j++) {
		  var rule = device.rules[j];
		  if (this.matchRule_(rule, userAgent, width, height)) {
			matched = true;
			break;
		  }
		}
		if (!matched) continue;
		var xdpi = device.dpi[0] || device.dpi;
		var ydpi = device.dpi[1] || device.dpi;
		return new DeviceParams({ xdpi: xdpi, ydpi: ydpi, bevelMm: device.bw });
	  }
	  console.warn('No DPDB device match.');
	  return null;
	};
	Dpdb.prototype.matchRule_ = function (rule, ua, screenWidth, screenHeight) {
	  if (!rule.ua && !rule.res) return false;
	  if (rule.ua && ua.indexOf(rule.ua) < 0) return false;
	  if (rule.res) {
		if (!rule.res[0] || !rule.res[1]) return false;
		var resX = rule.res[0];
		var resY = rule.res[1];
		if (Math.min(screenWidth, screenHeight) != Math.min(resX, resY) || Math.max(screenWidth, screenHeight) != Math.max(resX, resY)) {
		  return false;
		}
	  }
	  return true;
	};
	function DeviceParams(params) {
	  this.xdpi = params.xdpi;
	  this.ydpi = params.ydpi;
	  this.bevelMm = params.bevelMm;
	}
	function SensorSample(sample, timestampS) {
	  this.set(sample, timestampS);
	}
	SensorSample.prototype.set = function (sample, timestampS) {
	  this.sample = sample;
	  this.timestampS = timestampS;
	};
	SensorSample.prototype.copy = function (sensorSample) {
	  this.set(sensorSample.sample, sensorSample.timestampS);
	};
	function ComplementaryFilter(kFilter, isDebug) {
	  this.kFilter = kFilter;
	  this.isDebug = isDebug;
	  this.currentAccelMeasurement = new SensorSample();
	  this.currentGyroMeasurement = new SensorSample();
	  this.previousGyroMeasurement = new SensorSample();
	  if (isIOS()) {
		this.filterQ = new Quaternion(-1, 0, 0, 1);
	  } else {
		this.filterQ = new Quaternion(1, 0, 0, 1);
	  }
	  this.previousFilterQ = new Quaternion();
	  this.previousFilterQ.copy(this.filterQ);
	  this.accelQ = new Quaternion();
	  this.isOrientationInitialized = false;
	  this.estimatedGravity = new Vector3();
	  this.measuredGravity = new Vector3();
	  this.gyroIntegralQ = new Quaternion();
	}
	ComplementaryFilter.prototype.addAccelMeasurement = function (vector, timestampS) {
	  this.currentAccelMeasurement.set(vector, timestampS);
	};
	ComplementaryFilter.prototype.addGyroMeasurement = function (vector, timestampS) {
	  this.currentGyroMeasurement.set(vector, timestampS);
	  var deltaT = timestampS - this.previousGyroMeasurement.timestampS;
	  if (isTimestampDeltaValid(deltaT)) {
		this.run_();
	  }
	  this.previousGyroMeasurement.copy(this.currentGyroMeasurement);
	};
	ComplementaryFilter.prototype.run_ = function () {
	  if (!this.isOrientationInitialized) {
		this.accelQ = this.accelToQuaternion_(this.currentAccelMeasurement.sample);
		this.previousFilterQ.copy(this.accelQ);
		this.isOrientationInitialized = true;
		return;
	  }
	  var deltaT = this.currentGyroMeasurement.timestampS - this.previousGyroMeasurement.timestampS;
	  var gyroDeltaQ = this.gyroToQuaternionDelta_(this.currentGyroMeasurement.sample, deltaT);
	  this.gyroIntegralQ.multiply(gyroDeltaQ);
	  this.filterQ.copy(this.previousFilterQ);
	  this.filterQ.multiply(gyroDeltaQ);
	  var invFilterQ = new Quaternion();
	  invFilterQ.copy(this.filterQ);
	  invFilterQ.inverse();
	  this.estimatedGravity.set(0, 0, -1);
	  this.estimatedGravity.applyQuaternion(invFilterQ);
	  this.estimatedGravity.normalize();
	  this.measuredGravity.copy(this.currentAccelMeasurement.sample);
	  this.measuredGravity.normalize();
	  var deltaQ = new Quaternion();
	  deltaQ.setFromUnitVectors(this.estimatedGravity, this.measuredGravity);
	  deltaQ.inverse();
	  if (this.isDebug) {
		console.log('Delta: %d deg, G_est: (%s, %s, %s), G_meas: (%s, %s, %s)', radToDeg * getQuaternionAngle(deltaQ), this.estimatedGravity.x.toFixed(1), this.estimatedGravity.y.toFixed(1), this.estimatedGravity.z.toFixed(1), this.measuredGravity.x.toFixed(1), this.measuredGravity.y.toFixed(1), this.measuredGravity.z.toFixed(1));
	  }
	  var targetQ = new Quaternion();
	  targetQ.copy(this.filterQ);
	  targetQ.multiply(deltaQ);
	  this.filterQ.slerp(targetQ, 1 - this.kFilter);
	  this.previousFilterQ.copy(this.filterQ);
	};
	ComplementaryFilter.prototype.getOrientation = function () {
	  return this.filterQ;
	};
	ComplementaryFilter.prototype.accelToQuaternion_ = function (accel) {
	  var normAccel = new Vector3();
	  normAccel.copy(accel);
	  normAccel.normalize();
	  var quat = new Quaternion();
	  quat.setFromUnitVectors(new Vector3(0, 0, -1), normAccel);
	  quat.inverse();
	  return quat;
	};
	ComplementaryFilter.prototype.gyroToQuaternionDelta_ = function (gyro, dt) {
	  var quat = new Quaternion();
	  var axis = new Vector3();
	  axis.copy(gyro);
	  axis.normalize();
	  quat.setFromAxisAngle(axis, gyro.length() * dt);
	  return quat;
	};
	function PosePredictor(predictionTimeS, isDebug) {
	  this.predictionTimeS = predictionTimeS;
	  this.isDebug = isDebug;
	  this.previousQ = new Quaternion();
	  this.previousTimestampS = null;
	  this.deltaQ = new Quaternion();
	  this.outQ = new Quaternion();
	}
	PosePredictor.prototype.getPrediction = function (currentQ, gyro, timestampS) {
	  if (!this.previousTimestampS) {
		this.previousQ.copy(currentQ);
		this.previousTimestampS = timestampS;
		return currentQ;
	  }
	  var axis = new Vector3();
	  axis.copy(gyro);
	  axis.normalize();
	  var angularSpeed = gyro.length();
	  if (angularSpeed < degToRad * 20) {
		if (this.isDebug) {
		  console.log('Moving slowly, at %s deg/s: no prediction', (radToDeg * angularSpeed).toFixed(1));
		}
		this.outQ.copy(currentQ);
		this.previousQ.copy(currentQ);
		return this.outQ;
	  }
	  var predictAngle = angularSpeed * this.predictionTimeS;
	  this.deltaQ.setFromAxisAngle(axis, predictAngle);
	  this.outQ.copy(this.previousQ);
	  this.outQ.multiply(this.deltaQ);
	  this.previousQ.copy(currentQ);
	  this.previousTimestampS = timestampS;
	  return this.outQ;
	};
	function FusionPoseSensor(kFilter, predictionTime, yawOnly, isDebug) {
	  this.yawOnly = yawOnly;
	  this.accelerometer = new Vector3();
	  this.gyroscope = new Vector3();
	  this.filter = new ComplementaryFilter(kFilter, isDebug);
	  this.posePredictor = new PosePredictor(predictionTime, isDebug);
	  this.isFirefoxAndroid = isFirefoxAndroid();
	  this.isIOS = isIOS();
	  var chromeVersion = getChromeVersion();
	  this.isDeviceMotionInRadians = !this.isIOS && chromeVersion && chromeVersion < 66;
	  this.isWithoutDeviceMotion = isChromeWithoutDeviceMotion();
	  this.filterToWorldQ = new Quaternion();
	  if (isIOS()) {
		this.filterToWorldQ.setFromAxisAngle(new Vector3(1, 0, 0), Math.PI / 2);
	  } else {
		this.filterToWorldQ.setFromAxisAngle(new Vector3(1, 0, 0), -Math.PI / 2);
	  }
	  this.inverseWorldToScreenQ = new Quaternion();
	  this.worldToScreenQ = new Quaternion();
	  this.originalPoseAdjustQ = new Quaternion();
	  this.originalPoseAdjustQ.setFromAxisAngle(new Vector3(0, 0, 1), -window.orientation * Math.PI / 180);
	  this.setScreenTransform_();
	  if (isLandscapeMode()) {
		this.filterToWorldQ.multiply(this.inverseWorldToScreenQ);
	  }
	  this.resetQ = new Quaternion();
	  this.orientationOut_ = new Float32Array(4);
	  this.start();
	}
	FusionPoseSensor.prototype.getPosition = function () {
	  return null;
	};
	FusionPoseSensor.prototype.getOrientation = function () {
	  var orientation = void 0;
	  if (this.isWithoutDeviceMotion && this._deviceOrientationQ) {
		this.deviceOrientationFixQ = this.deviceOrientationFixQ || function () {
		  var z = new Quaternion().setFromAxisAngle(new Vector3(0, 0, -1), 0);
		  var y = new Quaternion();
		  if (window.orientation === -90) {
			y.setFromAxisAngle(new Vector3(0, 1, 0), Math.PI / -2);
		  } else {
			y.setFromAxisAngle(new Vector3(0, 1, 0), Math.PI / 2);
		  }
		  return z.multiply(y);
		}();
		this.deviceOrientationFilterToWorldQ = this.deviceOrientationFilterToWorldQ || function () {
		  var q = new Quaternion();
		  q.setFromAxisAngle(new Vector3(1, 0, 0), -Math.PI / 2);
		  return q;
		}();
		orientation = this._deviceOrientationQ;
		var out = new Quaternion();
		out.copy(orientation);
		out.multiply(this.deviceOrientationFilterToWorldQ);
		out.multiply(this.resetQ);
		out.multiply(this.worldToScreenQ);
		out.multiplyQuaternions(this.deviceOrientationFixQ, out);
		if (this.yawOnly) {
		  out.x = 0;
		  out.z = 0;
		  out.normalize();
		}
		this.orientationOut_[0] = out.x;
		this.orientationOut_[1] = out.y;
		this.orientationOut_[2] = out.z;
		this.orientationOut_[3] = out.w;
		return this.orientationOut_;
	  } else {
		var filterOrientation = this.filter.getOrientation();
		orientation = this.posePredictor.getPrediction(filterOrientation, this.gyroscope, this.previousTimestampS);
	  }
	  var out = new Quaternion();
	  out.copy(this.filterToWorldQ);
	  out.multiply(this.resetQ);
	  out.multiply(orientation);
	  out.multiply(this.worldToScreenQ);
	  if (this.yawOnly) {
		out.x = 0;
		out.z = 0;
		out.normalize();
	  }
	  this.orientationOut_[0] = out.x;
	  this.orientationOut_[1] = out.y;
	  this.orientationOut_[2] = out.z;
	  this.orientationOut_[3] = out.w;
	  return this.orientationOut_;
	};
	FusionPoseSensor.prototype.resetPose = function () {
	  this.resetQ.copy(this.filter.getOrientation());
	  this.resetQ.x = 0;
	  this.resetQ.y = 0;
	  this.resetQ.z *= -1;
	  this.resetQ.normalize();
	  if (isLandscapeMode()) {
		this.resetQ.multiply(this.inverseWorldToScreenQ);
	  }
	  this.resetQ.multiply(this.originalPoseAdjustQ);
	};
	FusionPoseSensor.prototype.onDeviceOrientation_ = function (e) {
	  this._deviceOrientationQ = this._deviceOrientationQ || new Quaternion();
	  var alpha = e.alpha,
		  beta = e.beta,
		  gamma = e.gamma;
	  alpha = (alpha || 0) * Math.PI / 180;
	  beta = (beta || 0) * Math.PI / 180;
	  gamma = (gamma || 0) * Math.PI / 180;
	  this._deviceOrientationQ.setFromEulerYXZ(beta, alpha, -gamma);
	};
	FusionPoseSensor.prototype.onDeviceMotion_ = function (deviceMotion) {
	  this.updateDeviceMotion_(deviceMotion);
	};
	FusionPoseSensor.prototype.updateDeviceMotion_ = function (deviceMotion) {
	  var accGravity = deviceMotion.accelerationIncludingGravity;
	  var rotRate = deviceMotion.rotationRate;
	  var timestampS = deviceMotion.timeStamp / 1000;
	  var deltaS = timestampS - this.previousTimestampS;
	  if (deltaS < 0) {
		warnOnce('fusion-pose-sensor:invalid:non-monotonic', 'Invalid timestamps detected: non-monotonic timestamp from devicemotion');
		this.previousTimestampS = timestampS;
		return;
	  } else if (deltaS <= MIN_TIMESTEP || deltaS > MAX_TIMESTEP) {
		warnOnce('fusion-pose-sensor:invalid:outside-threshold', 'Invalid timestamps detected: Timestamp from devicemotion outside expected range.');
		this.previousTimestampS = timestampS;
		return;
	  }
	  this.accelerometer.set(-accGravity.x, -accGravity.y, -accGravity.z);
	  if (isR7()) {
		this.gyroscope.set(-rotRate.beta, rotRate.alpha, rotRate.gamma);
	  } else {
		this.gyroscope.set(rotRate.alpha, rotRate.beta, rotRate.gamma);
	  }
	  if (!this.isDeviceMotionInRadians) {
		this.gyroscope.multiplyScalar(Math.PI / 180);
	  }
	  this.filter.addAccelMeasurement(this.accelerometer, timestampS);
	  this.filter.addGyroMeasurement(this.gyroscope, timestampS);
	  this.previousTimestampS = timestampS;
	};
	FusionPoseSensor.prototype.onOrientationChange_ = function (screenOrientation) {
	  this.setScreenTransform_();
	};
	FusionPoseSensor.prototype.onMessage_ = function (event) {
	  var message = event.data;
	  if (!message || !message.type) {
		return;
	  }
	  var type = message.type.toLowerCase();
	  if (type !== 'devicemotion') {
		return;
	  }
	  this.updateDeviceMotion_(message.deviceMotionEvent);
	};
	FusionPoseSensor.prototype.setScreenTransform_ = function () {
	  this.worldToScreenQ.set(0, 0, 0, 1);
	  switch (window.orientation) {
		case 0:
		  break;
		case 90:
		  this.worldToScreenQ.setFromAxisAngle(new Vector3(0, 0, 1), -Math.PI / 2);
		  break;
		case -90:
		  this.worldToScreenQ.setFromAxisAngle(new Vector3(0, 0, 1), Math.PI / 2);
		  break;
	  }
	  this.inverseWorldToScreenQ.copy(this.worldToScreenQ);
	  this.inverseWorldToScreenQ.inverse();
	};
	FusionPoseSensor.prototype.start = function () {
	  this.onDeviceMotionCallback_ = this.onDeviceMotion_.bind(this);
	  this.onOrientationChangeCallback_ = this.onOrientationChange_.bind(this);
	  this.onMessageCallback_ = this.onMessage_.bind(this);
	  this.onDeviceOrientationCallback_ = this.onDeviceOrientation_.bind(this);
	  if (isIOS() && isInsideCrossOriginIFrame()) {
		window.addEventListener('message', this.onMessageCallback_);
	  }
	  window.addEventListener('orientationchange', this.onOrientationChangeCallback_);
	  if (this.isWithoutDeviceMotion) {
		window.addEventListener('deviceorientation', this.onDeviceOrientationCallback_);
	  } else {
		window.addEventListener('devicemotion', this.onDeviceMotionCallback_);
	  }
	};
	FusionPoseSensor.prototype.stop = function () {
	  window.removeEventListener('devicemotion', this.onDeviceMotionCallback_);
	  window.removeEventListener('deviceorientation', this.onDeviceOrientationCallback_);
	  window.removeEventListener('orientationchange', this.onOrientationChangeCallback_);
	  window.removeEventListener('message', this.onMessageCallback_);
	};
	var SENSOR_FREQUENCY = 60;
	var X_AXIS = new Vector3(1, 0, 0);
	var Z_AXIS = new Vector3(0, 0, 1);
	var orientation = {};
	if (screen.orientation) {
	  orientation = screen.orientation;
	} else if (screen.msOrientation) {
	  orientation = screen.msOrientation;
	} else {
	  Object.defineProperty(orientation, 'angle', {
		get: function get$$1() {
		  return window.orientation || 0;
		}
	  });
	}
	var SENSOR_TO_VR = new Quaternion();
	SENSOR_TO_VR.setFromAxisAngle(X_AXIS, -Math.PI / 2);
	SENSOR_TO_VR.multiply(new Quaternion().setFromAxisAngle(Z_AXIS, Math.PI / 2));
	var PoseSensor = function () {
	  function PoseSensor(config) {
		classCallCheck(this, PoseSensor);
		this.config = config;
		this.sensor = null;
		this.fusionSensor = null;
		this._out = new Float32Array(4);
		this.api = null;
		this.errors = [];
		this._sensorQ = new Quaternion();
		this._worldToScreenQ = new Quaternion();
		this._outQ = new Quaternion();
		this._onSensorRead = this._onSensorRead.bind(this);
		this._onSensorError = this._onSensorError.bind(this);
		this._onOrientationChange = this._onOrientationChange.bind(this);
		this._onOrientationChange();
		this.init();
	  }
	  createClass(PoseSensor, [{
		key: 'init',
		value: function init() {
		  var sensor = null;
		  try {
			sensor = new RelativeOrientationSensor({ frequency: SENSOR_FREQUENCY });
			sensor.addEventListener('error', this._onSensorError);
		  } catch (error) {
			this.errors.push(error);
			if (error.name === 'SecurityError') {
			  console.error('Cannot construct sensors due to the Feature Policy');
			  console.warn('Attempting to fall back using "devicemotion"; however this will ' + 'fail in the future without correct permissions.');
			  this.useDeviceMotion();
			} else if (error.name === 'ReferenceError') {
			  this.useDeviceMotion();
			} else {
			  console.error(error);
			}
		  }
		  if (sensor) {
			this.api = 'sensor';
			this.sensor = sensor;
			this.sensor.addEventListener('reading', this._onSensorRead);
			this.sensor.start();
		  }
		  window.addEventListener('orientationchange', this._onOrientationChange);
		}
	  }, {
		key: 'useDeviceMotion',
		value: function useDeviceMotion() {
		  this.api = 'devicemotion';
		  this.fusionSensor = new FusionPoseSensor(this.config.K_FILTER, this.config.PREDICTION_TIME_S, this.config.YAW_ONLY, this.config.DEBUG);
		  if (this.sensor) {
			this.sensor.removeEventListener('reading', this._onSensorRead);
			this.sensor.removeEventListener('error', this._onSensorError);
			this.sensor = null;
		  }
		}
	  }, {
		key: 'getOrientation',
		value: function getOrientation() {
		  if (this.fusionSensor) {
			return this.fusionSensor.getOrientation();
		  }
		  if (!this.sensor || !this.sensor.quaternion) {
			this._out[0] = this._out[1] = this._out[2] = 0;
			this._out[3] = 1;
			return this._out;
		  }
		  var q = this.sensor.quaternion;
		  this._sensorQ.set(q[0], q[1], q[2], q[3]);
		  var out = this._outQ;
		  out.copy(SENSOR_TO_VR);
		  out.multiply(this._sensorQ);
		  out.multiply(this._worldToScreenQ);
		  if (this.config.YAW_ONLY) {
			out.x = out.z = 0;
			out.normalize();
		  }
		  this._out[0] = out.x;
		  this._out[1] = out.y;
		  this._out[2] = out.z;
		  this._out[3] = out.w;
		  return this._out;
		}
	  }, {
		key: '_onSensorError',
		value: function _onSensorError(event) {
		  this.errors.push(event.error);
		  if (event.error.name === 'NotAllowedError') {
			console.error('Permission to access sensor was denied');
		  } else if (event.error.name === 'NotReadableError') {
			console.error('Sensor could not be read');
		  } else {
			console.error(event.error);
		  }
		  this.useDeviceMotion();
		}
	  }, {
		key: '_onSensorRead',
		value: function _onSensorRead() {}
	  }, {
		key: '_onOrientationChange',
		value: function _onOrientationChange() {
		  var angle = -orientation.angle * Math.PI / 180;
		  this._worldToScreenQ.setFromAxisAngle(Z_AXIS, angle);
		}
	  }]);
	  return PoseSensor;
	}();
	var rotateInstructionsAsset = 'PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+Cjxzdmcgd2lkdGg9IjE5OHB4IiBoZWlnaHQ9IjI0MHB4IiB2aWV3Qm94PSIwIDAgMTk4IDI0MCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4bWxuczpza2V0Y2g9Imh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaC9ucyI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDMuMy4zICgxMjA4MSkgLSBodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2ggLS0+CiAgICA8dGl0bGU+dHJhbnNpdGlvbjwvdGl0bGU+CiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4KICAgIDxkZWZzPjwvZGVmcz4KICAgIDxnIGlkPSJQYWdlLTEiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHNrZXRjaDp0eXBlPSJNU1BhZ2UiPgogICAgICAgIDxnIGlkPSJ0cmFuc2l0aW9uIiBza2V0Y2g6dHlwZT0iTVNBcnRib2FyZEdyb3VwIj4KICAgICAgICAgICAgPGcgaWQ9IkltcG9ydGVkLUxheWVycy1Db3B5LTQtKy1JbXBvcnRlZC1MYXllcnMtQ29weS0rLUltcG9ydGVkLUxheWVycy1Db3B5LTItQ29weSIgc2tldGNoOnR5cGU9Ik1TTGF5ZXJHcm91cCI+CiAgICAgICAgICAgICAgICA8ZyBpZD0iSW1wb3J0ZWQtTGF5ZXJzLUNvcHktNCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMC4wMDAwMDAsIDEwNy4wMDAwMDApIiBza2V0Y2g6dHlwZT0iTVNTaGFwZUdyb3VwIj4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTQ5LjYyNSwyLjUyNyBDMTQ5LjYyNSwyLjUyNyAxNTUuODA1LDYuMDk2IDE1Ni4zNjIsNi40MTggTDE1Ni4zNjIsNy4zMDQgQzE1Ni4zNjIsNy40ODEgMTU2LjM3NSw3LjY2NCAxNTYuNCw3Ljg1MyBDMTU2LjQxLDcuOTM0IDE1Ni40Miw4LjAxNSAxNTYuNDI3LDguMDk1IEMxNTYuNTY3LDkuNTEgMTU3LjQwMSwxMS4wOTMgMTU4LjUzMiwxMi4wOTQgTDE2NC4yNTIsMTcuMTU2IEwxNjQuMzMzLDE3LjA2NiBDMTY0LjMzMywxNy4wNjYgMTY4LjcxNSwxNC41MzYgMTY5LjU2OCwxNC4wNDIgQzE3MS4wMjUsMTQuODgzIDE5NS41MzgsMjkuMDM1IDE5NS41MzgsMjkuMDM1IEwxOTUuNTM4LDgzLjAzNiBDMTk1LjUzOCw4My44MDcgMTk1LjE1Miw4NC4yNTMgMTk0LjU5LDg0LjI1MyBDMTk0LjM1Nyw4NC4yNTMgMTk0LjA5NSw4NC4xNzcgMTkzLjgxOCw4NC4wMTcgTDE2OS44NTEsNzAuMTc5IEwxNjkuODM3LDcwLjIwMyBMMTQyLjUxNSw4NS45NzggTDE0MS42NjUsODQuNjU1IEMxMzYuOTM0LDgzLjEyNiAxMzEuOTE3LDgxLjkxNSAxMjYuNzE0LDgxLjA0NSBDMTI2LjcwOSw4MS4wNiAxMjYuNzA3LDgxLjA2OSAxMjYuNzA3LDgxLjA2OSBMMTIxLjY0LDk4LjAzIEwxMTMuNzQ5LDEwMi41ODYgTDExMy43MTIsMTAyLjUyMyBMMTEzLjcxMiwxMzAuMTEzIEMxMTMuNzEyLDEzMC44ODUgMTEzLjMyNiwxMzEuMzMgMTEyLjc2NCwxMzEuMzMgQzExMi41MzIsMTMxLjMzIDExMi4yNjksMTMxLjI1NCAxMTEuOTkyLDEzMS4wOTQgTDY5LjUxOSwxMDYuNTcyIEM2OC41NjksMTA2LjAyMyA2Ny43OTksMTA0LjY5NSA2Ny43OTksMTAzLjYwNSBMNjcuNzk5LDEwMi41NyBMNjcuNzc4LDEwMi42MTcgQzY3LjI3LDEwMi4zOTMgNjYuNjQ4LDEwMi4yNDkgNjUuOTYyLDEwMi4yMTggQzY1Ljg3NSwxMDIuMjE0IDY1Ljc4OCwxMDIuMjEyIDY1LjcwMSwxMDIuMjEyIEM2NS42MDYsMTAyLjIxMiA2NS41MTEsMTAyLjIxNSA2NS40MTYsMTAyLjIxOSBDNjUuMTk1LDEwMi4yMjkgNjQuOTc0LDEwMi4yMzUgNjQuNzU0LDEwMi4yMzUgQzY0LjMzMSwxMDIuMjM1IDYzLjkxMSwxMDIuMjE2IDYzLjQ5OCwxMDIuMTc4IEM2MS44NDMsMTAyLjAyNSA2MC4yOTgsMTAxLjU3OCA1OS4wOTQsMTAwLjg4MiBMMTIuNTE4LDczLjk5MiBMMTIuNTIzLDc0LjAwNCBMMi4yNDUsNTUuMjU0IEMxLjI0NCw1My40MjcgMi4wMDQsNTEuMDM4IDMuOTQzLDQ5LjkxOCBMNTkuOTU0LDE3LjU3MyBDNjAuNjI2LDE3LjE4NSA2MS4zNSwxNy4wMDEgNjIuMDUzLDE3LjAwMSBDNjMuMzc5LDE3LjAwMSA2NC42MjUsMTcuNjYgNjUuMjgsMTguODU0IEw2NS4yODUsMTguODUxIEw2NS41MTIsMTkuMjY0IEw2NS41MDYsMTkuMjY4IEM2NS45MDksMjAuMDAzIDY2LjQwNSwyMC42OCA2Ni45ODMsMjEuMjg2IEw2Ny4yNiwyMS41NTYgQzY5LjE3NCwyMy40MDYgNzEuNzI4LDI0LjM1NyA3NC4zNzMsMjQuMzU3IEM3Ni4zMjIsMjQuMzU3IDc4LjMyMSwyMy44NCA4MC4xNDgsMjIuNzg1IEM4MC4xNjEsMjIuNzg1IDg3LjQ2NywxOC41NjYgODcuNDY3LDE4LjU2NiBDODguMTM5LDE4LjE3OCA4OC44NjMsMTcuOTk0IDg5LjU2NiwxNy45OTQgQzkwLjg5MiwxNy45OTQgOTIuMTM4LDE4LjY1MiA5Mi43OTIsMTkuODQ3IEw5Ni4wNDIsMjUuNzc1IEw5Ni4wNjQsMjUuNzU3IEwxMDIuODQ5LDI5LjY3NCBMMTAyLjc0NCwyOS40OTIgTDE0OS42MjUsMi41MjcgTTE0OS42MjUsMC44OTIgQzE0OS4zNDMsMC44OTIgMTQ5LjA2MiwwLjk2NSAxNDguODEsMS4xMSBMMTAyLjY0MSwyNy42NjYgTDk3LjIzMSwyNC41NDIgTDk0LjIyNiwxOS4wNjEgQzkzLjMxMywxNy4zOTQgOTEuNTI3LDE2LjM1OSA4OS41NjYsMTYuMzU4IEM4OC41NTUsMTYuMzU4IDg3LjU0NiwxNi42MzIgODYuNjQ5LDE3LjE1IEM4My44NzgsMTguNzUgNzkuNjg3LDIxLjE2OSA3OS4zNzQsMjEuMzQ1IEM3OS4zNTksMjEuMzUzIDc5LjM0NSwyMS4zNjEgNzkuMzMsMjEuMzY5IEM3Ny43OTgsMjIuMjU0IDc2LjA4NCwyMi43MjIgNzQuMzczLDIyLjcyMiBDNzIuMDgxLDIyLjcyMiA2OS45NTksMjEuODkgNjguMzk3LDIwLjM4IEw2OC4xNDUsMjAuMTM1IEM2Ny43MDYsMTkuNjcyIDY3LjMyMywxOS4xNTYgNjcuMDA2LDE4LjYwMSBDNjYuOTg4LDE4LjU1OSA2Ni45NjgsMTguNTE5IDY2Ljk0NiwxOC40NzkgTDY2LjcxOSwxOC4wNjUgQzY2LjY5LDE4LjAxMiA2Ni42NTgsMTcuOTYgNjYuNjI0LDE3LjkxMSBDNjUuNjg2LDE2LjMzNyA2My45NTEsMTUuMzY2IDYyLjA1MywxNS4zNjYgQzYxLjA0MiwxNS4zNjYgNjAuMDMzLDE1LjY0IDU5LjEzNiwxNi4xNTggTDMuMTI1LDQ4LjUwMiBDMC40MjYsNTAuMDYxIC0wLjYxMyw1My40NDIgMC44MTEsNTYuMDQgTDExLjA4OSw3NC43OSBDMTEuMjY2LDc1LjExMyAxMS41MzcsNzUuMzUzIDExLjg1LDc1LjQ5NCBMNTguMjc2LDEwMi4yOTggQzU5LjY3OSwxMDMuMTA4IDYxLjQzMywxMDMuNjMgNjMuMzQ4LDEwMy44MDYgQzYzLjgxMiwxMDMuODQ4IDY0LjI4NSwxMDMuODcgNjQuNzU0LDEwMy44NyBDNjUsMTAzLjg3IDY1LjI0OSwxMDMuODY0IDY1LjQ5NCwxMDMuODUyIEM2NS41NjMsMTAzLjg0OSA2NS42MzIsMTAzLjg0NyA2NS43MDEsMTAzLjg0NyBDNjUuNzY0LDEwMy44NDcgNjUuODI4LDEwMy44NDkgNjUuODksMTAzLjg1MiBDNjUuOTg2LDEwMy44NTYgNjYuMDgsMTAzLjg2MyA2Ni4xNzMsMTAzLjg3NCBDNjYuMjgyLDEwNS40NjcgNjcuMzMyLDEwNy4xOTcgNjguNzAyLDEwNy45ODggTDExMS4xNzQsMTMyLjUxIEMxMTEuNjk4LDEzMi44MTIgMTEyLjIzMiwxMzIuOTY1IDExMi43NjQsMTMyLjk2NSBDMTE0LjI2MSwxMzIuOTY1IDExNS4zNDcsMTMxLjc2NSAxMTUuMzQ3LDEzMC4xMTMgTDExNS4zNDcsMTAzLjU1MSBMMTIyLjQ1OCw5OS40NDYgQzEyMi44MTksOTkuMjM3IDEyMy4wODcsOTguODk4IDEyMy4yMDcsOTguNDk4IEwxMjcuODY1LDgyLjkwNSBDMTMyLjI3OSw4My43MDIgMTM2LjU1Nyw4NC43NTMgMTQwLjYwNyw4Ni4wMzMgTDE0MS4xNCw4Ni44NjIgQzE0MS40NTEsODcuMzQ2IDE0MS45NzcsODcuNjEzIDE0Mi41MTYsODcuNjEzIEMxNDIuNzk0LDg3LjYxMyAxNDMuMDc2LDg3LjU0MiAxNDMuMzMzLDg3LjM5MyBMMTY5Ljg2NSw3Mi4wNzYgTDE5Myw4NS40MzMgQzE5My41MjMsODUuNzM1IDE5NC4wNTgsODUuODg4IDE5NC41OSw4NS44ODggQzE5Ni4wODcsODUuODg4IDE5Ny4xNzMsODQuNjg5IDE5Ny4xNzMsODMuMDM2IEwxOTcuMTczLDI5LjAzNSBDMTk3LjE3MywyOC40NTEgMTk2Ljg2MSwyNy45MTEgMTk2LjM1NSwyNy42MTkgQzE5Ni4zNTUsMjcuNjE5IDE3MS44NDMsMTMuNDY3IDE3MC4zODUsMTIuNjI2IEMxNzAuMTMyLDEyLjQ4IDE2OS44NSwxMi40MDcgMTY5LjU2OCwxMi40MDcgQzE2OS4yODUsMTIuNDA3IDE2OS4wMDIsMTIuNDgxIDE2OC43NDksMTIuNjI3IEMxNjguMTQzLDEyLjk3OCAxNjUuNzU2LDE0LjM1NyAxNjQuNDI0LDE1LjEyNSBMMTU5LjYxNSwxMC44NyBDMTU4Ljc5NiwxMC4xNDUgMTU4LjE1NCw4LjkzNyAxNTguMDU0LDcuOTM0IEMxNTguMDQ1LDcuODM3IDE1OC4wMzQsNy43MzkgMTU4LjAyMSw3LjY0IEMxNTguMDA1LDcuNTIzIDE1Ny45OTgsNy40MSAxNTcuOTk4LDcuMzA0IEwxNTcuOTk4LDYuNDE4IEMxNTcuOTk4LDUuODM0IDE1Ny42ODYsNS4yOTUgMTU3LjE4MSw1LjAwMiBDMTU2LjYyNCw0LjY4IDE1MC40NDIsMS4xMTEgMTUwLjQ0MiwxLjExMSBDMTUwLjE4OSwwLjk2NSAxNDkuOTA3LDAuODkyIDE0OS42MjUsMC44OTIiIGlkPSJGaWxsLTEiIGZpbGw9IiM0NTVBNjQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNOTYuMDI3LDI1LjYzNiBMMTQyLjYwMyw1Mi41MjcgQzE0My44MDcsNTMuMjIyIDE0NC41ODIsNTQuMTE0IDE0NC44NDUsNTUuMDY4IEwxNDQuODM1LDU1LjA3NSBMNjMuNDYxLDEwMi4wNTcgTDYzLjQ2LDEwMi4wNTcgQzYxLjgwNiwxMDEuOTA1IDYwLjI2MSwxMDEuNDU3IDU5LjA1NywxMDAuNzYyIEwxMi40ODEsNzMuODcxIEw5Ni4wMjcsMjUuNjM2IiBpZD0iRmlsbC0yIiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTYzLjQ2MSwxMDIuMTc0IEM2My40NTMsMTAyLjE3NCA2My40NDYsMTAyLjE3NCA2My40MzksMTAyLjE3MiBDNjEuNzQ2LDEwMi4wMTYgNjAuMjExLDEwMS41NjMgNTguOTk4LDEwMC44NjMgTDEyLjQyMiw3My45NzMgQzEyLjM4Niw3My45NTIgMTIuMzY0LDczLjkxNCAxMi4zNjQsNzMuODcxIEMxMi4zNjQsNzMuODMgMTIuMzg2LDczLjc5MSAxMi40MjIsNzMuNzcgTDk1Ljk2OCwyNS41MzUgQzk2LjAwNCwyNS41MTQgOTYuMDQ5LDI1LjUxNCA5Ni4wODUsMjUuNTM1IEwxNDIuNjYxLDUyLjQyNiBDMTQzLjg4OCw1My4xMzQgMTQ0LjY4Miw1NC4wMzggMTQ0Ljk1Nyw1NS4wMzcgQzE0NC45Nyw1NS4wODMgMTQ0Ljk1Myw1NS4xMzMgMTQ0LjkxNSw1NS4xNjEgQzE0NC45MTEsNTUuMTY1IDE0NC44OTgsNTUuMTc0IDE0NC44OTQsNTUuMTc3IEw2My41MTksMTAyLjE1OCBDNjMuNTAxLDEwMi4xNjkgNjMuNDgxLDEwMi4xNzQgNjMuNDYxLDEwMi4xNzQgTDYzLjQ2MSwxMDIuMTc0IFogTTEyLjcxNCw3My44NzEgTDU5LjExNSwxMDAuNjYxIEM2MC4yOTMsMTAxLjM0MSA2MS43ODYsMTAxLjc4MiA2My40MzUsMTAxLjkzNyBMMTQ0LjcwNyw1NS4wMTUgQzE0NC40MjgsNTQuMTA4IDE0My42ODIsNTMuMjg1IDE0Mi41NDQsNTIuNjI4IEw5Ni4wMjcsMjUuNzcxIEwxMi43MTQsNzMuODcxIEwxMi43MTQsNzMuODcxIFoiIGlkPSJGaWxsLTMiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTQ4LjMyNyw1OC40NzEgQzE0OC4xNDUsNTguNDggMTQ3Ljk2Miw1OC40OCAxNDcuNzgxLDU4LjQ3MiBDMTQ1Ljg4Nyw1OC4zODkgMTQ0LjQ3OSw1Ny40MzQgMTQ0LjYzNiw1Ni4zNCBDMTQ0LjY4OSw1NS45NjcgMTQ0LjY2NCw1NS41OTcgMTQ0LjU2NCw1NS4yMzUgTDYzLjQ2MSwxMDIuMDU3IEM2NC4wODksMTAyLjExNSA2NC43MzMsMTAyLjEzIDY1LjM3OSwxMDIuMDk5IEM2NS41NjEsMTAyLjA5IDY1Ljc0MywxMDIuMDkgNjUuOTI1LDEwMi4wOTggQzY3LjgxOSwxMDIuMTgxIDY5LjIyNywxMDMuMTM2IDY5LjA3LDEwNC4yMyBMMTQ4LjMyNyw1OC40NzEiIGlkPSJGaWxsLTQiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNjkuMDcsMTA0LjM0NyBDNjkuMDQ4LDEwNC4zNDcgNjkuMDI1LDEwNC4zNCA2OS4wMDUsMTA0LjMyNyBDNjguOTY4LDEwNC4zMDEgNjguOTQ4LDEwNC4yNTcgNjguOTU1LDEwNC4yMTMgQzY5LDEwMy44OTYgNjguODk4LDEwMy41NzYgNjguNjU4LDEwMy4yODggQzY4LjE1MywxMDIuNjc4IDY3LjEwMywxMDIuMjY2IDY1LjkyLDEwMi4yMTQgQzY1Ljc0MiwxMDIuMjA2IDY1LjU2MywxMDIuMjA3IDY1LjM4NSwxMDIuMjE1IEM2NC43NDIsMTAyLjI0NiA2NC4wODcsMTAyLjIzMiA2My40NSwxMDIuMTc0IEM2My4zOTksMTAyLjE2OSA2My4zNTgsMTAyLjEzMiA2My4zNDcsMTAyLjA4MiBDNjMuMzM2LDEwMi4wMzMgNjMuMzU4LDEwMS45ODEgNjMuNDAyLDEwMS45NTYgTDE0NC41MDYsNTUuMTM0IEMxNDQuNTM3LDU1LjExNiAxNDQuNTc1LDU1LjExMyAxNDQuNjA5LDU1LjEyNyBDMTQ0LjY0Miw1NS4xNDEgMTQ0LjY2OCw1NS4xNyAxNDQuNjc3LDU1LjIwNCBDMTQ0Ljc4MSw1NS41ODUgMTQ0LjgwNiw1NS45NzIgMTQ0Ljc1MSw1Ni4zNTcgQzE0NC43MDYsNTYuNjczIDE0NC44MDgsNTYuOTk0IDE0NS4wNDcsNTcuMjgyIEMxNDUuNTUzLDU3Ljg5MiAxNDYuNjAyLDU4LjMwMyAxNDcuNzg2LDU4LjM1NSBDMTQ3Ljk2NCw1OC4zNjMgMTQ4LjE0Myw1OC4zNjMgMTQ4LjMyMSw1OC4zNTQgQzE0OC4zNzcsNTguMzUyIDE0OC40MjQsNTguMzg3IDE0OC40MzksNTguNDM4IEMxNDguNDU0LDU4LjQ5IDE0OC40MzIsNTguNTQ1IDE0OC4zODUsNTguNTcyIEw2OS4xMjksMTA0LjMzMSBDNjkuMTExLDEwNC4zNDIgNjkuMDksMTA0LjM0NyA2OS4wNywxMDQuMzQ3IEw2OS4wNywxMDQuMzQ3IFogTTY1LjY2NSwxMDEuOTc1IEM2NS43NTQsMTAxLjk3NSA2NS44NDIsMTAxLjk3NyA2NS45MywxMDEuOTgxIEM2Ny4xOTYsMTAyLjAzNyA2OC4yODMsMTAyLjQ2OSA2OC44MzgsMTAzLjEzOSBDNjkuMDY1LDEwMy40MTMgNjkuMTg4LDEwMy43MTQgNjkuMTk4LDEwNC4wMjEgTDE0Ny44ODMsNTguNTkyIEMxNDcuODQ3LDU4LjU5MiAxNDcuODExLDU4LjU5MSAxNDcuNzc2LDU4LjU4OSBDMTQ2LjUwOSw1OC41MzMgMTQ1LjQyMiw1OC4xIDE0NC44NjcsNTcuNDMxIEMxNDQuNTg1LDU3LjA5MSAxNDQuNDY1LDU2LjcwNyAxNDQuNTIsNTYuMzI0IEMxNDQuNTYzLDU2LjAyMSAxNDQuNTUyLDU1LjcxNiAxNDQuNDg4LDU1LjQxNCBMNjMuODQ2LDEwMS45NyBDNjQuMzUzLDEwMi4wMDIgNjQuODY3LDEwMi4wMDYgNjUuMzc0LDEwMS45ODIgQzY1LjQ3MSwxMDEuOTc3IDY1LjU2OCwxMDEuOTc1IDY1LjY2NSwxMDEuOTc1IEw2NS42NjUsMTAxLjk3NSBaIiBpZD0iRmlsbC01IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTIuMjA4LDU1LjEzNCBDMS4yMDcsNTMuMzA3IDEuOTY3LDUwLjkxNyAzLjkwNiw0OS43OTcgTDU5LjkxNywxNy40NTMgQzYxLjg1NiwxNi4zMzMgNjQuMjQxLDE2LjkwNyA2NS4yNDMsMTguNzM0IEw2NS40NzUsMTkuMTQ0IEM2NS44NzIsMTkuODgyIDY2LjM2OCwyMC41NiA2Ni45NDUsMjEuMTY1IEw2Ny4yMjMsMjEuNDM1IEM3MC41NDgsMjQuNjQ5IDc1LjgwNiwyNS4xNTEgODAuMTExLDIyLjY2NSBMODcuNDMsMTguNDQ1IEM4OS4zNywxNy4zMjYgOTEuNzU0LDE3Ljg5OSA5Mi43NTUsMTkuNzI3IEw5Ni4wMDUsMjUuNjU1IEwxMi40ODYsNzMuODg0IEwyLjIwOCw1NS4xMzQgWiIgaWQ9IkZpbGwtNiIgZmlsbD0iI0ZBRkFGQSI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMi40ODYsNzQuMDAxIEMxMi40NzYsNzQuMDAxIDEyLjQ2NSw3My45OTkgMTIuNDU1LDczLjk5NiBDMTIuNDI0LDczLjk4OCAxMi4zOTksNzMuOTY3IDEyLjM4NCw3My45NCBMMi4xMDYsNTUuMTkgQzEuMDc1LDUzLjMxIDEuODU3LDUwLjg0NSAzLjg0OCw0OS42OTYgTDU5Ljg1OCwxNy4zNTIgQzYwLjUyNSwxNi45NjcgNjEuMjcxLDE2Ljc2NCA2Mi4wMTYsMTYuNzY0IEM2My40MzEsMTYuNzY0IDY0LjY2NiwxNy40NjYgNjUuMzI3LDE4LjY0NiBDNjUuMzM3LDE4LjY1NCA2NS4zNDUsMTguNjYzIDY1LjM1MSwxOC42NzQgTDY1LjU3OCwxOS4wODggQzY1LjU4NCwxOS4xIDY1LjU4OSwxOS4xMTIgNjUuNTkxLDE5LjEyNiBDNjUuOTg1LDE5LjgzOCA2Ni40NjksMjAuNDk3IDY3LjAzLDIxLjA4NSBMNjcuMzA1LDIxLjM1MSBDNjkuMTUxLDIzLjEzNyA3MS42NDksMjQuMTIgNzQuMzM2LDI0LjEyIEM3Ni4zMTMsMjQuMTIgNzguMjksMjMuNTgyIDgwLjA1MywyMi41NjMgQzgwLjA2NCwyMi41NTcgODAuMDc2LDIyLjU1MyA4MC4wODgsMjIuNTUgTDg3LjM3MiwxOC4zNDQgQzg4LjAzOCwxNy45NTkgODguNzg0LDE3Ljc1NiA4OS41MjksMTcuNzU2IEM5MC45NTYsMTcuNzU2IDkyLjIwMSwxOC40NzIgOTIuODU4LDE5LjY3IEw5Ni4xMDcsMjUuNTk5IEM5Ni4xMzgsMjUuNjU0IDk2LjExOCwyNS43MjQgOTYuMDYzLDI1Ljc1NiBMMTIuNTQ1LDczLjk4NSBDMTIuNTI2LDczLjk5NiAxMi41MDYsNzQuMDAxIDEyLjQ4Niw3NC4wMDEgTDEyLjQ4Niw3NC4wMDEgWiBNNjIuMDE2LDE2Ljk5NyBDNjEuMzEyLDE2Ljk5NyA2MC42MDYsMTcuMTkgNTkuOTc1LDE3LjU1NCBMMy45NjUsNDkuODk5IEMyLjA4Myw1MC45ODUgMS4zNDEsNTMuMzA4IDIuMzEsNTUuMDc4IEwxMi41MzEsNzMuNzIzIEw5NS44NDgsMjUuNjExIEw5Mi42NTMsMTkuNzgyIEM5Mi4wMzgsMTguNjYgOTAuODcsMTcuOTkgODkuNTI5LDE3Ljk5IEM4OC44MjUsMTcuOTkgODguMTE5LDE4LjE4MiA4Ny40ODksMTguNTQ3IEw4MC4xNzIsMjIuNzcyIEM4MC4xNjEsMjIuNzc4IDgwLjE0OSwyMi43ODIgODAuMTM3LDIyLjc4NSBDNzguMzQ2LDIzLjgxMSA3Ni4zNDEsMjQuMzU0IDc0LjMzNiwyNC4zNTQgQzcxLjU4OCwyNC4zNTQgNjkuMDMzLDIzLjM0NyA2Ny4xNDIsMjEuNTE5IEw2Ni44NjQsMjEuMjQ5IEM2Ni4yNzcsMjAuNjM0IDY1Ljc3NCwxOS45NDcgNjUuMzY3LDE5LjIwMyBDNjUuMzYsMTkuMTkyIDY1LjM1NiwxOS4xNzkgNjUuMzU0LDE5LjE2NiBMNjUuMTYzLDE4LjgxOSBDNjUuMTU0LDE4LjgxMSA2NS4xNDYsMTguODAxIDY1LjE0LDE4Ljc5IEM2NC41MjUsMTcuNjY3IDYzLjM1NywxNi45OTcgNjIuMDE2LDE2Ljk5NyBMNjIuMDE2LDE2Ljk5NyBaIiBpZD0iRmlsbC03IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTQyLjQzNCw0OC44MDggTDQyLjQzNCw0OC44MDggQzM5LjkyNCw0OC44MDcgMzcuNzM3LDQ3LjU1IDM2LjU4Miw0NS40NDMgQzM0Ljc3MSw0Mi4xMzkgMzYuMTQ0LDM3LjgwOSAzOS42NDEsMzUuNzg5IEw1MS45MzIsMjguNjkxIEM1My4xMDMsMjguMDE1IDU0LjQxMywyNy42NTggNTUuNzIxLDI3LjY1OCBDNTguMjMxLDI3LjY1OCA2MC40MTgsMjguOTE2IDYxLjU3MywzMS4wMjMgQzYzLjM4NCwzNC4zMjcgNjIuMDEyLDM4LjY1NyA1OC41MTQsNDAuNjc3IEw0Ni4yMjMsNDcuNzc1IEM0NS4wNTMsNDguNDUgNDMuNzQyLDQ4LjgwOCA0Mi40MzQsNDguODA4IEw0Mi40MzQsNDguODA4IFogTTU1LjcyMSwyOC4xMjUgQzU0LjQ5NSwyOC4xMjUgNTMuMjY1LDI4LjQ2MSA1Mi4xNjYsMjkuMDk2IEwzOS44NzUsMzYuMTk0IEMzNi41OTYsMzguMDg3IDM1LjMwMiw0Mi4xMzYgMzYuOTkyLDQ1LjIxOCBDMzguMDYzLDQ3LjE3MyA0MC4wOTgsNDguMzQgNDIuNDM0LDQ4LjM0IEM0My42NjEsNDguMzQgNDQuODksNDguMDA1IDQ1Ljk5LDQ3LjM3IEw1OC4yODEsNDAuMjcyIEM2MS41NiwzOC4zNzkgNjIuODUzLDM0LjMzIDYxLjE2NCwzMS4yNDggQzYwLjA5MiwyOS4yOTMgNTguMDU4LDI4LjEyNSA1NS43MjEsMjguMTI1IEw1NS43MjEsMjguMTI1IFoiIGlkPSJGaWxsLTgiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTQ5LjU4OCwyLjQwNyBDMTQ5LjU4OCwyLjQwNyAxNTUuNzY4LDUuOTc1IDE1Ni4zMjUsNi4yOTcgTDE1Ni4zMjUsNy4xODQgQzE1Ni4zMjUsNy4zNiAxNTYuMzM4LDcuNTQ0IDE1Ni4zNjIsNy43MzMgQzE1Ni4zNzMsNy44MTQgMTU2LjM4Miw3Ljg5NCAxNTYuMzksNy45NzUgQzE1Ni41Myw5LjM5IDE1Ny4zNjMsMTAuOTczIDE1OC40OTUsMTEuOTc0IEwxNjUuODkxLDE4LjUxOSBDMTY2LjA2OCwxOC42NzUgMTY2LjI0OSwxOC44MTQgMTY2LjQzMiwxOC45MzQgQzE2OC4wMTEsMTkuOTc0IDE2OS4zODIsMTkuNCAxNjkuNDk0LDE3LjY1MiBDMTY5LjU0MywxNi44NjggMTY5LjU1MSwxNi4wNTcgMTY5LjUxNywxNS4yMjMgTDE2OS41MTQsMTUuMDYzIEwxNjkuNTE0LDEzLjkxMiBDMTcwLjc4LDE0LjY0MiAxOTUuNTAxLDI4LjkxNSAxOTUuNTAxLDI4LjkxNSBMMTk1LjUwMSw4Mi45MTUgQzE5NS41MDEsODQuMDA1IDE5NC43MzEsODQuNDQ1IDE5My43ODEsODMuODk3IEwxNTEuMzA4LDU5LjM3NCBDMTUwLjM1OCw1OC44MjYgMTQ5LjU4OCw1Ny40OTcgMTQ5LjU4OCw1Ni40MDggTDE0OS41ODgsMjIuMzc1IiBpZD0iRmlsbC05IiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE5NC41NTMsODQuMjUgQzE5NC4yOTYsODQuMjUgMTk0LjAxMyw4NC4xNjUgMTkzLjcyMiw4My45OTcgTDE1MS4yNSw1OS40NzYgQzE1MC4yNjksNTguOTA5IDE0OS40NzEsNTcuNTMzIDE0OS40NzEsNTYuNDA4IEwxNDkuNDcxLDIyLjM3NSBMMTQ5LjcwNSwyMi4zNzUgTDE0OS43MDUsNTYuNDA4IEMxNDkuNzA1LDU3LjQ1OSAxNTAuNDUsNTguNzQ0IDE1MS4zNjYsNTkuMjc0IEwxOTMuODM5LDgzLjc5NSBDMTk0LjI2Myw4NC4wNCAxOTQuNjU1LDg0LjA4MyAxOTQuOTQyLDgzLjkxNyBDMTk1LjIyNyw4My43NTMgMTk1LjM4NCw4My4zOTcgMTk1LjM4NCw4Mi45MTUgTDE5NS4zODQsMjguOTgyIEMxOTQuMTAyLDI4LjI0MiAxNzIuMTA0LDE1LjU0MiAxNjkuNjMxLDE0LjExNCBMMTY5LjYzNCwxNS4yMiBDMTY5LjY2OCwxNi4wNTIgMTY5LjY2LDE2Ljg3NCAxNjkuNjEsMTcuNjU5IEMxNjkuNTU2LDE4LjUwMyAxNjkuMjE0LDE5LjEyMyAxNjguNjQ3LDE5LjQwNSBDMTY4LjAyOCwxOS43MTQgMTY3LjE5NywxOS41NzggMTY2LjM2NywxOS4wMzIgQzE2Ni4xODEsMTguOTA5IDE2NS45OTUsMTguNzY2IDE2NS44MTQsMTguNjA2IEwxNTguNDE3LDEyLjA2MiBDMTU3LjI1OSwxMS4wMzYgMTU2LjQxOCw5LjQzNyAxNTYuMjc0LDcuOTg2IEMxNTYuMjY2LDcuOTA3IDE1Ni4yNTcsNy44MjcgMTU2LjI0Nyw3Ljc0OCBDMTU2LjIyMSw3LjU1NSAxNTYuMjA5LDcuMzY1IDE1Ni4yMDksNy4xODQgTDE1Ni4yMDksNi4zNjQgQzE1NS4zNzUsNS44ODMgMTQ5LjUyOSwyLjUwOCAxNDkuNTI5LDIuNTA4IEwxNDkuNjQ2LDIuMzA2IEMxNDkuNjQ2LDIuMzA2IDE1NS44MjcsNS44NzQgMTU2LjM4NCw2LjE5NiBMMTU2LjQ0Miw2LjIzIEwxNTYuNDQyLDcuMTg0IEMxNTYuNDQyLDcuMzU1IDE1Ni40NTQsNy41MzUgMTU2LjQ3OCw3LjcxNyBDMTU2LjQ4OSw3LjggMTU2LjQ5OSw3Ljg4MiAxNTYuNTA3LDcuOTYzIEMxNTYuNjQ1LDkuMzU4IDE1Ny40NTUsMTAuODk4IDE1OC41NzIsMTEuODg2IEwxNjUuOTY5LDE4LjQzMSBDMTY2LjE0MiwxOC41ODQgMTY2LjMxOSwxOC43MiAxNjYuNDk2LDE4LjgzNyBDMTY3LjI1NCwxOS4zMzYgMTY4LDE5LjQ2NyAxNjguNTQzLDE5LjE5NiBDMTY5LjAzMywxOC45NTMgMTY5LjMyOSwxOC40MDEgMTY5LjM3NywxNy42NDUgQzE2OS40MjcsMTYuODY3IDE2OS40MzQsMTYuMDU0IDE2OS40MDEsMTUuMjI4IEwxNjkuMzk3LDE1LjA2NSBMMTY5LjM5NywxMy43MSBMMTY5LjU3MiwxMy44MSBDMTcwLjgzOSwxNC41NDEgMTk1LjU1OSwyOC44MTQgMTk1LjU1OSwyOC44MTQgTDE5NS42MTgsMjguODQ3IEwxOTUuNjE4LDgyLjkxNSBDMTk1LjYxOCw4My40ODQgMTk1LjQyLDgzLjkxMSAxOTUuMDU5LDg0LjExOSBDMTk0LjkwOCw4NC4yMDYgMTk0LjczNyw4NC4yNSAxOTQuNTUzLDg0LjI1IiBpZD0iRmlsbC0xMCIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNDUuNjg1LDU2LjE2MSBMMTY5LjgsNzAuMDgzIEwxNDMuODIyLDg1LjA4MSBMMTQyLjM2LDg0Ljc3NCBDMTM1LjgyNiw4Mi42MDQgMTI4LjczMiw4MS4wNDYgMTIxLjM0MSw4MC4xNTggQzExNi45NzYsNzkuNjM0IDExMi42NzgsODEuMjU0IDExMS43NDMsODMuNzc4IEMxMTEuNTA2LDg0LjQxNCAxMTEuNTAzLDg1LjA3MSAxMTEuNzMyLDg1LjcwNiBDMTEzLjI3LDg5Ljk3MyAxMTUuOTY4LDk0LjA2OSAxMTkuNzI3LDk3Ljg0MSBMMTIwLjI1OSw5OC42ODYgQzEyMC4yNiw5OC42ODUgOTQuMjgyLDExMy42ODMgOTQuMjgyLDExMy42ODMgTDcwLjE2Nyw5OS43NjEgTDE0NS42ODUsNTYuMTYxIiBpZD0iRmlsbC0xMSIgZmlsbD0iI0ZGRkZGRiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik05NC4yODIsMTEzLjgxOCBMOTQuMjIzLDExMy43ODUgTDY5LjkzMyw5OS43NjEgTDcwLjEwOCw5OS42NiBMMTQ1LjY4NSw1Ni4wMjYgTDE0NS43NDMsNTYuMDU5IEwxNzAuMDMzLDcwLjA4MyBMMTQzLjg0Miw4NS4yMDUgTDE0My43OTcsODUuMTk1IEMxNDMuNzcyLDg1LjE5IDE0Mi4zMzYsODQuODg4IDE0Mi4zMzYsODQuODg4IEMxMzUuNzg3LDgyLjcxNCAxMjguNzIzLDgxLjE2MyAxMjEuMzI3LDgwLjI3NCBDMTIwLjc4OCw4MC4yMDkgMTIwLjIzNiw4MC4xNzcgMTE5LjY4OSw4MC4xNzcgQzExNS45MzEsODAuMTc3IDExMi42MzUsODEuNzA4IDExMS44NTIsODMuODE5IEMxMTEuNjI0LDg0LjQzMiAxMTEuNjIxLDg1LjA1MyAxMTEuODQyLDg1LjY2NyBDMTEzLjM3Nyw4OS45MjUgMTE2LjA1OCw5My45OTMgMTE5LjgxLDk3Ljc1OCBMMTE5LjgyNiw5Ny43NzkgTDEyMC4zNTIsOTguNjE0IEMxMjAuMzU0LDk4LjYxNyAxMjAuMzU2LDk4LjYyIDEyMC4zNTgsOTguNjI0IEwxMjAuNDIyLDk4LjcyNiBMMTIwLjMxNyw5OC43ODcgQzEyMC4yNjQsOTguODE4IDk0LjU5OSwxMTMuNjM1IDk0LjM0LDExMy43ODUgTDk0LjI4MiwxMTMuODE4IEw5NC4yODIsMTEzLjgxOCBaIE03MC40MDEsOTkuNzYxIEw5NC4yODIsMTEzLjU0OSBMMTE5LjA4NCw5OS4yMjkgQzExOS42Myw5OC45MTQgMTE5LjkzLDk4Ljc0IDEyMC4xMDEsOTguNjU0IEwxMTkuNjM1LDk3LjkxNCBDMTE1Ljg2NCw5NC4xMjcgMTEzLjE2OCw5MC4wMzMgMTExLjYyMiw4NS43NDYgQzExMS4zODIsODUuMDc5IDExMS4zODYsODQuNDA0IDExMS42MzMsODMuNzM4IEMxMTIuNDQ4LDgxLjUzOSAxMTUuODM2LDc5Ljk0MyAxMTkuNjg5LDc5Ljk0MyBDMTIwLjI0Niw3OS45NDMgMTIwLjgwNiw3OS45NzYgMTIxLjM1NSw4MC4wNDIgQzEyOC43NjcsODAuOTMzIDEzNS44NDYsODIuNDg3IDE0Mi4zOTYsODQuNjYzIEMxNDMuMjMyLDg0LjgzOCAxNDMuNjExLDg0LjkxNyAxNDMuNzg2LDg0Ljk2NyBMMTY5LjU2Niw3MC4wODMgTDE0NS42ODUsNTYuMjk1IEw3MC40MDEsOTkuNzYxIEw3MC40MDEsOTkuNzYxIFoiIGlkPSJGaWxsLTEyIiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE2Ny4yMywxOC45NzkgTDE2Ny4yMyw2OS44NSBMMTM5LjkwOSw4NS42MjMgTDEzMy40NDgsNzEuNDU2IEMxMzIuNTM4LDY5LjQ2IDEzMC4wMiw2OS43MTggMTI3LjgyNCw3Mi4wMyBDMTI2Ljc2OSw3My4xNCAxMjUuOTMxLDc0LjU4NSAxMjUuNDk0LDc2LjA0OCBMMTE5LjAzNCw5Ny42NzYgTDkxLjcxMiwxMTMuNDUgTDkxLjcxMiw2Mi41NzkgTDE2Ny4yMywxOC45NzkiIGlkPSJGaWxsLTEzIiBmaWxsPSIjRkZGRkZGIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTkxLjcxMiwxMTMuNTY3IEM5MS42OTIsMTEzLjU2NyA5MS42NzIsMTEzLjU2MSA5MS42NTMsMTEzLjU1MSBDOTEuNjE4LDExMy41MyA5MS41OTUsMTEzLjQ5MiA5MS41OTUsMTEzLjQ1IEw5MS41OTUsNjIuNTc5IEM5MS41OTUsNjIuNTM3IDkxLjYxOCw2Mi40OTkgOTEuNjUzLDYyLjQ3OCBMMTY3LjE3MiwxOC44NzggQzE2Ny4yMDgsMTguODU3IDE2Ny4yNTIsMTguODU3IDE2Ny4yODgsMTguODc4IEMxNjcuMzI0LDE4Ljg5OSAxNjcuMzQ3LDE4LjkzNyAxNjcuMzQ3LDE4Ljk3OSBMMTY3LjM0Nyw2OS44NSBDMTY3LjM0Nyw2OS44OTEgMTY3LjMyNCw2OS45MyAxNjcuMjg4LDY5Ljk1IEwxMzkuOTY3LDg1LjcyNSBDMTM5LjkzOSw4NS43NDEgMTM5LjkwNSw4NS43NDUgMTM5Ljg3Myw4NS43MzUgQzEzOS44NDIsODUuNzI1IDEzOS44MTYsODUuNzAyIDEzOS44MDIsODUuNjcyIEwxMzMuMzQyLDcxLjUwNCBDMTMyLjk2Nyw3MC42ODIgMTMyLjI4LDcwLjIyOSAxMzEuNDA4LDcwLjIyOSBDMTMwLjMxOSw3MC4yMjkgMTI5LjA0NCw3MC45MTUgMTI3LjkwOCw3Mi4xMSBDMTI2Ljg3NCw3My4yIDEyNi4wMzQsNzQuNjQ3IDEyNS42MDYsNzYuMDgyIEwxMTkuMTQ2LDk3LjcwOSBDMTE5LjEzNyw5Ny43MzggMTE5LjExOCw5Ny43NjIgMTE5LjA5Miw5Ny43NzcgTDkxLjc3LDExMy41NTEgQzkxLjc1MiwxMTMuNTYxIDkxLjczMiwxMTMuNTY3IDkxLjcxMiwxMTMuNTY3IEw5MS43MTIsMTEzLjU2NyBaIE05MS44MjksNjIuNjQ3IEw5MS44MjksMTEzLjI0OCBMMTE4LjkzNSw5Ny41OTggTDEyNS4zODIsNzYuMDE1IEMxMjUuODI3LDc0LjUyNSAxMjYuNjY0LDczLjA4MSAxMjcuNzM5LDcxLjk1IEMxMjguOTE5LDcwLjcwOCAxMzAuMjU2LDY5Ljk5NiAxMzEuNDA4LDY5Ljk5NiBDMTMyLjM3Nyw2OS45OTYgMTMzLjEzOSw3MC40OTcgMTMzLjU1NCw3MS40MDcgTDEzOS45NjEsODUuNDU4IEwxNjcuMTEzLDY5Ljc4MiBMMTY3LjExMywxOS4xODEgTDkxLjgyOSw2Mi42NDcgTDkxLjgyOSw2Mi42NDcgWiIgaWQ9IkZpbGwtMTQiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTY4LjU0MywxOS4yMTMgTDE2OC41NDMsNzAuMDgzIEwxNDEuMjIxLDg1Ljg1NyBMMTM0Ljc2MSw3MS42ODkgQzEzMy44NTEsNjkuNjk0IDEzMS4zMzMsNjkuOTUxIDEyOS4xMzcsNzIuMjYzIEMxMjguMDgyLDczLjM3NCAxMjcuMjQ0LDc0LjgxOSAxMjYuODA3LDc2LjI4MiBMMTIwLjM0Niw5Ny45MDkgTDkzLjAyNSwxMTMuNjgzIEw5My4wMjUsNjIuODEzIEwxNjguNTQzLDE5LjIxMyIgaWQ9IkZpbGwtMTUiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNOTMuMDI1LDExMy44IEM5My4wMDUsMTEzLjggOTIuOTg0LDExMy43OTUgOTIuOTY2LDExMy43ODUgQzkyLjkzMSwxMTMuNzY0IDkyLjkwOCwxMTMuNzI1IDkyLjkwOCwxMTMuNjg0IEw5Mi45MDgsNjIuODEzIEM5Mi45MDgsNjIuNzcxIDkyLjkzMSw2Mi43MzMgOTIuOTY2LDYyLjcxMiBMMTY4LjQ4NCwxOS4xMTIgQzE2OC41MiwxOS4wOSAxNjguNTY1LDE5LjA5IDE2OC42MDEsMTkuMTEyIEMxNjguNjM3LDE5LjEzMiAxNjguNjYsMTkuMTcxIDE2OC42NiwxOS4yMTIgTDE2OC42Niw3MC4wODMgQzE2OC42Niw3MC4xMjUgMTY4LjYzNyw3MC4xNjQgMTY4LjYwMSw3MC4xODQgTDE0MS4yOCw4NS45NTggQzE0MS4yNTEsODUuOTc1IDE0MS4yMTcsODUuOTc5IDE0MS4xODYsODUuOTY4IEMxNDEuMTU0LDg1Ljk1OCAxNDEuMTI5LDg1LjkzNiAxNDEuMTE1LDg1LjkwNiBMMTM0LjY1NSw3MS43MzggQzEzNC4yOCw3MC45MTUgMTMzLjU5Myw3MC40NjMgMTMyLjcyLDcwLjQ2MyBDMTMxLjYzMiw3MC40NjMgMTMwLjM1Nyw3MS4xNDggMTI5LjIyMSw3Mi4zNDQgQzEyOC4xODYsNzMuNDMzIDEyNy4zNDcsNzQuODgxIDEyNi45MTksNzYuMzE1IEwxMjAuNDU4LDk3Ljk0MyBDMTIwLjQ1LDk3Ljk3MiAxMjAuNDMxLDk3Ljk5NiAxMjAuNDA1LDk4LjAxIEw5My4wODMsMTEzLjc4NSBDOTMuMDY1LDExMy43OTUgOTMuMDQ1LDExMy44IDkzLjAyNSwxMTMuOCBMOTMuMDI1LDExMy44IFogTTkzLjE0Miw2Mi44ODEgTDkzLjE0MiwxMTMuNDgxIEwxMjAuMjQ4LDk3LjgzMiBMMTI2LjY5NSw3Ni4yNDggQzEyNy4xNCw3NC43NTggMTI3Ljk3Nyw3My4zMTUgMTI5LjA1Miw3Mi4xODMgQzEzMC4yMzEsNzAuOTQyIDEzMS41NjgsNzAuMjI5IDEzMi43Miw3MC4yMjkgQzEzMy42ODksNzAuMjI5IDEzNC40NTIsNzAuNzMxIDEzNC44NjcsNzEuNjQxIEwxNDEuMjc0LDg1LjY5MiBMMTY4LjQyNiw3MC4wMTYgTDE2OC40MjYsMTkuNDE1IEw5My4xNDIsNjIuODgxIEw5My4xNDIsNjIuODgxIFoiIGlkPSJGaWxsLTE2IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE2OS44LDcwLjA4MyBMMTQyLjQ3OCw4NS44NTcgTDEzNi4wMTgsNzEuNjg5IEMxMzUuMTA4LDY5LjY5NCAxMzIuNTksNjkuOTUxIDEzMC4zOTMsNzIuMjYzIEMxMjkuMzM5LDczLjM3NCAxMjguNSw3NC44MTkgMTI4LjA2NCw3Ni4yODIgTDEyMS42MDMsOTcuOTA5IEw5NC4yODIsMTEzLjY4MyBMOTQuMjgyLDYyLjgxMyBMMTY5LjgsMTkuMjEzIEwxNjkuOCw3MC4wODMgWiIgaWQ9IkZpbGwtMTciIGZpbGw9IiNGQUZBRkEiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNOTQuMjgyLDExMy45MTcgQzk0LjI0MSwxMTMuOTE3IDk0LjIwMSwxMTMuOTA3IDk0LjE2NSwxMTMuODg2IEM5NC4wOTMsMTEzLjg0NSA5NC4wNDgsMTEzLjc2NyA5NC4wNDgsMTEzLjY4NCBMOTQuMDQ4LDYyLjgxMyBDOTQuMDQ4LDYyLjczIDk0LjA5Myw2Mi42NTIgOTQuMTY1LDYyLjYxMSBMMTY5LjY4MywxOS4wMSBDMTY5Ljc1NSwxOC45NjkgMTY5Ljg0NCwxOC45NjkgMTY5LjkxNywxOS4wMSBDMTY5Ljk4OSwxOS4wNTIgMTcwLjAzMywxOS4xMjkgMTcwLjAzMywxOS4yMTIgTDE3MC4wMzMsNzAuMDgzIEMxNzAuMDMzLDcwLjE2NiAxNjkuOTg5LDcwLjI0NCAxNjkuOTE3LDcwLjI4NSBMMTQyLjU5NSw4Ni4wNiBDMTQyLjUzOCw4Ni4wOTIgMTQyLjQ2OSw4Ni4xIDE0Mi40MDcsODYuMDggQzE0Mi4zNDQsODYuMDYgMTQyLjI5Myw4Ni4wMTQgMTQyLjI2Niw4NS45NTQgTDEzNS44MDUsNzEuNzg2IEMxMzUuNDQ1LDcwLjk5NyAxMzQuODEzLDcwLjU4IDEzMy45NzcsNzAuNTggQzEzMi45MjEsNzAuNTggMTMxLjY3Niw3MS4yNTIgMTMwLjU2Miw3Mi40MjQgQzEyOS41NCw3My41MDEgMTI4LjcxMSw3NC45MzEgMTI4LjI4Nyw3Ni4zNDggTDEyMS44MjcsOTcuOTc2IEMxMjEuODEsOTguMDM0IDEyMS43NzEsOTguMDgyIDEyMS43Miw5OC4xMTIgTDk0LjM5OCwxMTMuODg2IEM5NC4zNjIsMTEzLjkwNyA5NC4zMjIsMTEzLjkxNyA5NC4yODIsMTEzLjkxNyBMOTQuMjgyLDExMy45MTcgWiBNOTQuNTE1LDYyLjk0OCBMOTQuNTE1LDExMy4yNzkgTDEyMS40MDYsOTcuNzU0IEwxMjcuODQsNzYuMjE1IEMxMjguMjksNzQuNzA4IDEyOS4xMzcsNzMuMjQ3IDEzMC4yMjQsNzIuMTAzIEMxMzEuNDI1LDcwLjgzOCAxMzIuNzkzLDcwLjExMiAxMzMuOTc3LDcwLjExMiBDMTM0Ljk5NSw3MC4xMTIgMTM1Ljc5NSw3MC42MzggMTM2LjIzLDcxLjU5MiBMMTQyLjU4NCw4NS41MjYgTDE2OS41NjYsNjkuOTQ4IEwxNjkuNTY2LDE5LjYxNyBMOTQuNTE1LDYyLjk0OCBMOTQuNTE1LDYyLjk0OCBaIiBpZD0iRmlsbC0xOCIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMDkuODk0LDkyLjk0MyBMMTA5Ljg5NCw5Mi45NDMgQzEwOC4xMiw5Mi45NDMgMTA2LjY1Myw5Mi4yMTggMTA1LjY1LDkwLjgyMyBDMTA1LjU4Myw5MC43MzEgMTA1LjU5Myw5MC42MSAxMDUuNjczLDkwLjUyOSBDMTA1Ljc1Myw5MC40NDggMTA1Ljg4LDkwLjQ0IDEwNS45NzQsOTAuNTA2IEMxMDYuNzU0LDkxLjA1MyAxMDcuNjc5LDkxLjMzMyAxMDguNzI0LDkxLjMzMyBDMTEwLjA0Nyw5MS4zMzMgMTExLjQ3OCw5MC44OTQgMTEyLjk4LDkwLjAyNyBDMTE4LjI5MSw4Ni45NiAxMjIuNjExLDc5LjUwOSAxMjIuNjExLDczLjQxNiBDMTIyLjYxMSw3MS40ODkgMTIyLjE2OSw2OS44NTYgMTIxLjMzMyw2OC42OTIgQzEyMS4yNjYsNjguNiAxMjEuMjc2LDY4LjQ3MyAxMjEuMzU2LDY4LjM5MiBDMTIxLjQzNiw2OC4zMTEgMTIxLjU2Myw2OC4yOTkgMTIxLjY1Niw2OC4zNjUgQzEyMy4zMjcsNjkuNTM3IDEyNC4yNDcsNzEuNzQ2IDEyNC4yNDcsNzQuNTg0IEMxMjQuMjQ3LDgwLjgyNiAxMTkuODIxLDg4LjQ0NyAxMTQuMzgyLDkxLjU4NyBDMTEyLjgwOCw5Mi40OTUgMTExLjI5OCw5Mi45NDMgMTA5Ljg5NCw5Mi45NDMgTDEwOS44OTQsOTIuOTQzIFogTTEwNi45MjUsOTEuNDAxIEMxMDcuNzM4LDkyLjA1MiAxMDguNzQ1LDkyLjI3OCAxMDkuODkzLDkyLjI3OCBMMTA5Ljg5NCw5Mi4yNzggQzExMS4yMTUsOTIuMjc4IDExMi42NDcsOTEuOTUxIDExNC4xNDgsOTEuMDg0IEMxMTkuNDU5LDg4LjAxNyAxMjMuNzgsODAuNjIxIDEyMy43OCw3NC41MjggQzEyMy43OCw3Mi41NDkgMTIzLjMxNyw3MC45MjkgMTIyLjQ1NCw2OS43NjcgQzEyMi44NjUsNzAuODAyIDEyMy4wNzksNzIuMDQyIDEyMy4wNzksNzMuNDAyIEMxMjMuMDc5LDc5LjY0NSAxMTguNjUzLDg3LjI4NSAxMTMuMjE0LDkwLjQyNSBDMTExLjY0LDkxLjMzNCAxMTAuMTMsOTEuNzQyIDEwOC43MjQsOTEuNzQyIEMxMDguMDgzLDkxLjc0MiAxMDcuNDgxLDkxLjU5MyAxMDYuOTI1LDkxLjQwMSBMMTA2LjkyNSw5MS40MDEgWiIgaWQ9IkZpbGwtMTkiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEzLjA5Nyw5MC4yMyBDMTE4LjQ4MSw4Ny4xMjIgMTIyLjg0NSw3OS41OTQgMTIyLjg0NSw3My40MTYgQzEyMi44NDUsNzEuMzY1IDEyMi4zNjIsNjkuNzI0IDEyMS41MjIsNjguNTU2IEMxMTkuNzM4LDY3LjMwNCAxMTcuMTQ4LDY3LjM2MiAxMTQuMjY1LDY5LjAyNiBDMTA4Ljg4MSw3Mi4xMzQgMTA0LjUxNyw3OS42NjIgMTA0LjUxNyw4NS44NCBDMTA0LjUxNyw4Ny44OTEgMTA1LDg5LjUzMiAxMDUuODQsOTAuNyBDMTA3LjYyNCw5MS45NTIgMTEwLjIxNCw5MS44OTQgMTEzLjA5Nyw5MC4yMyIgaWQ9IkZpbGwtMjAiIGZpbGw9IiNGQUZBRkEiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTA4LjcyNCw5MS42MTQgTDEwOC43MjQsOTEuNjE0IEMxMDcuNTgyLDkxLjYxNCAxMDYuNTY2LDkxLjQwMSAxMDUuNzA1LDkwLjc5NyBDMTA1LjY4NCw5MC43ODMgMTA1LjY2NSw5MC44MTEgMTA1LjY1LDkwLjc5IEMxMDQuNzU2LDg5LjU0NiAxMDQuMjgzLDg3Ljg0MiAxMDQuMjgzLDg1LjgxNyBDMTA0LjI4Myw3OS41NzUgMTA4LjcwOSw3MS45NTMgMTE0LjE0OCw2OC44MTIgQzExNS43MjIsNjcuOTA0IDExNy4yMzIsNjcuNDQ5IDExOC42MzgsNjcuNDQ5IEMxMTkuNzgsNjcuNDQ5IDEyMC43OTYsNjcuNzU4IDEyMS42NTYsNjguMzYyIEMxMjEuNjc4LDY4LjM3NyAxMjEuNjk3LDY4LjM5NyAxMjEuNzEyLDY4LjQxOCBDMTIyLjYwNiw2OS42NjIgMTIzLjA3OSw3MS4zOSAxMjMuMDc5LDczLjQxNSBDMTIzLjA3OSw3OS42NTggMTE4LjY1Myw4Ny4xOTggMTEzLjIxNCw5MC4zMzggQzExMS42NCw5MS4yNDcgMTEwLjEzLDkxLjYxNCAxMDguNzI0LDkxLjYxNCBMMTA4LjcyNCw5MS42MTQgWiBNMTA2LjAwNiw5MC41MDUgQzEwNi43OCw5MS4wMzcgMTA3LjY5NCw5MS4yODEgMTA4LjcyNCw5MS4yODEgQzExMC4wNDcsOTEuMjgxIDExMS40NzgsOTAuODY4IDExMi45OCw5MC4wMDEgQzExOC4yOTEsODYuOTM1IDEyMi42MTEsNzkuNDk2IDEyMi42MTEsNzMuNDAzIEMxMjIuNjExLDcxLjQ5NCAxMjIuMTc3LDY5Ljg4IDEyMS4zNTYsNjguNzE4IEMxMjAuNTgyLDY4LjE4NSAxMTkuNjY4LDY3LjkxOSAxMTguNjM4LDY3LjkxOSBDMTE3LjMxNSw2Ny45MTkgMTE1Ljg4Myw2OC4zNiAxMTQuMzgyLDY5LjIyNyBDMTA5LjA3MSw3Mi4yOTMgMTA0Ljc1MSw3OS43MzMgMTA0Ljc1MSw4NS44MjYgQzEwNC43NTEsODcuNzM1IDEwNS4xODUsODkuMzQzIDEwNi4wMDYsOTAuNTA1IEwxMDYuMDA2LDkwLjUwNSBaIiBpZD0iRmlsbC0yMSIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNDkuMzE4LDcuMjYyIEwxMzkuMzM0LDE2LjE0IEwxNTUuMjI3LDI3LjE3MSBMMTYwLjgxNiwyMS4wNTkgTDE0OS4zMTgsNy4yNjIiIGlkPSJGaWxsLTIyIiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE2OS42NzYsMTMuODQgTDE1OS45MjgsMTkuNDY3IEMxNTYuMjg2LDIxLjU3IDE1MC40LDIxLjU4IDE0Ni43ODEsMTkuNDkxIEMxNDMuMTYxLDE3LjQwMiAxNDMuMTgsMTQuMDAzIDE0Ni44MjIsMTEuOSBMMTU2LjMxNyw2LjI5MiBMMTQ5LjU4OCwyLjQwNyBMNjcuNzUyLDQ5LjQ3OCBMMTEzLjY3NSw3NS45OTIgTDExNi43NTYsNzQuMjEzIEMxMTcuMzg3LDczLjg0OCAxMTcuNjI1LDczLjMxNSAxMTcuMzc0LDcyLjgyMyBDMTE1LjAxNyw2OC4xOTEgMTE0Ljc4MSw2My4yNzcgMTE2LjY5MSw1OC41NjEgQzEyMi4zMjksNDQuNjQxIDE0MS4yLDMzLjc0NiAxNjUuMzA5LDMwLjQ5MSBDMTczLjQ3OCwyOS4zODggMTgxLjk4OSwyOS41MjQgMTkwLjAxMywzMC44ODUgQzE5MC44NjUsMzEuMDMgMTkxLjc4OSwzMC44OTMgMTkyLjQyLDMwLjUyOCBMMTk1LjUwMSwyOC43NSBMMTY5LjY3NiwxMy44NCIgaWQ9IkZpbGwtMjMiIGZpbGw9IiNGQUZBRkEiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEzLjY3NSw3Ni40NTkgQzExMy41OTQsNzYuNDU5IDExMy41MTQsNzYuNDM4IDExMy40NDIsNzYuMzk3IEw2Ny41MTgsNDkuODgyIEM2Ny4zNzQsNDkuNzk5IDY3LjI4NCw0OS42NDUgNjcuMjg1LDQ5LjQ3OCBDNjcuMjg1LDQ5LjMxMSA2Ny4zNzQsNDkuMTU3IDY3LjUxOSw0OS4wNzMgTDE0OS4zNTUsMi4wMDIgQzE0OS40OTksMS45MTkgMTQ5LjY3NywxLjkxOSAxNDkuODIxLDIuMDAyIEwxNTYuNTUsNS44ODcgQzE1Ni43NzQsNi4wMTcgMTU2Ljg1LDYuMzAyIDE1Ni43MjIsNi41MjYgQzE1Ni41OTIsNi43NDkgMTU2LjMwNyw2LjgyNiAxNTYuMDgzLDYuNjk2IEwxNDkuNTg3LDIuOTQ2IEw2OC42ODcsNDkuNDc5IEwxMTMuNjc1LDc1LjQ1MiBMMTE2LjUyMyw3My44MDggQzExNi43MTUsNzMuNjk3IDExNy4xNDMsNzMuMzk5IDExNi45NTgsNzMuMDM1IEMxMTQuNTQyLDY4LjI4NyAxMTQuMyw2My4yMjEgMTE2LjI1OCw1OC4zODUgQzExOS4wNjQsNTEuNDU4IDEyNS4xNDMsNDUuMTQzIDEzMy44NCw0MC4xMjIgQzE0Mi40OTcsMzUuMTI0IDE1My4zNTgsMzEuNjMzIDE2NS4yNDcsMzAuMDI4IEMxNzMuNDQ1LDI4LjkyMSAxODIuMDM3LDI5LjA1OCAxOTAuMDkxLDMwLjQyNSBDMTkwLjgzLDMwLjU1IDE5MS42NTIsMzAuNDMyIDE5Mi4xODYsMzAuMTI0IEwxOTQuNTY3LDI4Ljc1IEwxNjkuNDQyLDE0LjI0NCBDMTY5LjIxOSwxNC4xMTUgMTY5LjE0MiwxMy44MjkgMTY5LjI3MSwxMy42MDYgQzE2OS40LDEzLjM4MiAxNjkuNjg1LDEzLjMwNiAxNjkuOTA5LDEzLjQzNSBMMTk1LjczNCwyOC4zNDUgQzE5NS44NzksMjguNDI4IDE5NS45NjgsMjguNTgzIDE5NS45NjgsMjguNzUgQzE5NS45NjgsMjguOTE2IDE5NS44NzksMjkuMDcxIDE5NS43MzQsMjkuMTU0IEwxOTIuNjUzLDMwLjkzMyBDMTkxLjkzMiwzMS4zNSAxOTAuODksMzEuNTA4IDE4OS45MzUsMzEuMzQ2IEMxODEuOTcyLDI5Ljk5NSAxNzMuNDc4LDI5Ljg2IDE2NS4zNzIsMzAuOTU0IEMxNTMuNjAyLDMyLjU0MyAxNDIuODYsMzUuOTkzIDEzNC4zMDcsNDAuOTMxIEMxMjUuNzkzLDQ1Ljg0NyAxMTkuODUxLDUyLjAwNCAxMTcuMTI0LDU4LjczNiBDMTE1LjI3LDYzLjMxNCAxMTUuNTAxLDY4LjExMiAxMTcuNzksNzIuNjExIEMxMTguMTYsNzMuMzM2IDExNy44NDUsNzQuMTI0IDExNi45OSw3NC42MTcgTDExMy45MDksNzYuMzk3IEMxMTMuODM2LDc2LjQzOCAxMTMuNzU2LDc2LjQ1OSAxMTMuNjc1LDc2LjQ1OSIgaWQ9IkZpbGwtMjQiIGZpbGw9IiM0NTVBNjQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTUzLjMxNiwyMS4yNzkgQzE1MC45MDMsMjEuMjc5IDE0OC40OTUsMjAuNzUxIDE0Ni42NjQsMTkuNjkzIEMxNDQuODQ2LDE4LjY0NCAxNDMuODQ0LDE3LjIzMiAxNDMuODQ0LDE1LjcxOCBDMTQzLjg0NCwxNC4xOTEgMTQ0Ljg2LDEyLjc2MyAxNDYuNzA1LDExLjY5OCBMMTU2LjE5OCw2LjA5MSBDMTU2LjMwOSw2LjAyNSAxNTYuNDUyLDYuMDYyIDE1Ni41MTgsNi4xNzMgQzE1Ni41ODMsNi4yODQgMTU2LjU0Nyw2LjQyNyAxNTYuNDM2LDYuNDkzIEwxNDYuOTQsMTIuMTAyIEMxNDUuMjQ0LDEzLjA4MSAxNDQuMzEyLDE0LjM2NSAxNDQuMzEyLDE1LjcxOCBDMTQ0LjMxMiwxNy4wNTggMTQ1LjIzLDE4LjMyNiAxNDYuODk3LDE5LjI4OSBDMTUwLjQ0NiwyMS4zMzggMTU2LjI0LDIxLjMyNyAxNTkuODExLDE5LjI2NSBMMTY5LjU1OSwxMy42MzcgQzE2OS42NywxMy41NzMgMTY5LjgxMywxMy42MTEgMTY5Ljg3OCwxMy43MjMgQzE2OS45NDMsMTMuODM0IDE2OS45MDQsMTMuOTc3IDE2OS43OTMsMTQuMDQyIEwxNjAuMDQ1LDE5LjY3IEMxNTguMTg3LDIwLjc0MiAxNTUuNzQ5LDIxLjI3OSAxNTMuMzE2LDIxLjI3OSIgaWQ9IkZpbGwtMjUiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEzLjY3NSw3NS45OTIgTDY3Ljc2Miw0OS40ODQiIGlkPSJGaWxsLTI2IiBmaWxsPSIjNDU1QTY0Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTExMy42NzUsNzYuMzQyIEMxMTMuNjE1LDc2LjM0MiAxMTMuNTU1LDc2LjMyNyAxMTMuNSw3Ni4yOTUgTDY3LjU4Nyw0OS43ODcgQzY3LjQxOSw0OS42OSA2Ny4zNjIsNDkuNDc2IDY3LjQ1OSw0OS4zMDkgQzY3LjU1Niw0OS4xNDEgNjcuNzcsNDkuMDgzIDY3LjkzNyw0OS4xOCBMMTEzLjg1LDc1LjY4OCBDMTE0LjAxOCw3NS43ODUgMTE0LjA3NSw3NiAxMTMuOTc4LDc2LjE2NyBDMTEzLjkxNCw3Ni4yNzkgMTEzLjc5Niw3Ni4zNDIgMTEzLjY3NSw3Ni4zNDIiIGlkPSJGaWxsLTI3IiBmaWxsPSIjNDU1QTY0Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTY3Ljc2Miw0OS40ODQgTDY3Ljc2MiwxMDMuNDg1IEM2Ny43NjIsMTA0LjU3NSA2OC41MzIsMTA1LjkwMyA2OS40ODIsMTA2LjQ1MiBMMTExLjk1NSwxMzAuOTczIEMxMTIuOTA1LDEzMS41MjIgMTEzLjY3NSwxMzEuMDgzIDExMy42NzUsMTI5Ljk5MyBMMTEzLjY3NSw3NS45OTIiIGlkPSJGaWxsLTI4IiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTExMi43MjcsMTMxLjU2MSBDMTEyLjQzLDEzMS41NjEgMTEyLjEwNywxMzEuNDY2IDExMS43OCwxMzEuMjc2IEw2OS4zMDcsMTA2Ljc1NSBDNjguMjQ0LDEwNi4xNDIgNjcuNDEyLDEwNC43MDUgNjcuNDEyLDEwMy40ODUgTDY3LjQxMiw0OS40ODQgQzY3LjQxMiw0OS4yOSA2Ny41NjksNDkuMTM0IDY3Ljc2Miw0OS4xMzQgQzY3Ljk1Niw0OS4xMzQgNjguMTEzLDQ5LjI5IDY4LjExMyw0OS40ODQgTDY4LjExMywxMDMuNDg1IEM2OC4xMTMsMTA0LjQ0NSA2OC44MiwxMDUuNjY1IDY5LjY1NywxMDYuMTQ4IEwxMTIuMTMsMTMwLjY3IEMxMTIuNDc0LDEzMC44NjggMTEyLjc5MSwxMzAuOTEzIDExMywxMzAuNzkyIEMxMTMuMjA2LDEzMC42NzMgMTEzLjMyNSwxMzAuMzgxIDExMy4zMjUsMTI5Ljk5MyBMMTEzLjMyNSw3NS45OTIgQzExMy4zMjUsNzUuNzk4IDExMy40ODIsNzUuNjQxIDExMy42NzUsNzUuNjQxIEMxMTMuODY5LDc1LjY0MSAxMTQuMDI1LDc1Ljc5OCAxMTQuMDI1LDc1Ljk5MiBMMTE0LjAyNSwxMjkuOTkzIEMxMTQuMDI1LDEzMC42NDggMTEzLjc4NiwxMzEuMTQ3IDExMy4zNSwxMzEuMzk5IEMxMTMuMTYyLDEzMS41MDcgMTEyLjk1MiwxMzEuNTYxIDExMi43MjcsMTMxLjU2MSIgaWQ9IkZpbGwtMjkiIGZpbGw9IiM0NTVBNjQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTEyLjg2LDQwLjUxMiBDMTEyLjg2LDQwLjUxMiAxMTIuODYsNDAuNTEyIDExMi44NTksNDAuNTEyIEMxMTAuNTQxLDQwLjUxMiAxMDguMzYsMzkuOTkgMTA2LjcxNywzOS4wNDEgQzEwNS4wMTIsMzguMDU3IDEwNC4wNzQsMzYuNzI2IDEwNC4wNzQsMzUuMjkyIEMxMDQuMDc0LDMzLjg0NyAxMDUuMDI2LDMyLjUwMSAxMDYuNzU0LDMxLjUwNCBMMTE4Ljc5NSwyNC41NTEgQzEyMC40NjMsMjMuNTg5IDEyMi42NjksMjMuMDU4IDEyNS4wMDcsMjMuMDU4IEMxMjcuMzI1LDIzLjA1OCAxMjkuNTA2LDIzLjU4MSAxMzEuMTUsMjQuNTMgQzEzMi44NTQsMjUuNTE0IDEzMy43OTMsMjYuODQ1IDEzMy43OTMsMjguMjc4IEMxMzMuNzkzLDI5LjcyNCAxMzIuODQxLDMxLjA2OSAxMzEuMTEzLDMyLjA2NyBMMTE5LjA3MSwzOS4wMTkgQzExNy40MDMsMzkuOTgyIDExNS4xOTcsNDAuNTEyIDExMi44Niw0MC41MTIgTDExMi44Niw0MC41MTIgWiBNMTI1LjAwNywyMy43NTkgQzEyMi43OSwyMy43NTkgMTIwLjcwOSwyNC4yNTYgMTE5LjE0NiwyNS4xNTggTDEwNy4xMDQsMzIuMTEgQzEwNS42MDIsMzIuOTc4IDEwNC43NzQsMzQuMTA4IDEwNC43NzQsMzUuMjkyIEMxMDQuNzc0LDM2LjQ2NSAxMDUuNTg5LDM3LjU4MSAxMDcuMDY3LDM4LjQzNCBDMTA4LjYwNSwzOS4zMjMgMTEwLjY2MywzOS44MTIgMTEyLjg1OSwzOS44MTIgTDExMi44NiwzOS44MTIgQzExNS4wNzYsMzkuODEyIDExNy4xNTgsMzkuMzE1IDExOC43MjEsMzguNDEzIEwxMzAuNzYyLDMxLjQ2IEMxMzIuMjY0LDMwLjU5MyAxMzMuMDkyLDI5LjQ2MyAxMzMuMDkyLDI4LjI3OCBDMTMzLjA5MiwyNy4xMDYgMTMyLjI3OCwyNS45OSAxMzAuOCwyNS4xMzYgQzEyOS4yNjEsMjQuMjQ4IDEyNy4yMDQsMjMuNzU5IDEyNS4wMDcsMjMuNzU5IEwxMjUuMDA3LDIzLjc1OSBaIiBpZD0iRmlsbC0zMCIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNjUuNjMsMTYuMjE5IEwxNTkuODk2LDE5LjUzIEMxNTYuNzI5LDIxLjM1OCAxNTEuNjEsMjEuMzY3IDE0OC40NjMsMTkuNTUgQzE0NS4zMTYsMTcuNzMzIDE0NS4zMzIsMTQuNzc4IDE0OC40OTksMTIuOTQ5IEwxNTQuMjMzLDkuNjM5IEwxNjUuNjMsMTYuMjE5IiBpZD0iRmlsbC0zMSIgZmlsbD0iI0ZBRkFGQSI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNTQuMjMzLDEwLjQ0OCBMMTY0LjIyOCwxNi4yMTkgTDE1OS41NDYsMTguOTIzIEMxNTguMTEyLDE5Ljc1IDE1Ni4xOTQsMjAuMjA2IDE1NC4xNDcsMjAuMjA2IEMxNTIuMTE4LDIwLjIwNiAxNTAuMjI0LDE5Ljc1NyAxNDguODE0LDE4Ljk0MyBDMTQ3LjUyNCwxOC4xOTkgMTQ2LjgxNCwxNy4yNDkgMTQ2LjgxNCwxNi4yNjkgQzE0Ni44MTQsMTUuMjc4IDE0Ny41MzcsMTQuMzE0IDE0OC44NSwxMy41NTYgTDE1NC4yMzMsMTAuNDQ4IE0xNTQuMjMzLDkuNjM5IEwxNDguNDk5LDEyLjk0OSBDMTQ1LjMzMiwxNC43NzggMTQ1LjMxNiwxNy43MzMgMTQ4LjQ2MywxOS41NSBDMTUwLjAzMSwyMC40NTUgMTUyLjA4NiwyMC45MDcgMTU0LjE0NywyMC45MDcgQzE1Ni4yMjQsMjAuOTA3IDE1OC4zMDYsMjAuNDQ3IDE1OS44OTYsMTkuNTMgTDE2NS42MywxNi4yMTkgTDE1NC4yMzMsOS42MzkiIGlkPSJGaWxsLTMyIiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE0NS40NDUsNzIuNjY3IEwxNDUuNDQ1LDcyLjY2NyBDMTQzLjY3Miw3Mi42NjcgMTQyLjIwNCw3MS44MTcgMTQxLjIwMiw3MC40MjIgQzE0MS4xMzUsNzAuMzMgMTQxLjE0NSw3MC4xNDcgMTQxLjIyNSw3MC4wNjYgQzE0MS4zMDUsNjkuOTg1IDE0MS40MzIsNjkuOTQ2IDE0MS41MjUsNzAuMDExIEMxNDIuMzA2LDcwLjU1OSAxNDMuMjMxLDcwLjgyMyAxNDQuMjc2LDcwLjgyMiBDMTQ1LjU5OCw3MC44MjIgMTQ3LjAzLDcwLjM3NiAxNDguNTMyLDY5LjUwOSBDMTUzLjg0Miw2Ni40NDMgMTU4LjE2Myw1OC45ODcgMTU4LjE2Myw1Mi44OTQgQzE1OC4xNjMsNTAuOTY3IDE1Ny43MjEsNDkuMzMyIDE1Ni44ODQsNDguMTY4IEMxNTYuODE4LDQ4LjA3NiAxNTYuODI4LDQ3Ljk0OCAxNTYuOTA4LDQ3Ljg2NyBDMTU2Ljk4OCw0Ny43ODYgMTU3LjExNCw0Ny43NzQgMTU3LjIwOCw0Ny44NCBDMTU4Ljg3OCw0OS4wMTIgMTU5Ljc5OCw1MS4yMiAxNTkuNzk4LDU0LjA1OSBDMTU5Ljc5OCw2MC4zMDEgMTU1LjM3Myw2OC4wNDYgMTQ5LjkzMyw3MS4xODYgQzE0OC4zNiw3Mi4wOTQgMTQ2Ljg1LDcyLjY2NyAxNDUuNDQ1LDcyLjY2NyBMMTQ1LjQ0NSw3Mi42NjcgWiBNMTQyLjQ3Niw3MSBDMTQzLjI5LDcxLjY1MSAxNDQuMjk2LDcyLjAwMiAxNDUuNDQ1LDcyLjAwMiBDMTQ2Ljc2Nyw3Mi4wMDIgMTQ4LjE5OCw3MS41NSAxNDkuNyw3MC42ODIgQzE1NS4wMSw2Ny42MTcgMTU5LjMzMSw2MC4xNTkgMTU5LjMzMSw1NC4wNjUgQzE1OS4zMzEsNTIuMDg1IDE1OC44NjgsNTAuNDM1IDE1OC4wMDYsNDkuMjcyIEMxNTguNDE3LDUwLjMwNyAxNTguNjMsNTEuNTMyIDE1OC42Myw1Mi44OTIgQzE1OC42Myw1OS4xMzQgMTU0LjIwNSw2Ni43NjcgMTQ4Ljc2NSw2OS45MDcgQzE0Ny4xOTIsNzAuODE2IDE0NS42ODEsNzEuMjgzIDE0NC4yNzYsNzEuMjgzIEMxNDMuNjM0LDcxLjI4MyAxNDMuMDMzLDcxLjE5MiAxNDIuNDc2LDcxIEwxNDIuNDc2LDcxIFoiIGlkPSJGaWxsLTMzIiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE0OC42NDgsNjkuNzA0IEMxNTQuMDMyLDY2LjU5NiAxNTguMzk2LDU5LjA2OCAxNTguMzk2LDUyLjg5MSBDMTU4LjM5Niw1MC44MzkgMTU3LjkxMyw0OS4xOTggMTU3LjA3NCw0OC4wMyBDMTU1LjI4OSw0Ni43NzggMTUyLjY5OSw0Ni44MzYgMTQ5LjgxNiw0OC41MDEgQzE0NC40MzMsNTEuNjA5IDE0MC4wNjgsNTkuMTM3IDE0MC4wNjgsNjUuMzE0IEMxNDAuMDY4LDY3LjM2NSAxNDAuNTUyLDY5LjAwNiAxNDEuMzkxLDcwLjE3NCBDMTQzLjE3Niw3MS40MjcgMTQ1Ljc2NSw3MS4zNjkgMTQ4LjY0OCw2OS43MDQiIGlkPSJGaWxsLTM0IiBmaWxsPSIjRkFGQUZBIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE0NC4yNzYsNzEuMjc2IEwxNDQuMjc2LDcxLjI3NiBDMTQzLjEzMyw3MS4yNzYgMTQyLjExOCw3MC45NjkgMTQxLjI1Nyw3MC4zNjUgQzE0MS4yMzYsNzAuMzUxIDE0MS4yMTcsNzAuMzMyIDE0MS4yMDIsNzAuMzExIEMxNDAuMzA3LDY5LjA2NyAxMzkuODM1LDY3LjMzOSAxMzkuODM1LDY1LjMxNCBDMTM5LjgzNSw1OS4wNzMgMTQ0LjI2LDUxLjQzOSAxNDkuNyw0OC4yOTggQzE1MS4yNzMsNDcuMzkgMTUyLjc4NCw0Ni45MjkgMTU0LjE4OSw0Ni45MjkgQzE1NS4zMzIsNDYuOTI5IDE1Ni4zNDcsNDcuMjM2IDE1Ny4yMDgsNDcuODM5IEMxNTcuMjI5LDQ3Ljg1NCAxNTcuMjQ4LDQ3Ljg3MyAxNTcuMjYzLDQ3Ljg5NCBDMTU4LjE1Nyw0OS4xMzggMTU4LjYzLDUwLjg2NSAxNTguNjMsNTIuODkxIEMxNTguNjMsNTkuMTMyIDE1NC4yMDUsNjYuNzY2IDE0OC43NjUsNjkuOTA3IEMxNDcuMTkyLDcwLjgxNSAxNDUuNjgxLDcxLjI3NiAxNDQuMjc2LDcxLjI3NiBMMTQ0LjI3Niw3MS4yNzYgWiBNMTQxLjU1OCw3MC4xMDQgQzE0Mi4zMzEsNzAuNjM3IDE0My4yNDUsNzEuMDA1IDE0NC4yNzYsNzEuMDA1IEMxNDUuNTk4LDcxLjAwNSAxNDcuMDMsNzAuNDY3IDE0OC41MzIsNjkuNiBDMTUzLjg0Miw2Ni41MzQgMTU4LjE2Myw1OS4wMzMgMTU4LjE2Myw1Mi45MzkgQzE1OC4xNjMsNTEuMDMxIDE1Ny43MjksNDkuMzg1IDE1Ni45MDcsNDguMjIzIEMxNTYuMTMzLDQ3LjY5MSAxNTUuMjE5LDQ3LjQwOSAxNTQuMTg5LDQ3LjQwOSBDMTUyLjg2Nyw0Ny40MDkgMTUxLjQzNSw0Ny44NDIgMTQ5LjkzMyw0OC43MDkgQzE0NC42MjMsNTEuNzc1IDE0MC4zMDIsNTkuMjczIDE0MC4zMDIsNjUuMzY2IEMxNDAuMzAyLDY3LjI3NiAxNDAuNzM2LDY4Ljk0MiAxNDEuNTU4LDcwLjEwNCBMMTQxLjU1OCw3MC4xMDQgWiIgaWQ9IkZpbGwtMzUiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTUwLjcyLDY1LjM2MSBMMTUwLjM1Nyw2NS4wNjYgQzE1MS4xNDcsNjQuMDkyIDE1MS44NjksNjMuMDQgMTUyLjUwNSw2MS45MzggQzE1My4zMTMsNjAuNTM5IDE1My45NzgsNTkuMDY3IDE1NC40ODIsNTcuNTYzIEwxNTQuOTI1LDU3LjcxMiBDMTU0LjQxMiw1OS4yNDUgMTUzLjczMyw2MC43NDUgMTUyLjkxLDYyLjE3MiBDMTUyLjI2Miw2My4yOTUgMTUxLjUyNSw2NC4zNjggMTUwLjcyLDY1LjM2MSIgaWQ9IkZpbGwtMzYiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTE1LjkxNyw4NC41MTQgTDExNS41NTQsODQuMjIgQzExNi4zNDQsODMuMjQ1IDExNy4wNjYsODIuMTk0IDExNy43MDIsODEuMDkyIEMxMTguNTEsNzkuNjkyIDExOS4xNzUsNzguMjIgMTE5LjY3OCw3Ni43MTcgTDEyMC4xMjEsNzYuODY1IEMxMTkuNjA4LDc4LjM5OCAxMTguOTMsNzkuODk5IDExOC4xMDYsODEuMzI2IEMxMTcuNDU4LDgyLjQ0OCAxMTYuNzIyLDgzLjUyMSAxMTUuOTE3LDg0LjUxNCIgaWQ9IkZpbGwtMzciIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTE0LDEzMC40NzYgTDExNCwxMzAuMDA4IEwxMTQsNzYuMDUyIEwxMTQsNzUuNTg0IEwxMTQsNzYuMDUyIEwxMTQsMTMwLjAwOCBMMTE0LDEzMC40NzYiIGlkPSJGaWxsLTM4IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICA8L2c+CiAgICAgICAgICAgICAgICA8ZyBpZD0iSW1wb3J0ZWQtTGF5ZXJzLUNvcHkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDYyLjAwMDAwMCwgMC4wMDAwMDApIiBza2V0Y2g6dHlwZT0iTVNTaGFwZUdyb3VwIj4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTkuODIyLDM3LjQ3NCBDMTkuODM5LDM3LjMzOSAxOS43NDcsMzcuMTk0IDE5LjU1NSwzNy4wODIgQzE5LjIyOCwzNi44OTQgMTguNzI5LDM2Ljg3MiAxOC40NDYsMzcuMDM3IEwxMi40MzQsNDAuNTA4IEMxMi4zMDMsNDAuNTg0IDEyLjI0LDQwLjY4NiAxMi4yNDMsNDAuNzkzIEMxMi4yNDUsNDAuOTI1IDEyLjI0NSw0MS4yNTQgMTIuMjQ1LDQxLjM3MSBMMTIuMjQ1LDQxLjQxNCBMMTIuMjM4LDQxLjU0MiBDOC4xNDgsNDMuODg3IDUuNjQ3LDQ1LjMyMSA1LjY0Nyw0NS4zMjEgQzUuNjQ2LDQ1LjMyMSAzLjU3LDQ2LjM2NyAyLjg2LDUwLjUxMyBDMi44Niw1MC41MTMgMS45NDgsNTcuNDc0IDEuOTYyLDcwLjI1OCBDMS45NzcsODIuODI4IDIuNTY4LDg3LjMyOCAzLjEyOSw5MS42MDkgQzMuMzQ5LDkzLjI5MyA2LjEzLDkzLjczNCA2LjEzLDkzLjczNCBDNi40NjEsOTMuNzc0IDYuODI4LDkzLjcwNyA3LjIxLDkzLjQ4NiBMODIuNDgzLDQ5LjkzNSBDODQuMjkxLDQ4Ljg2NiA4NS4xNSw0Ni4yMTYgODUuNTM5LDQzLjY1MSBDODYuNzUyLDM1LjY2MSA4Ny4yMTQsMTAuNjczIDg1LjI2NCwzLjc3MyBDODUuMDY4LDMuMDggODQuNzU0LDIuNjkgODQuMzk2LDIuNDkxIEw4Mi4zMSwxLjcwMSBDODEuNTgzLDEuNzI5IDgwLjg5NCwyLjE2OCA4MC43NzYsMi4yMzYgQzgwLjYzNiwyLjMxNyA0MS44MDcsMjQuNTg1IDIwLjAzMiwzNy4wNzIgTDE5LjgyMiwzNy40NzQiIGlkPSJGaWxsLTEiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNODIuMzExLDEuNzAxIEw4NC4zOTYsMi40OTEgQzg0Ljc1NCwyLjY5IDg1LjA2OCwzLjA4IDg1LjI2NCwzLjc3MyBDODcuMjEzLDEwLjY3MyA4Ni43NTEsMzUuNjYgODUuNTM5LDQzLjY1MSBDODUuMTQ5LDQ2LjIxNiA4NC4yOSw0OC44NjYgODIuNDgzLDQ5LjkzNSBMNy4yMSw5My40ODYgQzYuODk3LDkzLjY2NyA2LjU5NSw5My43NDQgNi4zMTQsOTMuNzQ0IEw2LjEzMSw5My43MzMgQzYuMTMxLDkzLjczNCAzLjM0OSw5My4yOTMgMy4xMjgsOTEuNjA5IEMyLjU2OCw4Ny4zMjcgMS45NzcsODIuODI4IDEuOTYzLDcwLjI1OCBDMS45NDgsNTcuNDc0IDIuODYsNTAuNTEzIDIuODYsNTAuNTEzIEMzLjU3LDQ2LjM2NyA1LjY0Nyw0NS4zMjEgNS42NDcsNDUuMzIxIEM1LjY0Nyw0NS4zMjEgOC4xNDgsNDMuODg3IDEyLjIzOCw0MS41NDIgTDEyLjI0NSw0MS40MTQgTDEyLjI0NSw0MS4zNzEgQzEyLjI0NSw0MS4yNTQgMTIuMjQ1LDQwLjkyNSAxMi4yNDMsNDAuNzkzIEMxMi4yNCw0MC42ODYgMTIuMzAyLDQwLjU4MyAxMi40MzQsNDAuNTA4IEwxOC40NDYsMzcuMDM2IEMxOC41NzQsMzYuOTYyIDE4Ljc0NiwzNi45MjYgMTguOTI3LDM2LjkyNiBDMTkuMTQ1LDM2LjkyNiAxOS4zNzYsMzYuOTc5IDE5LjU1NCwzNy4wODIgQzE5Ljc0NywzNy4xOTQgMTkuODM5LDM3LjM0IDE5LjgyMiwzNy40NzQgTDIwLjAzMywzNy4wNzIgQzQxLjgwNiwyNC41ODUgODAuNjM2LDIuMzE4IDgwLjc3NywyLjIzNiBDODAuODk0LDIuMTY4IDgxLjU4MywxLjcyOSA4Mi4zMTEsMS43MDEgTTgyLjMxMSwwLjcwNCBMODIuMjcyLDAuNzA1IEM4MS42NTQsMC43MjggODAuOTg5LDAuOTQ5IDgwLjI5OCwxLjM2MSBMODAuMjc3LDEuMzczIEM4MC4xMjksMS40NTggNTkuNzY4LDEzLjEzNSAxOS43NTgsMzYuMDc5IEMxOS41LDM1Ljk4MSAxOS4yMTQsMzUuOTI5IDE4LjkyNywzNS45MjkgQzE4LjU2MiwzNS45MjkgMTguMjIzLDM2LjAxMyAxNy45NDcsMzYuMTczIEwxMS45MzUsMzkuNjQ0IEMxMS40OTMsMzkuODk5IDExLjIzNiw0MC4zMzQgMTEuMjQ2LDQwLjgxIEwxMS4yNDcsNDAuOTYgTDUuMTY3LDQ0LjQ0NyBDNC43OTQsNDQuNjQ2IDIuNjI1LDQ1Ljk3OCAxLjg3Nyw1MC4zNDUgTDEuODcxLDUwLjM4NCBDMS44NjIsNTAuNDU0IDAuOTUxLDU3LjU1NyAwLjk2NSw3MC4yNTkgQzAuOTc5LDgyLjg3OSAxLjU2OCw4Ny4zNzUgMi4xMzcsOTEuNzI0IEwyLjEzOSw5MS43MzkgQzIuNDQ3LDk0LjA5NCA1LjYxNCw5NC42NjIgNS45NzUsOTQuNzE5IEw2LjAwOSw5NC43MjMgQzYuMTEsOTQuNzM2IDYuMjEzLDk0Ljc0MiA2LjMxNCw5NC43NDIgQzYuNzksOTQuNzQyIDcuMjYsOTQuNjEgNy43MSw5NC4zNSBMODIuOTgzLDUwLjc5OCBDODQuNzk0LDQ5LjcyNyA4NS45ODIsNDcuMzc1IDg2LjUyNSw0My44MDEgQzg3LjcxMSwzNS45ODcgODguMjU5LDEwLjcwNSA4Ni4yMjQsMy41MDIgQzg1Ljk3MSwyLjYwOSA4NS41MiwxLjk3NSA4NC44ODEsMS42MiBMODQuNzQ5LDEuNTU4IEw4Mi42NjQsMC43NjkgQzgyLjU1MSwwLjcyNSA4Mi40MzEsMC43MDQgODIuMzExLDAuNzA0IiBpZD0iRmlsbC0yIiBmaWxsPSIjNDU1QTY0Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTY2LjI2NywxMS41NjUgTDY3Ljc2MiwxMS45OTkgTDExLjQyMyw0NC4zMjUiIGlkPSJGaWxsLTMiIGZpbGw9IiNGRkZGRkYiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTIuMjAyLDkwLjU0NSBDMTIuMDI5LDkwLjU0NSAxMS44NjIsOTAuNDU1IDExLjc2OSw5MC4yOTUgQzExLjYzMiw5MC4wNTcgMTEuNzEzLDg5Ljc1MiAxMS45NTIsODkuNjE0IEwzMC4zODksNzguOTY5IEMzMC42MjgsNzguODMxIDMwLjkzMyw3OC45MTMgMzEuMDcxLDc5LjE1MiBDMzEuMjA4LDc5LjM5IDMxLjEyNyw3OS42OTYgMzAuODg4LDc5LjgzMyBMMTIuNDUxLDkwLjQ3OCBMMTIuMjAyLDkwLjU0NSIgaWQ9IkZpbGwtNCIgZmlsbD0iIzYwN0Q4QiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMy43NjQsNDIuNjU0IEwxMy42NTYsNDIuNTkyIEwxMy43MDIsNDIuNDIxIEwxOC44MzcsMzkuNDU3IEwxOS4wMDcsMzkuNTAyIEwxOC45NjIsMzkuNjczIEwxMy44MjcsNDIuNjM3IEwxMy43NjQsNDIuNjU0IiBpZD0iRmlsbC01IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTguNTIsOTAuMzc1IEw4LjUyLDQ2LjQyMSBMOC41ODMsNDYuMzg1IEw3NS44NCw3LjU1NCBMNzUuODQsNTEuNTA4IEw3NS43NzgsNTEuNTQ0IEw4LjUyLDkwLjM3NSBMOC41Miw5MC4zNzUgWiBNOC43Nyw0Ni41NjQgTDguNzcsODkuOTQ0IEw3NS41OTEsNTEuMzY1IEw3NS41OTEsNy45ODUgTDguNzcsNDYuNTY0IEw4Ljc3LDQ2LjU2NCBaIiBpZD0iRmlsbC02IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTI0Ljk4Niw4My4xODIgQzI0Ljc1Niw4My4zMzEgMjQuMzc0LDgzLjU2NiAyNC4xMzcsODMuNzA1IEwxMi42MzIsOTAuNDA2IEMxMi4zOTUsOTAuNTQ1IDEyLjQyNiw5MC42NTggMTIuNyw5MC42NTggTDEzLjI2NSw5MC42NTggQzEzLjU0LDkwLjY1OCAxMy45NTgsOTAuNTQ1IDE0LjE5NSw5MC40MDYgTDI1LjcsODMuNzA1IEMyNS45MzcsODMuNTY2IDI2LjEyOCw4My40NTIgMjYuMTI1LDgzLjQ0OSBDMjYuMTIyLDgzLjQ0NyAyNi4xMTksODMuMjIgMjYuMTE5LDgyLjk0NiBDMjYuMTE5LDgyLjY3MiAyNS45MzEsODIuNTY5IDI1LjcwMSw4Mi43MTkgTDI0Ljk4Niw4My4xODIiIGlkPSJGaWxsLTciIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTMuMjY2LDkwLjc4MiBMMTIuNyw5MC43ODIgQzEyLjUsOTAuNzgyIDEyLjM4NCw5MC43MjYgMTIuMzU0LDkwLjYxNiBDMTIuMzI0LDkwLjUwNiAxMi4zOTcsOTAuMzk5IDEyLjU2OSw5MC4yOTkgTDI0LjA3NCw4My41OTcgQzI0LjMxLDgzLjQ1OSAyNC42ODksODMuMjI2IDI0LjkxOCw4My4wNzggTDI1LjYzMyw4Mi42MTQgQzI1LjcyMyw4Mi41NTUgMjUuODEzLDgyLjUyNSAyNS44OTksODIuNTI1IEMyNi4wNzEsODIuNTI1IDI2LjI0NCw4Mi42NTUgMjYuMjQ0LDgyLjk0NiBDMjYuMjQ0LDgzLjE2IDI2LjI0NSw4My4zMDkgMjYuMjQ3LDgzLjM4MyBMMjYuMjUzLDgzLjM4NyBMMjYuMjQ5LDgzLjQ1NiBDMjYuMjQ2LDgzLjUzMSAyNi4yNDYsODMuNTMxIDI1Ljc2Myw4My44MTIgTDE0LjI1OCw5MC41MTQgQzE0LDkwLjY2NSAxMy41NjQsOTAuNzgyIDEzLjI2Niw5MC43ODIgTDEzLjI2Niw5MC43ODIgWiBNMTIuNjY2LDkwLjUzMiBMMTIuNyw5MC41MzMgTDEzLjI2Niw5MC41MzMgQzEzLjUxOCw5MC41MzMgMTMuOTE1LDkwLjQyNSAxNC4xMzIsOTAuMjk5IEwyNS42MzcsODMuNTk3IEMyNS44MDUsODMuNDk5IDI1LjkzMSw4My40MjQgMjUuOTk4LDgzLjM4MyBDMjUuOTk0LDgzLjI5OSAyNS45OTQsODMuMTY1IDI1Ljk5NCw4Mi45NDYgTDI1Ljg5OSw4Mi43NzUgTDI1Ljc2OCw4Mi44MjQgTDI1LjA1NCw4My4yODcgQzI0LjgyMiw4My40MzcgMjQuNDM4LDgzLjY3MyAyNC4yLDgzLjgxMiBMMTIuNjk1LDkwLjUxNCBMMTIuNjY2LDkwLjUzMiBMMTIuNjY2LDkwLjUzMiBaIiBpZD0iRmlsbC04IiBmaWxsPSIjNjA3RDhCIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTEzLjI2Niw4OS44NzEgTDEyLjcsODkuODcxIEMxMi41LDg5Ljg3MSAxMi4zODQsODkuODE1IDEyLjM1NCw4OS43MDUgQzEyLjMyNCw4OS41OTUgMTIuMzk3LDg5LjQ4OCAxMi41NjksODkuMzg4IEwyNC4wNzQsODIuNjg2IEMyNC4zMzIsODIuNTM1IDI0Ljc2OCw4Mi40MTggMjUuMDY3LDgyLjQxOCBMMjUuNjMyLDgyLjQxOCBDMjUuODMyLDgyLjQxOCAyNS45NDgsODIuNDc0IDI1Ljk3OCw4Mi41ODQgQzI2LjAwOCw4Mi42OTQgMjUuOTM1LDgyLjgwMSAyNS43NjMsODIuOTAxIEwxNC4yNTgsODkuNjAzIEMxNCw4OS43NTQgMTMuNTY0LDg5Ljg3MSAxMy4yNjYsODkuODcxIEwxMy4yNjYsODkuODcxIFogTTEyLjY2Niw4OS42MjEgTDEyLjcsODkuNjIyIEwxMy4yNjYsODkuNjIyIEMxMy41MTgsODkuNjIyIDEzLjkxNSw4OS41MTUgMTQuMTMyLDg5LjM4OCBMMjUuNjM3LDgyLjY4NiBMMjUuNjY3LDgyLjY2OCBMMjUuNjMyLDgyLjY2NyBMMjUuMDY3LDgyLjY2NyBDMjQuODE1LDgyLjY2NyAyNC40MTgsODIuNzc1IDI0LjIsODIuOTAxIEwxMi42OTUsODkuNjAzIEwxMi42NjYsODkuNjIxIEwxMi42NjYsODkuNjIxIFoiIGlkPSJGaWxsLTkiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTIuMzcsOTAuODAxIEwxMi4zNyw4OS41NTQgTDEyLjM3LDkwLjgwMSIgaWQ9IkZpbGwtMTAiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNi4xMyw5My45MDEgQzUuMzc5LDkzLjgwOCA0LjgxNiw5My4xNjQgNC42OTEsOTIuNTI1IEMzLjg2LDg4LjI4NyAzLjU0LDgzLjc0MyAzLjUyNiw3MS4xNzMgQzMuNTExLDU4LjM4OSA0LjQyMyw1MS40MjggNC40MjMsNTEuNDI4IEM1LjEzNCw0Ny4yODIgNy4yMSw0Ni4yMzYgNy4yMSw0Ni4yMzYgQzcuMjEsNDYuMjM2IDgxLjY2NywzLjI1IDgyLjA2OSwzLjAxNyBDODIuMjkyLDIuODg4IDg0LjU1NiwxLjQzMyA4NS4yNjQsMy45NCBDODcuMjE0LDEwLjg0IDg2Ljc1MiwzNS44MjcgODUuNTM5LDQzLjgxOCBDODUuMTUsNDYuMzgzIDg0LjI5MSw0OS4wMzMgODIuNDgzLDUwLjEwMSBMNy4yMSw5My42NTMgQzYuODI4LDkzLjg3NCA2LjQ2MSw5My45NDEgNi4xMyw5My45MDEgQzYuMTMsOTMuOTAxIDMuMzQ5LDkzLjQ2IDMuMTI5LDkxLjc3NiBDMi41NjgsODcuNDk1IDEuOTc3LDgyLjk5NSAxLjk2Miw3MC40MjUgQzEuOTQ4LDU3LjY0MSAyLjg2LDUwLjY4IDIuODYsNTAuNjggQzMuNTcsNDYuNTM0IDUuNjQ3LDQ1LjQ4OSA1LjY0Nyw0NS40ODkgQzUuNjQ2LDQ1LjQ4OSA4LjA2NSw0NC4wOTIgMTIuMjQ1LDQxLjY3OSBMMTMuMTE2LDQxLjU2IEwxOS43MTUsMzcuNzMgTDE5Ljc2MSwzNy4yNjkgTDYuMTMsOTMuOTAxIiBpZD0iRmlsbC0xMSIgZmlsbD0iI0ZBRkFGQSI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik02LjMxNyw5NC4xNjEgTDYuMTAyLDk0LjE0OCBMNi4xMDEsOTQuMTQ4IEw1Ljg1Nyw5NC4xMDEgQzUuMTM4LDkzLjk0NSAzLjA4NSw5My4zNjUgMi44ODEsOTEuODA5IEMyLjMxMyw4Ny40NjkgMS43MjcsODIuOTk2IDEuNzEzLDcwLjQyNSBDMS42OTksNTcuNzcxIDIuNjA0LDUwLjcxOCAyLjYxMyw1MC42NDggQzMuMzM4LDQ2LjQxNyA1LjQ0NSw0NS4zMSA1LjUzNSw0NS4yNjYgTDEyLjE2Myw0MS40MzkgTDEzLjAzMyw0MS4zMiBMMTkuNDc5LDM3LjU3OCBMMTkuNTEzLDM3LjI0NCBDMTkuNTI2LDM3LjEwNyAxOS42NDcsMzcuMDA4IDE5Ljc4NiwzNy4wMjEgQzE5LjkyMiwzNy4wMzQgMjAuMDIzLDM3LjE1NiAyMC4wMDksMzcuMjkzIEwxOS45NSwzNy44ODIgTDEzLjE5OCw0MS44MDEgTDEyLjMyOCw0MS45MTkgTDUuNzcyLDQ1LjcwNCBDNS43NDEsNDUuNzIgMy43ODIsNDYuNzcyIDMuMTA2LDUwLjcyMiBDMy4wOTksNTAuNzgyIDIuMTk4LDU3LjgwOCAyLjIxMiw3MC40MjQgQzIuMjI2LDgyLjk2MyAyLjgwOSw4Ny40MiAzLjM3Myw5MS43MjkgQzMuNDY0LDkyLjQyIDQuMDYyLDkyLjg4MyA0LjY4Miw5My4xODEgQzQuNTY2LDkyLjk4NCA0LjQ4Niw5Mi43NzYgNC40NDYsOTIuNTcyIEMzLjY2NSw4OC41ODggMy4yOTEsODQuMzcgMy4yNzYsNzEuMTczIEMzLjI2Miw1OC41MiA0LjE2Nyw1MS40NjYgNC4xNzYsNTEuMzk2IEM0LjkwMSw0Ny4xNjUgNy4wMDgsNDYuMDU5IDcuMDk4LDQ2LjAxNCBDNy4wOTQsNDYuMDE1IDgxLjU0MiwzLjAzNCA4MS45NDQsMi44MDIgTDgxLjk3MiwyLjc4NSBDODIuODc2LDIuMjQ3IDgzLjY5MiwyLjA5NyA4NC4zMzIsMi4zNTIgQzg0Ljg4NywyLjU3MyA4NS4yODEsMy4wODUgODUuNTA0LDMuODcyIEM4Ny41MTgsMTEgODYuOTY0LDM2LjA5MSA4NS43ODUsNDMuODU1IEM4NS4yNzgsNDcuMTk2IDg0LjIxLDQ5LjM3IDgyLjYxLDUwLjMxNyBMNy4zMzUsOTMuODY5IEM2Ljk5OSw5NC4wNjMgNi42NTgsOTQuMTYxIDYuMzE3LDk0LjE2MSBMNi4zMTcsOTQuMTYxIFogTTYuMTcsOTMuNjU0IEM2LjQ2Myw5My42OSA2Ljc3NCw5My42MTcgNy4wODUsOTMuNDM3IEw4Mi4zNTgsNDkuODg2IEM4NC4xODEsNDguODA4IDg0Ljk2LDQ1Ljk3MSA4NS4yOTIsNDMuNzggQzg2LjQ2NiwzNi4wNDkgODcuMDIzLDExLjA4NSA4NS4wMjQsNC4wMDggQzg0Ljg0NiwzLjM3NyA4NC41NTEsMi45NzYgODQuMTQ4LDIuODE2IEM4My42NjQsMi42MjMgODIuOTgyLDIuNzY0IDgyLjIyNywzLjIxMyBMODIuMTkzLDMuMjM0IEM4MS43OTEsMy40NjYgNy4zMzUsNDYuNDUyIDcuMzM1LDQ2LjQ1MiBDNy4zMDQsNDYuNDY5IDUuMzQ2LDQ3LjUyMSA0LjY2OSw1MS40NzEgQzQuNjYyLDUxLjUzIDMuNzYxLDU4LjU1NiAzLjc3NSw3MS4xNzMgQzMuNzksODQuMzI4IDQuMTYxLDg4LjUyNCA0LjkzNiw5Mi40NzYgQzUuMDI2LDkyLjkzNyA1LjQxMiw5My40NTkgNS45NzMsOTMuNjE1IEM2LjA4Nyw5My42NCA2LjE1OCw5My42NTIgNi4xNjksOTMuNjU0IEw2LjE3LDkzLjY1NCBMNi4xNyw5My42NTQgWiIgaWQ9IkZpbGwtMTIiIGZpbGw9IiM0NTVBNjQiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNy4zMTcsNjguOTgyIEM3LjgwNiw2OC43MDEgOC4yMDIsNjguOTI2IDguMjAyLDY5LjQ4NyBDOC4yMDIsNzAuMDQ3IDcuODA2LDcwLjczIDcuMzE3LDcxLjAxMiBDNi44MjksNzEuMjk0IDYuNDMzLDcxLjA2OSA2LjQzMyw3MC41MDggQzYuNDMzLDY5Ljk0OCA2LjgyOSw2OS4yNjUgNy4zMTcsNjguOTgyIiBpZD0iRmlsbC0xMyIgZmlsbD0iI0ZGRkZGRiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik02LjkyLDcxLjEzMyBDNi42MzEsNzEuMTMzIDYuNDMzLDcwLjkwNSA2LjQzMyw3MC41MDggQzYuNDMzLDY5Ljk0OCA2LjgyOSw2OS4yNjUgNy4zMTcsNjguOTgyIEM3LjQ2LDY4LjkgNy41OTUsNjguODYxIDcuNzE0LDY4Ljg2MSBDOC4wMDMsNjguODYxIDguMjAyLDY5LjA5IDguMjAyLDY5LjQ4NyBDOC4yMDIsNzAuMDQ3IDcuODA2LDcwLjczIDcuMzE3LDcxLjAxMiBDNy4xNzQsNzEuMDk0IDcuMDM5LDcxLjEzMyA2LjkyLDcxLjEzMyBNNy43MTQsNjguNjc0IEM3LjU1Nyw2OC42NzQgNy4zOTIsNjguNzIzIDcuMjI0LDY4LjgyMSBDNi42NzYsNjkuMTM4IDYuMjQ2LDY5Ljg3OSA2LjI0Niw3MC41MDggQzYuMjQ2LDcwLjk5NCA2LjUxNyw3MS4zMiA2LjkyLDcxLjMyIEM3LjA3OCw3MS4zMiA3LjI0Myw3MS4yNzEgNy40MTEsNzEuMTc0IEM3Ljk1OSw3MC44NTcgOC4zODksNzAuMTE3IDguMzg5LDY5LjQ4NyBDOC4zODksNjkuMDAxIDguMTE3LDY4LjY3NCA3LjcxNCw2OC42NzQiIGlkPSJGaWxsLTE0IiBmaWxsPSIjODA5N0EyIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTYuOTIsNzAuOTQ3IEM2LjY0OSw3MC45NDcgNi42MjEsNzAuNjQgNi42MjEsNzAuNTA4IEM2LjYyMSw3MC4wMTcgNi45ODIsNjkuMzkyIDcuNDExLDY5LjE0NSBDNy41MjEsNjkuMDgyIDcuNjI1LDY5LjA0OSA3LjcxNCw2OS4wNDkgQzcuOTg2LDY5LjA0OSA4LjAxNSw2OS4zNTUgOC4wMTUsNjkuNDg3IEM4LjAxNSw2OS45NzggNy42NTIsNzAuNjAzIDcuMjI0LDcwLjg1MSBDNy4xMTUsNzAuOTE0IDcuMDEsNzAuOTQ3IDYuOTIsNzAuOTQ3IE03LjcxNCw2OC44NjEgQzcuNTk1LDY4Ljg2MSA3LjQ2LDY4LjkgNy4zMTcsNjguOTgyIEM2LjgyOSw2OS4yNjUgNi40MzMsNjkuOTQ4IDYuNDMzLDcwLjUwOCBDNi40MzMsNzAuOTA1IDYuNjMxLDcxLjEzMyA2LjkyLDcxLjEzMyBDNy4wMzksNzEuMTMzIDcuMTc0LDcxLjA5NCA3LjMxNyw3MS4wMTIgQzcuODA2LDcwLjczIDguMjAyLDcwLjA0NyA4LjIwMiw2OS40ODcgQzguMjAyLDY5LjA5IDguMDAzLDY4Ljg2MSA3LjcxNCw2OC44NjEiIGlkPSJGaWxsLTE1IiBmaWxsPSIjODA5N0EyIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTcuNDQ0LDg1LjM1IEM3LjcwOCw4NS4xOTggNy45MjEsODUuMzE5IDcuOTIxLDg1LjYyMiBDNy45MjEsODUuOTI1IDcuNzA4LDg2LjI5MiA3LjQ0NCw4Ni40NDQgQzcuMTgxLDg2LjU5NyA2Ljk2Nyw4Ni40NzUgNi45NjcsODYuMTczIEM2Ljk2Nyw4NS44NzEgNy4xODEsODUuNTAyIDcuNDQ0LDg1LjM1IiBpZD0iRmlsbC0xNiIgZmlsbD0iI0ZGRkZGRiI+PC9wYXRoPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik03LjIzLDg2LjUxIEM3LjA3NCw4Ni41MSA2Ljk2Nyw4Ni4zODcgNi45NjcsODYuMTczIEM2Ljk2Nyw4NS44NzEgNy4xODEsODUuNTAyIDcuNDQ0LDg1LjM1IEM3LjUyMSw4NS4zMDUgNy41OTQsODUuMjg0IDcuNjU4LDg1LjI4NCBDNy44MTQsODUuMjg0IDcuOTIxLDg1LjQwOCA3LjkyMSw4NS42MjIgQzcuOTIxLDg1LjkyNSA3LjcwOCw4Ni4yOTIgNy40NDQsODYuNDQ0IEM3LjM2Nyw4Ni40ODkgNy4yOTQsODYuNTEgNy4yMyw4Ni41MSBNNy42NTgsODUuMDk4IEM3LjU1OCw4NS4wOTggNy40NTUsODUuMTI3IDcuMzUxLDg1LjE4OCBDNy4wMzEsODUuMzczIDYuNzgxLDg1LjgwNiA2Ljc4MSw4Ni4xNzMgQzYuNzgxLDg2LjQ4MiA2Ljk2Niw4Ni42OTcgNy4yMyw4Ni42OTcgQzcuMzMsODYuNjk3IDcuNDMzLDg2LjY2NiA3LjUzOCw4Ni42MDcgQzcuODU4LDg2LjQyMiA4LjEwOCw4NS45ODkgOC4xMDgsODUuNjIyIEM4LjEwOCw4NS4zMTMgNy45MjMsODUuMDk4IDcuNjU4LDg1LjA5OCIgaWQ9IkZpbGwtMTciIGZpbGw9IiM4MDk3QTIiPjwvcGF0aD4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNy4yMyw4Ni4zMjIgTDcuMTU0LDg2LjE3MyBDNy4xNTQsODUuOTM4IDcuMzMzLDg1LjYyOSA3LjUzOCw4NS41MTIgTDcuNjU4LDg1LjQ3MSBMNy43MzQsODUuNjIyIEM3LjczNCw4NS44NTYgNy41NTUsODYuMTY0IDcuMzUxLDg2LjI4MiBMNy4yMyw4Ni4zMjIgTTcuNjU4LDg1LjI4NCBDNy41OTQsODUuMjg0IDcuNTIxLDg1LjMwNSA3LjQ0NCw4NS4zNSBDNy4xODEsODUuNTAyIDYuOTY3LDg1Ljg3MSA2Ljk2Nyw4Ni4xNzMgQzYuOTY3LDg2LjM4NyA3LjA3NCw4Ni41MSA3LjIzLDg2LjUxIEM3LjI5NCw4Ni41MSA3LjM2Nyw4Ni40ODkgNy40NDQsODYuNDQ0IEM3LjcwOCw4Ni4yOTIgNy45MjEsODUuOTI1IDcuOTIxLDg1LjYyMiBDNy45MjEsODUuNDA4IDcuODE0LDg1LjI4NCA3LjY1OCw4NS4yODQiIGlkPSJGaWxsLTE4IiBmaWxsPSIjODA5N0EyIj48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTc3LjI3OCw3Ljc2OSBMNzcuMjc4LDUxLjQzNiBMMTAuMjA4LDkwLjE2IEwxMC4yMDgsNDYuNDkzIEw3Ny4yNzgsNy43NjkiIGlkPSJGaWxsLTE5IiBmaWxsPSIjNDU1QTY0Ij48L3BhdGg+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTEwLjA4Myw5MC4zNzUgTDEwLjA4Myw0Ni40MjEgTDEwLjE0Niw0Ni4zODUgTDc3LjQwMyw3LjU1NCBMNzcuNDAzLDUxLjUwOCBMNzcuMzQxLDUxLjU0NCBMMTAuMDgzLDkwLjM3NSBMMTAuMDgzLDkwLjM3NSBaIE0xMC4zMzMsNDYuNTY0IEwxMC4zMzMsODkuOTQ0IEw3Ny4xNTQsNTEuMzY1IEw3Ny4xNTQsNy45ODUgTDEwLjMzMyw0Ni41NjQgTDEwLjMzMyw0Ni41NjQgWiIgaWQ9IkZpbGwtMjAiIGZpbGw9IiM2MDdEOEIiPjwvcGF0aD4KICAgICAgICAgICAgICAgIDwvZz4KICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMjUuNzM3LDg4LjY0NyBMMTE4LjA5OCw5MS45ODEgTDExOC4wOTgsODQgTDEwNi42MzksODguNzEzIEwxMDYuNjM5LDk2Ljk4MiBMOTksMTAwLjMxNSBMMTEyLjM2OSwxMDMuOTYxIEwxMjUuNzM3LDg4LjY0NyIgaWQ9IkltcG9ydGVkLUxheWVycy1Db3B5LTIiIGZpbGw9IiM0NTVBNjQiIHNrZXRjaDp0eXBlPSJNU1NoYXBlR3JvdXAiPjwvcGF0aD4KICAgICAgICAgICAgPC9nPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+';
	function RotateInstructions() {
	  this.loadIcon_();
	  var overlay = document.createElement('div');
	  var s = overlay.style;
	  s.position = 'fixed';
	  s.top = 0;
	  s.right = 0;
	  s.bottom = 0;
	  s.left = 0;
	  s.backgroundColor = 'gray';
	  s.fontFamily = 'sans-serif';
	  s.zIndex = 1000000;
	  var img = document.createElement('img');
	  img.src = this.icon;
	  var s = img.style;
	  s.marginLeft = '25%';
	  s.marginTop = '25%';
	  s.width = '50%';
	  overlay.appendChild(img);
	  var text = document.createElement('div');
	  var s = text.style;
	  s.textAlign = 'center';
	  s.fontSize = '16px';
	  s.lineHeight = '24px';
	  s.margin = '24px 25%';
	  s.width = '50%';
	  text.innerHTML = 'Place your phone into your Cardboard viewer.';
	  overlay.appendChild(text);
	  var snackbar = document.createElement('div');
	  var s = snackbar.style;
	  s.backgroundColor = '#CFD8DC';
	  s.position = 'fixed';
	  s.bottom = 0;
	  s.width = '100%';
	  s.height = '48px';
	  s.padding = '14px 24px';
	  s.boxSizing = 'border-box';
	  s.color = '#656A6B';
	  overlay.appendChild(snackbar);
	  var snackbarText = document.createElement('div');
	  snackbarText.style.float = 'left';
	  snackbarText.innerHTML = 'No Cardboard viewer?';
	  var snackbarButton = document.createElement('a');
	  snackbarButton.href = 'https://www.google.com/get/cardboard/get-cardboard/';
	  snackbarButton.innerHTML = 'get one';
	  snackbarButton.target = '_blank';
	  var s = snackbarButton.style;
	  s.float = 'right';
	  s.fontWeight = 600;
	  s.textTransform = 'uppercase';
	  s.borderLeft = '1px solid gray';
	  s.paddingLeft = '24px';
	  s.textDecoration = 'none';
	  s.color = '#656A6B';
	  snackbar.appendChild(snackbarText);
	  snackbar.appendChild(snackbarButton);
	  this.overlay = overlay;
	  this.text = text;
	  this.hide();
	}
	RotateInstructions.prototype.show = function (parent) {
	  if (!parent && !this.overlay.parentElement) {
		document.body.appendChild(this.overlay);
	  } else if (parent) {
		if (this.overlay.parentElement && this.overlay.parentElement != parent) this.overlay.parentElement.removeChild(this.overlay);
		parent.appendChild(this.overlay);
	  }
	  this.overlay.style.display = 'block';
	  var img = this.overlay.querySelector('img');
	  var s = img.style;
	  if (isLandscapeMode()) {
		s.width = '20%';
		s.marginLeft = '40%';
		s.marginTop = '3%';
	  } else {
		s.width = '50%';
		s.marginLeft = '25%';
		s.marginTop = '25%';
	  }
	};
	RotateInstructions.prototype.hide = function () {
	  this.overlay.style.display = 'none';
	};
	RotateInstructions.prototype.showTemporarily = function (ms, parent) {
	  this.show(parent);
	  this.timer = setTimeout(this.hide.bind(this), ms);
	};
	RotateInstructions.prototype.disableShowTemporarily = function () {
	  clearTimeout(this.timer);
	};
	RotateInstructions.prototype.update = function () {
	  this.disableShowTemporarily();
	  if (!isLandscapeMode() && isMobile()) {
		this.show();
	  } else {
		this.hide();
	  }
	};
	RotateInstructions.prototype.loadIcon_ = function () {
	  this.icon = base64('image/svg+xml', rotateInstructionsAsset);
	};
	var DEFAULT_VIEWER = 'CardboardV1';
	var VIEWER_KEY = 'WEBVR_CARDBOARD_VIEWER';
	var CLASS_NAME = 'webvr-polyfill-viewer-selector';
	function ViewerSelector(defaultViewer) {
	  try {
		this.selectedKey = localStorage.getItem(VIEWER_KEY);
	  } catch (error) {
		console.error('Failed to load viewer profile: %s', error);
	  }
	  if (!this.selectedKey) {
		this.selectedKey = defaultViewer || DEFAULT_VIEWER;
	  }
	  this.dialog = this.createDialog_(DeviceInfo.Viewers);
	  this.root = null;
	  this.onChangeCallbacks_ = [];
	}
	ViewerSelector.prototype.show = function (root) {
	  this.root = root;
	  root.appendChild(this.dialog);
	  var selected = this.dialog.querySelector('#' + this.selectedKey);
	  selected.checked = true;
	  this.dialog.style.display = 'block';
	};
	ViewerSelector.prototype.hide = function () {
	  if (this.root && this.root.contains(this.dialog)) {
		this.root.removeChild(this.dialog);
	  }
	  this.dialog.style.display = 'none';
	};
	ViewerSelector.prototype.getCurrentViewer = function () {
	  return DeviceInfo.Viewers[this.selectedKey];
	};
	ViewerSelector.prototype.getSelectedKey_ = function () {
	  var input = this.dialog.querySelector('input[name=field]:checked');
	  if (input) {
		return input.id;
	  }
	  return null;
	};
	ViewerSelector.prototype.onChange = function (cb) {
	  this.onChangeCallbacks_.push(cb);
	};
	ViewerSelector.prototype.fireOnChange_ = function (viewer) {
	  for (var i = 0; i < this.onChangeCallbacks_.length; i++) {
		this.onChangeCallbacks_[i](viewer);
	  }
	};
	ViewerSelector.prototype.onSave_ = function () {
	  this.selectedKey = this.getSelectedKey_();
	  if (!this.selectedKey || !DeviceInfo.Viewers[this.selectedKey]) {
		console.error('ViewerSelector.onSave_: this should never happen!');
		return;
	  }
	  this.fireOnChange_(DeviceInfo.Viewers[this.selectedKey]);
	  try {
		localStorage.setItem(VIEWER_KEY, this.selectedKey);
	  } catch (error) {
		console.error('Failed to save viewer profile: %s', error);
	  }
	  this.hide();
	};
	ViewerSelector.prototype.createDialog_ = function (options) {
	  var container = document.createElement('div');
	  container.classList.add(CLASS_NAME);
	  container.style.display = 'none';
	  var overlay = document.createElement('div');
	  var s = overlay.style;
	  s.position = 'fixed';
	  s.left = 0;
	  s.top = 0;
	  s.width = '100%';
	  s.height = '100%';
	  s.background = 'rgba(0, 0, 0, 0.3)';
	  overlay.addEventListener('click', this.hide.bind(this));
	  var width = 280;
	  var dialog = document.createElement('div');
	  var s = dialog.style;
	  s.boxSizing = 'border-box';
	  s.position = 'fixed';
	  s.top = '24px';
	  s.left = '50%';
	  s.marginLeft = -width / 2 + 'px';
	  s.width = width + 'px';
	  s.padding = '24px';
	  s.overflow = 'hidden';
	  s.background = '#fafafa';
	  s.fontFamily = "'Roboto', sans-serif";
	  s.boxShadow = '0px 5px 20px #666';
	  dialog.appendChild(this.createH1_('Select your viewer'));
	  for (var id in options) {
		dialog.appendChild(this.createChoice_(id, options[id].label));
	  }
	  dialog.appendChild(this.createButton_('Save', this.onSave_.bind(this)));
	  container.appendChild(overlay);
	  container.appendChild(dialog);
	  return container;
	};
	ViewerSelector.prototype.createH1_ = function (name) {
	  var h1 = document.createElement('h1');
	  var s = h1.style;
	  s.color = 'black';
	  s.fontSize = '20px';
	  s.fontWeight = 'bold';
	  s.marginTop = 0;
	  s.marginBottom = '24px';
	  h1.innerHTML = name;
	  return h1;
	};
	ViewerSelector.prototype.createChoice_ = function (id, name) {
	  var div = document.createElement('div');
	  div.style.marginTop = '8px';
	  div.style.color = 'black';
	  var input = document.createElement('input');
	  input.style.fontSize = '30px';
	  input.setAttribute('id', id);
	  input.setAttribute('type', 'radio');
	  input.setAttribute('value', id);
	  input.setAttribute('name', 'field');
	  var label = document.createElement('label');
	  label.style.marginLeft = '4px';
	  label.setAttribute('for', id);
	  label.innerHTML = name;
	  div.appendChild(input);
	  div.appendChild(label);
	  return div;
	};
	ViewerSelector.prototype.createButton_ = function (label, onclick) {
	  var button = document.createElement('button');
	  button.innerHTML = label;
	  var s = button.style;
	  s.float = 'right';
	  s.textTransform = 'uppercase';
	  s.color = '#1094f7';
	  s.fontSize = '14px';
	  s.letterSpacing = 0;
	  s.border = 0;
	  s.background = 'none';
	  s.marginTop = '16px';
	  button.addEventListener('click', onclick);
	  return button;
	};
	var commonjsGlobal$$1 = typeof window !== 'undefined' ? window : typeof commonjsGlobal$1 !== 'undefined' ? commonjsGlobal$1 : typeof self !== 'undefined' ? self : {};
	function unwrapExports$$1 (x) {
		return x && x.__esModule ? x['default'] : x;
	}
	function createCommonjsModule$$1(fn, module) {
		return module = { exports: {} }, fn(module, module.exports), module.exports;
	}
	var NoSleep = createCommonjsModule$$1(function (module, exports) {
	(function webpackUniversalModuleDefinition(root, factory) {
		module.exports = factory();
	})(commonjsGlobal$$1, function() {
	return          (function(modules) {
				 var installedModules = {};
				 function __webpack_require__(moduleId) {
					 if(installedModules[moduleId]) {
						 return installedModules[moduleId].exports;
					 }
					 var module = installedModules[moduleId] = {
						 i: moduleId,
						 l: false,
						 exports: {}
					 };
					 modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
					 module.l = true;
					 return module.exports;
				 }
				 __webpack_require__.m = modules;
				 __webpack_require__.c = installedModules;
				 __webpack_require__.d = function(exports, name, getter) {
					 if(!__webpack_require__.o(exports, name)) {
						 Object.defineProperty(exports, name, {
							 configurable: false,
							 enumerable: true,
							 get: getter
						 });
					 }
				 };
				 __webpack_require__.n = function(module) {
					 var getter = module && module.__esModule ?
						 function getDefault() { return module['default']; } :
						 function getModuleExports() { return module; };
					 __webpack_require__.d(getter, 'a', getter);
					 return getter;
				 };
				 __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
				 __webpack_require__.p = "";
				 return __webpack_require__(__webpack_require__.s = 0);
			 })
			 ([
		  (function(module, exports, __webpack_require__) {
	var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
	function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
	var mediaFile = __webpack_require__(1);
	var oldIOS = typeof navigator !== 'undefined' && parseFloat(('' + (/CPU.*OS ([0-9_]{3,4})[0-9_]{0,1}|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent) || [0, ''])[1]).replace('undefined', '3_2').replace('_', '.').replace('_', '')) < 10 && !window.MSStream;
	var NoSleep = function () {
	  function NoSleep() {
		_classCallCheck(this, NoSleep);
		if (oldIOS) {
		  this.noSleepTimer = null;
		} else {
		  this.noSleepVideo = document.createElement('video');
		  this.noSleepVideo.setAttribute('playsinline', '');
		  this.noSleepVideo.setAttribute('src', mediaFile);
		  this.noSleepVideo.addEventListener('timeupdate', function (e) {
			if (this.noSleepVideo.currentTime > 0.5) {
			  this.noSleepVideo.currentTime = Math.random();
			}
		  }.bind(this));
		}
	  }
	  _createClass(NoSleep, [{
		key: 'enable',
		value: function enable() {
		  if (oldIOS) {
			this.disable();
			this.noSleepTimer = window.setInterval(function () {
			  window.location.href = '/';
			  window.setTimeout(window.stop, 0);
			}, 15000);
		  } else {
			this.noSleepVideo.play();
		  }
		}
	  }, {
		key: 'disable',
		value: function disable() {
		  if (oldIOS) {
			if (this.noSleepTimer) {
			  window.clearInterval(this.noSleepTimer);
			  this.noSleepTimer = null;
			}
		  } else {
			this.noSleepVideo.pause();
		  }
		}
	  }]);
	  return NoSleep;
	}();
	module.exports = NoSleep;
		  }),
		  (function(module, exports, __webpack_require__) {
	module.exports = 'data:video/mp4;base64,AAAAIGZ0eXBtcDQyAAACAGlzb21pc28yYXZjMW1wNDEAAAAIZnJlZQAACKBtZGF0AAAC8wYF///v3EXpvebZSLeWLNgg2SPu73gyNjQgLSBjb3JlIDE0MiByMjQ3OSBkZDc5YTYxIC0gSC4yNjQvTVBFRy00IEFWQyBjb2RlYyAtIENvcHlsZWZ0IDIwMDMtMjAxNCAtIGh0dHA6Ly93d3cudmlkZW9sYW4ub3JnL3gyNjQuaHRtbCAtIG9wdGlvbnM6IGNhYmFjPTEgcmVmPTEgZGVibG9jaz0xOjA6MCBhbmFseXNlPTB4MToweDExMSBtZT1oZXggc3VibWU9MiBwc3k9MSBwc3lfcmQ9MS4wMDowLjAwIG1peGVkX3JlZj0wIG1lX3JhbmdlPTE2IGNocm9tYV9tZT0xIHRyZWxsaXM9MCA4eDhkY3Q9MCBjcW09MCBkZWFkem9uZT0yMSwxMSBmYXN0X3Bza2lwPTEgY2hyb21hX3FwX29mZnNldD0wIHRocmVhZHM9NiBsb29rYWhlYWRfdGhyZWFkcz0xIHNsaWNlZF90aHJlYWRzPTAgbnI9MCBkZWNpbWF0ZT0xIGludGVybGFjZWQ9MCBibHVyYXlfY29tcGF0PTAgY29uc3RyYWluZWRfaW50cmE9MCBiZnJhbWVzPTMgYl9weXJhbWlkPTIgYl9hZGFwdD0xIGJfYmlhcz0wIGRpcmVjdD0xIHdlaWdodGI9MSBvcGVuX2dvcD0wIHdlaWdodHA9MSBrZXlpbnQ9MzAwIGtleWludF9taW49MzAgc2NlbmVjdXQ9NDAgaW50cmFfcmVmcmVzaD0wIHJjX2xvb2thaGVhZD0xMCByYz1jcmYgbWJ0cmVlPTEgY3JmPTIwLjAgcWNvbXA9MC42MCBxcG1pbj0wIHFwbWF4PTY5IHFwc3RlcD00IHZidl9tYXhyYXRlPTIwMDAwIHZidl9idWZzaXplPTI1MDAwIGNyZl9tYXg9MC4wIG5hbF9ocmQ9bm9uZSBmaWxsZXI9MCBpcF9yYXRpbz0xLjQwIGFxPTE6MS4wMACAAAAAOWWIhAA3//p+C7v8tDDSTjf97w55i3SbRPO4ZY+hkjD5hbkAkL3zpJ6h/LR1CAABzgB1kqqzUorlhQAAAAxBmiQYhn/+qZYADLgAAAAJQZ5CQhX/AAj5IQADQGgcIQADQGgcAAAACQGeYUQn/wALKCEAA0BoHAAAAAkBnmNEJ/8ACykhAANAaBwhAANAaBwAAAANQZpoNExDP/6plgAMuSEAA0BoHAAAAAtBnoZFESwr/wAI+SEAA0BoHCEAA0BoHAAAAAkBnqVEJ/8ACykhAANAaBwAAAAJAZ6nRCf/AAsoIQADQGgcIQADQGgcAAAADUGarDRMQz/+qZYADLghAANAaBwAAAALQZ7KRRUsK/8ACPkhAANAaBwAAAAJAZ7pRCf/AAsoIQADQGgcIQADQGgcAAAACQGe60Qn/wALKCEAA0BoHAAAAA1BmvA0TEM//qmWAAy5IQADQGgcIQADQGgcAAAAC0GfDkUVLCv/AAj5IQADQGgcAAAACQGfLUQn/wALKSEAA0BoHCEAA0BoHAAAAAkBny9EJ/8ACyghAANAaBwAAAANQZs0NExDP/6plgAMuCEAA0BoHAAAAAtBn1JFFSwr/wAI+SEAA0BoHCEAA0BoHAAAAAkBn3FEJ/8ACyghAANAaBwAAAAJAZ9zRCf/AAsoIQADQGgcIQADQGgcAAAADUGbeDRMQz/+qZYADLkhAANAaBwAAAALQZ+WRRUsK/8ACPghAANAaBwhAANAaBwAAAAJAZ+1RCf/AAspIQADQGgcAAAACQGft0Qn/wALKSEAA0BoHCEAA0BoHAAAAA1Bm7w0TEM//qmWAAy4IQADQGgcAAAAC0Gf2kUVLCv/AAj5IQADQGgcAAAACQGf+UQn/wALKCEAA0BoHCEAA0BoHAAAAAkBn/tEJ/8ACykhAANAaBwAAAANQZvgNExDP/6plgAMuSEAA0BoHCEAA0BoHAAAAAtBnh5FFSwr/wAI+CEAA0BoHAAAAAkBnj1EJ/8ACyghAANAaBwhAANAaBwAAAAJAZ4/RCf/AAspIQADQGgcAAAADUGaJDRMQz/+qZYADLghAANAaBwAAAALQZ5CRRUsK/8ACPkhAANAaBwhAANAaBwAAAAJAZ5hRCf/AAsoIQADQGgcAAAACQGeY0Qn/wALKSEAA0BoHCEAA0BoHAAAAA1Bmmg0TEM//qmWAAy5IQADQGgcAAAAC0GehkUVLCv/AAj5IQADQGgcIQADQGgcAAAACQGepUQn/wALKSEAA0BoHAAAAAkBnqdEJ/8ACyghAANAaBwAAAANQZqsNExDP/6plgAMuCEAA0BoHCEAA0BoHAAAAAtBnspFFSwr/wAI+SEAA0BoHAAAAAkBnulEJ/8ACyghAANAaBwhAANAaBwAAAAJAZ7rRCf/AAsoIQADQGgcAAAADUGa8DRMQz/+qZYADLkhAANAaBwhAANAaBwAAAALQZ8ORRUsK/8ACPkhAANAaBwAAAAJAZ8tRCf/AAspIQADQGgcIQADQGgcAAAACQGfL0Qn/wALKCEAA0BoHAAAAA1BmzQ0TEM//qmWAAy4IQADQGgcAAAAC0GfUkUVLCv/AAj5IQADQGgcIQADQGgcAAAACQGfcUQn/wALKCEAA0BoHAAAAAkBn3NEJ/8ACyghAANAaBwhAANAaBwAAAANQZt4NExC//6plgAMuSEAA0BoHAAAAAtBn5ZFFSwr/wAI+CEAA0BoHCEAA0BoHAAAAAkBn7VEJ/8ACykhAANAaBwAAAAJAZ+3RCf/AAspIQADQGgcAAAADUGbuzRMQn/+nhAAYsAhAANAaBwhAANAaBwAAAAJQZ/aQhP/AAspIQADQGgcAAAACQGf+UQn/wALKCEAA0BoHCEAA0BoHCEAA0BoHCEAA0BoHCEAA0BoHCEAA0BoHAAACiFtb292AAAAbG12aGQAAAAA1YCCX9WAgl8AAAPoAAAH/AABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAGGlvZHMAAAAAEICAgAcAT////v7/AAAF+XRyYWsAAABcdGtoZAAAAAPVgIJf1YCCXwAAAAEAAAAAAAAH0AAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAygAAAMoAAAAAACRlZHRzAAAAHGVsc3QAAAAAAAAAAQAAB9AAABdwAAEAAAAABXFtZGlhAAAAIG1kaGQAAAAA1YCCX9WAgl8AAV+QAAK/IFXEAAAAAAAtaGRscgAAAAAAAAAAdmlkZQAAAAAAAAAAAAAAAFZpZGVvSGFuZGxlcgAAAAUcbWluZgAAABR2bWhkAAAAAQAAAAAAAAAAAAAAJGRpbmYAAAAcZHJlZgAAAAAAAAABAAAADHVybCAAAAABAAAE3HN0YmwAAACYc3RzZAAAAAAAAAABAAAAiGF2YzEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAygDKAEgAAABIAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY//8AAAAyYXZjQwFNQCj/4QAbZ01AKOyho3ySTUBAQFAAAAMAEAAr8gDxgxlgAQAEaO+G8gAAABhzdHRzAAAAAAAAAAEAAAA8AAALuAAAABRzdHNzAAAAAAAAAAEAAAABAAAB8GN0dHMAAAAAAAAAPAAAAAEAABdwAAAAAQAAOpgAAAABAAAXcAAAAAEAAAAAAAAAAQAAC7gAAAABAAA6mAAAAAEAABdwAAAAAQAAAAAAAAABAAALuAAAAAEAADqYAAAAAQAAF3AAAAABAAAAAAAAAAEAAAu4AAAAAQAAOpgAAAABAAAXcAAAAAEAAAAAAAAAAQAAC7gAAAABAAA6mAAAAAEAABdwAAAAAQAAAAAAAAABAAALuAAAAAEAADqYAAAAAQAAF3AAAAABAAAAAAAAAAEAAAu4AAAAAQAAOpgAAAABAAAXcAAAAAEAAAAAAAAAAQAAC7gAAAABAAA6mAAAAAEAABdwAAAAAQAAAAAAAAABAAALuAAAAAEAADqYAAAAAQAAF3AAAAABAAAAAAAAAAEAAAu4AAAAAQAAOpgAAAABAAAXcAAAAAEAAAAAAAAAAQAAC7gAAAABAAA6mAAAAAEAABdwAAAAAQAAAAAAAAABAAALuAAAAAEAADqYAAAAAQAAF3AAAAABAAAAAAAAAAEAAAu4AAAAAQAAOpgAAAABAAAXcAAAAAEAAAAAAAAAAQAAC7gAAAABAAA6mAAAAAEAABdwAAAAAQAAAAAAAAABAAALuAAAAAEAAC7gAAAAAQAAF3AAAAABAAAAAAAAABxzdHNjAAAAAAAAAAEAAAABAAAAAQAAAAEAAAEEc3RzegAAAAAAAAAAAAAAPAAAAzQAAAAQAAAADQAAAA0AAAANAAAAEQAAAA8AAAANAAAADQAAABEAAAAPAAAADQAAAA0AAAARAAAADwAAAA0AAAANAAAAEQAAAA8AAAANAAAADQAAABEAAAAPAAAADQAAAA0AAAARAAAADwAAAA0AAAANAAAAEQAAAA8AAAANAAAADQAAABEAAAAPAAAADQAAAA0AAAARAAAADwAAAA0AAAANAAAAEQAAAA8AAAANAAAADQAAABEAAAAPAAAADQAAAA0AAAARAAAADwAAAA0AAAANAAAAEQAAAA8AAAANAAAADQAAABEAAAANAAAADQAAAQBzdGNvAAAAAAAAADwAAAAwAAADZAAAA3QAAAONAAADoAAAA7kAAAPQAAAD6wAAA/4AAAQXAAAELgAABEMAAARcAAAEbwAABIwAAAShAAAEugAABM0AAATkAAAE/wAABRIAAAUrAAAFQgAABV0AAAVwAAAFiQAABaAAAAW1AAAFzgAABeEAAAX+AAAGEwAABiwAAAY/AAAGVgAABnEAAAaEAAAGnQAABrQAAAbPAAAG4gAABvUAAAcSAAAHJwAAB0AAAAdTAAAHcAAAB4UAAAeeAAAHsQAAB8gAAAfjAAAH9gAACA8AAAgmAAAIQQAACFQAAAhnAAAIhAAACJcAAAMsdHJhawAAAFx0a2hkAAAAA9WAgl/VgIJfAAAAAgAAAAAAAAf8AAAAAAAAAAAAAAABAQAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAACsm1kaWEAAAAgbWRoZAAAAADVgIJf1YCCXwAArEQAAWAAVcQAAAAAACdoZGxyAAAAAAAAAABzb3VuAAAAAAAAAAAAAAAAU3RlcmVvAAAAAmNtaW5mAAAAEHNtaGQAAAAAAAAAAAAAACRkaW5mAAAAHGRyZWYAAAAAAAAAAQAAAAx1cmwgAAAAAQAAAidzdGJsAAAAZ3N0c2QAAAAAAAAAAQAAAFdtcDRhAAAAAAAAAAEAAAAAAAAAAAACABAAAAAArEQAAAAAADNlc2RzAAAAAAOAgIAiAAIABICAgBRAFQAAAAADDUAAAAAABYCAgAISEAaAgIABAgAAABhzdHRzAAAAAAAAAAEAAABYAAAEAAAAABxzdHNjAAAAAAAAAAEAAAABAAAAAQAAAAEAAAAUc3RzegAAAAAAAAAGAAAAWAAAAXBzdGNvAAAAAAAAAFgAAAOBAAADhwAAA5oAAAOtAAADswAAA8oAAAPfAAAD5QAAA/gAAAQLAAAEEQAABCgAAAQ9AAAEUAAABFYAAARpAAAEgAAABIYAAASbAAAErgAABLQAAATHAAAE3gAABPMAAAT5AAAFDAAABR8AAAUlAAAFPAAABVEAAAVXAAAFagAABX0AAAWDAAAFmgAABa8AAAXCAAAFyAAABdsAAAXyAAAF+AAABg0AAAYgAAAGJgAABjkAAAZQAAAGZQAABmsAAAZ+AAAGkQAABpcAAAauAAAGwwAABskAAAbcAAAG7wAABwYAAAcMAAAHIQAABzQAAAc6AAAHTQAAB2QAAAdqAAAHfwAAB5IAAAeYAAAHqwAAB8IAAAfXAAAH3QAAB/AAAAgDAAAICQAACCAAAAg1AAAIOwAACE4AAAhhAAAIeAAACH4AAAiRAAAIpAAACKoAAAiwAAAItgAACLwAAAjCAAAAFnVkdGEAAAAObmFtZVN0ZXJlbwAAAHB1ZHRhAAAAaG1ldGEAAAAAAAAAIWhkbHIAAAAAAAAAAG1kaXJhcHBsAAAAAAAAAAAAAAAAO2lsc3QAAAAzqXRvbwAAACtkYXRhAAAAAQAAAABIYW5kQnJha2UgMC4xMC4yIDIwMTUwNjExMDA=';
		  })
			 ]);
	});
	});
	var NoSleep$1 = unwrapExports$$1(NoSleep);
	var nextDisplayId = 1000;
	var defaultLeftBounds = [0, 0, 0.5, 1];
	var defaultRightBounds = [0.5, 0, 0.5, 1];
	var raf = window.requestAnimationFrame;
	var caf = window.cancelAnimationFrame;
	function VRFrameData() {
	  this.leftProjectionMatrix = new Float32Array(16);
	  this.leftViewMatrix = new Float32Array(16);
	  this.rightProjectionMatrix = new Float32Array(16);
	  this.rightViewMatrix = new Float32Array(16);
	  this.pose = null;
	}
	function VRDisplayCapabilities(config) {
	  Object.defineProperties(this, {
		hasPosition: {
		  writable: false, enumerable: true, value: config.hasPosition
		},
		hasExternalDisplay: {
		  writable: false, enumerable: true, value: config.hasExternalDisplay
		},
		canPresent: {
		  writable: false, enumerable: true, value: config.canPresent
		},
		maxLayers: {
		  writable: false, enumerable: true, value: config.maxLayers
		},
		hasOrientation: {
		  enumerable: true, get: function get() {
			deprecateWarning('VRDisplayCapabilities.prototype.hasOrientation', 'VRDisplay.prototype.getFrameData');
			return config.hasOrientation;
		  }
		}
	  });
	}
	function VRDisplay(config) {
	  config = config || {};
	  var USE_WAKELOCK = 'wakelock' in config ? config.wakelock : true;
	  this.isPolyfilled = true;
	  this.displayId = nextDisplayId++;
	  this.displayName = '';
	  this.depthNear = 0.01;
	  this.depthFar = 10000.0;
	  this.isPresenting = false;
	  Object.defineProperty(this, 'isConnected', {
		get: function get() {
		  deprecateWarning('VRDisplay.prototype.isConnected', 'VRDisplayCapabilities.prototype.hasExternalDisplay');
		  return false;
		}
	  });
	  this.capabilities = new VRDisplayCapabilities({
		hasPosition: false,
		hasOrientation: false,
		hasExternalDisplay: false,
		canPresent: false,
		maxLayers: 1
	  });
	  this.stageParameters = null;
	  this.waitingForPresent_ = false;
	  this.layer_ = null;
	  this.originalParent_ = null;
	  this.fullscreenElement_ = null;
	  this.fullscreenWrapper_ = null;
	  this.fullscreenElementCachedStyle_ = null;
	  this.fullscreenEventTarget_ = null;
	  this.fullscreenChangeHandler_ = null;
	  this.fullscreenErrorHandler_ = null;
	  if (USE_WAKELOCK && isMobile()) {
		this.wakelock_ = new NoSleep$1();
	  }
	}
	VRDisplay.prototype.getFrameData = function (frameData) {
	  return frameDataFromPose(frameData, this._getPose(), this);
	};
	VRDisplay.prototype.getPose = function () {
	  deprecateWarning('VRDisplay.prototype.getPose', 'VRDisplay.prototype.getFrameData');
	  return this._getPose();
	};
	VRDisplay.prototype.resetPose = function () {
	  deprecateWarning('VRDisplay.prototype.resetPose');
	  return this._resetPose();
	};
	VRDisplay.prototype.getImmediatePose = function () {
	  deprecateWarning('VRDisplay.prototype.getImmediatePose', 'VRDisplay.prototype.getFrameData');
	  return this._getPose();
	};
	VRDisplay.prototype.requestAnimationFrame = function (callback) {
	  return raf(callback);
	};
	VRDisplay.prototype.cancelAnimationFrame = function (id) {
	  return caf(id);
	};
	VRDisplay.prototype.wrapForFullscreen = function (element) {
	  if (isIOS()) {
		return element;
	  }
	  if (!this.fullscreenWrapper_) {
		this.fullscreenWrapper_ = document.createElement('div');
		var cssProperties = ['height: ' + Math.min(screen.height, screen.width) + 'px !important', 'top: 0 !important', 'left: 0 !important', 'right: 0 !important', 'border: 0', 'margin: 0', 'padding: 0', 'z-index: 999999 !important', 'position: fixed'];
		this.fullscreenWrapper_.setAttribute('style', cssProperties.join('; ') + ';');
		this.fullscreenWrapper_.classList.add('webvr-polyfill-fullscreen-wrapper');
	  }
	  if (this.fullscreenElement_ == element) {
		return this.fullscreenWrapper_;
	  }
	  if (this.fullscreenElement_) {
		if (this.originalParent_) {
		  this.originalParent_.appendChild(this.fullscreenElement_);
		} else {
		  this.fullscreenElement_.parentElement.removeChild(this.fullscreenElement_);
		}
	  }
	  this.fullscreenElement_ = element;
	  this.originalParent_ = element.parentElement;
	  if (!this.originalParent_) {
		document.body.appendChild(element);
	  }
	  if (!this.fullscreenWrapper_.parentElement) {
		var parent = this.fullscreenElement_.parentElement;
		parent.insertBefore(this.fullscreenWrapper_, this.fullscreenElement_);
		parent.removeChild(this.fullscreenElement_);
	  }
	  this.fullscreenWrapper_.insertBefore(this.fullscreenElement_, this.fullscreenWrapper_.firstChild);
	  this.fullscreenElementCachedStyle_ = this.fullscreenElement_.getAttribute('style');
	  var self = this;
	  function applyFullscreenElementStyle() {
		if (!self.fullscreenElement_) {
		  return;
		}
		var cssProperties = ['position: absolute', 'top: 0', 'left: 0', 'width: ' + Math.max(screen.width, screen.height) + 'px', 'height: ' + Math.min(screen.height, screen.width) + 'px', 'border: 0', 'margin: 0', 'padding: 0'];
		self.fullscreenElement_.setAttribute('style', cssProperties.join('; ') + ';');
	  }
	  applyFullscreenElementStyle();
	  return this.fullscreenWrapper_;
	};
	VRDisplay.prototype.removeFullscreenWrapper = function () {
	  if (!this.fullscreenElement_) {
		return;
	  }
	  var element = this.fullscreenElement_;
	  if (this.fullscreenElementCachedStyle_) {
		element.setAttribute('style', this.fullscreenElementCachedStyle_);
	  } else {
		element.removeAttribute('style');
	  }
	  this.fullscreenElement_ = null;
	  this.fullscreenElementCachedStyle_ = null;
	  var parent = this.fullscreenWrapper_.parentElement;
	  this.fullscreenWrapper_.removeChild(element);
	  if (this.originalParent_ === parent) {
		parent.insertBefore(element, this.fullscreenWrapper_);
	  }
	  else if (this.originalParent_) {
		  this.originalParent_.appendChild(element);
		}
	  parent.removeChild(this.fullscreenWrapper_);
	  return element;
	};
	VRDisplay.prototype.requestPresent = function (layers) {
	  var wasPresenting = this.isPresenting;
	  var self = this;
	  if (!(layers instanceof Array)) {
		deprecateWarning('VRDisplay.prototype.requestPresent with non-array argument', 'an array of VRLayers as the first argument');
		layers = [layers];
	  }
	  return new Promise(function (resolve, reject) {
		if (!self.capabilities.canPresent) {
		  reject(new Error('VRDisplay is not capable of presenting.'));
		  return;
		}
		if (layers.length == 0 || layers.length > self.capabilities.maxLayers) {
		  reject(new Error('Invalid number of layers.'));
		  return;
		}
		var incomingLayer = layers[0];
		if (!incomingLayer.source) {
		  resolve();
		  return;
		}
		var leftBounds = incomingLayer.leftBounds || defaultLeftBounds;
		var rightBounds = incomingLayer.rightBounds || defaultRightBounds;
		if (wasPresenting) {
		  var layer = self.layer_;
		  if (layer.source !== incomingLayer.source) {
			layer.source = incomingLayer.source;
		  }
		  for (var i = 0; i < 4; i++) {
			layer.leftBounds[i] = leftBounds[i];
			layer.rightBounds[i] = rightBounds[i];
		  }
		  self.wrapForFullscreen(self.layer_.source);
		  self.updatePresent_();
		  resolve();
		  return;
		}
		self.layer_ = {
		  predistorted: incomingLayer.predistorted,
		  source: incomingLayer.source,
		  leftBounds: leftBounds.slice(0),
		  rightBounds: rightBounds.slice(0)
		};
		self.waitingForPresent_ = false;
		if (self.layer_ && self.layer_.source) {
		  var fullscreenElement = self.wrapForFullscreen(self.layer_.source);
		  var onFullscreenChange = function onFullscreenChange() {
			var actualFullscreenElement = getFullscreenElement();
			self.isPresenting = fullscreenElement === actualFullscreenElement;
			if (self.isPresenting) {
			  if (screen.orientation && screen.orientation.lock) {
				screen.orientation.lock('landscape-primary').catch(function (error) {
				  console.error('screen.orientation.lock() failed due to', error.message);
				});
			  }
			  self.waitingForPresent_ = false;
			  self.beginPresent_();
			  resolve();
			} else {
			  if (screen.orientation && screen.orientation.unlock) {
				screen.orientation.unlock();
			  }
			  self.removeFullscreenWrapper();
			  self.disableWakeLock();
			  self.endPresent_();
			  self.removeFullscreenListeners_();
			}
			self.fireVRDisplayPresentChange_();
		  };
		  var onFullscreenError = function onFullscreenError() {
			if (!self.waitingForPresent_) {
			  return;
			}
			self.removeFullscreenWrapper();
			self.removeFullscreenListeners_();
			self.disableWakeLock();
			self.waitingForPresent_ = false;
			self.isPresenting = false;
			reject(new Error('Unable to present.'));
		  };
		  self.addFullscreenListeners_(fullscreenElement, onFullscreenChange, onFullscreenError);
		  if (requestFullscreen(fullscreenElement)) {
			self.enableWakeLock();
			self.waitingForPresent_ = true;
		  } else if (isIOS() || isWebViewAndroid()) {
			self.enableWakeLock();
			self.isPresenting = true;
			self.beginPresent_();
			self.fireVRDisplayPresentChange_();
			resolve();
		  }
		}
		if (!self.waitingForPresent_ && !isIOS()) {
		  exitFullscreen();
		  reject(new Error('Unable to present.'));
		}
	  });
	};
	VRDisplay.prototype.exitPresent = function () {
	  var wasPresenting = this.isPresenting;
	  var self = this;
	  this.isPresenting = false;
	  this.layer_ = null;
	  this.disableWakeLock();
	  return new Promise(function (resolve, reject) {
		if (wasPresenting) {
		  if (!exitFullscreen() && isIOS()) {
			self.endPresent_();
			self.fireVRDisplayPresentChange_();
		  }
		  if (isWebViewAndroid()) {
			self.removeFullscreenWrapper();
			self.removeFullscreenListeners_();
			self.endPresent_();
			self.fireVRDisplayPresentChange_();
		  }
		  resolve();
		} else {
		  reject(new Error('Was not presenting to VRDisplay.'));
		}
	  });
	};
	VRDisplay.prototype.getLayers = function () {
	  if (this.layer_) {
		return [this.layer_];
	  }
	  return [];
	};
	VRDisplay.prototype.fireVRDisplayPresentChange_ = function () {
	  var event = new CustomEvent('vrdisplaypresentchange', { detail: { display: this } });
	  window.dispatchEvent(event);
	};
	VRDisplay.prototype.fireVRDisplayConnect_ = function () {
	  var event = new CustomEvent('vrdisplayconnect', { detail: { display: this } });
	  window.dispatchEvent(event);
	};
	VRDisplay.prototype.addFullscreenListeners_ = function (element, changeHandler, errorHandler) {
	  this.removeFullscreenListeners_();
	  this.fullscreenEventTarget_ = element;
	  this.fullscreenChangeHandler_ = changeHandler;
	  this.fullscreenErrorHandler_ = errorHandler;
	  if (changeHandler) {
		if (document.fullscreenEnabled) {
		  element.addEventListener('fullscreenchange', changeHandler, false);
		} else if (document.webkitFullscreenEnabled) {
		  element.addEventListener('webkitfullscreenchange', changeHandler, false);
		} else if (document.mozFullScreenEnabled) {
		  document.addEventListener('mozfullscreenchange', changeHandler, false);
		} else if (document.msFullscreenEnabled) {
		  element.addEventListener('msfullscreenchange', changeHandler, false);
		}
	  }
	  if (errorHandler) {
		if (document.fullscreenEnabled) {
		  element.addEventListener('fullscreenerror', errorHandler, false);
		} else if (document.webkitFullscreenEnabled) {
		  element.addEventListener('webkitfullscreenerror', errorHandler, false);
		} else if (document.mozFullScreenEnabled) {
		  document.addEventListener('mozfullscreenerror', errorHandler, false);
		} else if (document.msFullscreenEnabled) {
		  element.addEventListener('msfullscreenerror', errorHandler, false);
		}
	  }
	};
	VRDisplay.prototype.removeFullscreenListeners_ = function () {
	  if (!this.fullscreenEventTarget_) return;
	  var element = this.fullscreenEventTarget_;
	  if (this.fullscreenChangeHandler_) {
		var changeHandler = this.fullscreenChangeHandler_;
		element.removeEventListener('fullscreenchange', changeHandler, false);
		element.removeEventListener('webkitfullscreenchange', changeHandler, false);
		document.removeEventListener('mozfullscreenchange', changeHandler, false);
		element.removeEventListener('msfullscreenchange', changeHandler, false);
	  }
	  if (this.fullscreenErrorHandler_) {
		var errorHandler = this.fullscreenErrorHandler_;
		element.removeEventListener('fullscreenerror', errorHandler, false);
		element.removeEventListener('webkitfullscreenerror', errorHandler, false);
		document.removeEventListener('mozfullscreenerror', errorHandler, false);
		element.removeEventListener('msfullscreenerror', errorHandler, false);
	  }
	  this.fullscreenEventTarget_ = null;
	  this.fullscreenChangeHandler_ = null;
	  this.fullscreenErrorHandler_ = null;
	};
	VRDisplay.prototype.enableWakeLock = function () {
	  if (this.wakelock_) {
		this.wakelock_.enable();
	  }
	};
	VRDisplay.prototype.disableWakeLock = function () {
	  if (this.wakelock_) {
		this.wakelock_.disable();
	  }
	};
	VRDisplay.prototype.beginPresent_ = function () {
	};
	VRDisplay.prototype.endPresent_ = function () {
	};
	VRDisplay.prototype.submitFrame = function (pose) {
	};
	VRDisplay.prototype.getEyeParameters = function (whichEye) {
	  return null;
	};
	var config = {
	  ADDITIONAL_VIEWERS: [],
	  DEFAULT_VIEWER: '',
	  MOBILE_WAKE_LOCK: true,
	  DEBUG: false,
	  DPDB_URL: 'https://dpdb.webvr.rocks/dpdb.json',
	  K_FILTER: 0.98,
	  PREDICTION_TIME_S: 0.040,
	  CARDBOARD_UI_DISABLED: false,
	  ROTATE_INSTRUCTIONS_DISABLED: false,
	  YAW_ONLY: false,
	  BUFFER_SCALE: 0.5,
	  DIRTY_SUBMIT_FRAME_BINDINGS: false
	};
	var Eye = {
	  LEFT: 'left',
	  RIGHT: 'right'
	};
	function CardboardVRDisplay(config$$1) {
	  var defaults = extend({}, config);
	  config$$1 = extend(defaults, config$$1 || {});
	  VRDisplay.call(this, {
		wakelock: config$$1.MOBILE_WAKE_LOCK
	  });
	  this.config = config$$1;
	  this.displayName = 'Cardboard VRDisplay';
	  this.capabilities = new VRDisplayCapabilities({
		hasPosition: false,
		hasOrientation: true,
		hasExternalDisplay: false,
		canPresent: true,
		maxLayers: 1
	  });
	  this.stageParameters = null;
	  this.bufferScale_ = this.config.BUFFER_SCALE;
	  this.poseSensor_ = new PoseSensor(this.config);
	  this.distorter_ = null;
	  this.cardboardUI_ = null;
	  this.dpdb_ = new Dpdb(this.config.DPDB_URL, this.onDeviceParamsUpdated_.bind(this));
	  this.deviceInfo_ = new DeviceInfo(this.dpdb_.getDeviceParams(), config$$1.ADDITIONAL_VIEWERS);
	  this.viewerSelector_ = new ViewerSelector(config$$1.DEFAULT_VIEWER);
	  this.viewerSelector_.onChange(this.onViewerChanged_.bind(this));
	  this.deviceInfo_.setViewer(this.viewerSelector_.getCurrentViewer());
	  if (!this.config.ROTATE_INSTRUCTIONS_DISABLED) {
		this.rotateInstructions_ = new RotateInstructions();
	  }
	  if (isIOS()) {
		window.addEventListener('resize', this.onResize_.bind(this));
	  }
	}
	CardboardVRDisplay.prototype = Object.create(VRDisplay.prototype);
	CardboardVRDisplay.prototype._getPose = function () {
	  return {
		position: null,
		orientation: this.poseSensor_.getOrientation(),
		linearVelocity: null,
		linearAcceleration: null,
		angularVelocity: null,
		angularAcceleration: null
	  };
	};
	CardboardVRDisplay.prototype._resetPose = function () {
	  if (this.poseSensor_.resetPose) {
		this.poseSensor_.resetPose();
	  }
	};
	CardboardVRDisplay.prototype._getFieldOfView = function (whichEye) {
	  var fieldOfView;
	  if (whichEye == Eye.LEFT) {
		fieldOfView = this.deviceInfo_.getFieldOfViewLeftEye();
	  } else if (whichEye == Eye.RIGHT) {
		fieldOfView = this.deviceInfo_.getFieldOfViewRightEye();
	  } else {
		console.error('Invalid eye provided: %s', whichEye);
		return null;
	  }
	  return fieldOfView;
	};
	CardboardVRDisplay.prototype._getEyeOffset = function (whichEye) {
	  var offset;
	  if (whichEye == Eye.LEFT) {
		offset = [-this.deviceInfo_.viewer.interLensDistance * 0.5, 0.0, 0.0];
	  } else if (whichEye == Eye.RIGHT) {
		offset = [this.deviceInfo_.viewer.interLensDistance * 0.5, 0.0, 0.0];
	  } else {
		console.error('Invalid eye provided: %s', whichEye);
		return null;
	  }
	  return offset;
	};
	CardboardVRDisplay.prototype.getEyeParameters = function (whichEye) {
	  var offset = this._getEyeOffset(whichEye);
	  var fieldOfView = this._getFieldOfView(whichEye);
	  var eyeParams = {
		offset: offset,
		renderWidth: this.deviceInfo_.device.width * 0.5 * this.bufferScale_,
		renderHeight: this.deviceInfo_.device.height * this.bufferScale_
	  };
	  Object.defineProperty(eyeParams, 'fieldOfView', {
		enumerable: true,
		get: function get() {
		  deprecateWarning('VRFieldOfView', 'VRFrameData\'s projection matrices');
		  return fieldOfView;
		}
	  });
	  return eyeParams;
	};
	CardboardVRDisplay.prototype.onDeviceParamsUpdated_ = function (newParams) {
	  if (this.config.DEBUG) {
		console.log('DPDB reported that device params were updated.');
	  }
	  this.deviceInfo_.updateDeviceParams(newParams);
	  if (this.distorter_) {
		this.distorter_.updateDeviceInfo(this.deviceInfo_);
	  }
	};
	CardboardVRDisplay.prototype.updateBounds_ = function () {
	  if (this.layer_ && this.distorter_ && (this.layer_.leftBounds || this.layer_.rightBounds)) {
		this.distorter_.setTextureBounds(this.layer_.leftBounds, this.layer_.rightBounds);
	  }
	};
	CardboardVRDisplay.prototype.beginPresent_ = function () {
	  var gl = this.layer_.source.getContext('webgl');
	  if (!gl) gl = this.layer_.source.getContext('experimental-webgl');
	  if (!gl) gl = this.layer_.source.getContext('webgl2');
	  if (!gl) return;
	  if (this.layer_.predistorted) {
		if (!this.config.CARDBOARD_UI_DISABLED) {
		  gl.canvas.width = getScreenWidth() * this.bufferScale_;
		  gl.canvas.height = getScreenHeight() * this.bufferScale_;
		  this.cardboardUI_ = new CardboardUI(gl);
		}
	  } else {
		if (!this.config.CARDBOARD_UI_DISABLED) {
		  this.cardboardUI_ = new CardboardUI(gl);
		}
		this.distorter_ = new CardboardDistorter(gl, this.cardboardUI_, this.config.BUFFER_SCALE, this.config.DIRTY_SUBMIT_FRAME_BINDINGS);
		this.distorter_.updateDeviceInfo(this.deviceInfo_);
	  }
	  if (this.cardboardUI_) {
		this.cardboardUI_.listen(function (e) {
		  this.viewerSelector_.show(this.layer_.source.parentElement);
		  e.stopPropagation();
		  e.preventDefault();
		}.bind(this), function (e) {
		  this.exitPresent();
		  e.stopPropagation();
		  e.preventDefault();
		}.bind(this));
	  }
	  if (this.rotateInstructions_) {
		if (isLandscapeMode() && isMobile()) {
		  this.rotateInstructions_.showTemporarily(3000, this.layer_.source.parentElement);
		} else {
		  this.rotateInstructions_.update();
		}
	  }
	  this.orientationHandler = this.onOrientationChange_.bind(this);
	  window.addEventListener('orientationchange', this.orientationHandler);
	  this.vrdisplaypresentchangeHandler = this.updateBounds_.bind(this);
	  window.addEventListener('vrdisplaypresentchange', this.vrdisplaypresentchangeHandler);
	  this.fireVRDisplayDeviceParamsChange_();
	};
	CardboardVRDisplay.prototype.endPresent_ = function () {
	  if (this.distorter_) {
		this.distorter_.destroy();
		this.distorter_ = null;
	  }
	  if (this.cardboardUI_) {
		this.cardboardUI_.destroy();
		this.cardboardUI_ = null;
	  }
	  if (this.rotateInstructions_) {
		this.rotateInstructions_.hide();
	  }
	  this.viewerSelector_.hide();
	  window.removeEventListener('orientationchange', this.orientationHandler);
	  window.removeEventListener('vrdisplaypresentchange', this.vrdisplaypresentchangeHandler);
	};
	CardboardVRDisplay.prototype.updatePresent_ = function () {
	  this.endPresent_();
	  this.beginPresent_();
	};
	CardboardVRDisplay.prototype.submitFrame = function (pose) {
	  if (this.distorter_) {
		this.updateBounds_();
		this.distorter_.submitFrame();
	  } else if (this.cardboardUI_ && this.layer_) {
		var canvas = this.layer_.source.getContext('webgl').canvas;
		if (canvas.width != this.lastWidth || canvas.height != this.lastHeight) {
		  this.cardboardUI_.onResize();
		}
		this.lastWidth = canvas.width;
		this.lastHeight = canvas.height;
		this.cardboardUI_.render();
	  }
	};
	CardboardVRDisplay.prototype.onOrientationChange_ = function (e) {
	  this.viewerSelector_.hide();
	  if (this.rotateInstructions_) {
		this.rotateInstructions_.update();
	  }
	  this.onResize_();
	};
	CardboardVRDisplay.prototype.onResize_ = function (e) {
	  if (this.layer_) {
		var gl = this.layer_.source.getContext('webgl');
		var cssProperties = ['position: absolute', 'top: 0', 'left: 0',
		'width: 100vw', 'height: 100vh', 'border: 0', 'margin: 0',
		'padding: 0px', 'box-sizing: content-box'];
		gl.canvas.setAttribute('style', cssProperties.join('; ') + ';');
		safariCssSizeWorkaround(gl.canvas);
	  }
	};
	CardboardVRDisplay.prototype.onViewerChanged_ = function (viewer) {
	  this.deviceInfo_.setViewer(viewer);
	  if (this.distorter_) {
		this.distorter_.updateDeviceInfo(this.deviceInfo_);
	  }
	  this.fireVRDisplayDeviceParamsChange_();
	};
	CardboardVRDisplay.prototype.fireVRDisplayDeviceParamsChange_ = function () {
	  var event = new CustomEvent('vrdisplaydeviceparamschange', {
		detail: {
		  vrdisplay: this,
		  deviceInfo: this.deviceInfo_
		}
	  });
	  window.dispatchEvent(event);
	};
	CardboardVRDisplay.VRFrameData = VRFrameData;
	CardboardVRDisplay.VRDisplay = VRDisplay;
	return CardboardVRDisplay;
	})));
	});
	var CardboardVRDisplay = unwrapExports(cardboardVrDisplay);
  
	var version = "0.10.6";
  
	var DefaultConfig = {
	  ADDITIONAL_VIEWERS: [],
	  DEFAULT_VIEWER: '',
	  PROVIDE_MOBILE_VRDISPLAY: true,
	  GET_VR_DISPLAYS_TIMEOUT: 1000,
	  MOBILE_WAKE_LOCK: true,
	  DEBUG: false,
	  DPDB_URL: 'https://dpdb.webvr.rocks/dpdb.json',
	  K_FILTER: 0.98,
	  PREDICTION_TIME_S: 0.040,
	  TOUCH_PANNER_DISABLED: true,
	  CARDBOARD_UI_DISABLED: false,
	  ROTATE_INSTRUCTIONS_DISABLED: false,
	  YAW_ONLY: false,
	  BUFFER_SCALE: 0.5,
	  DIRTY_SUBMIT_FRAME_BINDINGS: false
	};
  
	function WebVRPolyfill(config) {
	  this.config = extend(extend({}, DefaultConfig), config);
	  this.polyfillDisplays = [];
	  this.enabled = false;
	  this.hasNative = 'getVRDisplays' in navigator;
	  this.native = {};
	  this.native.getVRDisplays = navigator.getVRDisplays;
	  this.native.VRFrameData = window.VRFrameData;
	  this.native.VRDisplay = window.VRDisplay;
	  if (!this.hasNative || this.config.PROVIDE_MOBILE_VRDISPLAY && isMobile()) {
		this.enable();
		this.getVRDisplays().then(function (displays) {
		  if (displays && displays[0] && displays[0].fireVRDisplayConnect_) {
			displays[0].fireVRDisplayConnect_();
		  }
		});
	  }
	}
	WebVRPolyfill.prototype.getPolyfillDisplays = function () {
	  if (this._polyfillDisplaysPopulated) {
		return this.polyfillDisplays;
	  }
	  if (isMobile()) {
		var vrDisplay = new CardboardVRDisplay({
		  ADDITIONAL_VIEWERS: this.config.ADDITIONAL_VIEWERS,
		  DEFAULT_VIEWER: this.config.DEFAULT_VIEWER,
		  MOBILE_WAKE_LOCK: this.config.MOBILE_WAKE_LOCK,
		  DEBUG: this.config.DEBUG,
		  DPDB_URL: this.config.DPDB_URL,
		  CARDBOARD_UI_DISABLED: this.config.CARDBOARD_UI_DISABLED,
		  K_FILTER: this.config.K_FILTER,
		  PREDICTION_TIME_S: this.config.PREDICTION_TIME_S,
		  TOUCH_PANNER_DISABLED: this.config.TOUCH_PANNER_DISABLED,
		  ROTATE_INSTRUCTIONS_DISABLED: this.config.ROTATE_INSTRUCTIONS_DISABLED,
		  YAW_ONLY: this.config.YAW_ONLY,
		  BUFFER_SCALE: this.config.BUFFER_SCALE,
		  DIRTY_SUBMIT_FRAME_BINDINGS: this.config.DIRTY_SUBMIT_FRAME_BINDINGS
		});
		this.polyfillDisplays.push(vrDisplay);
	  }
	  this._polyfillDisplaysPopulated = true;
	  return this.polyfillDisplays;
	};
	WebVRPolyfill.prototype.enable = function () {
	  this.enabled = true;
	  if (this.hasNative && this.native.VRFrameData) {
		var NativeVRFrameData = this.native.VRFrameData;
		var nativeFrameData = new this.native.VRFrameData();
		var nativeGetFrameData = this.native.VRDisplay.prototype.getFrameData;
		window.VRDisplay.prototype.getFrameData = function (frameData) {
		  if (frameData instanceof NativeVRFrameData) {
			nativeGetFrameData.call(this, frameData);
			return;
		  }
		  nativeGetFrameData.call(this, nativeFrameData);
		  frameData.pose = nativeFrameData.pose;
		  copyArray(nativeFrameData.leftProjectionMatrix, frameData.leftProjectionMatrix);
		  copyArray(nativeFrameData.rightProjectionMatrix, frameData.rightProjectionMatrix);
		  copyArray(nativeFrameData.leftViewMatrix, frameData.leftViewMatrix);
		  copyArray(nativeFrameData.rightViewMatrix, frameData.rightViewMatrix);
		};
	  }
	  navigator.getVRDisplays = this.getVRDisplays.bind(this);
	  window.VRDisplay = CardboardVRDisplay.VRDisplay;
	  window.VRFrameData = CardboardVRDisplay.VRFrameData;
	};
	WebVRPolyfill.prototype.getVRDisplays = function () {
	  var _this = this;
	  var config = this.config;
	  if (!this.hasNative) {
		return Promise.resolve(this.getPolyfillDisplays());
	  }
	  var timeoutId;
	  var vrDisplaysNative = this.native.getVRDisplays.call(navigator);
	  var timeoutPromise = new Promise(function (resolve) {
		timeoutId = setTimeout(function () {
		  console.warn('Native WebVR implementation detected, but `getVRDisplays()` failed to resolve. Falling back to polyfill.');
		  resolve([]);
		}, config.GET_VR_DISPLAYS_TIMEOUT);
	  });
	  return race([vrDisplaysNative, timeoutPromise]).then(function (nativeDisplays) {
		clearTimeout(timeoutId);
		return nativeDisplays.length > 0 ? nativeDisplays : _this.getPolyfillDisplays();
	  });
	};
	WebVRPolyfill.version = version;
	WebVRPolyfill.VRFrameData = CardboardVRDisplay.VRFrameData;
	WebVRPolyfill.VRDisplay = CardboardVRDisplay.VRDisplay;
  
  
	var webvrPolyfill = Object.freeze({
		default: WebVRPolyfill
	});
  
	var require$$0 = ( webvrPolyfill && WebVRPolyfill ) || webvrPolyfill;
  
	if (typeof commonjsGlobal$1 !== 'undefined' && commonjsGlobal$1.window) {
	  if (!commonjsGlobal$1.document) {
		commonjsGlobal$1.document = commonjsGlobal$1.window.document;
	  }
	  if (!commonjsGlobal$1.navigator) {
		commonjsGlobal$1.navigator = commonjsGlobal$1.window.navigator;
	  }
	}
	var src = require$$0;
  
	return src;
  
	})));
	});
  
	var WebVRPolyfill = unwrapExports(webvrPolyfill);
  
	// Polyfills
  
	if ( Number.EPSILON === undefined ) {
  
		Number.EPSILON = Math.pow( 2, - 52 );
  
	}
  
	if ( Number.isInteger === undefined ) {
  
		// Missing in IE
		// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger
  
		Number.isInteger = function ( value ) {
  
			return typeof value === 'number' && isFinite( value ) && Math.floor( value ) === value;
  
		};
  
	}
  
	//
  
	if ( Math.sign === undefined ) {
  
		// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign
  
		Math.sign = function ( x ) {
  
			return ( x < 0 ) ? - 1 : ( x > 0 ) ? 1 : + x;
  
		};
  
	}
  
	if ( 'name' in Function.prototype === false ) {
  
		// Missing in IE
		// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name
  
		Object.defineProperty( Function.prototype, 'name', {
  
			get: function () {
  
				return this.toString().match( /^\s*function\s*([^\(\s]*)/ )[ 1 ];
  
			}
  
		} );
  
	}
  
	if ( Object.assign === undefined ) {
  
		// Missing in IE
		// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
  
		( function () {
  
			Object.assign = function ( target ) {
  
				if ( target === undefined || target === null ) {
  
					throw new TypeError( 'Cannot convert undefined or null to object' );
  
				}
  
				var output = Object( target );
  
				for ( var index = 1; index < arguments.length; index ++ ) {
  
					var source = arguments[ index ];
  
					if ( source !== undefined && source !== null ) {
  
						for ( var nextKey in source ) {
  
							if ( Object.prototype.hasOwnProperty.call( source, nextKey ) ) {
  
								output[ nextKey ] = source[ nextKey ];
  
							}
  
						}
  
					}
  
				}
  
				return output;
  
			};
  
		} )();
  
	}
  
	/**
	 * https://github.com/mrdoob/eventdispatcher.js/
	 */
  
	function EventDispatcher() {}
  
	Object.assign( EventDispatcher.prototype, {
  
		addEventListener: function ( type, listener ) {
  
			if ( this._listeners === undefined ) this._listeners = {};
  
			var listeners = this._listeners;
  
			if ( listeners[ type ] === undefined ) {
  
				listeners[ type ] = [];
  
			}
  
			if ( listeners[ type ].indexOf( listener ) === - 1 ) {
  
				listeners[ type ].push( listener );
  
			}
  
		},
  
		hasEventListener: function ( type, listener ) {
  
			if ( this._listeners === undefined ) return false;
  
			var listeners = this._listeners;
  
			return listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1;
  
		},
  
		removeEventListener: function ( type, listener ) {
  
			if ( this._listeners === undefined ) return;
  
			var listeners = this._listeners;
			var listenerArray = listeners[ type ];
  
			if ( listenerArray !== undefined ) {
  
				var index = listenerArray.indexOf( listener );
  
				if ( index !== - 1 ) {
  
					listenerArray.splice( index, 1 );
  
				}
  
			}
  
		},
  
		dispatchEvent: function ( event ) {
  
			if ( this._listeners === undefined ) return;
  
			var listeners = this._listeners;
			var listenerArray = listeners[ event.type ];
  
			if ( listenerArray !== undefined ) {
  
				event.target = this;
  
				var array = listenerArray.slice( 0 );
  
				for ( var i = 0, l = array.length; i < l; i ++ ) {
  
					array[ i ].call( this, event );
  
				}
  
			}
  
		}
  
	} );
  
	var REVISION = '93';
	var MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2 };
	var CullFaceNone = 0;
	var CullFaceBack = 1;
	var CullFaceFront = 2;
	var PCFShadowMap = 1;
	var PCFSoftShadowMap = 2;
	var FrontSide = 0;
	var BackSide = 1;
	var DoubleSide = 2;
	var FlatShading = 1;
	var NoColors = 0;
	var FaceColors = 1;
	var VertexColors = 2;
	var NoBlending = 0;
	var NormalBlending = 1;
	var AdditiveBlending = 2;
	var SubtractiveBlending = 3;
	var MultiplyBlending = 4;
	var CustomBlending = 5;
	var AddEquation = 100;
	var SubtractEquation = 101;
	var ReverseSubtractEquation = 102;
	var MinEquation = 103;
	var MaxEquation = 104;
	var ZeroFactor = 200;
	var OneFactor = 201;
	var SrcColorFactor = 202;
	var OneMinusSrcColorFactor = 203;
	var SrcAlphaFactor = 204;
	var OneMinusSrcAlphaFactor = 205;
	var DstAlphaFactor = 206;
	var OneMinusDstAlphaFactor = 207;
	var DstColorFactor = 208;
	var OneMinusDstColorFactor = 209;
	var SrcAlphaSaturateFactor = 210;
	var NeverDepth = 0;
	var AlwaysDepth = 1;
	var LessDepth = 2;
	var LessEqualDepth = 3;
	var EqualDepth = 4;
	var GreaterEqualDepth = 5;
	var GreaterDepth = 6;
	var NotEqualDepth = 7;
	var MultiplyOperation = 0;
	var MixOperation = 1;
	var AddOperation = 2;
	var NoToneMapping = 0;
	var LinearToneMapping = 1;
	var ReinhardToneMapping = 2;
	var Uncharted2ToneMapping = 3;
	var CineonToneMapping = 4;
	var UVMapping = 300;
	var CubeReflectionMapping = 301;
	var CubeRefractionMapping = 302;
	var EquirectangularReflectionMapping = 303;
	var EquirectangularRefractionMapping = 304;
	var SphericalReflectionMapping = 305;
	var CubeUVReflectionMapping = 306;
	var CubeUVRefractionMapping = 307;
	var RepeatWrapping = 1000;
	var ClampToEdgeWrapping = 1001;
	var MirroredRepeatWrapping = 1002;
	var NearestFilter = 1003;
	var NearestMipMapNearestFilter = 1004;
	var NearestMipMapLinearFilter = 1005;
	var LinearFilter = 1006;
	var LinearMipMapNearestFilter = 1007;
	var LinearMipMapLinearFilter = 1008;
	var UnsignedByteType = 1009;
	var ByteType = 1010;
	var ShortType = 1011;
	var UnsignedShortType = 1012;
	var IntType = 1013;
	var UnsignedIntType = 1014;
	var FloatType = 1015;
	var HalfFloatType = 1016;
	var UnsignedShort4444Type = 1017;
	var UnsignedShort5551Type = 1018;
	var UnsignedShort565Type = 1019;
	var UnsignedInt248Type = 1020;
	var AlphaFormat = 1021;
	var RGBFormat = 1022;
	var RGBAFormat = 1023;
	var LuminanceFormat = 1024;
	var LuminanceAlphaFormat = 1025;
	var DepthFormat = 1026;
	var DepthStencilFormat = 1027;
	var RGB_S3TC_DXT1_Format = 33776;
	var RGBA_S3TC_DXT1_Format = 33777;
	var RGBA_S3TC_DXT3_Format = 33778;
	var RGBA_S3TC_DXT5_Format = 33779;
	var RGB_PVRTC_4BPPV1_Format = 35840;
	var RGB_PVRTC_2BPPV1_Format = 35841;
	var RGBA_PVRTC_4BPPV1_Format = 35842;
	var RGBA_PVRTC_2BPPV1_Format = 35843;
	var RGB_ETC1_Format = 36196;
	var RGBA_ASTC_4x4_Format = 37808;
	var RGBA_ASTC_5x4_Format = 37809;
	var RGBA_ASTC_5x5_Format = 37810;
	var RGBA_ASTC_6x5_Format = 37811;
	var RGBA_ASTC_6x6_Format = 37812;
	var RGBA_ASTC_8x5_Format = 37813;
	var RGBA_ASTC_8x6_Format = 37814;
	var RGBA_ASTC_8x8_Format = 37815;
	var RGBA_ASTC_10x5_Format = 37816;
	var RGBA_ASTC_10x6_Format = 37817;
	var RGBA_ASTC_10x8_Format = 37818;
	var RGBA_ASTC_10x10_Format = 37819;
	var RGBA_ASTC_12x10_Format = 37820;
	var RGBA_ASTC_12x12_Format = 37821;
	var LoopOnce = 2200;
	var LoopRepeat = 2201;
	var LoopPingPong = 2202;
	var InterpolateDiscrete = 2300;
	var InterpolateLinear = 2301;
	var InterpolateSmooth = 2302;
	var ZeroCurvatureEnding = 2400;
	var ZeroSlopeEnding = 2401;
	var WrapAroundEnding = 2402;
	var TrianglesDrawMode = 0;
	var TriangleStripDrawMode = 1;
	var TriangleFanDrawMode = 2;
	var LinearEncoding = 3000;
	var sRGBEncoding = 3001;
	var GammaEncoding = 3007;
	var RGBEEncoding = 3002;
	var RGBM7Encoding = 3004;
	var RGBM16Encoding = 3005;
	var RGBDEncoding = 3006;
	var BasicDepthPacking = 3200;
	var RGBADepthPacking = 3201;
  
	/**
	 * @author alteredq / http://alteredqualia.com/
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	var _Math = {
  
		DEG2RAD: Math.PI / 180,
		RAD2DEG: 180 / Math.PI,
  
		generateUUID: ( function () {
  
			// http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136
  
			var lut = [];
  
			for ( var i = 0; i < 256; i ++ ) {
  
				lut[ i ] = ( i < 16 ? '0' : '' ) + ( i ).toString( 16 );
  
			}
  
			return function generateUUID() {
  
				var d0 = Math.random() * 0xffffffff | 0;
				var d1 = Math.random() * 0xffffffff | 0;
				var d2 = Math.random() * 0xffffffff | 0;
				var d3 = Math.random() * 0xffffffff | 0;
				var uuid = lut[ d0 & 0xff ] + lut[ d0 >> 8 & 0xff ] + lut[ d0 >> 16 & 0xff ] + lut[ d0 >> 24 & 0xff ] + '-' +
					lut[ d1 & 0xff ] + lut[ d1 >> 8 & 0xff ] + '-' + lut[ d1 >> 16 & 0x0f | 0x40 ] + lut[ d1 >> 24 & 0xff ] + '-' +
					lut[ d2 & 0x3f | 0x80 ] + lut[ d2 >> 8 & 0xff ] + '-' + lut[ d2 >> 16 & 0xff ] + lut[ d2 >> 24 & 0xff ] +
					lut[ d3 & 0xff ] + lut[ d3 >> 8 & 0xff ] + lut[ d3 >> 16 & 0xff ] + lut[ d3 >> 24 & 0xff ];
  
				// .toUpperCase() here flattens concatenated strings to save heap memory space.
				return uuid.toUpperCase();
  
			};
  
		} )(),
  
		clamp: function ( value, min, max ) {
  
			return Math.max( min, Math.min( max, value ) );
  
		},
  
		// compute euclidian modulo of m % n
		// https://en.wikipedia.org/wiki/Modulo_operation
  
		euclideanModulo: function ( n, m ) {
  
			return ( ( n % m ) + m ) % m;
  
		},
  
		// Linear mapping from range <a1, a2> to range <b1, b2>
  
		mapLinear: function ( x, a1, a2, b1, b2 ) {
  
			return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 );
  
		},
  
		// https://en.wikipedia.org/wiki/Linear_interpolation
  
		lerp: function ( x, y, t ) {
  
			return ( 1 - t ) * x + t * y;
  
		},
  
		// http://en.wikipedia.org/wiki/Smoothstep
  
		smoothstep: function ( x, min, max ) {
  
			if ( x <= min ) return 0;
			if ( x >= max ) return 1;
  
			x = ( x - min ) / ( max - min );
  
			return x * x * ( 3 - 2 * x );
  
		},
  
		smootherstep: function ( x, min, max ) {
  
			if ( x <= min ) return 0;
			if ( x >= max ) return 1;
  
			x = ( x - min ) / ( max - min );
  
			return x * x * x * ( x * ( x * 6 - 15 ) + 10 );
  
		},
  
		// Random integer from <low, high> interval
  
		randInt: function ( low, high ) {
  
			return low + Math.floor( Math.random() * ( high - low + 1 ) );
  
		},
  
		// Random float from <low, high> interval
  
		randFloat: function ( low, high ) {
  
			return low + Math.random() * ( high - low );
  
		},
  
		// Random float from <-range/2, range/2> interval
  
		randFloatSpread: function ( range ) {
  
			return range * ( 0.5 - Math.random() );
  
		},
  
		degToRad: function ( degrees ) {
  
			return degrees * _Math.DEG2RAD;
  
		},
  
		radToDeg: function ( radians ) {
  
			return radians * _Math.RAD2DEG;
  
		},
  
		isPowerOfTwo: function ( value ) {
  
			return ( value & ( value - 1 ) ) === 0 && value !== 0;
  
		},
  
		ceilPowerOfTwo: function ( value ) {
  
			return Math.pow( 2, Math.ceil( Math.log( value ) / Math.LN2 ) );
  
		},
  
		floorPowerOfTwo: function ( value ) {
  
			return Math.pow( 2, Math.floor( Math.log( value ) / Math.LN2 ) );
  
		}
  
	};
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 * @author philogb / http://blog.thejit.org/
	 * @author egraether / http://egraether.com/
	 * @author zz85 / http://www.lab4games.net/zz85/blog
	 */
  
	function Vector2( x, y ) {
  
		this.x = x || 0;
		this.y = y || 0;
  
	}
  
	Object.defineProperties( Vector2.prototype, {
  
		"width": {
  
			get: function () {
  
				return this.x;
  
			},
  
			set: function ( value ) {
  
				this.x = value;
  
			}
  
		},
  
		"height": {
  
			get: function () {
  
				return this.y;
  
			},
  
			set: function ( value ) {
  
				this.y = value;
  
			}
  
		}
  
	} );
  
	Object.assign( Vector2.prototype, {
  
		isVector2: true,
  
		set: function ( x, y ) {
  
			this.x = x;
			this.y = y;
  
			return this;
  
		},
  
		setScalar: function ( scalar ) {
  
			this.x = scalar;
			this.y = scalar;
  
			return this;
  
		},
  
		setX: function ( x ) {
  
			this.x = x;
  
			return this;
  
		},
  
		setY: function ( y ) {
  
			this.y = y;
  
			return this;
  
		},
  
		setComponent: function ( index, value ) {
  
			switch ( index ) {
  
				case 0: this.x = value; break;
				case 1: this.y = value; break;
				default: throw new Error( 'index is out of range: ' + index );
  
			}
  
			return this;
  
		},
  
		getComponent: function ( index ) {
  
			switch ( index ) {
  
				case 0: return this.x;
				case 1: return this.y;
				default: throw new Error( 'index is out of range: ' + index );
  
			}
  
		},
  
		clone: function () {
  
			return new this.constructor( this.x, this.y );
  
		},
  
		copy: function ( v ) {
  
			this.x = v.x;
			this.y = v.y;
  
			return this;
  
		},
  
		add: function ( v, w ) {
  
			if ( w !== undefined ) {
  
				console.warn( 'THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
				return this.addVectors( v, w );
  
			}
  
			this.x += v.x;
			this.y += v.y;
  
			return this;
  
		},
  
		addScalar: function ( s ) {
  
			this.x += s;
			this.y += s;
  
			return this;
  
		},
  
		addVectors: function ( a, b ) {
  
			this.x = a.x + b.x;
			this.y = a.y + b.y;
  
			return this;
  
		},
  
		addScaledVector: function ( v, s ) {
  
			this.x += v.x * s;
			this.y += v.y * s;
  
			return this;
  
		},
  
		sub: function ( v, w ) {
  
			if ( w !== undefined ) {
  
				console.warn( 'THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
				return this.subVectors( v, w );
  
			}
  
			this.x -= v.x;
			this.y -= v.y;
  
			return this;
  
		},
  
		subScalar: function ( s ) {
  
			this.x -= s;
			this.y -= s;
  
			return this;
  
		},
  
		subVectors: function ( a, b ) {
  
			this.x = a.x - b.x;
			this.y = a.y - b.y;
  
			return this;
  
		},
  
		multiply: function ( v ) {
  
			this.x *= v.x;
			this.y *= v.y;
  
			return this;
  
		},
  
		multiplyScalar: function ( scalar ) {
  
			this.x *= scalar;
			this.y *= scalar;
  
			return this;
  
		},
  
		divide: function ( v ) {
  
			this.x /= v.x;
			this.y /= v.y;
  
			return this;
  
		},
  
		divideScalar: function ( scalar ) {
  
			return this.multiplyScalar( 1 / scalar );
  
		},
  
		applyMatrix3: function ( m ) {
  
			var x = this.x, y = this.y;
			var e = m.elements;
  
			this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ];
			this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ];
  
			return this;
  
		},
  
		min: function ( v ) {
  
			this.x = Math.min( this.x, v.x );
			this.y = Math.min( this.y, v.y );
  
			return this;
  
		},
  
		max: function ( v ) {
  
			this.x = Math.max( this.x, v.x );
			this.y = Math.max( this.y, v.y );
  
			return this;
  
		},
  
		clamp: function ( min, max ) {
  
			// assumes min < max, componentwise
  
			this.x = Math.max( min.x, Math.min( max.x, this.x ) );
			this.y = Math.max( min.y, Math.min( max.y, this.y ) );
  
			return this;
  
		},
  
		clampScalar: function () {
  
			var min = new Vector2();
			var max = new Vector2();
  
			return function clampScalar( minVal, maxVal ) {
  
				min.set( minVal, minVal );
				max.set( maxVal, maxVal );
  
				return this.clamp( min, max );
  
			};
  
		}(),
  
		clampLength: function ( min, max ) {
  
			var length = this.length();
  
			return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) );
  
		},
  
		floor: function () {
  
			this.x = Math.floor( this.x );
			this.y = Math.floor( this.y );
  
			return this;
  
		},
  
		ceil: function () {
  
			this.x = Math.ceil( this.x );
			this.y = Math.ceil( this.y );
  
			return this;
  
		},
  
		round: function () {
  
			this.x = Math.round( this.x );
			this.y = Math.round( this.y );
  
			return this;
  
		},
  
		roundToZero: function () {
  
			this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
			this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
  
			return this;
  
		},
  
		negate: function () {
  
			this.x = - this.x;
			this.y = - this.y;
  
			return this;
  
		},
  
		dot: function ( v ) {
  
			return this.x * v.x + this.y * v.y;
  
		},
  
		lengthSq: function () {
  
			return this.x * this.x + this.y * this.y;
  
		},
  
		length: function () {
  
			return Math.sqrt( this.x * this.x + this.y * this.y );
  
		},
  
		manhattanLength: function () {
  
			return Math.abs( this.x ) + Math.abs( this.y );
  
		},
  
		normalize: function () {
  
			return this.divideScalar( this.length() || 1 );
  
		},
  
		angle: function () {
  
			// computes the angle in radians with respect to the positive x-axis
  
			var angle = Math.atan2( this.y, this.x );
  
			if ( angle < 0 ) angle += 2 * Math.PI;
  
			return angle;
  
		},
  
		distanceTo: function ( v ) {
  
			return Math.sqrt( this.distanceToSquared( v ) );
  
		},
  
		distanceToSquared: function ( v ) {
  
			var dx = this.x - v.x, dy = this.y - v.y;
			return dx * dx + dy * dy;
  
		},
  
		manhattanDistanceTo: function ( v ) {
  
			return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y );
  
		},
  
		setLength: function ( length ) {
  
			return this.normalize().multiplyScalar( length );
  
		},
  
		lerp: function ( v, alpha ) {
  
			this.x += ( v.x - this.x ) * alpha;
			this.y += ( v.y - this.y ) * alpha;
  
			return this;
  
		},
  
		lerpVectors: function ( v1, v2, alpha ) {
  
			return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );
  
		},
  
		equals: function ( v ) {
  
			return ( ( v.x === this.x ) && ( v.y === this.y ) );
  
		},
  
		fromArray: function ( array, offset ) {
  
			if ( offset === undefined ) offset = 0;
  
			this.x = array[ offset ];
			this.y = array[ offset + 1 ];
  
			return this;
  
		},
  
		toArray: function ( array, offset ) {
  
			if ( array === undefined ) array = [];
			if ( offset === undefined ) offset = 0;
  
			array[ offset ] = this.x;
			array[ offset + 1 ] = this.y;
  
			return array;
  
		},
  
		fromBufferAttribute: function ( attribute, index, offset ) {
  
			if ( offset !== undefined ) {
  
				console.warn( 'THREE.Vector2: offset has been removed from .fromBufferAttribute().' );
  
			}
  
			this.x = attribute.getX( index );
			this.y = attribute.getY( index );
  
			return this;
  
		},
  
		rotateAround: function ( center, angle ) {
  
			var c = Math.cos( angle ), s = Math.sin( angle );
  
			var x = this.x - center.x;
			var y = this.y - center.y;
  
			this.x = x * c - y * s + center.x;
			this.y = x * s + y * c + center.y;
  
			return this;
  
		}
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 * @author supereggbert / http://www.paulbrunt.co.uk/
	 * @author philogb / http://blog.thejit.org/
	 * @author jordi_ros / http://plattsoft.com
	 * @author D1plo1d / http://github.com/D1plo1d
	 * @author alteredq / http://alteredqualia.com/
	 * @author mikael emtinger / http://gomo.se/
	 * @author timknip / http://www.floorplanner.com/
	 * @author bhouston / http://clara.io
	 * @author WestLangley / http://github.com/WestLangley
	 */
  
	function Matrix4() {
  
		this.elements = [
  
			1, 0, 0, 0,
			0, 1, 0, 0,
			0, 0, 1, 0,
			0, 0, 0, 1
  
		];
  
		if ( arguments.length > 0 ) {
  
			console.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' );
  
		}
  
	}
  
	Object.assign( Matrix4.prototype, {
  
		isMatrix4: true,
  
		set: function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) {
  
			var te = this.elements;
  
			te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14;
			te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24;
			te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34;
			te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44;
  
			return this;
  
		},
  
		identity: function () {
  
			this.set(
  
				1, 0, 0, 0,
				0, 1, 0, 0,
				0, 0, 1, 0,
				0, 0, 0, 1
  
			);
  
			return this;
  
		},
  
		clone: function () {
  
			return new Matrix4().fromArray( this.elements );
  
		},
  
		copy: function ( m ) {
  
			var te = this.elements;
			var me = m.elements;
  
			te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ];
			te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ];
			te[ 8 ] = me[ 8 ]; te[ 9 ] = me[ 9 ]; te[ 10 ] = me[ 10 ]; te[ 11 ] = me[ 11 ];
			te[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; te[ 15 ] = me[ 15 ];
  
			return this;
  
		},
  
		copyPosition: function ( m ) {
  
			var te = this.elements, me = m.elements;
  
			te[ 12 ] = me[ 12 ];
			te[ 13 ] = me[ 13 ];
			te[ 14 ] = me[ 14 ];
  
			return this;
  
		},
  
		extractBasis: function ( xAxis, yAxis, zAxis ) {
  
			xAxis.setFromMatrixColumn( this, 0 );
			yAxis.setFromMatrixColumn( this, 1 );
			zAxis.setFromMatrixColumn( this, 2 );
  
			return this;
  
		},
  
		makeBasis: function ( xAxis, yAxis, zAxis ) {
  
			this.set(
				xAxis.x, yAxis.x, zAxis.x, 0,
				xAxis.y, yAxis.y, zAxis.y, 0,
				xAxis.z, yAxis.z, zAxis.z, 0,
				0, 0, 0, 1
			);
  
			return this;
  
		},
  
		extractRotation: function () {
  
			var v1 = new Vector3();
  
			return function extractRotation( m ) {
  
				// this method does not support reflection matrices
  
				var te = this.elements;
				var me = m.elements;
  
				var scaleX = 1 / v1.setFromMatrixColumn( m, 0 ).length();
				var scaleY = 1 / v1.setFromMatrixColumn( m, 1 ).length();
				var scaleZ = 1 / v1.setFromMatrixColumn( m, 2 ).length();
  
				te[ 0 ] = me[ 0 ] * scaleX;
				te[ 1 ] = me[ 1 ] * scaleX;
				te[ 2 ] = me[ 2 ] * scaleX;
				te[ 3 ] = 0;
  
				te[ 4 ] = me[ 4 ] * scaleY;
				te[ 5 ] = me[ 5 ] * scaleY;
				te[ 6 ] = me[ 6 ] * scaleY;
				te[ 7 ] = 0;
  
				te[ 8 ] = me[ 8 ] * scaleZ;
				te[ 9 ] = me[ 9 ] * scaleZ;
				te[ 10 ] = me[ 10 ] * scaleZ;
				te[ 11 ] = 0;
  
				te[ 12 ] = 0;
				te[ 13 ] = 0;
				te[ 14 ] = 0;
				te[ 15 ] = 1;
  
				return this;
  
			};
  
		}(),
  
		makeRotationFromEuler: function ( euler ) {
  
			if ( ! ( euler && euler.isEuler ) ) {
  
				console.error( 'THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' );
  
			}
  
			var te = this.elements;
  
			var x = euler.x, y = euler.y, z = euler.z;
			var a = Math.cos( x ), b = Math.sin( x );
			var c = Math.cos( y ), d = Math.sin( y );
			var e = Math.cos( z ), f = Math.sin( z );
  
			if ( euler.order === 'XYZ' ) {
  
				var ae = a * e, af = a * f, be = b * e, bf = b * f;
  
				te[ 0 ] = c * e;
				te[ 4 ] = - c * f;
				te[ 8 ] = d;
  
				te[ 1 ] = af + be * d;
				te[ 5 ] = ae - bf * d;
				te[ 9 ] = - b * c;
  
				te[ 2 ] = bf - ae * d;
				te[ 6 ] = be + af * d;
				te[ 10 ] = a * c;
  
			} else if ( euler.order === 'YXZ' ) {
  
				var ce = c * e, cf = c * f, de = d * e, df = d * f;
  
				te[ 0 ] = ce + df * b;
				te[ 4 ] = de * b - cf;
				te[ 8 ] = a * d;
  
				te[ 1 ] = a * f;
				te[ 5 ] = a * e;
				te[ 9 ] = - b;
  
				te[ 2 ] = cf * b - de;
				te[ 6 ] = df + ce * b;
				te[ 10 ] = a * c;
  
			} else if ( euler.order === 'ZXY' ) {
  
				var ce = c * e, cf = c * f, de = d * e, df = d * f;
  
				te[ 0 ] = ce - df * b;
				te[ 4 ] = - a * f;
				te[ 8 ] = de + cf * b;
  
				te[ 1 ] = cf + de * b;
				te[ 5 ] = a * e;
				te[ 9 ] = df - ce * b;
  
				te[ 2 ] = - a * d;
				te[ 6 ] = b;
				te[ 10 ] = a * c;
  
			} else if ( euler.order === 'ZYX' ) {
  
				var ae = a * e, af = a * f, be = b * e, bf = b * f;
  
				te[ 0 ] = c * e;
				te[ 4 ] = be * d - af;
				te[ 8 ] = ae * d + bf;
  
				te[ 1 ] = c * f;
				te[ 5 ] = bf * d + ae;
				te[ 9 ] = af * d - be;
  
				te[ 2 ] = - d;
				te[ 6 ] = b * c;
				te[ 10 ] = a * c;
  
			} else if ( euler.order === 'YZX' ) {
  
				var ac = a * c, ad = a * d, bc = b * c, bd = b * d;
  
				te[ 0 ] = c * e;
				te[ 4 ] = bd - ac * f;
				te[ 8 ] = bc * f + ad;
  
				te[ 1 ] = f;
				te[ 5 ] = a * e;
				te[ 9 ] = - b * e;
  
				te[ 2 ] = - d * e;
				te[ 6 ] = ad * f + bc;
				te[ 10 ] = ac - bd * f;
  
			} else if ( euler.order === 'XZY' ) {
  
				var ac = a * c, ad = a * d, bc = b * c, bd = b * d;
  
				te[ 0 ] = c * e;
				te[ 4 ] = - f;
				te[ 8 ] = d * e;
  
				te[ 1 ] = ac * f + bd;
				te[ 5 ] = a * e;
				te[ 9 ] = ad * f - bc;
  
				te[ 2 ] = bc * f - ad;
				te[ 6 ] = b * e;
				te[ 10 ] = bd * f + ac;
  
			}
  
			// bottom row
			te[ 3 ] = 0;
			te[ 7 ] = 0;
			te[ 11 ] = 0;
  
			// last column
			te[ 12 ] = 0;
			te[ 13 ] = 0;
			te[ 14 ] = 0;
			te[ 15 ] = 1;
  
			return this;
  
		},
  
		makeRotationFromQuaternion: function () {
  
			var zero = new Vector3( 0, 0, 0 );
			var one = new Vector3( 1, 1, 1 );
  
			return function makeRotationFromQuaternion( q ) {
  
				return this.compose( zero, q, one );
  
			};
  
		}(),
  
		lookAt: function () {
  
			var x = new Vector3();
			var y = new Vector3();
			var z = new Vector3();
  
			return function lookAt( eye, target, up ) {
  
				var te = this.elements;
  
				z.subVectors( eye, target );
  
				if ( z.lengthSq() === 0 ) {
  
					// eye and target are in the same position
  
					z.z = 1;
  
				}
  
				z.normalize();
				x.crossVectors( up, z );
  
				if ( x.lengthSq() === 0 ) {
  
					// up and z are parallel
  
					if ( Math.abs( up.z ) === 1 ) {
  
						z.x += 0.0001;
  
					} else {
  
						z.z += 0.0001;
  
					}
  
					z.normalize();
					x.crossVectors( up, z );
  
				}
  
				x.normalize();
				y.crossVectors( z, x );
  
				te[ 0 ] = x.x; te[ 4 ] = y.x; te[ 8 ] = z.x;
				te[ 1 ] = x.y; te[ 5 ] = y.y; te[ 9 ] = z.y;
				te[ 2 ] = x.z; te[ 6 ] = y.z; te[ 10 ] = z.z;
  
				return this;
  
			};
  
		}(),
  
		multiply: function ( m, n ) {
  
			if ( n !== undefined ) {
  
				console.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' );
				return this.multiplyMatrices( m, n );
  
			}
  
			return this.multiplyMatrices( this, m );
  
		},
  
		premultiply: function ( m ) {
  
			return this.multiplyMatrices( m, this );
  
		},
  
		multiplyMatrices: function ( a, b ) {
  
			var ae = a.elements;
			var be = b.elements;
			var te = this.elements;
  
			var a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ];
			var a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ];
			var a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ];
			var a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ];
  
			var b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ];
			var b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ];
			var b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ];
			var b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ];
  
			te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41;
			te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42;
			te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43;
			te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44;
  
			te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41;
			te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42;
			te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43;
			te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44;
  
			te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41;
			te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42;
			te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43;
			te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44;
  
			te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41;
			te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42;
			te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43;
			te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44;
  
			return this;
  
		},
  
		multiplyScalar: function ( s ) {
  
			var te = this.elements;
  
			te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s;
			te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s;
			te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s;
			te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s;
  
			return this;
  
		},
  
		applyToBufferAttribute: function () {
  
			var v1 = new Vector3();
  
			return function applyToBufferAttribute( attribute ) {
  
				for ( var i = 0, l = attribute.count; i < l; i ++ ) {
  
					v1.x = attribute.getX( i );
					v1.y = attribute.getY( i );
					v1.z = attribute.getZ( i );
  
					v1.applyMatrix4( this );
  
					attribute.setXYZ( i, v1.x, v1.y, v1.z );
  
				}
  
				return attribute;
  
			};
  
		}(),
  
		determinant: function () {
  
			var te = this.elements;
  
			var n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ];
			var n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ];
			var n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ];
			var n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ];
  
			//TODO: make this more efficient
			//( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm )
  
			return (
				n41 * (
					+ n14 * n23 * n32
					 - n13 * n24 * n32
					 - n14 * n22 * n33
					 + n12 * n24 * n33
					 + n13 * n22 * n34
					 - n12 * n23 * n34
				) +
				n42 * (
					+ n11 * n23 * n34
					 - n11 * n24 * n33
					 + n14 * n21 * n33
					 - n13 * n21 * n34
					 + n13 * n24 * n31
					 - n14 * n23 * n31
				) +
				n43 * (
					+ n11 * n24 * n32
					 - n11 * n22 * n34
					 - n14 * n21 * n32
					 + n12 * n21 * n34
					 + n14 * n22 * n31
					 - n12 * n24 * n31
				) +
				n44 * (
					- n13 * n22 * n31
					 - n11 * n23 * n32
					 + n11 * n22 * n33
					 + n13 * n21 * n32
					 - n12 * n21 * n33
					 + n12 * n23 * n31
				)
  
			);
  
		},
  
		transpose: function () {
  
			var te = this.elements;
			var tmp;
  
			tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp;
			tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp;
			tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp;
  
			tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp;
			tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp;
			tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp;
  
			return this;
  
		},
  
		setPosition: function ( v ) {
  
			var te = this.elements;
  
			te[ 12 ] = v.x;
			te[ 13 ] = v.y;
			te[ 14 ] = v.z;
  
			return this;
  
		},
  
		getInverse: function ( m, throwOnDegenerate ) {
  
			// based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm
			var te = this.elements,
				me = m.elements,
  
				n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], n41 = me[ 3 ],
				n12 = me[ 4 ], n22 = me[ 5 ], n32 = me[ 6 ], n42 = me[ 7 ],
				n13 = me[ 8 ], n23 = me[ 9 ], n33 = me[ 10 ], n43 = me[ 11 ],
				n14 = me[ 12 ], n24 = me[ 13 ], n34 = me[ 14 ], n44 = me[ 15 ],
  
				t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44,
				t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44,
				t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44,
				t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34;
  
			var det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14;
  
			if ( det === 0 ) {
  
				var msg = "THREE.Matrix4: .getInverse() can't invert matrix, determinant is 0";
  
				if ( throwOnDegenerate === true ) {
  
					throw new Error( msg );
  
				} else {
  
					console.warn( msg );
  
				}
  
				return this.identity();
  
			}
  
			var detInv = 1 / det;
  
			te[ 0 ] = t11 * detInv;
			te[ 1 ] = ( n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44 ) * detInv;
			te[ 2 ] = ( n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44 ) * detInv;
			te[ 3 ] = ( n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43 ) * detInv;
  
			te[ 4 ] = t12 * detInv;
			te[ 5 ] = ( n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44 ) * detInv;
			te[ 6 ] = ( n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44 ) * detInv;
			te[ 7 ] = ( n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43 ) * detInv;
  
			te[ 8 ] = t13 * detInv;
			te[ 9 ] = ( n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44 ) * detInv;
			te[ 10 ] = ( n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44 ) * detInv;
			te[ 11 ] = ( n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43 ) * detInv;
  
			te[ 12 ] = t14 * detInv;
			te[ 13 ] = ( n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34 ) * detInv;
			te[ 14 ] = ( n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34 ) * detInv;
			te[ 15 ] = ( n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33 ) * detInv;
  
			return this;
  
		},
  
		scale: function ( v ) {
  
			var te = this.elements;
			var x = v.x, y = v.y, z = v.z;
  
			te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z;
			te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z;
			te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z;
			te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z;
  
			return this;
  
		},
  
		getMaxScaleOnAxis: function () {
  
			var te = this.elements;
  
			var scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ];
			var scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ];
			var scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ];
  
			return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) );
  
		},
  
		makeTranslation: function ( x, y, z ) {
  
			this.set(
  
				1, 0, 0, x,
				0, 1, 0, y,
				0, 0, 1, z,
				0, 0, 0, 1
  
			);
  
			return this;
  
		},
  
		makeRotationX: function ( theta ) {
  
			var c = Math.cos( theta ), s = Math.sin( theta );
  
			this.set(
  
				1, 0, 0, 0,
				0, c, - s, 0,
				0, s, c, 0,
				0, 0, 0, 1
  
			);
  
			return this;
  
		},
  
		makeRotationY: function ( theta ) {
  
			var c = Math.cos( theta ), s = Math.sin( theta );
  
			this.set(
  
				 c, 0, s, 0,
				 0, 1, 0, 0,
				- s, 0, c, 0,
				 0, 0, 0, 1
  
			);
  
			return this;
  
		},
  
		makeRotationZ: function ( theta ) {
  
			var c = Math.cos( theta ), s = Math.sin( theta );
  
			this.set(
  
				c, - s, 0, 0,
				s, c, 0, 0,
				0, 0, 1, 0,
				0, 0, 0, 1
  
			);
  
			return this;
  
		},
  
		makeRotationAxis: function ( axis, angle ) {
  
			// Based on http://www.gamedev.net/reference/articles/article1199.asp
  
			var c = Math.cos( angle );
			var s = Math.sin( angle );
			var t = 1 - c;
			var x = axis.x, y = axis.y, z = axis.z;
			var tx = t * x, ty = t * y;
  
			this.set(
  
				tx * x + c, tx * y - s * z, tx * z + s * y, 0,
				tx * y + s * z, ty * y + c, ty * z - s * x, 0,
				tx * z - s * y, ty * z + s * x, t * z * z + c, 0,
				0, 0, 0, 1
  
			);
  
			 return this;
  
		},
  
		makeScale: function ( x, y, z ) {
  
			this.set(
  
				x, 0, 0, 0,
				0, y, 0, 0,
				0, 0, z, 0,
				0, 0, 0, 1
  
			);
  
			return this;
  
		},
  
		makeShear: function ( x, y, z ) {
  
			this.set(
  
				1, y, z, 0,
				x, 1, z, 0,
				x, y, 1, 0,
				0, 0, 0, 1
  
			);
  
			return this;
  
		},
  
		compose: function ( position, quaternion, scale ) {
  
			var te = this.elements;
  
			var x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w;
			var x2 = x + x,	y2 = y + y, z2 = z + z;
			var xx = x * x2, xy = x * y2, xz = x * z2;
			var yy = y * y2, yz = y * z2, zz = z * z2;
			var wx = w * x2, wy = w * y2, wz = w * z2;
  
			var sx = scale.x, sy = scale.y, sz = scale.z;
  
				te[ 0 ] = ( 1 - ( yy + zz ) ) * sx;
				te[ 1 ] = ( xy + wz ) * sx;
				te[ 2 ] = ( xz - wy ) * sx;
				te[ 3 ] = 0;
  
				te[ 4 ] = ( xy - wz ) * sy;
				te[ 5 ] = ( 1 - ( xx + zz ) ) * sy;
				te[ 6 ] = ( yz + wx ) * sy;
				te[ 7 ] = 0;
  
				te[ 8 ] = ( xz + wy ) * sz;
				te[ 9 ] = ( yz - wx ) * sz;
				te[ 10 ] = ( 1 - ( xx + yy ) ) * sz;
				te[ 11 ] = 0;
  
				te[ 12 ] = position.x;
				te[ 13 ] = position.y;
				te[ 14 ] = position.z;
				te[ 15 ] = 1;
  
				return this;
  
		},
  
		decompose: function () {
  
			var vector = new Vector3();
			var matrix = new Matrix4();
  
			return function decompose( position, quaternion, scale ) {
  
				var te = this.elements;
  
				var sx = vector.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length();
				var sy = vector.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length();
				var sz = vector.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length();
  
				// if determine is negative, we need to invert one scale
				var det = this.determinant();
				if ( det < 0 ) sx = - sx;
  
				position.x = te[ 12 ];
				position.y = te[ 13 ];
				position.z = te[ 14 ];
  
				// scale the rotation part
				matrix.copy( this );
  
				var invSX = 1 / sx;
				var invSY = 1 / sy;
				var invSZ = 1 / sz;
  
				matrix.elements[ 0 ] *= invSX;
				matrix.elements[ 1 ] *= invSX;
				matrix.elements[ 2 ] *= invSX;
  
				matrix.elements[ 4 ] *= invSY;
				matrix.elements[ 5 ] *= invSY;
				matrix.elements[ 6 ] *= invSY;
  
				matrix.elements[ 8 ] *= invSZ;
				matrix.elements[ 9 ] *= invSZ;
				matrix.elements[ 10 ] *= invSZ;
  
				quaternion.setFromRotationMatrix( matrix );
  
				scale.x = sx;
				scale.y = sy;
				scale.z = sz;
  
				return this;
  
			};
  
		}(),
  
		makePerspective: function ( left, right, top, bottom, near, far ) {
  
			if ( far === undefined ) {
  
				console.warn( 'THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs.' );
  
			}
  
			var te = this.elements;
			var x = 2 * near / ( right - left );
			var y = 2 * near / ( top - bottom );
  
			var a = ( right + left ) / ( right - left );
			var b = ( top + bottom ) / ( top - bottom );
			var c = - ( far + near ) / ( far - near );
			var d = - 2 * far * near / ( far - near );
  
			te[ 0 ] = x;	te[ 4 ] = 0;	te[ 8 ] = a;	te[ 12 ] = 0;
			te[ 1 ] = 0;	te[ 5 ] = y;	te[ 9 ] = b;	te[ 13 ] = 0;
			te[ 2 ] = 0;	te[ 6 ] = 0;	te[ 10 ] = c;	te[ 14 ] = d;
			te[ 3 ] = 0;	te[ 7 ] = 0;	te[ 11 ] = - 1;	te[ 15 ] = 0;
  
			return this;
  
		},
  
		makeOrthographic: function ( left, right, top, bottom, near, far ) {
  
			var te = this.elements;
			var w = 1.0 / ( right - left );
			var h = 1.0 / ( top - bottom );
			var p = 1.0 / ( far - near );
  
			var x = ( right + left ) * w;
			var y = ( top + bottom ) * h;
			var z = ( far + near ) * p;
  
			te[ 0 ] = 2 * w;	te[ 4 ] = 0;	te[ 8 ] = 0;	te[ 12 ] = - x;
			te[ 1 ] = 0;	te[ 5 ] = 2 * h;	te[ 9 ] = 0;	te[ 13 ] = - y;
			te[ 2 ] = 0;	te[ 6 ] = 0;	te[ 10 ] = - 2 * p;	te[ 14 ] = - z;
			te[ 3 ] = 0;	te[ 7 ] = 0;	te[ 11 ] = 0;	te[ 15 ] = 1;
  
			return this;
  
		},
  
		equals: function ( matrix ) {
  
			var te = this.elements;
			var me = matrix.elements;
  
			for ( var i = 0; i < 16; i ++ ) {
  
				if ( te[ i ] !== me[ i ] ) return false;
  
			}
  
			return true;
  
		},
  
		fromArray: function ( array, offset ) {
  
			if ( offset === undefined ) offset = 0;
  
			for ( var i = 0; i < 16; i ++ ) {
  
				this.elements[ i ] = array[ i + offset ];
  
			}
  
			return this;
  
		},
  
		toArray: function ( array, offset ) {
  
			if ( array === undefined ) array = [];
			if ( offset === undefined ) offset = 0;
  
			var te = this.elements;
  
			array[ offset ] = te[ 0 ];
			array[ offset + 1 ] = te[ 1 ];
			array[ offset + 2 ] = te[ 2 ];
			array[ offset + 3 ] = te[ 3 ];
  
			array[ offset + 4 ] = te[ 4 ];
			array[ offset + 5 ] = te[ 5 ];
			array[ offset + 6 ] = te[ 6 ];
			array[ offset + 7 ] = te[ 7 ];
  
			array[ offset + 8 ] = te[ 8 ];
			array[ offset + 9 ] = te[ 9 ];
			array[ offset + 10 ] = te[ 10 ];
			array[ offset + 11 ] = te[ 11 ];
  
			array[ offset + 12 ] = te[ 12 ];
			array[ offset + 13 ] = te[ 13 ];
			array[ offset + 14 ] = te[ 14 ];
			array[ offset + 15 ] = te[ 15 ];
  
			return array;
  
		}
  
	} );
  
	/**
	 * @author mikael emtinger / http://gomo.se/
	 * @author alteredq / http://alteredqualia.com/
	 * @author WestLangley / http://github.com/WestLangley
	 * @author bhouston / http://clara.io
	 */
  
	function Quaternion( x, y, z, w ) {
  
		this._x = x || 0;
		this._y = y || 0;
		this._z = z || 0;
		this._w = ( w !== undefined ) ? w : 1;
  
	}
  
	Object.assign( Quaternion, {
  
		slerp: function ( qa, qb, qm, t ) {
  
			return qm.copy( qa ).slerp( qb, t );
  
		},
  
		slerpFlat: function ( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) {
  
			// fuzz-free, array-based Quaternion SLERP operation
  
			var x0 = src0[ srcOffset0 + 0 ],
				y0 = src0[ srcOffset0 + 1 ],
				z0 = src0[ srcOffset0 + 2 ],
				w0 = src0[ srcOffset0 + 3 ],
  
				x1 = src1[ srcOffset1 + 0 ],
				y1 = src1[ srcOffset1 + 1 ],
				z1 = src1[ srcOffset1 + 2 ],
				w1 = src1[ srcOffset1 + 3 ];
  
			if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) {
  
				var s = 1 - t,
  
					cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1,
  
					dir = ( cos >= 0 ? 1 : - 1 ),
					sqrSin = 1 - cos * cos;
  
				// Skip the Slerp for tiny steps to avoid numeric problems:
				if ( sqrSin > Number.EPSILON ) {
  
					var sin = Math.sqrt( sqrSin ),
						len = Math.atan2( sin, cos * dir );
  
					s = Math.sin( s * len ) / sin;
					t = Math.sin( t * len ) / sin;
  
				}
  
				var tDir = t * dir;
  
				x0 = x0 * s + x1 * tDir;
				y0 = y0 * s + y1 * tDir;
				z0 = z0 * s + z1 * tDir;
				w0 = w0 * s + w1 * tDir;
  
				// Normalize in case we just did a lerp:
				if ( s === 1 - t ) {
  
					var f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 );
  
					x0 *= f;
					y0 *= f;
					z0 *= f;
					w0 *= f;
  
				}
  
			}
  
			dst[ dstOffset ] = x0;
			dst[ dstOffset + 1 ] = y0;
			dst[ dstOffset + 2 ] = z0;
			dst[ dstOffset + 3 ] = w0;
  
		}
  
	} );
  
	Object.defineProperties( Quaternion.prototype, {
  
		x: {
  
			get: function () {
  
				return this._x;
  
			},
  
			set: function ( value ) {
  
				this._x = value;
				this.onChangeCallback();
  
			}
  
		},
  
		y: {
  
			get: function () {
  
				return this._y;
  
			},
  
			set: function ( value ) {
  
				this._y = value;
				this.onChangeCallback();
  
			}
  
		},
  
		z: {
  
			get: function () {
  
				return this._z;
  
			},
  
			set: function ( value ) {
  
				this._z = value;
				this.onChangeCallback();
  
			}
  
		},
  
		w: {
  
			get: function () {
  
				return this._w;
  
			},
  
			set: function ( value ) {
  
				this._w = value;
				this.onChangeCallback();
  
			}
  
		}
  
	} );
  
	Object.assign( Quaternion.prototype, {
  
		set: function ( x, y, z, w ) {
  
			this._x = x;
			this._y = y;
			this._z = z;
			this._w = w;
  
			this.onChangeCallback();
  
			return this;
  
		},
  
		clone: function () {
  
			return new this.constructor( this._x, this._y, this._z, this._w );
  
		},
  
		copy: function ( quaternion ) {
  
			this._x = quaternion.x;
			this._y = quaternion.y;
			this._z = quaternion.z;
			this._w = quaternion.w;
  
			this.onChangeCallback();
  
			return this;
  
		},
  
		setFromEuler: function ( euler, update ) {
  
			if ( ! ( euler && euler.isEuler ) ) {
  
				throw new Error( 'THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.' );
  
			}
  
			var x = euler._x, y = euler._y, z = euler._z, order = euler.order;
  
			// http://www.mathworks.com/matlabcentral/fileexchange/
			// 	20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/
			//	content/SpinCalc.m
  
			var cos = Math.cos;
			var sin = Math.sin;
  
			var c1 = cos( x / 2 );
			var c2 = cos( y / 2 );
			var c3 = cos( z / 2 );
  
			var s1 = sin( x / 2 );
			var s2 = sin( y / 2 );
			var s3 = sin( z / 2 );
  
			if ( order === 'XYZ' ) {
  
				this._x = s1 * c2 * c3 + c1 * s2 * s3;
				this._y = c1 * s2 * c3 - s1 * c2 * s3;
				this._z = c1 * c2 * s3 + s1 * s2 * c3;
				this._w = c1 * c2 * c3 - s1 * s2 * s3;
  
			} else if ( order === 'YXZ' ) {
  
				this._x = s1 * c2 * c3 + c1 * s2 * s3;
				this._y = c1 * s2 * c3 - s1 * c2 * s3;
				this._z = c1 * c2 * s3 - s1 * s2 * c3;
				this._w = c1 * c2 * c3 + s1 * s2 * s3;
  
			} else if ( order === 'ZXY' ) {
  
				this._x = s1 * c2 * c3 - c1 * s2 * s3;
				this._y = c1 * s2 * c3 + s1 * c2 * s3;
				this._z = c1 * c2 * s3 + s1 * s2 * c3;
				this._w = c1 * c2 * c3 - s1 * s2 * s3;
  
			} else if ( order === 'ZYX' ) {
  
				this._x = s1 * c2 * c3 - c1 * s2 * s3;
				this._y = c1 * s2 * c3 + s1 * c2 * s3;
				this._z = c1 * c2 * s3 - s1 * s2 * c3;
				this._w = c1 * c2 * c3 + s1 * s2 * s3;
  
			} else if ( order === 'YZX' ) {
  
				this._x = s1 * c2 * c3 + c1 * s2 * s3;
				this._y = c1 * s2 * c3 + s1 * c2 * s3;
				this._z = c1 * c2 * s3 - s1 * s2 * c3;
				this._w = c1 * c2 * c3 - s1 * s2 * s3;
  
			} else if ( order === 'XZY' ) {
  
				this._x = s1 * c2 * c3 - c1 * s2 * s3;
				this._y = c1 * s2 * c3 - s1 * c2 * s3;
				this._z = c1 * c2 * s3 + s1 * s2 * c3;
				this._w = c1 * c2 * c3 + s1 * s2 * s3;
  
			}
  
			if ( update !== false ) this.onChangeCallback();
  
			return this;
  
		},
  
		setFromAxisAngle: function ( axis, angle ) {
  
			// http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm
  
			// assumes axis is normalized
  
			var halfAngle = angle / 2, s = Math.sin( halfAngle );
  
			this._x = axis.x * s;
			this._y = axis.y * s;
			this._z = axis.z * s;
			this._w = Math.cos( halfAngle );
  
			this.onChangeCallback();
  
			return this;
  
		},
  
		setFromRotationMatrix: function ( m ) {
  
			// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
  
			// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
  
			var te = m.elements,
  
				m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],
				m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],
				m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ],
  
				trace = m11 + m22 + m33,
				s;
  
			if ( trace > 0 ) {
  
				s = 0.5 / Math.sqrt( trace + 1.0 );
  
				this._w = 0.25 / s;
				this._x = ( m32 - m23 ) * s;
				this._y = ( m13 - m31 ) * s;
				this._z = ( m21 - m12 ) * s;
  
			} else if ( m11 > m22 && m11 > m33 ) {
  
				s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 );
  
				this._w = ( m32 - m23 ) / s;
				this._x = 0.25 * s;
				this._y = ( m12 + m21 ) / s;
				this._z = ( m13 + m31 ) / s;
  
			} else if ( m22 > m33 ) {
  
				s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 );
  
				this._w = ( m13 - m31 ) / s;
				this._x = ( m12 + m21 ) / s;
				this._y = 0.25 * s;
				this._z = ( m23 + m32 ) / s;
  
			} else {
  
				s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 );
  
				this._w = ( m21 - m12 ) / s;
				this._x = ( m13 + m31 ) / s;
				this._y = ( m23 + m32 ) / s;
				this._z = 0.25 * s;
  
			}
  
			this.onChangeCallback();
  
			return this;
  
		},
  
		setFromUnitVectors: function () {
  
			// assumes direction vectors vFrom and vTo are normalized
  
			var v1 = new Vector3();
			var r;
  
			var EPS = 0.000001;
  
			return function setFromUnitVectors( vFrom, vTo ) {
  
				if ( v1 === undefined ) v1 = new Vector3();
  
				r = vFrom.dot( vTo ) + 1;
  
				if ( r < EPS ) {
  
					r = 0;
  
					if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) {
  
						v1.set( - vFrom.y, vFrom.x, 0 );
  
					} else {
  
						v1.set( 0, - vFrom.z, vFrom.y );
  
					}
  
				} else {
  
					v1.crossVectors( vFrom, vTo );
  
				}
  
				this._x = v1.x;
				this._y = v1.y;
				this._z = v1.z;
				this._w = r;
  
				return this.normalize();
  
			};
  
		}(),
  
		inverse: function () {
  
			// quaternion is assumed to have unit length
  
			return this.conjugate();
  
		},
  
		conjugate: function () {
  
			this._x *= - 1;
			this._y *= - 1;
			this._z *= - 1;
  
			this.onChangeCallback();
  
			return this;
  
		},
  
		dot: function ( v ) {
  
			return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w;
  
		},
  
		lengthSq: function () {
  
			return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w;
  
		},
  
		length: function () {
  
			return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w );
  
		},
  
		normalize: function () {
  
			var l = this.length();
  
			if ( l === 0 ) {
  
				this._x = 0;
				this._y = 0;
				this._z = 0;
				this._w = 1;
  
			} else {
  
				l = 1 / l;
  
				this._x = this._x * l;
				this._y = this._y * l;
				this._z = this._z * l;
				this._w = this._w * l;
  
			}
  
			this.onChangeCallback();
  
			return this;
  
		},
  
		multiply: function ( q, p ) {
  
			if ( p !== undefined ) {
  
				console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' );
				return this.multiplyQuaternions( q, p );
  
			}
  
			return this.multiplyQuaternions( this, q );
  
		},
  
		premultiply: function ( q ) {
  
			return this.multiplyQuaternions( q, this );
  
		},
  
		multiplyQuaternions: function ( a, b ) {
  
			// from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm
  
			var qax = a._x, qay = a._y, qaz = a._z, qaw = a._w;
			var qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w;
  
			this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby;
			this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz;
			this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx;
			this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz;
  
			this.onChangeCallback();
  
			return this;
  
		},
  
		slerp: function ( qb, t ) {
  
			if ( t === 0 ) return this;
			if ( t === 1 ) return this.copy( qb );
  
			var x = this._x, y = this._y, z = this._z, w = this._w;
  
			// http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/
  
			var cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z;
  
			if ( cosHalfTheta < 0 ) {
  
				this._w = - qb._w;
				this._x = - qb._x;
				this._y = - qb._y;
				this._z = - qb._z;
  
				cosHalfTheta = - cosHalfTheta;
  
			} else {
  
				this.copy( qb );
  
			}
  
			if ( cosHalfTheta >= 1.0 ) {
  
				this._w = w;
				this._x = x;
				this._y = y;
				this._z = z;
  
				return this;
  
			}
  
			var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta );
  
			if ( Math.abs( sinHalfTheta ) < 0.001 ) {
  
				this._w = 0.5 * ( w + this._w );
				this._x = 0.5 * ( x + this._x );
				this._y = 0.5 * ( y + this._y );
				this._z = 0.5 * ( z + this._z );
  
				return this;
  
			}
  
			var halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta );
			var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta,
				ratioB = Math.sin( t * halfTheta ) / sinHalfTheta;
  
			this._w = ( w * ratioA + this._w * ratioB );
			this._x = ( x * ratioA + this._x * ratioB );
			this._y = ( y * ratioA + this._y * ratioB );
			this._z = ( z * ratioA + this._z * ratioB );
  
			this.onChangeCallback();
  
			return this;
  
		},
  
		equals: function ( quaternion ) {
  
			return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w );
  
		},
  
		fromArray: function ( array, offset ) {
  
			if ( offset === undefined ) offset = 0;
  
			this._x = array[ offset ];
			this._y = array[ offset + 1 ];
			this._z = array[ offset + 2 ];
			this._w = array[ offset + 3 ];
  
			this.onChangeCallback();
  
			return this;
  
		},
  
		toArray: function ( array, offset ) {
  
			if ( array === undefined ) array = [];
			if ( offset === undefined ) offset = 0;
  
			array[ offset ] = this._x;
			array[ offset + 1 ] = this._y;
			array[ offset + 2 ] = this._z;
			array[ offset + 3 ] = this._w;
  
			return array;
  
		},
  
		onChange: function ( callback ) {
  
			this.onChangeCallback = callback;
  
			return this;
  
		},
  
		onChangeCallback: function () {}
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 * @author kile / http://kile.stravaganza.org/
	 * @author philogb / http://blog.thejit.org/
	 * @author mikael emtinger / http://gomo.se/
	 * @author egraether / http://egraether.com/
	 * @author WestLangley / http://github.com/WestLangley
	 */
  
	function Vector3( x, y, z ) {
  
		this.x = x || 0;
		this.y = y || 0;
		this.z = z || 0;
  
	}
  
	Object.assign( Vector3.prototype, {
  
		isVector3: true,
  
		set: function ( x, y, z ) {
  
			this.x = x;
			this.y = y;
			this.z = z;
  
			return this;
  
		},
  
		setScalar: function ( scalar ) {
  
			this.x = scalar;
			this.y = scalar;
			this.z = scalar;
  
			return this;
  
		},
  
		setX: function ( x ) {
  
			this.x = x;
  
			return this;
  
		},
  
		setY: function ( y ) {
  
			this.y = y;
  
			return this;
  
		},
  
		setZ: function ( z ) {
  
			this.z = z;
  
			return this;
  
		},
  
		setComponent: function ( index, value ) {
  
			switch ( index ) {
  
				case 0: this.x = value; break;
				case 1: this.y = value; break;
				case 2: this.z = value; break;
				default: throw new Error( 'index is out of range: ' + index );
  
			}
  
			return this;
  
		},
  
		getComponent: function ( index ) {
  
			switch ( index ) {
  
				case 0: return this.x;
				case 1: return this.y;
				case 2: return this.z;
				default: throw new Error( 'index is out of range: ' + index );
  
			}
  
		},
  
		clone: function () {
  
			return new this.constructor( this.x, this.y, this.z );
  
		},
  
		copy: function ( v ) {
  
			this.x = v.x;
			this.y = v.y;
			this.z = v.z;
  
			return this;
  
		},
  
		add: function ( v, w ) {
  
			if ( w !== undefined ) {
  
				console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
				return this.addVectors( v, w );
  
			}
  
			this.x += v.x;
			this.y += v.y;
			this.z += v.z;
  
			return this;
  
		},
  
		addScalar: function ( s ) {
  
			this.x += s;
			this.y += s;
			this.z += s;
  
			return this;
  
		},
  
		addVectors: function ( a, b ) {
  
			this.x = a.x + b.x;
			this.y = a.y + b.y;
			this.z = a.z + b.z;
  
			return this;
  
		},
  
		addScaledVector: function ( v, s ) {
  
			this.x += v.x * s;
			this.y += v.y * s;
			this.z += v.z * s;
  
			return this;
  
		},
  
		sub: function ( v, w ) {
  
			if ( w !== undefined ) {
  
				console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
				return this.subVectors( v, w );
  
			}
  
			this.x -= v.x;
			this.y -= v.y;
			this.z -= v.z;
  
			return this;
  
		},
  
		subScalar: function ( s ) {
  
			this.x -= s;
			this.y -= s;
			this.z -= s;
  
			return this;
  
		},
  
		subVectors: function ( a, b ) {
  
			this.x = a.x - b.x;
			this.y = a.y - b.y;
			this.z = a.z - b.z;
  
			return this;
  
		},
  
		multiply: function ( v, w ) {
  
			if ( w !== undefined ) {
  
				console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' );
				return this.multiplyVectors( v, w );
  
			}
  
			this.x *= v.x;
			this.y *= v.y;
			this.z *= v.z;
  
			return this;
  
		},
  
		multiplyScalar: function ( scalar ) {
  
			this.x *= scalar;
			this.y *= scalar;
			this.z *= scalar;
  
			return this;
  
		},
  
		multiplyVectors: function ( a, b ) {
  
			this.x = a.x * b.x;
			this.y = a.y * b.y;
			this.z = a.z * b.z;
  
			return this;
  
		},
  
		applyEuler: function () {
  
			var quaternion = new Quaternion();
  
			return function applyEuler( euler ) {
  
				if ( ! ( euler && euler.isEuler ) ) {
  
					console.error( 'THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.' );
  
				}
  
				return this.applyQuaternion( quaternion.setFromEuler( euler ) );
  
			};
  
		}(),
  
		applyAxisAngle: function () {
  
			var quaternion = new Quaternion();
  
			return function applyAxisAngle( axis, angle ) {
  
				return this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) );
  
			};
  
		}(),
  
		applyMatrix3: function ( m ) {
  
			var x = this.x, y = this.y, z = this.z;
			var e = m.elements;
  
			this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z;
			this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z;
			this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z;
  
			return this;
  
		},
  
		applyMatrix4: function ( m ) {
  
			var x = this.x, y = this.y, z = this.z;
			var e = m.elements;
  
			var w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] );
  
			this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * w;
			this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * w;
			this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * w;
  
			return this;
  
		},
  
		applyQuaternion: function ( q ) {
  
			var x = this.x, y = this.y, z = this.z;
			var qx = q.x, qy = q.y, qz = q.z, qw = q.w;
  
			// calculate quat * vector
  
			var ix = qw * x + qy * z - qz * y;
			var iy = qw * y + qz * x - qx * z;
			var iz = qw * z + qx * y - qy * x;
			var iw = - qx * x - qy * y - qz * z;
  
			// calculate result * inverse quat
  
			this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy;
			this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz;
			this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx;
  
			return this;
  
		},
  
		project: function () {
  
			var matrix = new Matrix4();
  
			return function project( camera ) {
  
				matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) );
				return this.applyMatrix4( matrix );
  
			};
  
		}(),
  
		unproject: function () {
  
			var matrix = new Matrix4();
  
			return function unproject( camera ) {
  
				matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) );
				return this.applyMatrix4( matrix );
  
			};
  
		}(),
  
		transformDirection: function ( m ) {
  
			// input: THREE.Matrix4 affine matrix
			// vector interpreted as a direction
  
			var x = this.x, y = this.y, z = this.z;
			var e = m.elements;
  
			this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z;
			this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z;
			this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z;
  
			return this.normalize();
  
		},
  
		divide: function ( v ) {
  
			this.x /= v.x;
			this.y /= v.y;
			this.z /= v.z;
  
			return this;
  
		},
  
		divideScalar: function ( scalar ) {
  
			return this.multiplyScalar( 1 / scalar );
  
		},
  
		min: function ( v ) {
  
			this.x = Math.min( this.x, v.x );
			this.y = Math.min( this.y, v.y );
			this.z = Math.min( this.z, v.z );
  
			return this;
  
		},
  
		max: function ( v ) {
  
			this.x = Math.max( this.x, v.x );
			this.y = Math.max( this.y, v.y );
			this.z = Math.max( this.z, v.z );
  
			return this;
  
		},
  
		clamp: function ( min, max ) {
  
			// assumes min < max, componentwise
  
			this.x = Math.max( min.x, Math.min( max.x, this.x ) );
			this.y = Math.max( min.y, Math.min( max.y, this.y ) );
			this.z = Math.max( min.z, Math.min( max.z, this.z ) );
  
			return this;
  
		},
  
		clampScalar: function () {
  
			var min = new Vector3();
			var max = new Vector3();
  
			return function clampScalar( minVal, maxVal ) {
  
				min.set( minVal, minVal, minVal );
				max.set( maxVal, maxVal, maxVal );
  
				return this.clamp( min, max );
  
			};
  
		}(),
  
		clampLength: function ( min, max ) {
  
			var length = this.length();
  
			return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) );
  
		},
  
		floor: function () {
  
			this.x = Math.floor( this.x );
			this.y = Math.floor( this.y );
			this.z = Math.floor( this.z );
  
			return this;
  
		},
  
		ceil: function () {
  
			this.x = Math.ceil( this.x );
			this.y = Math.ceil( this.y );
			this.z = Math.ceil( this.z );
  
			return this;
  
		},
  
		round: function () {
  
			this.x = Math.round( this.x );
			this.y = Math.round( this.y );
			this.z = Math.round( this.z );
  
			return this;
  
		},
  
		roundToZero: function () {
  
			this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
			this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
			this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z );
  
			return this;
  
		},
  
		negate: function () {
  
			this.x = - this.x;
			this.y = - this.y;
			this.z = - this.z;
  
			return this;
  
		},
  
		dot: function ( v ) {
  
			return this.x * v.x + this.y * v.y + this.z * v.z;
  
		},
  
		// TODO lengthSquared?
  
		lengthSq: function () {
  
			return this.x * this.x + this.y * this.y + this.z * this.z;
  
		},
  
		length: function () {
  
			return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z );
  
		},
  
		manhattanLength: function () {
  
			return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z );
  
		},
  
		normalize: function () {
  
			return this.divideScalar( this.length() || 1 );
  
		},
  
		setLength: function ( length ) {
  
			return this.normalize().multiplyScalar( length );
  
		},
  
		lerp: function ( v, alpha ) {
  
			this.x += ( v.x - this.x ) * alpha;
			this.y += ( v.y - this.y ) * alpha;
			this.z += ( v.z - this.z ) * alpha;
  
			return this;
  
		},
  
		lerpVectors: function ( v1, v2, alpha ) {
  
			return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );
  
		},
  
		cross: function ( v, w ) {
  
			if ( w !== undefined ) {
  
				console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' );
				return this.crossVectors( v, w );
  
			}
  
			return this.crossVectors( this, v );
  
		},
  
		crossVectors: function ( a, b ) {
  
			var ax = a.x, ay = a.y, az = a.z;
			var bx = b.x, by = b.y, bz = b.z;
  
			this.x = ay * bz - az * by;
			this.y = az * bx - ax * bz;
			this.z = ax * by - ay * bx;
  
			return this;
  
		},
  
		projectOnVector: function ( vector ) {
  
			var scalar = vector.dot( this ) / vector.lengthSq();
  
			return this.copy( vector ).multiplyScalar( scalar );
  
		},
  
		projectOnPlane: function () {
  
			var v1 = new Vector3();
  
			return function projectOnPlane( planeNormal ) {
  
				v1.copy( this ).projectOnVector( planeNormal );
  
				return this.sub( v1 );
  
			};
  
		}(),
  
		reflect: function () {
  
			// reflect incident vector off plane orthogonal to normal
			// normal is assumed to have unit length
  
			var v1 = new Vector3();
  
			return function reflect( normal ) {
  
				return this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) );
  
			};
  
		}(),
  
		angleTo: function ( v ) {
  
			var theta = this.dot( v ) / ( Math.sqrt( this.lengthSq() * v.lengthSq() ) );
  
			// clamp, to handle numerical problems
  
			return Math.acos( _Math.clamp( theta, - 1, 1 ) );
  
		},
  
		distanceTo: function ( v ) {
  
			return Math.sqrt( this.distanceToSquared( v ) );
  
		},
  
		distanceToSquared: function ( v ) {
  
			var dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z;
  
			return dx * dx + dy * dy + dz * dz;
  
		},
  
		manhattanDistanceTo: function ( v ) {
  
			return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z );
  
		},
  
		setFromSpherical: function ( s ) {
  
			var sinPhiRadius = Math.sin( s.phi ) * s.radius;
  
			this.x = sinPhiRadius * Math.sin( s.theta );
			this.y = Math.cos( s.phi ) * s.radius;
			this.z = sinPhiRadius * Math.cos( s.theta );
  
			return this;
  
		},
  
		setFromCylindrical: function ( c ) {
  
			this.x = c.radius * Math.sin( c.theta );
			this.y = c.y;
			this.z = c.radius * Math.cos( c.theta );
  
			return this;
  
		},
  
		setFromMatrixPosition: function ( m ) {
  
			var e = m.elements;
  
			this.x = e[ 12 ];
			this.y = e[ 13 ];
			this.z = e[ 14 ];
  
			return this;
  
		},
  
		setFromMatrixScale: function ( m ) {
  
			var sx = this.setFromMatrixColumn( m, 0 ).length();
			var sy = this.setFromMatrixColumn( m, 1 ).length();
			var sz = this.setFromMatrixColumn( m, 2 ).length();
  
			this.x = sx;
			this.y = sy;
			this.z = sz;
  
			return this;
  
		},
  
		setFromMatrixColumn: function ( m, index ) {
  
			return this.fromArray( m.elements, index * 4 );
  
		},
  
		equals: function ( v ) {
  
			return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) );
  
		},
  
		fromArray: function ( array, offset ) {
  
			if ( offset === undefined ) offset = 0;
  
			this.x = array[ offset ];
			this.y = array[ offset + 1 ];
			this.z = array[ offset + 2 ];
  
			return this;
  
		},
  
		toArray: function ( array, offset ) {
  
			if ( array === undefined ) array = [];
			if ( offset === undefined ) offset = 0;
  
			array[ offset ] = this.x;
			array[ offset + 1 ] = this.y;
			array[ offset + 2 ] = this.z;
  
			return array;
  
		},
  
		fromBufferAttribute: function ( attribute, index, offset ) {
  
			if ( offset !== undefined ) {
  
				console.warn( 'THREE.Vector3: offset has been removed from .fromBufferAttribute().' );
  
			}
  
			this.x = attribute.getX( index );
			this.y = attribute.getY( index );
			this.z = attribute.getZ( index );
  
			return this;
  
		}
  
	} );
  
	/**
	 * @author alteredq / http://alteredqualia.com/
	 * @author WestLangley / http://github.com/WestLangley
	 * @author bhouston / http://clara.io
	 * @author tschw
	 */
  
	function Matrix3() {
  
		this.elements = [
  
			1, 0, 0,
			0, 1, 0,
			0, 0, 1
  
		];
  
		if ( arguments.length > 0 ) {
  
			console.error( 'THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.' );
  
		}
  
	}
  
	Object.assign( Matrix3.prototype, {
  
		isMatrix3: true,
  
		set: function ( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) {
  
			var te = this.elements;
  
			te[ 0 ] = n11; te[ 1 ] = n21; te[ 2 ] = n31;
			te[ 3 ] = n12; te[ 4 ] = n22; te[ 5 ] = n32;
			te[ 6 ] = n13; te[ 7 ] = n23; te[ 8 ] = n33;
  
			return this;
  
		},
  
		identity: function () {
  
			this.set(
  
				1, 0, 0,
				0, 1, 0,
				0, 0, 1
  
			);
  
			return this;
  
		},
  
		clone: function () {
  
			return new this.constructor().fromArray( this.elements );
  
		},
  
		copy: function ( m ) {
  
			var te = this.elements;
			var me = m.elements;
  
			te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ];
			te[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ];
			te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ];
  
			return this;
  
		},
  
		setFromMatrix4: function ( m ) {
  
			var me = m.elements;
  
			this.set(
  
				me[ 0 ], me[ 4 ], me[ 8 ],
				me[ 1 ], me[ 5 ], me[ 9 ],
				me[ 2 ], me[ 6 ], me[ 10 ]
  
			);
  
			return this;
  
		},
  
		applyToBufferAttribute: function () {
  
			var v1 = new Vector3();
  
			return function applyToBufferAttribute( attribute ) {
  
				for ( var i = 0, l = attribute.count; i < l; i ++ ) {
  
					v1.x = attribute.getX( i );
					v1.y = attribute.getY( i );
					v1.z = attribute.getZ( i );
  
					v1.applyMatrix3( this );
  
					attribute.setXYZ( i, v1.x, v1.y, v1.z );
  
				}
  
				return attribute;
  
			};
  
		}(),
  
		multiply: function ( m ) {
  
			return this.multiplyMatrices( this, m );
  
		},
  
		premultiply: function ( m ) {
  
			return this.multiplyMatrices( m, this );
  
		},
  
		multiplyMatrices: function ( a, b ) {
  
			var ae = a.elements;
			var be = b.elements;
			var te = this.elements;
  
			var a11 = ae[ 0 ], a12 = ae[ 3 ], a13 = ae[ 6 ];
			var a21 = ae[ 1 ], a22 = ae[ 4 ], a23 = ae[ 7 ];
			var a31 = ae[ 2 ], a32 = ae[ 5 ], a33 = ae[ 8 ];
  
			var b11 = be[ 0 ], b12 = be[ 3 ], b13 = be[ 6 ];
			var b21 = be[ 1 ], b22 = be[ 4 ], b23 = be[ 7 ];
			var b31 = be[ 2 ], b32 = be[ 5 ], b33 = be[ 8 ];
  
			te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31;
			te[ 3 ] = a11 * b12 + a12 * b22 + a13 * b32;
			te[ 6 ] = a11 * b13 + a12 * b23 + a13 * b33;
  
			te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31;
			te[ 4 ] = a21 * b12 + a22 * b22 + a23 * b32;
			te[ 7 ] = a21 * b13 + a22 * b23 + a23 * b33;
  
			te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31;
			te[ 5 ] = a31 * b12 + a32 * b22 + a33 * b32;
			te[ 8 ] = a31 * b13 + a32 * b23 + a33 * b33;
  
			return this;
  
		},
  
		multiplyScalar: function ( s ) {
  
			var te = this.elements;
  
			te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s;
			te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s;
			te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s;
  
			return this;
  
		},
  
		determinant: function () {
  
			var te = this.elements;
  
			var a = te[ 0 ], b = te[ 1 ], c = te[ 2 ],
				d = te[ 3 ], e = te[ 4 ], f = te[ 5 ],
				g = te[ 6 ], h = te[ 7 ], i = te[ 8 ];
  
			return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g;
  
		},
  
		getInverse: function ( matrix, throwOnDegenerate ) {
  
			if ( matrix && matrix.isMatrix4 ) {
  
				console.error( "THREE.Matrix3: .getInverse() no longer takes a Matrix4 argument." );
  
			}
  
			var me = matrix.elements,
				te = this.elements,
  
				n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ],
				n12 = me[ 3 ], n22 = me[ 4 ], n32 = me[ 5 ],
				n13 = me[ 6 ], n23 = me[ 7 ], n33 = me[ 8 ],
  
				t11 = n33 * n22 - n32 * n23,
				t12 = n32 * n13 - n33 * n12,
				t13 = n23 * n12 - n22 * n13,
  
				det = n11 * t11 + n21 * t12 + n31 * t13;
  
			if ( det === 0 ) {
  
				var msg = "THREE.Matrix3: .getInverse() can't invert matrix, determinant is 0";
  
				if ( throwOnDegenerate === true ) {
  
					throw new Error( msg );
  
				} else {
  
					console.warn( msg );
  
				}
  
				return this.identity();
  
			}
  
			var detInv = 1 / det;
  
			te[ 0 ] = t11 * detInv;
			te[ 1 ] = ( n31 * n23 - n33 * n21 ) * detInv;
			te[ 2 ] = ( n32 * n21 - n31 * n22 ) * detInv;
  
			te[ 3 ] = t12 * detInv;
			te[ 4 ] = ( n33 * n11 - n31 * n13 ) * detInv;
			te[ 5 ] = ( n31 * n12 - n32 * n11 ) * detInv;
  
			te[ 6 ] = t13 * detInv;
			te[ 7 ] = ( n21 * n13 - n23 * n11 ) * detInv;
			te[ 8 ] = ( n22 * n11 - n21 * n12 ) * detInv;
  
			return this;
  
		},
  
		transpose: function () {
  
			var tmp, m = this.elements;
  
			tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp;
			tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp;
			tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp;
  
			return this;
  
		},
  
		getNormalMatrix: function ( matrix4 ) {
  
			return this.setFromMatrix4( matrix4 ).getInverse( this ).transpose();
  
		},
  
		transposeIntoArray: function ( r ) {
  
			var m = this.elements;
  
			r[ 0 ] = m[ 0 ];
			r[ 1 ] = m[ 3 ];
			r[ 2 ] = m[ 6 ];
			r[ 3 ] = m[ 1 ];
			r[ 4 ] = m[ 4 ];
			r[ 5 ] = m[ 7 ];
			r[ 6 ] = m[ 2 ];
			r[ 7 ] = m[ 5 ];
			r[ 8 ] = m[ 8 ];
  
			return this;
  
		},
  
		setUvTransform: function ( tx, ty, sx, sy, rotation, cx, cy ) {
  
			var c = Math.cos( rotation );
			var s = Math.sin( rotation );
  
			this.set(
				sx * c, sx * s, - sx * ( c * cx + s * cy ) + cx + tx,
				- sy * s, sy * c, - sy * ( - s * cx + c * cy ) + cy + ty,
				0, 0, 1
			);
  
		},
  
		scale: function ( sx, sy ) {
  
			var te = this.elements;
  
			te[ 0 ] *= sx; te[ 3 ] *= sx; te[ 6 ] *= sx;
			te[ 1 ] *= sy; te[ 4 ] *= sy; te[ 7 ] *= sy;
  
			return this;
  
		},
  
		rotate: function ( theta ) {
  
			var c = Math.cos( theta );
			var s = Math.sin( theta );
  
			var te = this.elements;
  
			var a11 = te[ 0 ], a12 = te[ 3 ], a13 = te[ 6 ];
			var a21 = te[ 1 ], a22 = te[ 4 ], a23 = te[ 7 ];
  
			te[ 0 ] = c * a11 + s * a21;
			te[ 3 ] = c * a12 + s * a22;
			te[ 6 ] = c * a13 + s * a23;
  
			te[ 1 ] = - s * a11 + c * a21;
			te[ 4 ] = - s * a12 + c * a22;
			te[ 7 ] = - s * a13 + c * a23;
  
			return this;
  
		},
  
		translate: function ( tx, ty ) {
  
			var te = this.elements;
  
			te[ 0 ] += tx * te[ 2 ]; te[ 3 ] += tx * te[ 5 ]; te[ 6 ] += tx * te[ 8 ];
			te[ 1 ] += ty * te[ 2 ]; te[ 4 ] += ty * te[ 5 ]; te[ 7 ] += ty * te[ 8 ];
  
			return this;
  
		},
  
		equals: function ( matrix ) {
  
			var te = this.elements;
			var me = matrix.elements;
  
			for ( var i = 0; i < 9; i ++ ) {
  
				if ( te[ i ] !== me[ i ] ) return false;
  
			}
  
			return true;
  
		},
  
		fromArray: function ( array, offset ) {
  
			if ( offset === undefined ) offset = 0;
  
			for ( var i = 0; i < 9; i ++ ) {
  
				this.elements[ i ] = array[ i + offset ];
  
			}
  
			return this;
  
		},
  
		toArray: function ( array, offset ) {
  
			if ( array === undefined ) array = [];
			if ( offset === undefined ) offset = 0;
  
			var te = this.elements;
  
			array[ offset ] = te[ 0 ];
			array[ offset + 1 ] = te[ 1 ];
			array[ offset + 2 ] = te[ 2 ];
  
			array[ offset + 3 ] = te[ 3 ];
			array[ offset + 4 ] = te[ 4 ];
			array[ offset + 5 ] = te[ 5 ];
  
			array[ offset + 6 ] = te[ 6 ];
			array[ offset + 7 ] = te[ 7 ];
			array[ offset + 8 ] = te[ 8 ];
  
			return array;
  
		}
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 * @author alteredq / http://alteredqualia.com/
	 * @author szimek / https://github.com/szimek/
	 */
  
	var textureId = 0;
  
	function Texture( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) {
  
		Object.defineProperty( this, 'id', { value: textureId ++ } );
  
		this.uuid = _Math.generateUUID();
  
		this.name = '';
  
		this.image = image !== undefined ? image : Texture.DEFAULT_IMAGE;
		this.mipmaps = [];
  
		this.mapping = mapping !== undefined ? mapping : Texture.DEFAULT_MAPPING;
  
		this.wrapS = wrapS !== undefined ? wrapS : ClampToEdgeWrapping;
		this.wrapT = wrapT !== undefined ? wrapT : ClampToEdgeWrapping;
  
		this.magFilter = magFilter !== undefined ? magFilter : LinearFilter;
		this.minFilter = minFilter !== undefined ? minFilter : LinearMipMapLinearFilter;
  
		this.anisotropy = anisotropy !== undefined ? anisotropy : 1;
  
		this.format = format !== undefined ? format : RGBAFormat;
		this.type = type !== undefined ? type : UnsignedByteType;
  
		this.offset = new Vector2( 0, 0 );
		this.repeat = new Vector2( 1, 1 );
		this.center = new Vector2( 0, 0 );
		this.rotation = 0;
  
		this.matrixAutoUpdate = true;
		this.matrix = new Matrix3();
  
		this.generateMipmaps = true;
		this.premultiplyAlpha = false;
		this.flipY = true;
		this.unpackAlignment = 4;	// valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml)
  
		// Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap.
		//
		// Also changing the encoding after already used by a Material will not automatically make the Material
		// update.  You need to explicitly call Material.needsUpdate to trigger it to recompile.
		this.encoding = encoding !== undefined ? encoding : LinearEncoding;
  
		this.version = 0;
		this.onUpdate = null;
  
	}
  
	Texture.DEFAULT_IMAGE = undefined;
	Texture.DEFAULT_MAPPING = UVMapping;
  
	Texture.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {
  
		constructor: Texture,
  
		isTexture: true,
  
		updateMatrix: function () {
  
			this.matrix.setUvTransform( this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y );
  
		},
  
		clone: function () {
  
			return new this.constructor().copy( this );
  
		},
  
		copy: function ( source ) {
  
			this.name = source.name;
  
			this.image = source.image;
			this.mipmaps = source.mipmaps.slice( 0 );
  
			this.mapping = source.mapping;
  
			this.wrapS = source.wrapS;
			this.wrapT = source.wrapT;
  
			this.magFilter = source.magFilter;
			this.minFilter = source.minFilter;
  
			this.anisotropy = source.anisotropy;
  
			this.format = source.format;
			this.type = source.type;
  
			this.offset.copy( source.offset );
			this.repeat.copy( source.repeat );
			this.center.copy( source.center );
			this.rotation = source.rotation;
  
			this.matrixAutoUpdate = source.matrixAutoUpdate;
			this.matrix.copy( source.matrix );
  
			this.generateMipmaps = source.generateMipmaps;
			this.premultiplyAlpha = source.premultiplyAlpha;
			this.flipY = source.flipY;
			this.unpackAlignment = source.unpackAlignment;
			this.encoding = source.encoding;
  
			return this;
  
		},
  
		toJSON: function ( meta ) {
  
			var isRootObject = ( meta === undefined || typeof meta === 'string' );
  
			if ( ! isRootObject && meta.textures[ this.uuid ] !== undefined ) {
  
				return meta.textures[ this.uuid ];
  
			}
  
			function getDataURL( image ) {
  
				var canvas;
  
				if ( image instanceof HTMLCanvasElement ) {
  
					canvas = image;
  
				} else {
  
					canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' );
					canvas.width = image.width;
					canvas.height = image.height;
  
					var context = canvas.getContext( '2d' );
  
					if ( image instanceof ImageData ) {
  
						context.putImageData( image, 0, 0 );
  
					} else {
  
						context.drawImage( image, 0, 0, image.width, image.height );
  
					}
  
				}
  
				if ( canvas.width > 2048 || canvas.height > 2048 ) {
  
					return canvas.toDataURL( 'image/jpeg', 0.6 );
  
				} else {
  
					return canvas.toDataURL( 'image/png' );
  
				}
  
			}
  
			var output = {
  
				metadata: {
					version: 4.5,
					type: 'Texture',
					generator: 'Texture.toJSON'
				},
  
				uuid: this.uuid,
				name: this.name,
  
				mapping: this.mapping,
  
				repeat: [ this.repeat.x, this.repeat.y ],
				offset: [ this.offset.x, this.offset.y ],
				center: [ this.center.x, this.center.y ],
				rotation: this.rotation,
  
				wrap: [ this.wrapS, this.wrapT ],
  
				format: this.format,
				minFilter: this.minFilter,
				magFilter: this.magFilter,
				anisotropy: this.anisotropy,
  
				flipY: this.flipY
  
			};
  
			if ( this.image !== undefined ) {
  
				// TODO: Move to THREE.Image
  
				var image = this.image;
  
				if ( image.uuid === undefined ) {
  
					image.uuid = _Math.generateUUID(); // UGH
  
				}
  
				if ( ! isRootObject && meta.images[ image.uuid ] === undefined ) {
  
					meta.images[ image.uuid ] = {
						uuid: image.uuid,
						url: getDataURL( image )
					};
  
				}
  
				output.image = image.uuid;
  
			}
  
			if ( ! isRootObject ) {
  
				meta.textures[ this.uuid ] = output;
  
			}
  
			return output;
  
		},
  
		dispose: function () {
  
			this.dispatchEvent( { type: 'dispose' } );
  
		},
  
		transformUv: function ( uv ) {
  
			if ( this.mapping !== UVMapping ) return;
  
			uv.applyMatrix3( this.matrix );
  
			if ( uv.x < 0 || uv.x > 1 ) {
  
				switch ( this.wrapS ) {
  
					case RepeatWrapping:
  
						uv.x = uv.x - Math.floor( uv.x );
						break;
  
					case ClampToEdgeWrapping:
  
						uv.x = uv.x < 0 ? 0 : 1;
						break;
  
					case MirroredRepeatWrapping:
  
						if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) {
  
							uv.x = Math.ceil( uv.x ) - uv.x;
  
						} else {
  
							uv.x = uv.x - Math.floor( uv.x );
  
						}
						break;
  
				}
  
			}
  
			if ( uv.y < 0 || uv.y > 1 ) {
  
				switch ( this.wrapT ) {
  
					case RepeatWrapping:
  
						uv.y = uv.y - Math.floor( uv.y );
						break;
  
					case ClampToEdgeWrapping:
  
						uv.y = uv.y < 0 ? 0 : 1;
						break;
  
					case MirroredRepeatWrapping:
  
						if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) {
  
							uv.y = Math.ceil( uv.y ) - uv.y;
  
						} else {
  
							uv.y = uv.y - Math.floor( uv.y );
  
						}
						break;
  
				}
  
			}
  
			if ( this.flipY ) {
  
				uv.y = 1 - uv.y;
  
			}
  
		}
  
	} );
  
	Object.defineProperty( Texture.prototype, "needsUpdate", {
  
		set: function ( value ) {
  
			if ( value === true ) this.version ++;
  
		}
  
	} );
  
	/**
	 * @author supereggbert / http://www.paulbrunt.co.uk/
	 * @author philogb / http://blog.thejit.org/
	 * @author mikael emtinger / http://gomo.se/
	 * @author egraether / http://egraether.com/
	 * @author WestLangley / http://github.com/WestLangley
	 */
  
	function Vector4( x, y, z, w ) {
  
		this.x = x || 0;
		this.y = y || 0;
		this.z = z || 0;
		this.w = ( w !== undefined ) ? w : 1;
  
	}
  
	Object.assign( Vector4.prototype, {
  
		isVector4: true,
  
		set: function ( x, y, z, w ) {
  
			this.x = x;
			this.y = y;
			this.z = z;
			this.w = w;
  
			return this;
  
		},
  
		setScalar: function ( scalar ) {
  
			this.x = scalar;
			this.y = scalar;
			this.z = scalar;
			this.w = scalar;
  
			return this;
  
		},
  
		setX: function ( x ) {
  
			this.x = x;
  
			return this;
  
		},
  
		setY: function ( y ) {
  
			this.y = y;
  
			return this;
  
		},
  
		setZ: function ( z ) {
  
			this.z = z;
  
			return this;
  
		},
  
		setW: function ( w ) {
  
			this.w = w;
  
			return this;
  
		},
  
		setComponent: function ( index, value ) {
  
			switch ( index ) {
  
				case 0: this.x = value; break;
				case 1: this.y = value; break;
				case 2: this.z = value; break;
				case 3: this.w = value; break;
				default: throw new Error( 'index is out of range: ' + index );
  
			}
  
			return this;
  
		},
  
		getComponent: function ( index ) {
  
			switch ( index ) {
  
				case 0: return this.x;
				case 1: return this.y;
				case 2: return this.z;
				case 3: return this.w;
				default: throw new Error( 'index is out of range: ' + index );
  
			}
  
		},
  
		clone: function () {
  
			return new this.constructor( this.x, this.y, this.z, this.w );
  
		},
  
		copy: function ( v ) {
  
			this.x = v.x;
			this.y = v.y;
			this.z = v.z;
			this.w = ( v.w !== undefined ) ? v.w : 1;
  
			return this;
  
		},
  
		add: function ( v, w ) {
  
			if ( w !== undefined ) {
  
				console.warn( 'THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
				return this.addVectors( v, w );
  
			}
  
			this.x += v.x;
			this.y += v.y;
			this.z += v.z;
			this.w += v.w;
  
			return this;
  
		},
  
		addScalar: function ( s ) {
  
			this.x += s;
			this.y += s;
			this.z += s;
			this.w += s;
  
			return this;
  
		},
  
		addVectors: function ( a, b ) {
  
			this.x = a.x + b.x;
			this.y = a.y + b.y;
			this.z = a.z + b.z;
			this.w = a.w + b.w;
  
			return this;
  
		},
  
		addScaledVector: function ( v, s ) {
  
			this.x += v.x * s;
			this.y += v.y * s;
			this.z += v.z * s;
			this.w += v.w * s;
  
			return this;
  
		},
  
		sub: function ( v, w ) {
  
			if ( w !== undefined ) {
  
				console.warn( 'THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
				return this.subVectors( v, w );
  
			}
  
			this.x -= v.x;
			this.y -= v.y;
			this.z -= v.z;
			this.w -= v.w;
  
			return this;
  
		},
  
		subScalar: function ( s ) {
  
			this.x -= s;
			this.y -= s;
			this.z -= s;
			this.w -= s;
  
			return this;
  
		},
  
		subVectors: function ( a, b ) {
  
			this.x = a.x - b.x;
			this.y = a.y - b.y;
			this.z = a.z - b.z;
			this.w = a.w - b.w;
  
			return this;
  
		},
  
		multiplyScalar: function ( scalar ) {
  
			this.x *= scalar;
			this.y *= scalar;
			this.z *= scalar;
			this.w *= scalar;
  
			return this;
  
		},
  
		applyMatrix4: function ( m ) {
  
			var x = this.x, y = this.y, z = this.z, w = this.w;
			var e = m.elements;
  
			this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w;
			this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w;
			this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w;
			this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w;
  
			return this;
  
		},
  
		divideScalar: function ( scalar ) {
  
			return this.multiplyScalar( 1 / scalar );
  
		},
  
		setAxisAngleFromQuaternion: function ( q ) {
  
			// http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm
  
			// q is assumed to be normalized
  
			this.w = 2 * Math.acos( q.w );
  
			var s = Math.sqrt( 1 - q.w * q.w );
  
			if ( s < 0.0001 ) {
  
				this.x = 1;
				this.y = 0;
				this.z = 0;
  
			} else {
  
				this.x = q.x / s;
				this.y = q.y / s;
				this.z = q.z / s;
  
			}
  
			return this;
  
		},
  
		setAxisAngleFromRotationMatrix: function ( m ) {
  
			// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm
  
			// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
  
			var angle, x, y, z,		// variables for result
				epsilon = 0.01,		// margin to allow for rounding errors
				epsilon2 = 0.1,		// margin to distinguish between 0 and 180 degrees
  
				te = m.elements,
  
				m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],
				m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],
				m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ];
  
			if ( ( Math.abs( m12 - m21 ) < epsilon ) &&
				 ( Math.abs( m13 - m31 ) < epsilon ) &&
				 ( Math.abs( m23 - m32 ) < epsilon ) ) {
  
				// singularity found
				// first check for identity matrix which must have +1 for all terms
				// in leading diagonal and zero in other terms
  
				if ( ( Math.abs( m12 + m21 ) < epsilon2 ) &&
					 ( Math.abs( m13 + m31 ) < epsilon2 ) &&
					 ( Math.abs( m23 + m32 ) < epsilon2 ) &&
					 ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) {
  
					// this singularity is identity matrix so angle = 0
  
					this.set( 1, 0, 0, 0 );
  
					return this; // zero angle, arbitrary axis
  
				}
  
				// otherwise this singularity is angle = 180
  
				angle = Math.PI;
  
				var xx = ( m11 + 1 ) / 2;
				var yy = ( m22 + 1 ) / 2;
				var zz = ( m33 + 1 ) / 2;
				var xy = ( m12 + m21 ) / 4;
				var xz = ( m13 + m31 ) / 4;
				var yz = ( m23 + m32 ) / 4;
  
				if ( ( xx > yy ) && ( xx > zz ) ) {
  
					// m11 is the largest diagonal term
  
					if ( xx < epsilon ) {
  
						x = 0;
						y = 0.707106781;
						z = 0.707106781;
  
					} else {
  
						x = Math.sqrt( xx );
						y = xy / x;
						z = xz / x;
  
					}
  
				} else if ( yy > zz ) {
  
					// m22 is the largest diagonal term
  
					if ( yy < epsilon ) {
  
						x = 0.707106781;
						y = 0;
						z = 0.707106781;
  
					} else {
  
						y = Math.sqrt( yy );
						x = xy / y;
						z = yz / y;
  
					}
  
				} else {
  
					// m33 is the largest diagonal term so base result on this
  
					if ( zz < epsilon ) {
  
						x = 0.707106781;
						y = 0.707106781;
						z = 0;
  
					} else {
  
						z = Math.sqrt( zz );
						x = xz / z;
						y = yz / z;
  
					}
  
				}
  
				this.set( x, y, z, angle );
  
				return this; // return 180 deg rotation
  
			}
  
			// as we have reached here there are no singularities so we can handle normally
  
			var s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) +
							   ( m13 - m31 ) * ( m13 - m31 ) +
							   ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize
  
			if ( Math.abs( s ) < 0.001 ) s = 1;
  
			// prevent divide by zero, should not happen if matrix is orthogonal and should be
			// caught by singularity test above, but I've left it in just in case
  
			this.x = ( m32 - m23 ) / s;
			this.y = ( m13 - m31 ) / s;
			this.z = ( m21 - m12 ) / s;
			this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 );
  
			return this;
  
		},
  
		min: function ( v ) {
  
			this.x = Math.min( this.x, v.x );
			this.y = Math.min( this.y, v.y );
			this.z = Math.min( this.z, v.z );
			this.w = Math.min( this.w, v.w );
  
			return this;
  
		},
  
		max: function ( v ) {
  
			this.x = Math.max( this.x, v.x );
			this.y = Math.max( this.y, v.y );
			this.z = Math.max( this.z, v.z );
			this.w = Math.max( this.w, v.w );
  
			return this;
  
		},
  
		clamp: function ( min, max ) {
  
			// assumes min < max, componentwise
  
			this.x = Math.max( min.x, Math.min( max.x, this.x ) );
			this.y = Math.max( min.y, Math.min( max.y, this.y ) );
			this.z = Math.max( min.z, Math.min( max.z, this.z ) );
			this.w = Math.max( min.w, Math.min( max.w, this.w ) );
  
			return this;
  
		},
  
		clampScalar: function () {
  
			var min, max;
  
			return function clampScalar( minVal, maxVal ) {
  
				if ( min === undefined ) {
  
					min = new Vector4();
					max = new Vector4();
  
				}
  
				min.set( minVal, minVal, minVal, minVal );
				max.set( maxVal, maxVal, maxVal, maxVal );
  
				return this.clamp( min, max );
  
			};
  
		}(),
  
		clampLength: function ( min, max ) {
  
			var length = this.length();
  
			return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) );
  
		},
  
		floor: function () {
  
			this.x = Math.floor( this.x );
			this.y = Math.floor( this.y );
			this.z = Math.floor( this.z );
			this.w = Math.floor( this.w );
  
			return this;
  
		},
  
		ceil: function () {
  
			this.x = Math.ceil( this.x );
			this.y = Math.ceil( this.y );
			this.z = Math.ceil( this.z );
			this.w = Math.ceil( this.w );
  
			return this;
  
		},
  
		round: function () {
  
			this.x = Math.round( this.x );
			this.y = Math.round( this.y );
			this.z = Math.round( this.z );
			this.w = Math.round( this.w );
  
			return this;
  
		},
  
		roundToZero: function () {
  
			this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
			this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
			this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z );
			this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w );
  
			return this;
  
		},
  
		negate: function () {
  
			this.x = - this.x;
			this.y = - this.y;
			this.z = - this.z;
			this.w = - this.w;
  
			return this;
  
		},
  
		dot: function ( v ) {
  
			return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w;
  
		},
  
		lengthSq: function () {
  
			return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w;
  
		},
  
		length: function () {
  
			return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w );
  
		},
  
		manhattanLength: function () {
  
			return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w );
  
		},
  
		normalize: function () {
  
			return this.divideScalar( this.length() || 1 );
  
		},
  
		setLength: function ( length ) {
  
			return this.normalize().multiplyScalar( length );
  
		},
  
		lerp: function ( v, alpha ) {
  
			this.x += ( v.x - this.x ) * alpha;
			this.y += ( v.y - this.y ) * alpha;
			this.z += ( v.z - this.z ) * alpha;
			this.w += ( v.w - this.w ) * alpha;
  
			return this;
  
		},
  
		lerpVectors: function ( v1, v2, alpha ) {
  
			return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );
  
		},
  
		equals: function ( v ) {
  
			return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) );
  
		},
  
		fromArray: function ( array, offset ) {
  
			if ( offset === undefined ) offset = 0;
  
			this.x = array[ offset ];
			this.y = array[ offset + 1 ];
			this.z = array[ offset + 2 ];
			this.w = array[ offset + 3 ];
  
			return this;
  
		},
  
		toArray: function ( array, offset ) {
  
			if ( array === undefined ) array = [];
			if ( offset === undefined ) offset = 0;
  
			array[ offset ] = this.x;
			array[ offset + 1 ] = this.y;
			array[ offset + 2 ] = this.z;
			array[ offset + 3 ] = this.w;
  
			return array;
  
		},
  
		fromBufferAttribute: function ( attribute, index, offset ) {
  
			if ( offset !== undefined ) {
  
				console.warn( 'THREE.Vector4: offset has been removed from .fromBufferAttribute().' );
  
			}
  
			this.x = attribute.getX( index );
			this.y = attribute.getY( index );
			this.z = attribute.getZ( index );
			this.w = attribute.getW( index );
  
			return this;
  
		}
  
	} );
  
	/**
	 * @author szimek / https://github.com/szimek/
	 * @author alteredq / http://alteredqualia.com/
	 * @author Marius Kintel / https://github.com/kintel
	 */
  
	/*
	 In options, we can specify:
	 * Texture parameters for an auto-generated target texture
	 * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers
	*/
	function WebGLRenderTarget( width, height, options ) {
  
		this.width = width;
		this.height = height;
  
		this.scissor = new Vector4( 0, 0, width, height );
		this.scissorTest = false;
  
		this.viewport = new Vector4( 0, 0, width, height );
  
		options = options || {};
  
		if ( options.minFilter === undefined ) options.minFilter = LinearFilter;
  
		this.texture = new Texture( undefined, undefined, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding );
  
		this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : true;
  
		this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true;
		this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true;
		this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null;
  
	}
  
	WebGLRenderTarget.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {
  
		constructor: WebGLRenderTarget,
  
		isWebGLRenderTarget: true,
  
		setSize: function ( width, height ) {
  
			if ( this.width !== width || this.height !== height ) {
  
				this.width = width;
				this.height = height;
  
				this.dispose();
  
			}
  
			this.viewport.set( 0, 0, width, height );
			this.scissor.set( 0, 0, width, height );
  
		},
  
		clone: function () {
  
			return new this.constructor().copy( this );
  
		},
  
		copy: function ( source ) {
  
			this.width = source.width;
			this.height = source.height;
  
			this.viewport.copy( source.viewport );
  
			this.texture = source.texture.clone();
  
			this.depthBuffer = source.depthBuffer;
			this.stencilBuffer = source.stencilBuffer;
			this.depthTexture = source.depthTexture;
  
			return this;
  
		},
  
		dispose: function () {
  
			this.dispatchEvent( { type: 'dispose' } );
  
		}
  
	} );
  
	/**
	 * @author alteredq / http://alteredqualia.com
	 */
  
	function WebGLRenderTargetCube( width, height, options ) {
  
		WebGLRenderTarget.call( this, width, height, options );
  
		this.activeCubeFace = 0; // PX 0, NX 1, PY 2, NY 3, PZ 4, NZ 5
		this.activeMipMapLevel = 0;
  
	}
  
	WebGLRenderTargetCube.prototype = Object.create( WebGLRenderTarget.prototype );
	WebGLRenderTargetCube.prototype.constructor = WebGLRenderTargetCube;
  
	WebGLRenderTargetCube.prototype.isWebGLRenderTargetCube = true;
  
	/**
	 * @author alteredq / http://alteredqualia.com/
	 */
  
	function DataTexture( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) {
  
		Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding );
  
		this.image = { data: data, width: width, height: height };
  
		this.magFilter = magFilter !== undefined ? magFilter : NearestFilter;
		this.minFilter = minFilter !== undefined ? minFilter : NearestFilter;
  
		this.generateMipmaps = false;
		this.flipY = false;
		this.unpackAlignment = 1;
  
	}
  
	DataTexture.prototype = Object.create( Texture.prototype );
	DataTexture.prototype.constructor = DataTexture;
  
	DataTexture.prototype.isDataTexture = true;
  
	/**
	 * @author bhouston / http://clara.io
	 * @author WestLangley / http://github.com/WestLangley
	 */
  
	function Box3( min, max ) {
  
		this.min = ( min !== undefined ) ? min : new Vector3( + Infinity, + Infinity, + Infinity );
		this.max = ( max !== undefined ) ? max : new Vector3( - Infinity, - Infinity, - Infinity );
  
	}
  
	Object.assign( Box3.prototype, {
  
		isBox3: true,
  
		set: function ( min, max ) {
  
			this.min.copy( min );
			this.max.copy( max );
  
			return this;
  
		},
  
		setFromArray: function ( array ) {
  
			var minX = + Infinity;
			var minY = + Infinity;
			var minZ = + Infinity;
  
			var maxX = - Infinity;
			var maxY = - Infinity;
			var maxZ = - Infinity;
  
			for ( var i = 0, l = array.length; i < l; i += 3 ) {
  
				var x = array[ i ];
				var y = array[ i + 1 ];
				var z = array[ i + 2 ];
  
				if ( x < minX ) minX = x;
				if ( y < minY ) minY = y;
				if ( z < minZ ) minZ = z;
  
				if ( x > maxX ) maxX = x;
				if ( y > maxY ) maxY = y;
				if ( z > maxZ ) maxZ = z;
  
			}
  
			this.min.set( minX, minY, minZ );
			this.max.set( maxX, maxY, maxZ );
  
			return this;
  
		},
  
		setFromBufferAttribute: function ( attribute ) {
  
			var minX = + Infinity;
			var minY = + Infinity;
			var minZ = + Infinity;
  
			var maxX = - Infinity;
			var maxY = - Infinity;
			var maxZ = - Infinity;
  
			for ( var i = 0, l = attribute.count; i < l; i ++ ) {
  
				var x = attribute.getX( i );
				var y = attribute.getY( i );
				var z = attribute.getZ( i );
  
				if ( x < minX ) minX = x;
				if ( y < minY ) minY = y;
				if ( z < minZ ) minZ = z;
  
				if ( x > maxX ) maxX = x;
				if ( y > maxY ) maxY = y;
				if ( z > maxZ ) maxZ = z;
  
			}
  
			this.min.set( minX, minY, minZ );
			this.max.set( maxX, maxY, maxZ );
  
			return this;
  
		},
  
		setFromPoints: function ( points ) {
  
			this.makeEmpty();
  
			for ( var i = 0, il = points.length; i < il; i ++ ) {
  
				this.expandByPoint( points[ i ] );
  
			}
  
			return this;
  
		},
  
		setFromCenterAndSize: function () {
  
			var v1 = new Vector3();
  
			return function setFromCenterAndSize( center, size ) {
  
				var halfSize = v1.copy( size ).multiplyScalar( 0.5 );
  
				this.min.copy( center ).sub( halfSize );
				this.max.copy( center ).add( halfSize );
  
				return this;
  
			};
  
		}(),
  
		setFromObject: function ( object ) {
  
			this.makeEmpty();
  
			return this.expandByObject( object );
  
		},
  
		clone: function () {
  
			return new this.constructor().copy( this );
  
		},
  
		copy: function ( box ) {
  
			this.min.copy( box.min );
			this.max.copy( box.max );
  
			return this;
  
		},
  
		makeEmpty: function () {
  
			this.min.x = this.min.y = this.min.z = + Infinity;
			this.max.x = this.max.y = this.max.z = - Infinity;
  
			return this;
  
		},
  
		isEmpty: function () {
  
			// this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes
  
			return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z );
  
		},
  
		getCenter: function ( target ) {
  
			if ( target === undefined ) {
  
				console.warn( 'THREE.Box3: .getCenter() target is now required' );
				target = new Vector3();
  
			}
  
			return this.isEmpty() ? target.set( 0, 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 );
  
		},
  
		getSize: function ( target ) {
  
			if ( target === undefined ) {
  
				console.warn( 'THREE.Box3: .getSize() target is now required' );
				target = new Vector3();
  
			}
  
			return this.isEmpty() ? target.set( 0, 0, 0 ) : target.subVectors( this.max, this.min );
  
		},
  
		expandByPoint: function ( point ) {
  
			this.min.min( point );
			this.max.max( point );
  
			return this;
  
		},
  
		expandByVector: function ( vector ) {
  
			this.min.sub( vector );
			this.max.add( vector );
  
			return this;
  
		},
  
		expandByScalar: function ( scalar ) {
  
			this.min.addScalar( - scalar );
			this.max.addScalar( scalar );
  
			return this;
  
		},
  
		expandByObject: function () {
  
			// Computes the world-axis-aligned bounding box of an object (including its children),
			// accounting for both the object's, and children's, world transforms
  
			var scope, i, l;
  
			var v1 = new Vector3();
  
			function traverse( node ) {
  
				var geometry = node.geometry;
  
				if ( geometry !== undefined ) {
  
					if ( geometry.isGeometry ) {
  
						var vertices = geometry.vertices;
  
						for ( i = 0, l = vertices.length; i < l; i ++ ) {
  
							v1.copy( vertices[ i ] );
							v1.applyMatrix4( node.matrixWorld );
  
							scope.expandByPoint( v1 );
  
						}
  
					} else if ( geometry.isBufferGeometry ) {
  
						var attribute = geometry.attributes.position;
  
						if ( attribute !== undefined ) {
  
							for ( i = 0, l = attribute.count; i < l; i ++ ) {
  
								v1.fromBufferAttribute( attribute, i ).applyMatrix4( node.matrixWorld );
  
								scope.expandByPoint( v1 );
  
							}
  
						}
  
					}
  
				}
  
			}
  
			return function expandByObject( object ) {
  
				scope = this;
  
				object.updateMatrixWorld( true );
  
				object.traverse( traverse );
  
				return this;
  
			};
  
		}(),
  
		containsPoint: function ( point ) {
  
			return point.x < this.min.x || point.x > this.max.x ||
				point.y < this.min.y || point.y > this.max.y ||
				point.z < this.min.z || point.z > this.max.z ? false : true;
  
		},
  
		containsBox: function ( box ) {
  
			return this.min.x <= box.min.x && box.max.x <= this.max.x &&
				this.min.y <= box.min.y && box.max.y <= this.max.y &&
				this.min.z <= box.min.z && box.max.z <= this.max.z;
  
		},
  
		getParameter: function ( point, target ) {
  
			// This can potentially have a divide by zero if the box
			// has a size dimension of 0.
  
			if ( target === undefined ) {
  
				console.warn( 'THREE.Box3: .getParameter() target is now required' );
				target = new Vector3();
  
			}
  
			return target.set(
				( point.x - this.min.x ) / ( this.max.x - this.min.x ),
				( point.y - this.min.y ) / ( this.max.y - this.min.y ),
				( point.z - this.min.z ) / ( this.max.z - this.min.z )
			);
  
		},
  
		intersectsBox: function ( box ) {
  
			// using 6 splitting planes to rule out intersections.
			return box.max.x < this.min.x || box.min.x > this.max.x ||
				box.max.y < this.min.y || box.min.y > this.max.y ||
				box.max.z < this.min.z || box.min.z > this.max.z ? false : true;
  
		},
  
		intersectsSphere: ( function () {
  
			var closestPoint = new Vector3();
  
			return function intersectsSphere( sphere ) {
  
				// Find the point on the AABB closest to the sphere center.
				this.clampPoint( sphere.center, closestPoint );
  
				// If that point is inside the sphere, the AABB and sphere intersect.
				return closestPoint.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius );
  
			};
  
		} )(),
  
		intersectsPlane: function ( plane ) {
  
			// We compute the minimum and maximum dot product values. If those values
			// are on the same side (back or front) of the plane, then there is no intersection.
  
			var min, max;
  
			if ( plane.normal.x > 0 ) {
  
				min = plane.normal.x * this.min.x;
				max = plane.normal.x * this.max.x;
  
			} else {
  
				min = plane.normal.x * this.max.x;
				max = plane.normal.x * this.min.x;
  
			}
  
			if ( plane.normal.y > 0 ) {
  
				min += plane.normal.y * this.min.y;
				max += plane.normal.y * this.max.y;
  
			} else {
  
				min += plane.normal.y * this.max.y;
				max += plane.normal.y * this.min.y;
  
			}
  
			if ( plane.normal.z > 0 ) {
  
				min += plane.normal.z * this.min.z;
				max += plane.normal.z * this.max.z;
  
			} else {
  
				min += plane.normal.z * this.max.z;
				max += plane.normal.z * this.min.z;
  
			}
  
			return ( min <= plane.constant && max >= plane.constant );
  
		},
  
		intersectsTriangle: ( function () {
  
			// triangle centered vertices
			var v0 = new Vector3();
			var v1 = new Vector3();
			var v2 = new Vector3();
  
			// triangle edge vectors
			var f0 = new Vector3();
			var f1 = new Vector3();
			var f2 = new Vector3();
  
			var testAxis = new Vector3();
  
			var center = new Vector3();
			var extents = new Vector3();
  
			var triangleNormal = new Vector3();
  
			function satForAxes( axes ) {
  
				var i, j;
  
				for ( i = 0, j = axes.length - 3; i <= j; i += 3 ) {
  
					testAxis.fromArray( axes, i );
					// project the aabb onto the seperating axis
					var r = extents.x * Math.abs( testAxis.x ) + extents.y * Math.abs( testAxis.y ) + extents.z * Math.abs( testAxis.z );
					// project all 3 vertices of the triangle onto the seperating axis
					var p0 = v0.dot( testAxis );
					var p1 = v1.dot( testAxis );
					var p2 = v2.dot( testAxis );
					// actual test, basically see if either of the most extreme of the triangle points intersects r
					if ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) {
  
						// points of the projected triangle are outside the projected half-length of the aabb
						// the axis is seperating and we can exit
						return false;
  
					}
  
				}
  
				return true;
  
			}
  
			return function intersectsTriangle( triangle ) {
  
				if ( this.isEmpty() ) {
  
					return false;
  
				}
  
				// compute box center and extents
				this.getCenter( center );
				extents.subVectors( this.max, center );
  
				// translate triangle to aabb origin
				v0.subVectors( triangle.a, center );
				v1.subVectors( triangle.b, center );
				v2.subVectors( triangle.c, center );
  
				// compute edge vectors for triangle
				f0.subVectors( v1, v0 );
				f1.subVectors( v2, v1 );
				f2.subVectors( v0, v2 );
  
				// test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb
				// make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation
				// axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned)
				var axes = [
					0, - f0.z, f0.y, 0, - f1.z, f1.y, 0, - f2.z, f2.y,
					f0.z, 0, - f0.x, f1.z, 0, - f1.x, f2.z, 0, - f2.x,
					- f0.y, f0.x, 0, - f1.y, f1.x, 0, - f2.y, f2.x, 0
				];
				if ( ! satForAxes( axes ) ) {
  
					return false;
  
				}
  
				// test 3 face normals from the aabb
				axes = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ];
				if ( ! satForAxes( axes ) ) {
  
					return false;
  
				}
  
				// finally testing the face normal of the triangle
				// use already existing triangle edge vectors here
				triangleNormal.crossVectors( f0, f1 );
				axes = [ triangleNormal.x, triangleNormal.y, triangleNormal.z ];
				return satForAxes( axes );
  
			};
  
		} )(),
  
		clampPoint: function ( point, target ) {
  
			if ( target === undefined ) {
  
				console.warn( 'THREE.Box3: .clampPoint() target is now required' );
				target = new Vector3();
  
			}
  
			return target.copy( point ).clamp( this.min, this.max );
  
		},
  
		distanceToPoint: function () {
  
			var v1 = new Vector3();
  
			return function distanceToPoint( point ) {
  
				var clampedPoint = v1.copy( point ).clamp( this.min, this.max );
				return clampedPoint.sub( point ).length();
  
			};
  
		}(),
  
		getBoundingSphere: function () {
  
			var v1 = new Vector3();
  
			return function getBoundingSphere( target ) {
  
				if ( target === undefined ) {
  
					console.warn( 'THREE.Box3: .getBoundingSphere() target is now required' );
					target = new Sphere();
  
				}
  
				this.getCenter( target.center );
  
				target.radius = this.getSize( v1 ).length() * 0.5;
  
				return target;
  
			};
  
		}(),
  
		intersect: function ( box ) {
  
			this.min.max( box.min );
			this.max.min( box.max );
  
			// ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values.
			if ( this.isEmpty() ) this.makeEmpty();
  
			return this;
  
		},
  
		union: function ( box ) {
  
			this.min.min( box.min );
			this.max.max( box.max );
  
			return this;
  
		},
  
		applyMatrix4: function ( matrix ) {
  
			// transform of empty box is an empty box.
			if ( this.isEmpty( ) ) return this;
  
			var m = matrix.elements;
  
			var xax = m[ 0 ] * this.min.x, xay = m[ 1 ] * this.min.x, xaz = m[ 2 ] * this.min.x;
			var xbx = m[ 0 ] * this.max.x, xby = m[ 1 ] * this.max.x, xbz = m[ 2 ] * this.max.x;
			var yax = m[ 4 ] * this.min.y, yay = m[ 5 ] * this.min.y, yaz = m[ 6 ] * this.min.y;
			var ybx = m[ 4 ] * this.max.y, yby = m[ 5 ] * this.max.y, ybz = m[ 6 ] * this.max.y;
			var zax = m[ 8 ] * this.min.z, zay = m[ 9 ] * this.min.z, zaz = m[ 10 ] * this.min.z;
			var zbx = m[ 8 ] * this.max.z, zby = m[ 9 ] * this.max.z, zbz = m[ 10 ] * this.max.z;
  
			this.min.x = Math.min( xax, xbx ) + Math.min( yax, ybx ) + Math.min( zax, zbx ) + m[ 12 ];
			this.min.y = Math.min( xay, xby ) + Math.min( yay, yby ) + Math.min( zay, zby ) + m[ 13 ];
			this.min.z = Math.min( xaz, xbz ) + Math.min( yaz, ybz ) + Math.min( zaz, zbz ) + m[ 14 ];
			this.max.x = Math.max( xax, xbx ) + Math.max( yax, ybx ) + Math.max( zax, zbx ) + m[ 12 ];
			this.max.y = Math.max( xay, xby ) + Math.max( yay, yby ) + Math.max( zay, zby ) + m[ 13 ];
			this.max.z = Math.max( xaz, xbz ) + Math.max( yaz, ybz ) + Math.max( zaz, zbz ) + m[ 14 ];
  
			return this;
  
		},
  
		translate: function ( offset ) {
  
			this.min.add( offset );
			this.max.add( offset );
  
			return this;
  
		},
  
		equals: function ( box ) {
  
			return box.min.equals( this.min ) && box.max.equals( this.max );
  
		}
  
	} );
  
	/**
	 * @author bhouston / http://clara.io
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function Sphere( center, radius ) {
  
		this.center = ( center !== undefined ) ? center : new Vector3();
		this.radius = ( radius !== undefined ) ? radius : 0;
  
	}
  
	Object.assign( Sphere.prototype, {
  
		set: function ( center, radius ) {
  
			this.center.copy( center );
			this.radius = radius;
  
			return this;
  
		},
  
		setFromPoints: function () {
  
			var box = new Box3();
  
			return function setFromPoints( points, optionalCenter ) {
  
				var center = this.center;
  
				if ( optionalCenter !== undefined ) {
  
					center.copy( optionalCenter );
  
				} else {
  
					box.setFromPoints( points ).getCenter( center );
  
				}
  
				var maxRadiusSq = 0;
  
				for ( var i = 0, il = points.length; i < il; i ++ ) {
  
					maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) );
  
				}
  
				this.radius = Math.sqrt( maxRadiusSq );
  
				return this;
  
			};
  
		}(),
  
		clone: function () {
  
			return new this.constructor().copy( this );
  
		},
  
		copy: function ( sphere ) {
  
			this.center.copy( sphere.center );
			this.radius = sphere.radius;
  
			return this;
  
		},
  
		empty: function () {
  
			return ( this.radius <= 0 );
  
		},
  
		containsPoint: function ( point ) {
  
			return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) );
  
		},
  
		distanceToPoint: function ( point ) {
  
			return ( point.distanceTo( this.center ) - this.radius );
  
		},
  
		intersectsSphere: function ( sphere ) {
  
			var radiusSum = this.radius + sphere.radius;
  
			return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum );
  
		},
  
		intersectsBox: function ( box ) {
  
			return box.intersectsSphere( this );
  
		},
  
		intersectsPlane: function ( plane ) {
  
			return Math.abs( plane.distanceToPoint( this.center ) ) <= this.radius;
  
		},
  
		clampPoint: function ( point, target ) {
  
			var deltaLengthSq = this.center.distanceToSquared( point );
  
			if ( target === undefined ) {
  
				console.warn( 'THREE.Sphere: .clampPoint() target is now required' );
				target = new Vector3();
  
			}
  
			target.copy( point );
  
			if ( deltaLengthSq > ( this.radius * this.radius ) ) {
  
				target.sub( this.center ).normalize();
				target.multiplyScalar( this.radius ).add( this.center );
  
			}
  
			return target;
  
		},
  
		getBoundingBox: function ( target ) {
  
			if ( target === undefined ) {
  
				console.warn( 'THREE.Sphere: .getBoundingBox() target is now required' );
				target = new Box3();
  
			}
  
			target.set( this.center, this.center );
			target.expandByScalar( this.radius );
  
			return target;
  
		},
  
		applyMatrix4: function ( matrix ) {
  
			this.center.applyMatrix4( matrix );
			this.radius = this.radius * matrix.getMaxScaleOnAxis();
  
			return this;
  
		},
  
		translate: function ( offset ) {
  
			this.center.add( offset );
  
			return this;
  
		},
  
		equals: function ( sphere ) {
  
			return sphere.center.equals( this.center ) && ( sphere.radius === this.radius );
  
		}
  
	} );
  
	/**
	 * @author bhouston / http://clara.io
	 */
  
	function Plane( normal, constant ) {
  
		// normal is assumed to be normalized
  
		this.normal = ( normal !== undefined ) ? normal : new Vector3( 1, 0, 0 );
		this.constant = ( constant !== undefined ) ? constant : 0;
  
	}
  
	Object.assign( Plane.prototype, {
  
		set: function ( normal, constant ) {
  
			this.normal.copy( normal );
			this.constant = constant;
  
			return this;
  
		},
  
		setComponents: function ( x, y, z, w ) {
  
			this.normal.set( x, y, z );
			this.constant = w;
  
			return this;
  
		},
  
		setFromNormalAndCoplanarPoint: function ( normal, point ) {
  
			this.normal.copy( normal );
			this.constant = - point.dot( this.normal );
  
			return this;
  
		},
  
		setFromCoplanarPoints: function () {
  
			var v1 = new Vector3();
			var v2 = new Vector3();
  
			return function setFromCoplanarPoints( a, b, c ) {
  
				var normal = v1.subVectors( c, b ).cross( v2.subVectors( a, b ) ).normalize();
  
				// Q: should an error be thrown if normal is zero (e.g. degenerate plane)?
  
				this.setFromNormalAndCoplanarPoint( normal, a );
  
				return this;
  
			};
  
		}(),
  
		clone: function () {
  
			return new this.constructor().copy( this );
  
		},
  
		copy: function ( plane ) {
  
			this.normal.copy( plane.normal );
			this.constant = plane.constant;
  
			return this;
  
		},
  
		normalize: function () {
  
			// Note: will lead to a divide by zero if the plane is invalid.
  
			var inverseNormalLength = 1.0 / this.normal.length();
			this.normal.multiplyScalar( inverseNormalLength );
			this.constant *= inverseNormalLength;
  
			return this;
  
		},
  
		negate: function () {
  
			this.constant *= - 1;
			this.normal.negate();
  
			return this;
  
		},
  
		distanceToPoint: function ( point ) {
  
			return this.normal.dot( point ) + this.constant;
  
		},
  
		distanceToSphere: function ( sphere ) {
  
			return this.distanceToPoint( sphere.center ) - sphere.radius;
  
		},
  
		projectPoint: function ( point, target ) {
  
			if ( target === undefined ) {
  
				console.warn( 'THREE.Plane: .projectPoint() target is now required' );
				target = new Vector3();
  
			}
  
			return target.copy( this.normal ).multiplyScalar( - this.distanceToPoint( point ) ).add( point );
  
		},
  
		intersectLine: function () {
  
			var v1 = new Vector3();
  
			return function intersectLine( line, target ) {
  
				if ( target === undefined ) {
  
					console.warn( 'THREE.Plane: .intersectLine() target is now required' );
					target = new Vector3();
  
				}
  
				var direction = line.delta( v1 );
  
				var denominator = this.normal.dot( direction );
  
				if ( denominator === 0 ) {
  
					// line is coplanar, return origin
					if ( this.distanceToPoint( line.start ) === 0 ) {
  
						return target.copy( line.start );
  
					}
  
					// Unsure if this is the correct method to handle this case.
					return undefined;
  
				}
  
				var t = - ( line.start.dot( this.normal ) + this.constant ) / denominator;
  
				if ( t < 0 || t > 1 ) {
  
					return undefined;
  
				}
  
				return target.copy( direction ).multiplyScalar( t ).add( line.start );
  
			};
  
		}(),
  
		intersectsLine: function ( line ) {
  
			// Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it.
  
			var startSign = this.distanceToPoint( line.start );
			var endSign = this.distanceToPoint( line.end );
  
			return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 );
  
		},
  
		intersectsBox: function ( box ) {
  
			return box.intersectsPlane( this );
  
		},
  
		intersectsSphere: function ( sphere ) {
  
			return sphere.intersectsPlane( this );
  
		},
  
		coplanarPoint: function ( target ) {
  
			if ( target === undefined ) {
  
				console.warn( 'THREE.Plane: .coplanarPoint() target is now required' );
				target = new Vector3();
  
			}
  
			return target.copy( this.normal ).multiplyScalar( - this.constant );
  
		},
  
		applyMatrix4: function () {
  
			var v1 = new Vector3();
			var m1 = new Matrix3();
  
			return function applyMatrix4( matrix, optionalNormalMatrix ) {
  
				var normalMatrix = optionalNormalMatrix || m1.getNormalMatrix( matrix );
  
				var referencePoint = this.coplanarPoint( v1 ).applyMatrix4( matrix );
  
				var normal = this.normal.applyMatrix3( normalMatrix ).normalize();
  
				this.constant = - referencePoint.dot( normal );
  
				return this;
  
			};
  
		}(),
  
		translate: function ( offset ) {
  
			this.constant -= offset.dot( this.normal );
  
			return this;
  
		},
  
		equals: function ( plane ) {
  
			return plane.normal.equals( this.normal ) && ( plane.constant === this.constant );
  
		}
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 * @author alteredq / http://alteredqualia.com/
	 * @author bhouston / http://clara.io
	 */
  
	function Frustum( p0, p1, p2, p3, p4, p5 ) {
  
		this.planes = [
  
			( p0 !== undefined ) ? p0 : new Plane(),
			( p1 !== undefined ) ? p1 : new Plane(),
			( p2 !== undefined ) ? p2 : new Plane(),
			( p3 !== undefined ) ? p3 : new Plane(),
			( p4 !== undefined ) ? p4 : new Plane(),
			( p5 !== undefined ) ? p5 : new Plane()
  
		];
  
	}
  
	Object.assign( Frustum.prototype, {
  
		set: function ( p0, p1, p2, p3, p4, p5 ) {
  
			var planes = this.planes;
  
			planes[ 0 ].copy( p0 );
			planes[ 1 ].copy( p1 );
			planes[ 2 ].copy( p2 );
			planes[ 3 ].copy( p3 );
			planes[ 4 ].copy( p4 );
			planes[ 5 ].copy( p5 );
  
			return this;
  
		},
  
		clone: function () {
  
			return new this.constructor().copy( this );
  
		},
  
		copy: function ( frustum ) {
  
			var planes = this.planes;
  
			for ( var i = 0; i < 6; i ++ ) {
  
				planes[ i ].copy( frustum.planes[ i ] );
  
			}
  
			return this;
  
		},
  
		setFromMatrix: function ( m ) {
  
			var planes = this.planes;
			var me = m.elements;
			var me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ];
			var me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ];
			var me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ];
			var me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ];
  
			planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize();
			planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize();
			planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize();
			planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize();
			planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize();
			planes[ 5 ].setComponents( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ).normalize();
  
			return this;
  
		},
  
		intersectsObject: function () {
  
			var sphere = new Sphere();
  
			return function intersectsObject( object ) {
  
				var geometry = object.geometry;
  
				if ( geometry.boundingSphere === null )
					geometry.computeBoundingSphere();
  
				sphere.copy( geometry.boundingSphere )
					.applyMatrix4( object.matrixWorld );
  
				return this.intersectsSphere( sphere );
  
			};
  
		}(),
  
		intersectsSprite: function () {
  
			var sphere = new Sphere();
  
			return function intersectsSprite( sprite ) {
  
				sphere.center.set( 0, 0, 0 );
				sphere.radius = 0.7071067811865476;
				sphere.applyMatrix4( sprite.matrixWorld );
  
				return this.intersectsSphere( sphere );
  
			};
  
		}(),
  
		intersectsSphere: function ( sphere ) {
  
			var planes = this.planes;
			var center = sphere.center;
			var negRadius = - sphere.radius;
  
			for ( var i = 0; i < 6; i ++ ) {
  
				var distance = planes[ i ].distanceToPoint( center );
  
				if ( distance < negRadius ) {
  
					return false;
  
				}
  
			}
  
			return true;
  
		},
  
		intersectsBox: function () {
  
			var p1 = new Vector3(),
				p2 = new Vector3();
  
			return function intersectsBox( box ) {
  
				var planes = this.planes;
  
				for ( var i = 0; i < 6; i ++ ) {
  
					var plane = planes[ i ];
  
					p1.x = plane.normal.x > 0 ? box.min.x : box.max.x;
					p2.x = plane.normal.x > 0 ? box.max.x : box.min.x;
					p1.y = plane.normal.y > 0 ? box.min.y : box.max.y;
					p2.y = plane.normal.y > 0 ? box.max.y : box.min.y;
					p1.z = plane.normal.z > 0 ? box.min.z : box.max.z;
					p2.z = plane.normal.z > 0 ? box.max.z : box.min.z;
  
					var d1 = plane.distanceToPoint( p1 );
					var d2 = plane.distanceToPoint( p2 );
  
					// if both outside plane, no intersection
  
					if ( d1 < 0 && d2 < 0 ) {
  
						return false;
  
					}
  
				}
  
				return true;
  
			};
  
		}(),
  
		containsPoint: function ( point ) {
  
			var planes = this.planes;
  
			for ( var i = 0; i < 6; i ++ ) {
  
				if ( planes[ i ].distanceToPoint( point ) < 0 ) {
  
					return false;
  
				}
  
			}
  
			return true;
  
		}
  
	} );
  
	var alphamap_fragment = "#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, vUv ).g;\n#endif\n";
  
	var alphamap_pars_fragment = "#ifdef USE_ALPHAMAP\n\tuniform sampler2D alphaMap;\n#endif\n";
  
	var alphatest_fragment = "#ifdef ALPHATEST\n\tif ( diffuseColor.a < ALPHATEST ) discard;\n#endif\n";
  
	var aomap_fragment = "#ifdef USE_AOMAP\n\tfloat ambientOcclusion = ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0;\n\treflectedLight.indirectDiffuse *= ambientOcclusion;\n\t#if defined( USE_ENVMAP ) && defined( PHYSICAL )\n\t\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\t\treflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.specularRoughness );\n\t#endif\n#endif\n";
  
	var aomap_pars_fragment = "#ifdef USE_AOMAP\n\tuniform sampler2D aoMap;\n\tuniform float aoMapIntensity;\n#endif";
  
	var begin_vertex = "\nvec3 transformed = vec3( position );\n";
  
	var beginnormal_vertex = "\nvec3 objectNormal = vec3( normal );\n";
  
	var bsdfs = "float punctualLightIntensityToIrradianceFactor( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {\n\tif( decayExponent > 0.0 ) {\n#if defined ( PHYSICALLY_CORRECT_LIGHTS )\n\t\tfloat distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );\n\t\tfloat maxDistanceCutoffFactor = pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );\n\t\treturn distanceFalloff * maxDistanceCutoffFactor;\n#else\n\t\treturn pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\n#endif\n\t}\n\treturn 1.0;\n}\nvec3 BRDF_Diffuse_Lambert( const in vec3 diffuseColor ) {\n\treturn RECIPROCAL_PI * diffuseColor;\n}\nvec3 F_Schlick( const in vec3 specularColor, const in float dotLH ) {\n\tfloat fresnel = exp2( ( -5.55473 * dotLH - 6.98316 ) * dotLH );\n\treturn ( 1.0 - specularColor ) * fresnel + specularColor;\n}\nfloat G_GGX_Smith( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gl = dotNL + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\tfloat gv = dotNV + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\treturn 1.0 / ( gl * gv );\n}\nfloat G_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\tfloat gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\treturn 0.5 / max( gv + gl, EPSILON );\n}\nfloat D_GGX( const in float alpha, const in float dotNH ) {\n\tfloat a2 = pow2( alpha );\n\tfloat denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0;\n\treturn RECIPROCAL_PI * a2 / pow2( denom );\n}\nvec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) {\n\tfloat alpha = pow2( roughness );\n\tvec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\n\tfloat dotNL = saturate( dot( geometry.normal, incidentLight.direction ) );\n\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\tfloat dotNH = saturate( dot( geometry.normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV );\n\tfloat D = D_GGX( alpha, dotNH );\n\treturn F * ( G * D );\n}\nvec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) {\n\tconst float LUT_SIZE  = 64.0;\n\tconst float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;\n\tconst float LUT_BIAS  = 0.5 / LUT_SIZE;\n\tfloat dotNV = saturate( dot( N, V ) );\n\tvec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) );\n\tuv = uv * LUT_SCALE + LUT_BIAS;\n\treturn uv;\n}\nfloat LTC_ClippedSphereFormFactor( const in vec3 f ) {\n\tfloat l = length( f );\n\treturn max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 );\n}\nvec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) {\n\tfloat x = dot( v1, v2 );\n\tfloat y = abs( x );\n\tfloat a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y;\n\tfloat b = 3.4175940 + ( 4.1616724 + y ) * y;\n\tfloat v = a / b;\n\tfloat theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v;\n\treturn cross( v1, v2 ) * theta_sintheta;\n}\nvec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) {\n\tvec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ];\n\tvec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ];\n\tvec3 lightNormal = cross( v1, v2 );\n\tif( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 );\n\tvec3 T1, T2;\n\tT1 = normalize( V - N * dot( V, N ) );\n\tT2 = - cross( N, T1 );\n\tmat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) );\n\tvec3 coords[ 4 ];\n\tcoords[ 0 ] = mat * ( rectCoords[ 0 ] - P );\n\tcoords[ 1 ] = mat * ( rectCoords[ 1 ] - P );\n\tcoords[ 2 ] = mat * ( rectCoords[ 2 ] - P );\n\tcoords[ 3 ] = mat * ( rectCoords[ 3 ] - P );\n\tcoords[ 0 ] = normalize( coords[ 0 ] );\n\tcoords[ 1 ] = normalize( coords[ 1 ] );\n\tcoords[ 2 ] = normalize( coords[ 2 ] );\n\tcoords[ 3 ] = normalize( coords[ 3 ] );\n\tvec3 vectorFormFactor = vec3( 0.0 );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] );\n\tfloat result = LTC_ClippedSphereFormFactor( vectorFormFactor );\n\treturn vec3( result );\n}\nvec3 BRDF_Specular_GGX_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) {\n\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\tconst vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );\n\tconst vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 );\n\tvec4 r = roughness * c0 + c1;\n\tfloat a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;\n\tvec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw;\n\treturn specularColor * AB.x + AB.y;\n}\nfloat G_BlinnPhong_Implicit( ) {\n\treturn 0.25;\n}\nfloat D_BlinnPhong( const in float shininess, const in float dotNH ) {\n\treturn RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\n}\nvec3 BRDF_Specular_BlinnPhong( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float shininess ) {\n\tvec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\n\tfloat dotNH = saturate( dot( geometry.normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_BlinnPhong_Implicit( );\n\tfloat D = D_BlinnPhong( shininess, dotNH );\n\treturn F * ( G * D );\n}\nfloat GGXRoughnessToBlinnExponent( const in float ggxRoughness ) {\n\treturn ( 2.0 / pow2( ggxRoughness + 0.0001 ) - 2.0 );\n}\nfloat BlinnExponentToGGXRoughness( const in float blinnExponent ) {\n\treturn sqrt( 2.0 / ( blinnExponent + 2.0 ) );\n}\n";
  
	var bumpmap_pars_fragment = "#ifdef USE_BUMPMAP\n\tuniform sampler2D bumpMap;\n\tuniform float bumpScale;\n\tvec2 dHdxy_fwd() {\n\t\tvec2 dSTdx = dFdx( vUv );\n\t\tvec2 dSTdy = dFdy( vUv );\n\t\tfloat Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n\t\tfloat dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n\t\tfloat dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\t\treturn vec2( dBx, dBy );\n\t}\n\tvec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n\t\tvec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) );\n\t\tvec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) );\n\t\tvec3 vN = surf_norm;\n\t\tvec3 R1 = cross( vSigmaY, vN );\n\t\tvec3 R2 = cross( vN, vSigmaX );\n\t\tfloat fDet = dot( vSigmaX, R1 );\n\t\tfDet *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\tvec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n\t\treturn normalize( abs( fDet ) * surf_norm - vGrad );\n\t}\n#endif\n";
  
	var clipping_planes_fragment = "#if NUM_CLIPPING_PLANES > 0\n\tvec4 plane;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) {\n\t\tplane = clippingPlanes[ i ];\n\t\tif ( dot( vViewPosition, plane.xyz ) > plane.w ) discard;\n\t}\n\t#if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES\n\t\tbool clipped = true;\n\t\t#pragma unroll_loop\n\t\tfor ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) {\n\t\t\tplane = clippingPlanes[ i ];\n\t\t\tclipped = ( dot( vViewPosition, plane.xyz ) > plane.w ) && clipped;\n\t\t}\n\t\tif ( clipped ) discard;\n\t#endif\n#endif\n";
  
	var clipping_planes_pars_fragment = "#if NUM_CLIPPING_PLANES > 0\n\t#if ! defined( PHYSICAL ) && ! defined( PHONG )\n\t\tvarying vec3 vViewPosition;\n\t#endif\n\tuniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ];\n#endif\n";
  
	var clipping_planes_pars_vertex = "#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG )\n\tvarying vec3 vViewPosition;\n#endif\n";
  
	var clipping_planes_vertex = "#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n";
  
	var color_fragment = "#ifdef USE_COLOR\n\tdiffuseColor.rgb *= vColor;\n#endif";
  
	var color_pars_fragment = "#ifdef USE_COLOR\n\tvarying vec3 vColor;\n#endif\n";
  
	var color_pars_vertex = "#ifdef USE_COLOR\n\tvarying vec3 vColor;\n#endif";
  
	var color_vertex = "#ifdef USE_COLOR\n\tvColor.xyz = color.xyz;\n#endif";
  
	var common = "#define PI 3.14159265359\n#define PI2 6.28318530718\n#define PI_HALF 1.5707963267949\n#define RECIPROCAL_PI 0.31830988618\n#define RECIPROCAL_PI2 0.15915494\n#define LOG2 1.442695\n#define EPSILON 1e-6\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#define whiteCompliment(a) ( 1.0 - saturate( a ) )\nfloat pow2( const in float x ) { return x*x; }\nfloat pow3( const in float x ) { return x*x*x; }\nfloat pow4( const in float x ) { float x2 = x*x; return x2*x2; }\nfloat average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); }\nhighp float rand( const in vec2 uv ) {\n\tconst highp float a = 12.9898, b = 78.233, c = 43758.5453;\n\thighp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );\n\treturn fract(sin(sn) * c);\n}\nstruct IncidentLight {\n\tvec3 color;\n\tvec3 direction;\n\tbool visible;\n};\nstruct ReflectedLight {\n\tvec3 directDiffuse;\n\tvec3 directSpecular;\n\tvec3 indirectDiffuse;\n\tvec3 indirectSpecular;\n};\nstruct GeometricContext {\n\tvec3 position;\n\tvec3 normal;\n\tvec3 viewDir;\n};\nvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n}\nvec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );\n}\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\tfloat distance = dot( planeNormal, point - pointOnPlane );\n\treturn - distance * planeNormal + point;\n}\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn sign( dot( point - pointOnPlane, planeNormal ) );\n}\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;\n}\nmat3 transposeMat3( const in mat3 m ) {\n\tmat3 tmp;\n\ttmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x );\n\ttmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y );\n\ttmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z );\n\treturn tmp;\n}\nfloat linearToRelativeLuminance( const in vec3 color ) {\n\tvec3 weights = vec3( 0.2126, 0.7152, 0.0722 );\n\treturn dot( weights, color.rgb );\n}\n";
  
	var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV\n#define cubeUV_textureSize (1024.0)\nint getFaceFromDirection(vec3 direction) {\n\tvec3 absDirection = abs(direction);\n\tint face = -1;\n\tif( absDirection.x > absDirection.z ) {\n\t\tif(absDirection.x > absDirection.y )\n\t\t\tface = direction.x > 0.0 ? 0 : 3;\n\t\telse\n\t\t\tface = direction.y > 0.0 ? 1 : 4;\n\t}\n\telse {\n\t\tif(absDirection.z > absDirection.y )\n\t\t\tface = direction.z > 0.0 ? 2 : 5;\n\t\telse\n\t\t\tface = direction.y > 0.0 ? 1 : 4;\n\t}\n\treturn face;\n}\n#define cubeUV_maxLods1  (log2(cubeUV_textureSize*0.25) - 1.0)\n#define cubeUV_rangeClamp (exp2((6.0 - 1.0) * 2.0))\nvec2 MipLevelInfo( vec3 vec, float roughnessLevel, float roughness ) {\n\tfloat scale = exp2(cubeUV_maxLods1 - roughnessLevel);\n\tfloat dxRoughness = dFdx(roughness);\n\tfloat dyRoughness = dFdy(roughness);\n\tvec3 dx = dFdx( vec * scale * dxRoughness );\n\tvec3 dy = dFdy( vec * scale * dyRoughness );\n\tfloat d = max( dot( dx, dx ), dot( dy, dy ) );\n\td = clamp(d, 1.0, cubeUV_rangeClamp);\n\tfloat mipLevel = 0.5 * log2(d);\n\treturn vec2(floor(mipLevel), fract(mipLevel));\n}\n#define cubeUV_maxLods2 (log2(cubeUV_textureSize*0.25) - 2.0)\n#define cubeUV_rcpTextureSize (1.0 / cubeUV_textureSize)\nvec2 getCubeUV(vec3 direction, float roughnessLevel, float mipLevel) {\n\tmipLevel = roughnessLevel > cubeUV_maxLods2 - 3.0 ? 0.0 : mipLevel;\n\tfloat a = 16.0 * cubeUV_rcpTextureSize;\n\tvec2 exp2_packed = exp2( vec2( roughnessLevel, mipLevel ) );\n\tvec2 rcp_exp2_packed = vec2( 1.0 ) / exp2_packed;\n\tfloat powScale = exp2_packed.x * exp2_packed.y;\n\tfloat scale = rcp_exp2_packed.x * rcp_exp2_packed.y * 0.25;\n\tfloat mipOffset = 0.75*(1.0 - rcp_exp2_packed.y) * rcp_exp2_packed.x;\n\tbool bRes = mipLevel == 0.0;\n\tscale =  bRes && (scale < a) ? a : scale;\n\tvec3 r;\n\tvec2 offset;\n\tint face = getFaceFromDirection(direction);\n\tfloat rcpPowScale = 1.0 / powScale;\n\tif( face == 0) {\n\t\tr = vec3(direction.x, -direction.z, direction.y);\n\t\toffset = vec2(0.0+mipOffset,0.75 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\n\t}\n\telse if( face == 1) {\n\t\tr = vec3(direction.y, direction.x, direction.z);\n\t\toffset = vec2(scale+mipOffset, 0.75 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\n\t}\n\telse if( face == 2) {\n\t\tr = vec3(direction.z, direction.x, direction.y);\n\t\toffset = vec2(2.0*scale+mipOffset, 0.75 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\n\t}\n\telse if( face == 3) {\n\t\tr = vec3(direction.x, direction.z, direction.y);\n\t\toffset = vec2(0.0+mipOffset,0.5 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\n\t}\n\telse if( face == 4) {\n\t\tr = vec3(direction.y, direction.x, -direction.z);\n\t\toffset = vec2(scale+mipOffset, 0.5 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\n\t}\n\telse {\n\t\tr = vec3(direction.z, -direction.x, direction.y);\n\t\toffset = vec2(2.0*scale+mipOffset, 0.5 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\n\t}\n\tr = normalize(r);\n\tfloat texelOffset = 0.5 * cubeUV_rcpTextureSize;\n\tvec2 s = ( r.yz / abs( r.x ) + vec2( 1.0 ) ) * 0.5;\n\tvec2 base = offset + vec2( texelOffset );\n\treturn base + s * ( scale - 2.0 * texelOffset );\n}\n#define cubeUV_maxLods3 (log2(cubeUV_textureSize*0.25) - 3.0)\nvec4 textureCubeUV(vec3 reflectedDirection, float roughness ) {\n\tfloat roughnessVal = roughness* cubeUV_maxLods3;\n\tfloat r1 = floor(roughnessVal);\n\tfloat r2 = r1 + 1.0;\n\tfloat t = fract(roughnessVal);\n\tvec2 mipInfo = MipLevelInfo(reflectedDirection, r1, roughness);\n\tfloat s = mipInfo.y;\n\tfloat level0 = mipInfo.x;\n\tfloat level1 = level0 + 1.0;\n\tlevel1 = level1 > 5.0 ? 5.0 : level1;\n\tlevel0 += min( floor( s + 0.5 ), 5.0 );\n\tvec2 uv_10 = getCubeUV(reflectedDirection, r1, level0);\n\tvec4 color10 = envMapTexelToLinear(texture2D(envMap, uv_10));\n\tvec2 uv_20 = getCubeUV(reflectedDirection, r2, level0);\n\tvec4 color20 = envMapTexelToLinear(texture2D(envMap, uv_20));\n\tvec4 result = mix(color10, color20, t);\n\treturn vec4(result.rgb, 1.0);\n}\n#endif\n";
  
	var defaultnormal_vertex = "vec3 transformedNormal = normalMatrix * objectNormal;\n#ifdef FLIP_SIDED\n\ttransformedNormal = - transformedNormal;\n#endif\n";
  
	var displacementmap_pars_vertex = "#ifdef USE_DISPLACEMENTMAP\n\tuniform sampler2D displacementMap;\n\tuniform float displacementScale;\n\tuniform float displacementBias;\n#endif\n";
  
	var displacementmap_vertex = "#ifdef USE_DISPLACEMENTMAP\n\ttransformed += normalize( objectNormal ) * ( texture2D( displacementMap, uv ).x * displacementScale + displacementBias );\n#endif\n";
  
	var emissivemap_fragment = "#ifdef USE_EMISSIVEMAP\n\tvec4 emissiveColor = texture2D( emissiveMap, vUv );\n\temissiveColor.rgb = emissiveMapTexelToLinear( emissiveColor ).rgb;\n\ttotalEmissiveRadiance *= emissiveColor.rgb;\n#endif\n";
  
	var emissivemap_pars_fragment = "#ifdef USE_EMISSIVEMAP\n\tuniform sampler2D emissiveMap;\n#endif\n";
  
	var encodings_fragment = "  gl_FragColor = linearToOutputTexel( gl_FragColor );\n";
  
	var encodings_pars_fragment = "\nvec4 LinearToLinear( in vec4 value ) {\n\treturn value;\n}\nvec4 GammaToLinear( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.xyz, vec3( gammaFactor ) ), value.w );\n}\nvec4 LinearToGamma( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.xyz, vec3( 1.0 / gammaFactor ) ), value.w );\n}\nvec4 sRGBToLinear( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.w );\n}\nvec4 LinearTosRGB( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.w );\n}\nvec4 RGBEToLinear( in vec4 value ) {\n\treturn vec4( value.rgb * exp2( value.a * 255.0 - 128.0 ), 1.0 );\n}\nvec4 LinearToRGBE( in vec4 value ) {\n\tfloat maxComponent = max( max( value.r, value.g ), value.b );\n\tfloat fExp = clamp( ceil( log2( maxComponent ) ), -128.0, 127.0 );\n\treturn vec4( value.rgb / exp2( fExp ), ( fExp + 128.0 ) / 255.0 );\n}\nvec4 RGBMToLinear( in vec4 value, in float maxRange ) {\n\treturn vec4( value.xyz * value.w * maxRange, 1.0 );\n}\nvec4 LinearToRGBM( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.x, max( value.g, value.b ) );\n\tfloat M      = clamp( maxRGB / maxRange, 0.0, 1.0 );\n\tM            = ceil( M * 255.0 ) / 255.0;\n\treturn vec4( value.rgb / ( M * maxRange ), M );\n}\nvec4 RGBDToLinear( in vec4 value, in float maxRange ) {\n\treturn vec4( value.rgb * ( ( maxRange / 255.0 ) / value.a ), 1.0 );\n}\nvec4 LinearToRGBD( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.x, max( value.g, value.b ) );\n\tfloat D      = max( maxRange / maxRGB, 1.0 );\n\tD            = min( floor( D ) / 255.0, 1.0 );\n\treturn vec4( value.rgb * ( D * ( 255.0 / maxRange ) ), D );\n}\nconst mat3 cLogLuvM = mat3( 0.2209, 0.3390, 0.4184, 0.1138, 0.6780, 0.7319, 0.0102, 0.1130, 0.2969 );\nvec4 LinearToLogLuv( in vec4 value )  {\n\tvec3 Xp_Y_XYZp = value.rgb * cLogLuvM;\n\tXp_Y_XYZp = max(Xp_Y_XYZp, vec3(1e-6, 1e-6, 1e-6));\n\tvec4 vResult;\n\tvResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z;\n\tfloat Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0;\n\tvResult.w = fract(Le);\n\tvResult.z = (Le - (floor(vResult.w*255.0))/255.0)/255.0;\n\treturn vResult;\n}\nconst mat3 cLogLuvInverseM = mat3( 6.0014, -2.7008, -1.7996, -1.3320, 3.1029, -5.7721, 0.3008, -1.0882, 5.6268 );\nvec4 LogLuvToLinear( in vec4 value ) {\n\tfloat Le = value.z * 255.0 + value.w;\n\tvec3 Xp_Y_XYZp;\n\tXp_Y_XYZp.y = exp2((Le - 127.0) / 2.0);\n\tXp_Y_XYZp.z = Xp_Y_XYZp.y / value.y;\n\tXp_Y_XYZp.x = value.x * Xp_Y_XYZp.z;\n\tvec3 vRGB = Xp_Y_XYZp.rgb * cLogLuvInverseM;\n\treturn vec4( max(vRGB, 0.0), 1.0 );\n}\n";
  
	var envmap_fragment = "#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\tvec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( cameraToVertex, worldNormal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\n\t\t#endif\n\t#else\n\t\tvec3 reflectVec = vReflect;\n\t#endif\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tvec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\t#elif defined( ENVMAP_TYPE_EQUIREC )\n\t\tvec2 sampleUV;\n\t\treflectVec = normalize( reflectVec );\n\t\tsampleUV.y = asin( clamp( reflectVec.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\t\tsampleUV.x = atan( reflectVec.z, reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n\t\tvec4 envColor = texture2D( envMap, sampleUV );\n\t#elif defined( ENVMAP_TYPE_SPHERE )\n\t\treflectVec = normalize( reflectVec );\n\t\tvec3 reflectView = normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0, 0.0, 1.0 ) );\n\t\tvec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );\n\t#else\n\t\tvec4 envColor = vec4( 0.0 );\n\t#endif\n\tenvColor = envMapTexelToLinear( envColor );\n\t#ifdef ENVMAP_BLENDING_MULTIPLY\n\t\toutgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_MIX )\n\t\toutgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_ADD )\n\t\toutgoingLight += envColor.xyz * specularStrength * reflectivity;\n\t#endif\n#endif\n";
  
	var envmap_pars_fragment = "#if defined( USE_ENVMAP ) || defined( PHYSICAL )\n\tuniform float reflectivity;\n\tuniform float envMapIntensity;\n#endif\n#ifdef USE_ENVMAP\n\t#if ! defined( PHYSICAL ) && ( defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) )\n\t\tvarying vec3 vWorldPosition;\n\t#endif\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tuniform samplerCube envMap;\n\t#else\n\t\tuniform sampler2D envMap;\n\t#endif\n\tuniform float flipEnvMap;\n\tuniform int maxMipLevel;\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( PHYSICAL )\n\t\tuniform float refractionRatio;\n\t#else\n\t\tvarying vec3 vReflect;\n\t#endif\n#endif\n";
  
	var envmap_pars_vertex = "#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\tvarying vec3 vWorldPosition;\n\t#else\n\t\tvarying vec3 vReflect;\n\t\tuniform float refractionRatio;\n\t#endif\n#endif\n";
  
	var envmap_vertex = "#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\tvWorldPosition = worldPosition.xyz;\n\t#else\n\t\tvec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\t\tvec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvReflect = reflect( cameraToVertex, worldNormal );\n\t\t#else\n\t\t\tvReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\t\t#endif\n\t#endif\n#endif\n";
  
	var fog_vertex = "\n#ifdef USE_FOG\nfogDepth = -mvPosition.z;\n#endif";
  
	var fog_pars_vertex = "#ifdef USE_FOG\n  varying float fogDepth;\n#endif\n";
  
	var fog_fragment = "#ifdef USE_FOG\n\t#ifdef FOG_EXP2\n\t\tfloat fogFactor = whiteCompliment( exp2( - fogDensity * fogDensity * fogDepth * fogDepth * LOG2 ) );\n\t#else\n\t\tfloat fogFactor = smoothstep( fogNear, fogFar, fogDepth );\n\t#endif\n\tgl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );\n#endif\n";
  
	var fog_pars_fragment = "#ifdef USE_FOG\n\tuniform vec3 fogColor;\n\tvarying float fogDepth;\n\t#ifdef FOG_EXP2\n\t\tuniform float fogDensity;\n\t#else\n\t\tuniform float fogNear;\n\t\tuniform float fogFar;\n\t#endif\n#endif\n";
  
	var gradientmap_pars_fragment = "#ifdef TOON\n\tuniform sampler2D gradientMap;\n\tvec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) {\n\t\tfloat dotNL = dot( normal, lightDirection );\n\t\tvec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 );\n\t\t#ifdef USE_GRADIENTMAP\n\t\t\treturn texture2D( gradientMap, coord ).rgb;\n\t\t#else\n\t\t\treturn ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 );\n\t\t#endif\n\t}\n#endif\n";
  
	var lightmap_fragment = "#ifdef USE_LIGHTMAP\n\treflectedLight.indirectDiffuse += PI * texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n#endif\n";
  
	var lightmap_pars_fragment = "#ifdef USE_LIGHTMAP\n\tuniform sampler2D lightMap;\n\tuniform float lightMapIntensity;\n#endif";
  
	var lights_lambert_vertex = "vec3 diffuse = vec3( 1.0 );\nGeometricContext geometry;\ngeometry.position = mvPosition.xyz;\ngeometry.normal = normalize( transformedNormal );\ngeometry.viewDir = normalize( -mvPosition.xyz );\nGeometricContext backGeometry;\nbackGeometry.position = geometry.position;\nbackGeometry.normal = -geometry.normal;\nbackGeometry.viewDir = geometry.viewDir;\nvLightFront = vec3( 0.0 );\n#ifdef DOUBLE_SIDED\n\tvLightBack = vec3( 0.0 );\n#endif\nIncidentLight directLight;\nfloat dotNL;\nvec3 directLightColor_Diffuse;\n#if NUM_POINT_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tgetPointDirectLightIrradiance( pointLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tgetSpotDirectLightIrradiance( spotLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n#endif\n#if NUM_DIR_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tgetDirectionalDirectLightIrradiance( directionalLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\tvLightFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry );\n\t\t#endif\n\t}\n#endif\n";
  
	var lights_pars_begin = "uniform vec3 ambientLightColor;\nvec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {\n\tvec3 irradiance = ambientLightColor;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treturn irradiance;\n}\n#if NUM_DIR_LIGHTS > 0\n\tstruct DirectionalLight {\n\t\tvec3 direction;\n\t\tvec3 color;\n\t\tint shadow;\n\t\tfloat shadowBias;\n\t\tfloat shadowRadius;\n\t\tvec2 shadowMapSize;\n\t};\n\tuniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];\n\tvoid getDirectionalDirectLightIrradiance( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tdirectLight.color = directionalLight.color;\n\t\tdirectLight.direction = directionalLight.direction;\n\t\tdirectLight.visible = true;\n\t}\n#endif\n#if NUM_POINT_LIGHTS > 0\n\tstruct PointLight {\n\t\tvec3 position;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tint shadow;\n\t\tfloat shadowBias;\n\t\tfloat shadowRadius;\n\t\tvec2 shadowMapSize;\n\t\tfloat shadowCameraNear;\n\t\tfloat shadowCameraFar;\n\t};\n\tuniform PointLight pointLights[ NUM_POINT_LIGHTS ];\n\tvoid getPointDirectLightIrradiance( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = pointLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tdirectLight.color = pointLight.color;\n\t\tdirectLight.color *= punctualLightIntensityToIrradianceFactor( lightDistance, pointLight.distance, pointLight.decay );\n\t\tdirectLight.visible = ( directLight.color != vec3( 0.0 ) );\n\t}\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\tstruct SpotLight {\n\t\tvec3 position;\n\t\tvec3 direction;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tfloat coneCos;\n\t\tfloat penumbraCos;\n\t\tint shadow;\n\t\tfloat shadowBias;\n\t\tfloat shadowRadius;\n\t\tvec2 shadowMapSize;\n\t};\n\tuniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];\n\tvoid getSpotDirectLightIrradiance( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight directLight  ) {\n\t\tvec3 lVector = spotLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tfloat angleCos = dot( directLight.direction, spotLight.direction );\n\t\tif ( angleCos > spotLight.coneCos ) {\n\t\t\tfloat spotEffect = smoothstep( spotLight.coneCos, spotLight.penumbraCos, angleCos );\n\t\t\tdirectLight.color = spotLight.color;\n\t\t\tdirectLight.color *= spotEffect * punctualLightIntensityToIrradianceFactor( lightDistance, spotLight.distance, spotLight.decay );\n\t\t\tdirectLight.visible = true;\n\t\t} else {\n\t\t\tdirectLight.color = vec3( 0.0 );\n\t\t\tdirectLight.visible = false;\n\t\t}\n\t}\n#endif\n#if NUM_RECT_AREA_LIGHTS > 0\n\tstruct RectAreaLight {\n\t\tvec3 color;\n\t\tvec3 position;\n\t\tvec3 halfWidth;\n\t\tvec3 halfHeight;\n\t};\n\tuniform sampler2D ltc_1;\tuniform sampler2D ltc_2;\n\tuniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ];\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\tstruct HemisphereLight {\n\t\tvec3 direction;\n\t\tvec3 skyColor;\n\t\tvec3 groundColor;\n\t};\n\tuniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ];\n\tvec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in GeometricContext geometry ) {\n\t\tfloat dotNL = dot( geometry.normal, hemiLight.direction );\n\t\tfloat hemiDiffuseWeight = 0.5 * dotNL + 0.5;\n\t\tvec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight );\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tirradiance *= PI;\n\t\t#endif\n\t\treturn irradiance;\n\t}\n#endif\n";
  
	var lights_pars_maps = "#if defined( USE_ENVMAP ) && defined( PHYSICAL )\n\tvec3 getLightProbeIndirectIrradiance( const in GeometricContext geometry, const in int maxMIPLevel ) {\n\t\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\n\t\t\tvec4 envMapColor = textureCubeUV( queryVec, 1.0 );\n\t\t#else\n\t\t\tvec4 envMapColor = vec4( 0.0 );\n\t\t#endif\n\t\treturn PI * envMapColor.rgb * envMapIntensity;\n\t}\n\tfloat getSpecularMIPLevel( const in float blinnShininessExponent, const in int maxMIPLevel ) {\n\t\tfloat maxMIPLevelScalar = float( maxMIPLevel );\n\t\tfloat desiredMIPLevel = maxMIPLevelScalar + 0.79248 - 0.5 * log2( pow2( blinnShininessExponent ) + 1.0 );\n\t\treturn clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );\n\t}\n\tvec3 getLightProbeIndirectRadiance( const in GeometricContext geometry, const in float blinnShininessExponent, const in int maxMIPLevel ) {\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( -geometry.viewDir, geometry.normal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( -geometry.viewDir, geometry.normal, refractionRatio );\n\t\t#endif\n\t\treflectVec = inverseTransformDirection( reflectVec, viewMatrix );\n\t\tfloat specularMIPLevel = getSpecularMIPLevel( blinnShininessExponent, maxMIPLevel );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\n\t\t\tvec4 envMapColor = textureCubeUV(queryReflectVec, BlinnExponentToGGXRoughness(blinnShininessExponent));\n\t\t#elif defined( ENVMAP_TYPE_EQUIREC )\n\t\t\tvec2 sampleUV;\n\t\t\tsampleUV.y = asin( clamp( reflectVec.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\t\t\tsampleUV.x = atan( reflectVec.z, reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = texture2DLodEXT( envMap, sampleUV, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = texture2D( envMap, sampleUV, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_SPHERE )\n\t\t\tvec3 reflectView = normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0,0.0,1.0 ) );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = texture2DLodEXT( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#endif\n\t\treturn envMapColor.rgb * envMapIntensity;\n\t}\n#endif\n";
  
	var lights_phong_fragment = "BlinnPhongMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;\nmaterial.specularColor = specular;\nmaterial.specularShininess = shininess;\nmaterial.specularStrength = specularStrength;\n";
  
	var lights_phong_pars_fragment = "varying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\nstruct BlinnPhongMaterial {\n\tvec3\tdiffuseColor;\n\tvec3\tspecularColor;\n\tfloat\tspecularShininess;\n\tfloat\tspecularStrength;\n};\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\t#ifdef TOON\n\t\tvec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color;\n\t#else\n\t\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\t\tvec3 irradiance = dotNL * directLight.color;\n\t#endif\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n\treflectedLight.directSpecular += irradiance * BRDF_Specular_BlinnPhong( directLight, geometry, material.specularColor, material.specularShininess ) * material.specularStrength;\n}\nvoid RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_BlinnPhong\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_BlinnPhong\n#define Material_LightProbeLOD( material )\t(0)\n";
  
	var lights_physical_fragment = "PhysicalMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\nmaterial.specularRoughness = clamp( roughnessFactor, 0.04, 1.0 );\n#ifdef STANDARD\n\tmaterial.specularColor = mix( vec3( DEFAULT_SPECULAR_COEFFICIENT ), diffuseColor.rgb, metalnessFactor );\n#else\n\tmaterial.specularColor = mix( vec3( MAXIMUM_SPECULAR_COEFFICIENT * pow2( reflectivity ) ), diffuseColor.rgb, metalnessFactor );\n\tmaterial.clearCoat = saturate( clearCoat );\tmaterial.clearCoatRoughness = clamp( clearCoatRoughness, 0.04, 1.0 );\n#endif\n";
  
	var lights_physical_pars_fragment = "struct PhysicalMaterial {\n\tvec3\tdiffuseColor;\n\tfloat\tspecularRoughness;\n\tvec3\tspecularColor;\n\t#ifndef STANDARD\n\t\tfloat clearCoat;\n\t\tfloat clearCoatRoughness;\n\t#endif\n};\n#define MAXIMUM_SPECULAR_COEFFICIENT 0.16\n#define DEFAULT_SPECULAR_COEFFICIENT 0.04\nfloat clearCoatDHRApprox( const in float roughness, const in float dotNL ) {\n\treturn DEFAULT_SPECULAR_COEFFICIENT + ( 1.0 - DEFAULT_SPECULAR_COEFFICIENT ) * ( pow( 1.0 - dotNL, 5.0 ) * pow( 1.0 - roughness, 2.0 ) );\n}\n#if NUM_RECT_AREA_LIGHTS > 0\n\tvoid RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\t\tvec3 normal = geometry.normal;\n\t\tvec3 viewDir = geometry.viewDir;\n\t\tvec3 position = geometry.position;\n\t\tvec3 lightPos = rectAreaLight.position;\n\t\tvec3 halfWidth = rectAreaLight.halfWidth;\n\t\tvec3 halfHeight = rectAreaLight.halfHeight;\n\t\tvec3 lightColor = rectAreaLight.color;\n\t\tfloat roughness = material.specularRoughness;\n\t\tvec3 rectCoords[ 4 ];\n\t\trectCoords[ 0 ] = lightPos - halfWidth - halfHeight;\t\trectCoords[ 1 ] = lightPos + halfWidth - halfHeight;\n\t\trectCoords[ 2 ] = lightPos + halfWidth + halfHeight;\n\t\trectCoords[ 3 ] = lightPos - halfWidth + halfHeight;\n\t\tvec2 uv = LTC_Uv( normal, viewDir, roughness );\n\t\tvec4 t1 = texture2D( ltc_1, uv );\n\t\tvec4 t2 = texture2D( ltc_2, uv );\n\t\tmat3 mInv = mat3(\n\t\t\tvec3( t1.x, 0, t1.y ),\n\t\t\tvec3(    0, 1,    0 ),\n\t\t\tvec3( t1.z, 0, t1.w )\n\t\t);\n\t\tvec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y );\n\t\treflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords );\n\t\treflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords );\n\t}\n#endif\nvoid RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\t#ifndef STANDARD\n\t\tfloat clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL );\n\t#else\n\t\tfloat clearCoatDHR = 0.0;\n\t#endif\n\treflectedLight.directSpecular += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Specular_GGX( directLight, geometry, material.specularColor, material.specularRoughness );\n\treflectedLight.directDiffuse += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n\t#ifndef STANDARD\n\t\treflectedLight.directSpecular += irradiance * material.clearCoat * BRDF_Specular_GGX( directLight, geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );\n\t#endif\n}\nvoid RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 clearCoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\t#ifndef STANDARD\n\t\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\t\tfloat dotNL = dotNV;\n\t\tfloat clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL );\n\t#else\n\t\tfloat clearCoatDHR = 0.0;\n\t#endif\n\treflectedLight.indirectSpecular += ( 1.0 - clearCoatDHR ) * radiance * BRDF_Specular_GGX_Environment( geometry, material.specularColor, material.specularRoughness );\n\t#ifndef STANDARD\n\t\treflectedLight.indirectSpecular += clearCoatRadiance * material.clearCoat * BRDF_Specular_GGX_Environment( geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );\n\t#endif\n}\n#define RE_Direct\t\t\t\tRE_Direct_Physical\n#define RE_Direct_RectArea\t\tRE_Direct_RectArea_Physical\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Physical\n#define RE_IndirectSpecular\t\tRE_IndirectSpecular_Physical\n#define Material_BlinnShininessExponent( material )   GGXRoughnessToBlinnExponent( material.specularRoughness )\n#define Material_ClearCoat_BlinnShininessExponent( material )   GGXRoughnessToBlinnExponent( material.clearCoatRoughness )\nfloat computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) {\n\treturn saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion );\n}\n";
  
	var lights_fragment_begin = "\nGeometricContext geometry;\ngeometry.position = - vViewPosition;\ngeometry.normal = normal;\ngeometry.viewDir = normalize( vViewPosition );\nIncidentLight directLight;\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\n\tPointLight pointLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tgetPointDirectLightIrradiance( pointLight, geometry, directLight );\n\t\t#ifdef USE_SHADOWMAP\n\t\tdirectLight.color *= all( bvec2( pointLight.shadow, directLight.visible ) ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\n\tSpotLight spotLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tgetSpotDirectLightIrradiance( spotLight, geometry, directLight );\n\t\t#ifdef USE_SHADOWMAP\n\t\tdirectLight.color *= all( bvec2( spotLight.shadow, directLight.visible ) ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\n\tDirectionalLight directionalLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tgetDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );\n\t\t#ifdef USE_SHADOWMAP\n\t\tdirectLight.color *= all( bvec2( directionalLight.shadow, directLight.visible ) ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )\n\tRectAreaLight rectAreaLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {\n\t\trectAreaLight = rectAreaLights[ i ];\n\t\tRE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if defined( RE_IndirectDiffuse )\n\tvec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\n\t#if ( NUM_HEMI_LIGHTS > 0 )\n\t\t#pragma unroll_loop\n\t\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\t\tirradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t}\n\t#endif\n#endif\n#if defined( RE_IndirectSpecular )\n\tvec3 radiance = vec3( 0.0 );\n\tvec3 clearCoatRadiance = vec3( 0.0 );\n#endif\n";
  
	var lights_fragment_maps = "#if defined( RE_IndirectDiffuse )\n\t#ifdef USE_LIGHTMAP\n\t\tvec3 lightMapIrradiance = texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tlightMapIrradiance *= PI;\n\t\t#endif\n\t\tirradiance += lightMapIrradiance;\n\t#endif\n\t#if defined( USE_ENVMAP ) && defined( PHYSICAL ) && defined( ENVMAP_TYPE_CUBE_UV )\n\t\tirradiance += getLightProbeIndirectIrradiance( geometry, maxMipLevel );\n\t#endif\n#endif\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\n\tradiance += getLightProbeIndirectRadiance( geometry, Material_BlinnShininessExponent( material ), maxMipLevel );\n\t#ifndef STANDARD\n\t\tclearCoatRadiance += getLightProbeIndirectRadiance( geometry, Material_ClearCoat_BlinnShininessExponent( material ), maxMipLevel );\n\t#endif\n#endif\n";
  
	var lights_fragment_end = "#if defined( RE_IndirectDiffuse )\n\tRE_IndirectDiffuse( irradiance, geometry, material, reflectedLight );\n#endif\n#if defined( RE_IndirectSpecular )\n\tRE_IndirectSpecular( radiance, clearCoatRadiance, geometry, material, reflectedLight );\n#endif\n";
  
	var logdepthbuf_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\n\tgl_FragDepthEXT = log2( vFragDepth ) * logDepthBufFC * 0.5;\n#endif";
  
	var logdepthbuf_pars_fragment = "#ifdef USE_LOGDEPTHBUF\n\tuniform float logDepthBufFC;\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvarying float vFragDepth;\n\t#endif\n#endif\n";
  
	var logdepthbuf_pars_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvarying float vFragDepth;\n\t#endif\n\tuniform float logDepthBufFC;\n#endif";
  
	var logdepthbuf_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvFragDepth = 1.0 + gl_Position.w;\n\t#else\n\t\tgl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0;\n\t\tgl_Position.z *= gl_Position.w;\n\t#endif\n#endif\n";
  
	var map_fragment = "#ifdef USE_MAP\n\tvec4 texelColor = texture2D( map, vUv );\n\ttexelColor = mapTexelToLinear( texelColor );\n\tdiffuseColor *= texelColor;\n#endif\n";
  
	var map_pars_fragment = "#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif\n";
  
	var map_particle_fragment = "#ifdef USE_MAP\n\tvec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy;\n\tvec4 mapTexel = texture2D( map, uv );\n\tdiffuseColor *= mapTexelToLinear( mapTexel );\n#endif\n";
  
	var map_particle_pars_fragment = "#ifdef USE_MAP\n\tuniform mat3 uvTransform;\n\tuniform sampler2D map;\n#endif\n";
  
	var metalnessmap_fragment = "float metalnessFactor = metalness;\n#ifdef USE_METALNESSMAP\n\tvec4 texelMetalness = texture2D( metalnessMap, vUv );\n\tmetalnessFactor *= texelMetalness.b;\n#endif\n";
  
	var metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP\n\tuniform sampler2D metalnessMap;\n#endif";
  
	var morphnormal_vertex = "#ifdef USE_MORPHNORMALS\n\tobjectNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\n\tobjectNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\n\tobjectNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\n\tobjectNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\n#endif\n";
  
	var morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS\n\t#ifndef USE_MORPHNORMALS\n\tuniform float morphTargetInfluences[ 8 ];\n\t#else\n\tuniform float morphTargetInfluences[ 4 ];\n\t#endif\n#endif";
  
	var morphtarget_vertex = "#ifdef USE_MORPHTARGETS\n\ttransformed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\n\ttransformed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\n\ttransformed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\n\ttransformed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n\t#ifndef USE_MORPHNORMALS\n\ttransformed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\n\ttransformed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\n\ttransformed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\n\ttransformed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n\t#endif\n#endif\n";
  
	var normal_fragment_begin = "#ifdef FLAT_SHADED\n\tvec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) );\n\tvec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) );\n\tvec3 normal = normalize( cross( fdx, fdy ) );\n#else\n\tvec3 normal = normalize( vNormal );\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t#endif\n#endif\n";
  
	var normal_fragment_maps = "#ifdef USE_NORMALMAP\n\tnormal = perturbNormal2Arb( -vViewPosition, normal );\n#elif defined( USE_BUMPMAP )\n\tnormal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n#endif\n";
  
	var normalmap_pars_fragment = "#ifdef USE_NORMALMAP\n\tuniform sampler2D normalMap;\n\tuniform vec2 normalScale;\n\tvec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\n\t\tvec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );\n\t\tvec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );\n\t\tvec2 st0 = dFdx( vUv.st );\n\t\tvec2 st1 = dFdy( vUv.st );\n\t\tfloat scale = sign( st1.t * st0.s - st0.t * st1.s );\n\t\tvec3 S = normalize( ( q0 * st1.t - q1 * st0.t ) * scale );\n\t\tvec3 T = normalize( ( - q0 * st1.s + q1 * st0.s ) * scale );\n\t\tvec3 N = normalize( surf_norm );\n\t\tmat3 tsn = mat3( S, T, N );\n\t\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t\tmapN.xy *= normalScale;\n\t\tmapN.xy *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\treturn normalize( tsn * mapN );\n\t}\n#endif\n";
  
	var packing = "vec3 packNormalToRGB( const in vec3 normal ) {\n\treturn normalize( normal ) * 0.5 + 0.5;\n}\nvec3 unpackRGBToNormal( const in vec3 rgb ) {\n\treturn 2.0 * rgb.xyz - 1.0;\n}\nconst float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.;\nconst vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256.,  256. );\nconst vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. );\nconst float ShiftRight8 = 1. / 256.;\nvec4 packDepthToRGBA( const in float v ) {\n\tvec4 r = vec4( fract( v * PackFactors ), v );\n\tr.yzw -= r.xyz * ShiftRight8;\treturn r * PackUpscale;\n}\nfloat unpackRGBAToDepth( const in vec4 v ) {\n\treturn dot( v, UnpackFactors );\n}\nfloat viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn ( viewZ + near ) / ( near - far );\n}\nfloat orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) {\n\treturn linearClipZ * ( near - far ) - near;\n}\nfloat viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn (( near + viewZ ) * far ) / (( far - near ) * viewZ );\n}\nfloat perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) {\n\treturn ( near * far ) / ( ( far - near ) * invClipZ - far );\n}\n";
  
	var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA\n\tgl_FragColor.rgb *= gl_FragColor.a;\n#endif\n";
  
	var project_vertex = "vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );\ngl_Position = projectionMatrix * mvPosition;\n";
  
	var dithering_fragment = "#if defined( DITHERING )\n  gl_FragColor.rgb = dithering( gl_FragColor.rgb );\n#endif\n";
  
	var dithering_pars_fragment = "#if defined( DITHERING )\n\tvec3 dithering( vec3 color ) {\n\t\tfloat grid_position = rand( gl_FragCoord.xy );\n\t\tvec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 );\n\t\tdither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position );\n\t\treturn color + dither_shift_RGB;\n\t}\n#endif\n";
  
	var roughnessmap_fragment = "float roughnessFactor = roughness;\n#ifdef USE_ROUGHNESSMAP\n\tvec4 texelRoughness = texture2D( roughnessMap, vUv );\n\troughnessFactor *= texelRoughness.g;\n#endif\n";
  
	var roughnessmap_pars_fragment = "#ifdef USE_ROUGHNESSMAP\n\tuniform sampler2D roughnessMap;\n#endif";
  
	var shadowmap_pars_fragment = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\t\tuniform sampler2D directionalShadowMap[ NUM_DIR_LIGHTS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ];\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\t\tuniform sampler2D spotShadowMap[ NUM_SPOT_LIGHTS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ];\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\t\tuniform sampler2D pointShadowMap[ NUM_POINT_LIGHTS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ];\n\t#endif\n\tfloat texture2DCompare( sampler2D depths, vec2 uv, float compare ) {\n\t\treturn step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) );\n\t}\n\tfloat texture2DShadowLerp( sampler2D depths, vec2 size, vec2 uv, float compare ) {\n\t\tconst vec2 offset = vec2( 0.0, 1.0 );\n\t\tvec2 texelSize = vec2( 1.0 ) / size;\n\t\tvec2 centroidUV = floor( uv * size + 0.5 ) / size;\n\t\tfloat lb = texture2DCompare( depths, centroidUV + texelSize * offset.xx, compare );\n\t\tfloat lt = texture2DCompare( depths, centroidUV + texelSize * offset.xy, compare );\n\t\tfloat rb = texture2DCompare( depths, centroidUV + texelSize * offset.yx, compare );\n\t\tfloat rt = texture2DCompare( depths, centroidUV + texelSize * offset.yy, compare );\n\t\tvec2 f = fract( uv * size + 0.5 );\n\t\tfloat a = mix( lb, lt, f.y );\n\t\tfloat b = mix( rb, rt, f.y );\n\t\tfloat c = mix( a, b, f.x );\n\t\treturn c;\n\t}\n\tfloat getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n\t\tfloat shadow = 1.0;\n\t\tshadowCoord.xyz /= shadowCoord.w;\n\t\tshadowCoord.z += shadowBias;\n\t\tbvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n\t\tbool inFrustum = all( inFrustumVec );\n\t\tbvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\t\tbool frustumTest = all( frustumTestVec );\n\t\tif ( frustumTest ) {\n\t\t#if defined( SHADOWMAP_TYPE_PCF )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx0 = - texelSize.x * shadowRadius;\n\t\t\tfloat dy0 = - texelSize.y * shadowRadius;\n\t\t\tfloat dx1 = + texelSize.x * shadowRadius;\n\t\t\tfloat dy1 = + texelSize.y * shadowRadius;\n\t\t\tshadow = (\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx0 = - texelSize.x * shadowRadius;\n\t\t\tfloat dy0 = - texelSize.y * shadowRadius;\n\t\t\tfloat dx1 = + texelSize.x * shadowRadius;\n\t\t\tfloat dy1 = + texelSize.y * shadowRadius;\n\t\t\tshadow = (\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy, shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#else\n\t\t\tshadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#endif\n\t\t}\n\t\treturn shadow;\n\t}\n\tvec2 cubeToUV( vec3 v, float texelSizeY ) {\n\t\tvec3 absV = abs( v );\n\t\tfloat scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\n\t\tabsV *= scaleToCube;\n\t\tv *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\n\t\tvec2 planar = v.xy;\n\t\tfloat almostATexel = 1.5 * texelSizeY;\n\t\tfloat almostOne = 1.0 - almostATexel;\n\t\tif ( absV.z >= almostOne ) {\n\t\t\tif ( v.z > 0.0 )\n\t\t\t\tplanar.x = 4.0 - v.x;\n\t\t} else if ( absV.x >= almostOne ) {\n\t\t\tfloat signX = sign( v.x );\n\t\t\tplanar.x = v.z * signX + 2.0 * signX;\n\t\t} else if ( absV.y >= almostOne ) {\n\t\t\tfloat signY = sign( v.y );\n\t\t\tplanar.x = v.x + 2.0 * signY + 2.0;\n\t\t\tplanar.y = v.z * signY - 2.0;\n\t\t}\n\t\treturn vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\n\t}\n\tfloat getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) {\n\t\tvec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) );\n\t\tvec3 lightToPosition = shadowCoord.xyz;\n\t\tfloat dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear );\t\tdp += shadowBias;\n\t\tvec3 bd3D = normalize( lightToPosition );\n\t\t#if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT )\n\t\t\tvec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y;\n\t\t\treturn (\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#else\n\t\t\treturn texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp );\n\t\t#endif\n\t}\n#endif\n";
  
	var shadowmap_pars_vertex = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\t\tuniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHTS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ];\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\t\tuniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHTS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ];\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\t\tuniform mat4 pointShadowMatrix[ NUM_POINT_LIGHTS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ];\n\t#endif\n#endif\n";
  
	var shadowmap_vertex = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tvDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * worldPosition;\n\t}\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tvSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * worldPosition;\n\t}\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tvPointShadowCoord[ i ] = pointShadowMatrix[ i ] * worldPosition;\n\t}\n\t#endif\n#endif\n";
  
	var shadowmask_pars_fragment = "float getShadowMask() {\n\tfloat shadow = 1.0;\n\t#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\tDirectionalLight directionalLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tshadow *= bool( directionalLight.shadow ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t}\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\tSpotLight spotLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tshadow *= bool( spotLight.shadow ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t}\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\tPointLight pointLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tshadow *= bool( pointLight.shadow ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;\n\t}\n\t#endif\n\t#endif\n\treturn shadow;\n}\n";
  
	var skinbase_vertex = "#ifdef USE_SKINNING\n\tmat4 boneMatX = getBoneMatrix( skinIndex.x );\n\tmat4 boneMatY = getBoneMatrix( skinIndex.y );\n\tmat4 boneMatZ = getBoneMatrix( skinIndex.z );\n\tmat4 boneMatW = getBoneMatrix( skinIndex.w );\n#endif";
  
	var skinning_pars_vertex = "#ifdef USE_SKINNING\n\tuniform mat4 bindMatrix;\n\tuniform mat4 bindMatrixInverse;\n\t#ifdef BONE_TEXTURE\n\t\tuniform sampler2D boneTexture;\n\t\tuniform int boneTextureSize;\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tfloat j = i * 4.0;\n\t\t\tfloat x = mod( j, float( boneTextureSize ) );\n\t\t\tfloat y = floor( j / float( boneTextureSize ) );\n\t\t\tfloat dx = 1.0 / float( boneTextureSize );\n\t\t\tfloat dy = 1.0 / float( boneTextureSize );\n\t\t\ty = dy * ( y + 0.5 );\n\t\t\tvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n\t\t\tvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n\t\t\tvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n\t\t\tvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\t\t\tmat4 bone = mat4( v1, v2, v3, v4 );\n\t\t\treturn bone;\n\t\t}\n\t#else\n\t\tuniform mat4 boneMatrices[ MAX_BONES ];\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tmat4 bone = boneMatrices[ int(i) ];\n\t\t\treturn bone;\n\t\t}\n\t#endif\n#endif\n";
  
	var skinning_vertex = "#ifdef USE_SKINNING\n\tvec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\n\tvec4 skinned = vec4( 0.0 );\n\tskinned += boneMatX * skinVertex * skinWeight.x;\n\tskinned += boneMatY * skinVertex * skinWeight.y;\n\tskinned += boneMatZ * skinVertex * skinWeight.z;\n\tskinned += boneMatW * skinVertex * skinWeight.w;\n\ttransformed = ( bindMatrixInverse * skinned ).xyz;\n#endif\n";
  
	var skinnormal_vertex = "#ifdef USE_SKINNING\n\tmat4 skinMatrix = mat4( 0.0 );\n\tskinMatrix += skinWeight.x * boneMatX;\n\tskinMatrix += skinWeight.y * boneMatY;\n\tskinMatrix += skinWeight.z * boneMatZ;\n\tskinMatrix += skinWeight.w * boneMatW;\n\tskinMatrix  = bindMatrixInverse * skinMatrix * bindMatrix;\n\tobjectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\n#endif\n";
  
	var specularmap_fragment = "float specularStrength;\n#ifdef USE_SPECULARMAP\n\tvec4 texelSpecular = texture2D( specularMap, vUv );\n\tspecularStrength = texelSpecular.r;\n#else\n\tspecularStrength = 1.0;\n#endif";
  
	var specularmap_pars_fragment = "#ifdef USE_SPECULARMAP\n\tuniform sampler2D specularMap;\n#endif";
  
	var tonemapping_fragment = "#if defined( TONE_MAPPING )\n  gl_FragColor.rgb = toneMapping( gl_FragColor.rgb );\n#endif\n";
  
	var tonemapping_pars_fragment = "#ifndef saturate\n\t#define saturate(a) clamp( a, 0.0, 1.0 )\n#endif\nuniform float toneMappingExposure;\nuniform float toneMappingWhitePoint;\nvec3 LinearToneMapping( vec3 color ) {\n\treturn toneMappingExposure * color;\n}\nvec3 ReinhardToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( color / ( vec3( 1.0 ) + color ) );\n}\n#define Uncharted2Helper( x ) max( ( ( x * ( 0.15 * x + 0.10 * 0.50 ) + 0.20 * 0.02 ) / ( x * ( 0.15 * x + 0.50 ) + 0.20 * 0.30 ) ) - 0.02 / 0.30, vec3( 0.0 ) )\nvec3 Uncharted2ToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( Uncharted2Helper( color ) / Uncharted2Helper( vec3( toneMappingWhitePoint ) ) );\n}\nvec3 OptimizedCineonToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\tcolor = max( vec3( 0.0 ), color - 0.004 );\n\treturn pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) );\n}\n";
  
	var uv_pars_fragment = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\n\tvarying vec2 vUv;\n#endif";
  
	var uv_pars_vertex = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\n\tvarying vec2 vUv;\n\tuniform mat3 uvTransform;\n#endif\n";
  
	var uv_vertex = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\n\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n#endif";
  
	var uv2_pars_fragment = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvarying vec2 vUv2;\n#endif";
  
	var uv2_pars_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tattribute vec2 uv2;\n\tvarying vec2 vUv2;\n#endif";
  
	var uv2_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvUv2 = uv2;\n#endif";
  
	var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP )\n\tvec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );\n#endif\n";
  
	var cube_frag = "uniform samplerCube tCube;\nuniform float tFlip;\nuniform float opacity;\nvarying vec3 vWorldPosition;\nvoid main() {\n\tgl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );\n\tgl_FragColor.a *= opacity;\n}\n";
  
	var cube_vert = "varying vec3 vWorldPosition;\n#include <common>\nvoid main() {\n\tvWorldPosition = transformDirection( position, modelMatrix );\n\t#include <begin_vertex>\n\t#include <project_vertex>\n\tgl_Position.z = gl_Position.w;\n}\n";
  
	var depth_frag = "#if DEPTH_PACKING == 3200\n\tuniform float opacity;\n#endif\n#include <common>\n#include <packing>\n#include <uv_pars_fragment>\n#include <map_pars_fragment>\n#include <alphamap_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n\t#include <clipping_planes_fragment>\n\tvec4 diffuseColor = vec4( 1.0 );\n\t#if DEPTH_PACKING == 3200\n\t\tdiffuseColor.a = opacity;\n\t#endif\n\t#include <map_fragment>\n\t#include <alphamap_fragment>\n\t#include <alphatest_fragment>\n\t#include <logdepthbuf_fragment>\n\t#if DEPTH_PACKING == 3200\n\t\tgl_FragColor = vec4( vec3( 1.0 - gl_FragCoord.z ), opacity );\n\t#elif DEPTH_PACKING == 3201\n\t\tgl_FragColor = packDepthToRGBA( gl_FragCoord.z );\n\t#endif\n}\n";
  
	var depth_vert = "#include <common>\n#include <uv_pars_vertex>\n#include <displacementmap_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <uv_vertex>\n\t#include <skinbase_vertex>\n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include <beginnormal_vertex>\n\t\t#include <morphnormal_vertex>\n\t\t#include <skinnormal_vertex>\n\t#endif\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <displacementmap_vertex>\n\t#include <project_vertex>\n\t#include <logdepthbuf_vertex>\n\t#include <clipping_planes_vertex>\n}\n";
  
	var distanceRGBA_frag = "#define DISTANCE\nuniform vec3 referencePosition;\nuniform float nearDistance;\nuniform float farDistance;\nvarying vec3 vWorldPosition;\n#include <common>\n#include <packing>\n#include <uv_pars_fragment>\n#include <map_pars_fragment>\n#include <alphamap_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main () {\n\t#include <clipping_planes_fragment>\n\tvec4 diffuseColor = vec4( 1.0 );\n\t#include <map_fragment>\n\t#include <alphamap_fragment>\n\t#include <alphatest_fragment>\n\tfloat dist = length( vWorldPosition - referencePosition );\n\tdist = ( dist - nearDistance ) / ( farDistance - nearDistance );\n\tdist = saturate( dist );\n\tgl_FragColor = packDepthToRGBA( dist );\n}\n";
  
	var distanceRGBA_vert = "#define DISTANCE\nvarying vec3 vWorldPosition;\n#include <common>\n#include <uv_pars_vertex>\n#include <displacementmap_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <uv_vertex>\n\t#include <skinbase_vertex>\n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include <beginnormal_vertex>\n\t\t#include <morphnormal_vertex>\n\t\t#include <skinnormal_vertex>\n\t#endif\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <displacementmap_vertex>\n\t#include <project_vertex>\n\t#include <worldpos_vertex>\n\t#include <clipping_planes_vertex>\n\tvWorldPosition = worldPosition.xyz;\n}\n";
  
	var equirect_frag = "uniform sampler2D tEquirect;\nvarying vec3 vWorldPosition;\n#include <common>\nvoid main() {\n\tvec3 direction = normalize( vWorldPosition );\n\tvec2 sampleUV;\n\tsampleUV.y = asin( clamp( direction.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\tsampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;\n\tgl_FragColor = texture2D( tEquirect, sampleUV );\n}\n";
  
	var equirect_vert = "varying vec3 vWorldPosition;\n#include <common>\nvoid main() {\n\tvWorldPosition = transformDirection( position, modelMatrix );\n\t#include <begin_vertex>\n\t#include <project_vertex>\n}\n";
  
	var linedashed_frag = "uniform vec3 diffuse;\nuniform float opacity;\nuniform float dashSize;\nuniform float totalSize;\nvarying float vLineDistance;\n#include <common>\n#include <color_pars_fragment>\n#include <fog_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n\t#include <clipping_planes_fragment>\n\tif ( mod( vLineDistance, totalSize ) > dashSize ) {\n\t\tdiscard;\n\t}\n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include <logdepthbuf_fragment>\n\t#include <color_fragment>\n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include <premultiplied_alpha_fragment>\n\t#include <tonemapping_fragment>\n\t#include <encodings_fragment>\n\t#include <fog_fragment>\n}\n";
  
	var linedashed_vert = "uniform float scale;\nattribute float lineDistance;\nvarying float vLineDistance;\n#include <common>\n#include <color_pars_vertex>\n#include <fog_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <color_vertex>\n\tvLineDistance = scale * lineDistance;\n\tvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n\tgl_Position = projectionMatrix * mvPosition;\n\t#include <logdepthbuf_vertex>\n\t#include <clipping_planes_vertex>\n\t#include <fog_vertex>\n}\n";
  
	var meshbasic_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include <common>\n#include <color_pars_fragment>\n#include <uv_pars_fragment>\n#include <uv2_pars_fragment>\n#include <map_pars_fragment>\n#include <alphamap_pars_fragment>\n#include <aomap_pars_fragment>\n#include <lightmap_pars_fragment>\n#include <envmap_pars_fragment>\n#include <fog_pars_fragment>\n#include <specularmap_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n\t#include <clipping_planes_fragment>\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include <logdepthbuf_fragment>\n\t#include <map_fragment>\n\t#include <color_fragment>\n\t#include <alphamap_fragment>\n\t#include <alphatest_fragment>\n\t#include <specularmap_fragment>\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\t#ifdef USE_LIGHTMAP\n\t\treflectedLight.indirectDiffuse += texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n\t#else\n\t\treflectedLight.indirectDiffuse += vec3( 1.0 );\n\t#endif\n\t#include <aomap_fragment>\n\treflectedLight.indirectDiffuse *= diffuseColor.rgb;\n\tvec3 outgoingLight = reflectedLight.indirectDiffuse;\n\t#include <envmap_fragment>\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include <premultiplied_alpha_fragment>\n\t#include <tonemapping_fragment>\n\t#include <encodings_fragment>\n\t#include <fog_fragment>\n}\n";
  
	var meshbasic_vert = "#include <common>\n#include <uv_pars_vertex>\n#include <uv2_pars_vertex>\n#include <envmap_pars_vertex>\n#include <color_pars_vertex>\n#include <fog_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <uv_vertex>\n\t#include <uv2_vertex>\n\t#include <color_vertex>\n\t#include <skinbase_vertex>\n\t#ifdef USE_ENVMAP\n\t#include <beginnormal_vertex>\n\t#include <morphnormal_vertex>\n\t#include <skinnormal_vertex>\n\t#include <defaultnormal_vertex>\n\t#endif\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <project_vertex>\n\t#include <logdepthbuf_vertex>\n\t#include <worldpos_vertex>\n\t#include <clipping_planes_vertex>\n\t#include <envmap_vertex>\n\t#include <fog_vertex>\n}\n";
  
	var meshlambert_frag = "uniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\nvarying vec3 vLightFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n#endif\n#include <common>\n#include <packing>\n#include <dithering_pars_fragment>\n#include <color_pars_fragment>\n#include <uv_pars_fragment>\n#include <uv2_pars_fragment>\n#include <map_pars_fragment>\n#include <alphamap_pars_fragment>\n#include <aomap_pars_fragment>\n#include <lightmap_pars_fragment>\n#include <emissivemap_pars_fragment>\n#include <envmap_pars_fragment>\n#include <bsdfs>\n#include <lights_pars_begin>\n#include <lights_pars_maps>\n#include <fog_pars_fragment>\n#include <shadowmap_pars_fragment>\n#include <shadowmask_pars_fragment>\n#include <specularmap_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n\t#include <clipping_planes_fragment>\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include <logdepthbuf_fragment>\n\t#include <map_fragment>\n\t#include <color_fragment>\n\t#include <alphamap_fragment>\n\t#include <alphatest_fragment>\n\t#include <specularmap_fragment>\n\t#include <emissivemap_fragment>\n\treflectedLight.indirectDiffuse = getAmbientLightIrradiance( ambientLightColor );\n\t#include <lightmap_fragment>\n\treflectedLight.indirectDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb );\n\t#ifdef DOUBLE_SIDED\n\t\treflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack;\n\t#else\n\t\treflectedLight.directDiffuse = vLightFront;\n\t#endif\n\treflectedLight.directDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb ) * getShadowMask();\n\t#include <aomap_fragment>\n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\t#include <envmap_fragment>\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include <tonemapping_fragment>\n\t#include <encodings_fragment>\n\t#include <fog_fragment>\n\t#include <premultiplied_alpha_fragment>\n\t#include <dithering_fragment>\n}\n";
  
	var meshlambert_vert = "#define LAMBERT\nvarying vec3 vLightFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n#endif\n#include <common>\n#include <uv_pars_vertex>\n#include <uv2_pars_vertex>\n#include <envmap_pars_vertex>\n#include <bsdfs>\n#include <lights_pars_begin>\n#include <lights_pars_maps>\n#include <color_pars_vertex>\n#include <fog_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <shadowmap_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <uv_vertex>\n\t#include <uv2_vertex>\n\t#include <color_vertex>\n\t#include <beginnormal_vertex>\n\t#include <morphnormal_vertex>\n\t#include <skinbase_vertex>\n\t#include <skinnormal_vertex>\n\t#include <defaultnormal_vertex>\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <project_vertex>\n\t#include <logdepthbuf_vertex>\n\t#include <clipping_planes_vertex>\n\t#include <worldpos_vertex>\n\t#include <envmap_vertex>\n\t#include <lights_lambert_vertex>\n\t#include <shadowmap_vertex>\n\t#include <fog_vertex>\n}\n";
  
	var meshphong_frag = "#define PHONG\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform vec3 specular;\nuniform float shininess;\nuniform float opacity;\n#include <common>\n#include <packing>\n#include <dithering_pars_fragment>\n#include <color_pars_fragment>\n#include <uv_pars_fragment>\n#include <uv2_pars_fragment>\n#include <map_pars_fragment>\n#include <alphamap_pars_fragment>\n#include <aomap_pars_fragment>\n#include <lightmap_pars_fragment>\n#include <emissivemap_pars_fragment>\n#include <envmap_pars_fragment>\n#include <gradientmap_pars_fragment>\n#include <fog_pars_fragment>\n#include <bsdfs>\n#include <lights_pars_begin>\n#include <lights_pars_maps>\n#include <lights_phong_pars_fragment>\n#include <shadowmap_pars_fragment>\n#include <bumpmap_pars_fragment>\n#include <normalmap_pars_fragment>\n#include <specularmap_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n\t#include <clipping_planes_fragment>\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include <logdepthbuf_fragment>\n\t#include <map_fragment>\n\t#include <color_fragment>\n\t#include <alphamap_fragment>\n\t#include <alphatest_fragment>\n\t#include <specularmap_fragment>\n\t#include <normal_fragment_begin>\n\t#include <normal_fragment_maps>\n\t#include <emissivemap_fragment>\n\t#include <lights_phong_fragment>\n\t#include <lights_fragment_begin>\n\t#include <lights_fragment_maps>\n\t#include <lights_fragment_end>\n\t#include <aomap_fragment>\n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\t#include <envmap_fragment>\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include <tonemapping_fragment>\n\t#include <encodings_fragment>\n\t#include <fog_fragment>\n\t#include <premultiplied_alpha_fragment>\n\t#include <dithering_fragment>\n}\n";
  
	var meshphong_vert = "#define PHONG\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include <common>\n#include <uv_pars_vertex>\n#include <uv2_pars_vertex>\n#include <displacementmap_pars_vertex>\n#include <envmap_pars_vertex>\n#include <color_pars_vertex>\n#include <fog_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <shadowmap_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <uv_vertex>\n\t#include <uv2_vertex>\n\t#include <color_vertex>\n\t#include <beginnormal_vertex>\n\t#include <morphnormal_vertex>\n\t#include <skinbase_vertex>\n\t#include <skinnormal_vertex>\n\t#include <defaultnormal_vertex>\n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <displacementmap_vertex>\n\t#include <project_vertex>\n\t#include <logdepthbuf_vertex>\n\t#include <clipping_planes_vertex>\n\tvViewPosition = - mvPosition.xyz;\n\t#include <worldpos_vertex>\n\t#include <envmap_vertex>\n\t#include <shadowmap_vertex>\n\t#include <fog_vertex>\n}\n";
  
	var meshphysical_frag = "#define PHYSICAL\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float roughness;\nuniform float metalness;\nuniform float opacity;\n#ifndef STANDARD\n\tuniform float clearCoat;\n\tuniform float clearCoatRoughness;\n#endif\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include <common>\n#include <packing>\n#include <dithering_pars_fragment>\n#include <color_pars_fragment>\n#include <uv_pars_fragment>\n#include <uv2_pars_fragment>\n#include <map_pars_fragment>\n#include <alphamap_pars_fragment>\n#include <aomap_pars_fragment>\n#include <lightmap_pars_fragment>\n#include <emissivemap_pars_fragment>\n#include <envmap_pars_fragment>\n#include <fog_pars_fragment>\n#include <bsdfs>\n#include <cube_uv_reflection_fragment>\n#include <lights_pars_begin>\n#include <lights_pars_maps>\n#include <lights_physical_pars_fragment>\n#include <shadowmap_pars_fragment>\n#include <bumpmap_pars_fragment>\n#include <normalmap_pars_fragment>\n#include <roughnessmap_pars_fragment>\n#include <metalnessmap_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n\t#include <clipping_planes_fragment>\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include <logdepthbuf_fragment>\n\t#include <map_fragment>\n\t#include <color_fragment>\n\t#include <alphamap_fragment>\n\t#include <alphatest_fragment>\n\t#include <roughnessmap_fragment>\n\t#include <metalnessmap_fragment>\n\t#include <normal_fragment_begin>\n\t#include <normal_fragment_maps>\n\t#include <emissivemap_fragment>\n\t#include <lights_physical_fragment>\n\t#include <lights_fragment_begin>\n\t#include <lights_fragment_maps>\n\t#include <lights_fragment_end>\n\t#include <aomap_fragment>\n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include <tonemapping_fragment>\n\t#include <encodings_fragment>\n\t#include <fog_fragment>\n\t#include <premultiplied_alpha_fragment>\n\t#include <dithering_fragment>\n}\n";
  
	var meshphysical_vert = "#define PHYSICAL\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include <common>\n#include <uv_pars_vertex>\n#include <uv2_pars_vertex>\n#include <displacementmap_pars_vertex>\n#include <color_pars_vertex>\n#include <fog_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <shadowmap_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <uv_vertex>\n\t#include <uv2_vertex>\n\t#include <color_vertex>\n\t#include <beginnormal_vertex>\n\t#include <morphnormal_vertex>\n\t#include <skinbase_vertex>\n\t#include <skinnormal_vertex>\n\t#include <defaultnormal_vertex>\n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <displacementmap_vertex>\n\t#include <project_vertex>\n\t#include <logdepthbuf_vertex>\n\t#include <clipping_planes_vertex>\n\tvViewPosition = - mvPosition.xyz;\n\t#include <worldpos_vertex>\n\t#include <shadowmap_vertex>\n\t#include <fog_vertex>\n}\n";
  
	var normal_frag = "#define NORMAL\nuniform float opacity;\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include <packing>\n#include <uv_pars_fragment>\n#include <bumpmap_pars_fragment>\n#include <normalmap_pars_fragment>\n#include <logdepthbuf_pars_fragment>\nvoid main() {\n\t#include <logdepthbuf_fragment>\n\t#include <normal_fragment_begin>\n\t#include <normal_fragment_maps>\n\tgl_FragColor = vec4( packNormalToRGB( normal ), opacity );\n}\n";
  
	var normal_vert = "#define NORMAL\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include <uv_pars_vertex>\n#include <displacementmap_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <logdepthbuf_pars_vertex>\nvoid main() {\n\t#include <uv_vertex>\n\t#include <beginnormal_vertex>\n\t#include <morphnormal_vertex>\n\t#include <skinbase_vertex>\n\t#include <skinnormal_vertex>\n\t#include <defaultnormal_vertex>\n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <displacementmap_vertex>\n\t#include <project_vertex>\n\t#include <logdepthbuf_vertex>\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n}\n";
  
	var points_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#include <common>\n#include <packing>\n#include <color_pars_fragment>\n#include <map_particle_pars_fragment>\n#include <fog_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n\t#include <clipping_planes_fragment>\n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include <logdepthbuf_fragment>\n\t#include <map_particle_fragment>\n\t#include <color_fragment>\n\t#include <alphatest_fragment>\n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include <premultiplied_alpha_fragment>\n\t#include <tonemapping_fragment>\n\t#include <encodings_fragment>\n\t#include <fog_fragment>\n}\n";
  
	var points_vert = "uniform float size;\nuniform float scale;\n#include <common>\n#include <color_pars_vertex>\n#include <fog_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <color_vertex>\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <project_vertex>\n\t#ifdef USE_SIZEATTENUATION\n\t\tgl_PointSize = size * ( scale / - mvPosition.z );\n\t#else\n\t\tgl_PointSize = size;\n\t#endif\n\t#include <logdepthbuf_vertex>\n\t#include <clipping_planes_vertex>\n\t#include <worldpos_vertex>\n\t#include <fog_vertex>\n}\n";
  
	var shadow_frag = "uniform vec3 color;\nuniform float opacity;\n#include <common>\n#include <packing>\n#include <fog_pars_fragment>\n#include <bsdfs>\n#include <lights_pars_begin>\n#include <shadowmap_pars_fragment>\n#include <shadowmask_pars_fragment>\nvoid main() {\n\tgl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) );\n\t#include <fog_fragment>\n}\n";
  
	var shadow_vert = "#include <fog_pars_vertex>\n#include <shadowmap_pars_vertex>\nvoid main() {\n\t#include <begin_vertex>\n\t#include <project_vertex>\n\t#include <worldpos_vertex>\n\t#include <shadowmap_vertex>\n\t#include <fog_vertex>\n}\n";
  
	var ShaderChunk = {
		alphamap_fragment: alphamap_fragment,
		alphamap_pars_fragment: alphamap_pars_fragment,
		alphatest_fragment: alphatest_fragment,
		aomap_fragment: aomap_fragment,
		aomap_pars_fragment: aomap_pars_fragment,
		begin_vertex: begin_vertex,
		beginnormal_vertex: beginnormal_vertex,
		bsdfs: bsdfs,
		bumpmap_pars_fragment: bumpmap_pars_fragment,
		clipping_planes_fragment: clipping_planes_fragment,
		clipping_planes_pars_fragment: clipping_planes_pars_fragment,
		clipping_planes_pars_vertex: clipping_planes_pars_vertex,
		clipping_planes_vertex: clipping_planes_vertex,
		color_fragment: color_fragment,
		color_pars_fragment: color_pars_fragment,
		color_pars_vertex: color_pars_vertex,
		color_vertex: color_vertex,
		common: common,
		cube_uv_reflection_fragment: cube_uv_reflection_fragment,
		defaultnormal_vertex: defaultnormal_vertex,
		displacementmap_pars_vertex: displacementmap_pars_vertex,
		displacementmap_vertex: displacementmap_vertex,
		emissivemap_fragment: emissivemap_fragment,
		emissivemap_pars_fragment: emissivemap_pars_fragment,
		encodings_fragment: encodings_fragment,
		encodings_pars_fragment: encodings_pars_fragment,
		envmap_fragment: envmap_fragment,
		envmap_pars_fragment: envmap_pars_fragment,
		envmap_pars_vertex: envmap_pars_vertex,
		envmap_vertex: envmap_vertex,
		fog_vertex: fog_vertex,
		fog_pars_vertex: fog_pars_vertex,
		fog_fragment: fog_fragment,
		fog_pars_fragment: fog_pars_fragment,
		gradientmap_pars_fragment: gradientmap_pars_fragment,
		lightmap_fragment: lightmap_fragment,
		lightmap_pars_fragment: lightmap_pars_fragment,
		lights_lambert_vertex: lights_lambert_vertex,
		lights_pars_begin: lights_pars_begin,
		lights_pars_maps: lights_pars_maps,
		lights_phong_fragment: lights_phong_fragment,
		lights_phong_pars_fragment: lights_phong_pars_fragment,
		lights_physical_fragment: lights_physical_fragment,
		lights_physical_pars_fragment: lights_physical_pars_fragment,
		lights_fragment_begin: lights_fragment_begin,
		lights_fragment_maps: lights_fragment_maps,
		lights_fragment_end: lights_fragment_end,
		logdepthbuf_fragment: logdepthbuf_fragment,
		logdepthbuf_pars_fragment: logdepthbuf_pars_fragment,
		logdepthbuf_pars_vertex: logdepthbuf_pars_vertex,
		logdepthbuf_vertex: logdepthbuf_vertex,
		map_fragment: map_fragment,
		map_pars_fragment: map_pars_fragment,
		map_particle_fragment: map_particle_fragment,
		map_particle_pars_fragment: map_particle_pars_fragment,
		metalnessmap_fragment: metalnessmap_fragment,
		metalnessmap_pars_fragment: metalnessmap_pars_fragment,
		morphnormal_vertex: morphnormal_vertex,
		morphtarget_pars_vertex: morphtarget_pars_vertex,
		morphtarget_vertex: morphtarget_vertex,
		normal_fragment_begin: normal_fragment_begin,
		normal_fragment_maps: normal_fragment_maps,
		normalmap_pars_fragment: normalmap_pars_fragment,
		packing: packing,
		premultiplied_alpha_fragment: premultiplied_alpha_fragment,
		project_vertex: project_vertex,
		dithering_fragment: dithering_fragment,
		dithering_pars_fragment: dithering_pars_fragment,
		roughnessmap_fragment: roughnessmap_fragment,
		roughnessmap_pars_fragment: roughnessmap_pars_fragment,
		shadowmap_pars_fragment: shadowmap_pars_fragment,
		shadowmap_pars_vertex: shadowmap_pars_vertex,
		shadowmap_vertex: shadowmap_vertex,
		shadowmask_pars_fragment: shadowmask_pars_fragment,
		skinbase_vertex: skinbase_vertex,
		skinning_pars_vertex: skinning_pars_vertex,
		skinning_vertex: skinning_vertex,
		skinnormal_vertex: skinnormal_vertex,
		specularmap_fragment: specularmap_fragment,
		specularmap_pars_fragment: specularmap_pars_fragment,
		tonemapping_fragment: tonemapping_fragment,
		tonemapping_pars_fragment: tonemapping_pars_fragment,
		uv_pars_fragment: uv_pars_fragment,
		uv_pars_vertex: uv_pars_vertex,
		uv_vertex: uv_vertex,
		uv2_pars_fragment: uv2_pars_fragment,
		uv2_pars_vertex: uv2_pars_vertex,
		uv2_vertex: uv2_vertex,
		worldpos_vertex: worldpos_vertex,
  
		cube_frag: cube_frag,
		cube_vert: cube_vert,
		depth_frag: depth_frag,
		depth_vert: depth_vert,
		distanceRGBA_frag: distanceRGBA_frag,
		distanceRGBA_vert: distanceRGBA_vert,
		equirect_frag: equirect_frag,
		equirect_vert: equirect_vert,
		linedashed_frag: linedashed_frag,
		linedashed_vert: linedashed_vert,
		meshbasic_frag: meshbasic_frag,
		meshbasic_vert: meshbasic_vert,
		meshlambert_frag: meshlambert_frag,
		meshlambert_vert: meshlambert_vert,
		meshphong_frag: meshphong_frag,
		meshphong_vert: meshphong_vert,
		meshphysical_frag: meshphysical_frag,
		meshphysical_vert: meshphysical_vert,
		normal_frag: normal_frag,
		normal_vert: normal_vert,
		points_frag: points_frag,
		points_vert: points_vert,
		shadow_frag: shadow_frag,
		shadow_vert: shadow_vert
	};
  
	/**
	 * Uniform Utilities
	 */
  
	var UniformsUtils = {
  
		merge: function ( uniforms ) {
  
			var merged = {};
  
			for ( var u = 0; u < uniforms.length; u ++ ) {
  
				var tmp = this.clone( uniforms[ u ] );
  
				for ( var p in tmp ) {
  
					merged[ p ] = tmp[ p ];
  
				}
  
			}
  
			return merged;
  
		},
  
		clone: function ( uniforms_src ) {
  
			var uniforms_dst = {};
  
			for ( var u in uniforms_src ) {
  
				uniforms_dst[ u ] = {};
  
				for ( var p in uniforms_src[ u ] ) {
  
					var parameter_src = uniforms_src[ u ][ p ];
  
					if ( parameter_src && ( parameter_src.isColor ||
						parameter_src.isMatrix3 || parameter_src.isMatrix4 ||
						parameter_src.isVector2 || parameter_src.isVector3 || parameter_src.isVector4 ||
						parameter_src.isTexture ) ) {
  
						uniforms_dst[ u ][ p ] = parameter_src.clone();
  
					} else if ( Array.isArray( parameter_src ) ) {
  
						uniforms_dst[ u ][ p ] = parameter_src.slice();
  
					} else {
  
						uniforms_dst[ u ][ p ] = parameter_src;
  
					}
  
				}
  
			}
  
			return uniforms_dst;
  
		}
  
	};
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	var ColorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua': 0x00FFFF, 'aquamarine': 0x7FFFD4, 'azure': 0xF0FFFF,
		'beige': 0xF5F5DC, 'bisque': 0xFFE4C4, 'black': 0x000000, 'blanchedalmond': 0xFFEBCD, 'blue': 0x0000FF, 'blueviolet': 0x8A2BE2,
		'brown': 0xA52A2A, 'burlywood': 0xDEB887, 'cadetblue': 0x5F9EA0, 'chartreuse': 0x7FFF00, 'chocolate': 0xD2691E, 'coral': 0xFF7F50,
		'cornflowerblue': 0x6495ED, 'cornsilk': 0xFFF8DC, 'crimson': 0xDC143C, 'cyan': 0x00FFFF, 'darkblue': 0x00008B, 'darkcyan': 0x008B8B,
		'darkgoldenrod': 0xB8860B, 'darkgray': 0xA9A9A9, 'darkgreen': 0x006400, 'darkgrey': 0xA9A9A9, 'darkkhaki': 0xBDB76B, 'darkmagenta': 0x8B008B,
		'darkolivegreen': 0x556B2F, 'darkorange': 0xFF8C00, 'darkorchid': 0x9932CC, 'darkred': 0x8B0000, 'darksalmon': 0xE9967A, 'darkseagreen': 0x8FBC8F,
		'darkslateblue': 0x483D8B, 'darkslategray': 0x2F4F4F, 'darkslategrey': 0x2F4F4F, 'darkturquoise': 0x00CED1, 'darkviolet': 0x9400D3,
		'deeppink': 0xFF1493, 'deepskyblue': 0x00BFFF, 'dimgray': 0x696969, 'dimgrey': 0x696969, 'dodgerblue': 0x1E90FF, 'firebrick': 0xB22222,
		'floralwhite': 0xFFFAF0, 'forestgreen': 0x228B22, 'fuchsia': 0xFF00FF, 'gainsboro': 0xDCDCDC, 'ghostwhite': 0xF8F8FF, 'gold': 0xFFD700,
		'goldenrod': 0xDAA520, 'gray': 0x808080, 'green': 0x008000, 'greenyellow': 0xADFF2F, 'grey': 0x808080, 'honeydew': 0xF0FFF0, 'hotpink': 0xFF69B4,
		'indianred': 0xCD5C5C, 'indigo': 0x4B0082, 'ivory': 0xFFFFF0, 'khaki': 0xF0E68C, 'lavender': 0xE6E6FA, 'lavenderblush': 0xFFF0F5, 'lawngreen': 0x7CFC00,
		'lemonchiffon': 0xFFFACD, 'lightblue': 0xADD8E6, 'lightcoral': 0xF08080, 'lightcyan': 0xE0FFFF, 'lightgoldenrodyellow': 0xFAFAD2, 'lightgray': 0xD3D3D3,
		'lightgreen': 0x90EE90, 'lightgrey': 0xD3D3D3, 'lightpink': 0xFFB6C1, 'lightsalmon': 0xFFA07A, 'lightseagreen': 0x20B2AA, 'lightskyblue': 0x87CEFA,
		'lightslategray': 0x778899, 'lightslategrey': 0x778899, 'lightsteelblue': 0xB0C4DE, 'lightyellow': 0xFFFFE0, 'lime': 0x00FF00, 'limegreen': 0x32CD32,
		'linen': 0xFAF0E6, 'magenta': 0xFF00FF, 'maroon': 0x800000, 'mediumaquamarine': 0x66CDAA, 'mediumblue': 0x0000CD, 'mediumorchid': 0xBA55D3,
		'mediumpurple': 0x9370DB, 'mediumseagreen': 0x3CB371, 'mediumslateblue': 0x7B68EE, 'mediumspringgreen': 0x00FA9A, 'mediumturquoise': 0x48D1CC,
		'mediumvioletred': 0xC71585, 'midnightblue': 0x191970, 'mintcream': 0xF5FFFA, 'mistyrose': 0xFFE4E1, 'moccasin': 0xFFE4B5, 'navajowhite': 0xFFDEAD,
		'navy': 0x000080, 'oldlace': 0xFDF5E6, 'olive': 0x808000, 'olivedrab': 0x6B8E23, 'orange': 0xFFA500, 'orangered': 0xFF4500, 'orchid': 0xDA70D6,
		'palegoldenrod': 0xEEE8AA, 'palegreen': 0x98FB98, 'paleturquoise': 0xAFEEEE, 'palevioletred': 0xDB7093, 'papayawhip': 0xFFEFD5, 'peachpuff': 0xFFDAB9,
		'peru': 0xCD853F, 'pink': 0xFFC0CB, 'plum': 0xDDA0DD, 'powderblue': 0xB0E0E6, 'purple': 0x800080, 'rebeccapurple': 0x663399, 'red': 0xFF0000, 'rosybrown': 0xBC8F8F,
		'royalblue': 0x4169E1, 'saddlebrown': 0x8B4513, 'salmon': 0xFA8072, 'sandybrown': 0xF4A460, 'seagreen': 0x2E8B57, 'seashell': 0xFFF5EE,
		'sienna': 0xA0522D, 'silver': 0xC0C0C0, 'skyblue': 0x87CEEB, 'slateblue': 0x6A5ACD, 'slategray': 0x708090, 'slategrey': 0x708090, 'snow': 0xFFFAFA,
		'springgreen': 0x00FF7F, 'steelblue': 0x4682B4, 'tan': 0xD2B48C, 'teal': 0x008080, 'thistle': 0xD8BFD8, 'tomato': 0xFF6347, 'turquoise': 0x40E0D0,
		'violet': 0xEE82EE, 'wheat': 0xF5DEB3, 'white': 0xFFFFFF, 'whitesmoke': 0xF5F5F5, 'yellow': 0xFFFF00, 'yellowgreen': 0x9ACD32 };
  
	function Color( r, g, b ) {
  
		if ( g === undefined && b === undefined ) {
  
			// r is THREE.Color, hex or string
			return this.set( r );
  
		}
  
		return this.setRGB( r, g, b );
  
	}
  
	Object.assign( Color.prototype, {
  
		isColor: true,
  
		r: 1, g: 1, b: 1,
  
		set: function ( value ) {
  
			if ( value && value.isColor ) {
  
				this.copy( value );
  
			} else if ( typeof value === 'number' ) {
  
				this.setHex( value );
  
			} else if ( typeof value === 'string' ) {
  
				this.setStyle( value );
  
			}
  
			return this;
  
		},
  
		setScalar: function ( scalar ) {
  
			this.r = scalar;
			this.g = scalar;
			this.b = scalar;
  
			return this;
  
		},
  
		setHex: function ( hex ) {
  
			hex = Math.floor( hex );
  
			this.r = ( hex >> 16 & 255 ) / 255;
			this.g = ( hex >> 8 & 255 ) / 255;
			this.b = ( hex & 255 ) / 255;
  
			return this;
  
		},
  
		setRGB: function ( r, g, b ) {
  
			this.r = r;
			this.g = g;
			this.b = b;
  
			return this;
  
		},
  
		setHSL: function () {
  
			function hue2rgb( p, q, t ) {
  
				if ( t < 0 ) t += 1;
				if ( t > 1 ) t -= 1;
				if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t;
				if ( t < 1 / 2 ) return q;
				if ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t );
				return p;
  
			}
  
			return function setHSL( h, s, l ) {
  
				// h,s,l ranges are in 0.0 - 1.0
				h = _Math.euclideanModulo( h, 1 );
				s = _Math.clamp( s, 0, 1 );
				l = _Math.clamp( l, 0, 1 );
  
				if ( s === 0 ) {
  
					this.r = this.g = this.b = l;
  
				} else {
  
					var p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s );
					var q = ( 2 * l ) - p;
  
					this.r = hue2rgb( q, p, h + 1 / 3 );
					this.g = hue2rgb( q, p, h );
					this.b = hue2rgb( q, p, h - 1 / 3 );
  
				}
  
				return this;
  
			};
  
		}(),
  
		setStyle: function ( style ) {
  
			function handleAlpha( string ) {
  
				if ( string === undefined ) return;
  
				if ( parseFloat( string ) < 1 ) {
  
					console.warn( 'THREE.Color: Alpha component of ' + style + ' will be ignored.' );
  
				}
  
			}
  
  
			var m;
  
			if ( m = /^((?:rgb|hsl)a?)\(\s*([^\)]*)\)/.exec( style ) ) {
  
				// rgb / hsl
  
				var color;
				var name = m[ 1 ];
				var components = m[ 2 ];
  
				switch ( name ) {
  
					case 'rgb':
					case 'rgba':
  
						if ( color = /^(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) {
  
							// rgb(255,0,0) rgba(255,0,0,0.5)
							this.r = Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255;
							this.g = Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255;
							this.b = Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255;
  
							handleAlpha( color[ 5 ] );
  
							return this;
  
						}
  
						if ( color = /^(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) {
  
							// rgb(100%,0%,0%) rgba(100%,0%,0%,0.5)
							this.r = Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100;
							this.g = Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100;
							this.b = Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100;
  
							handleAlpha( color[ 5 ] );
  
							return this;
  
						}
  
						break;
  
					case 'hsl':
					case 'hsla':
  
						if ( color = /^([0-9]*\.?[0-9]+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) {
  
							// hsl(120,50%,50%) hsla(120,50%,50%,0.5)
							var h = parseFloat( color[ 1 ] ) / 360;
							var s = parseInt( color[ 2 ], 10 ) / 100;
							var l = parseInt( color[ 3 ], 10 ) / 100;
  
							handleAlpha( color[ 5 ] );
  
							return this.setHSL( h, s, l );
  
						}
  
						break;
  
				}
  
			} else if ( m = /^\#([A-Fa-f0-9]+)$/.exec( style ) ) {
  
				// hex color
  
				var hex = m[ 1 ];
				var size = hex.length;
  
				if ( size === 3 ) {
  
					// #ff0
					this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 0 ), 16 ) / 255;
					this.g = parseInt( hex.charAt( 1 ) + hex.charAt( 1 ), 16 ) / 255;
					this.b = parseInt( hex.charAt( 2 ) + hex.charAt( 2 ), 16 ) / 255;
  
					return this;
  
				} else if ( size === 6 ) {
  
					// #ff0000
					this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 1 ), 16 ) / 255;
					this.g = parseInt( hex.charAt( 2 ) + hex.charAt( 3 ), 16 ) / 255;
					this.b = parseInt( hex.charAt( 4 ) + hex.charAt( 5 ), 16 ) / 255;
  
					return this;
  
				}
  
			}
  
			if ( style && style.length > 0 ) {
  
				// color keywords
				var hex = ColorKeywords[ style ];
  
				if ( hex !== undefined ) {
  
					// red
					this.setHex( hex );
  
				} else {
  
					// unknown color
					console.warn( 'THREE.Color: Unknown color ' + style );
  
				}
  
			}
  
			return this;
  
		},
  
		clone: function () {
  
			return new this.constructor( this.r, this.g, this.b );
  
		},
  
		copy: function ( color ) {
  
			this.r = color.r;
			this.g = color.g;
			this.b = color.b;
  
			return this;
  
		},
  
		copyGammaToLinear: function ( color, gammaFactor ) {
  
			if ( gammaFactor === undefined ) gammaFactor = 2.0;
  
			this.r = Math.pow( color.r, gammaFactor );
			this.g = Math.pow( color.g, gammaFactor );
			this.b = Math.pow( color.b, gammaFactor );
  
			return this;
  
		},
  
		copyLinearToGamma: function ( color, gammaFactor ) {
  
			if ( gammaFactor === undefined ) gammaFactor = 2.0;
  
			var safeInverse = ( gammaFactor > 0 ) ? ( 1.0 / gammaFactor ) : 1.0;
  
			this.r = Math.pow( color.r, safeInverse );
			this.g = Math.pow( color.g, safeInverse );
			this.b = Math.pow( color.b, safeInverse );
  
			return this;
  
		},
  
		convertGammaToLinear: function ( gammaFactor ) {
  
			this.copyGammaToLinear( this, gammaFactor );
  
			return this;
  
		},
  
		convertLinearToGamma: function ( gammaFactor ) {
  
			this.copyLinearToGamma( this, gammaFactor );
  
			return this;
  
		},
  
		getHex: function () {
  
			return ( this.r * 255 ) << 16 ^ ( this.g * 255 ) << 8 ^ ( this.b * 255 ) << 0;
  
		},
  
		getHexString: function () {
  
			return ( '000000' + this.getHex().toString( 16 ) ).slice( - 6 );
  
		},
  
		getHSL: function ( target ) {
  
			// h,s,l ranges are in 0.0 - 1.0
  
			if ( target === undefined ) {
  
				console.warn( 'THREE.Color: .getHSL() target is now required' );
				target = { h: 0, s: 0, l: 0 };
  
			}
  
			var r = this.r, g = this.g, b = this.b;
  
			var max = Math.max( r, g, b );
			var min = Math.min( r, g, b );
  
			var hue, saturation;
			var lightness = ( min + max ) / 2.0;
  
			if ( min === max ) {
  
				hue = 0;
				saturation = 0;
  
			} else {
  
				var delta = max - min;
  
				saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min );
  
				switch ( max ) {
  
					case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break;
					case g: hue = ( b - r ) / delta + 2; break;
					case b: hue = ( r - g ) / delta + 4; break;
  
				}
  
				hue /= 6;
  
			}
  
			target.h = hue;
			target.s = saturation;
			target.l = lightness;
  
			return target;
  
		},
  
		getStyle: function () {
  
			return 'rgb(' + ( ( this.r * 255 ) | 0 ) + ',' + ( ( this.g * 255 ) | 0 ) + ',' + ( ( this.b * 255 ) | 0 ) + ')';
  
		},
  
		offsetHSL: function () {
  
			var hsl = {};
  
			return function ( h, s, l ) {
  
				this.getHSL( hsl );
  
				hsl.h += h; hsl.s += s; hsl.l += l;
  
				this.setHSL( hsl.h, hsl.s, hsl.l );
  
				return this;
  
			};
  
		}(),
  
		add: function ( color ) {
  
			this.r += color.r;
			this.g += color.g;
			this.b += color.b;
  
			return this;
  
		},
  
		addColors: function ( color1, color2 ) {
  
			this.r = color1.r + color2.r;
			this.g = color1.g + color2.g;
			this.b = color1.b + color2.b;
  
			return this;
  
		},
  
		addScalar: function ( s ) {
  
			this.r += s;
			this.g += s;
			this.b += s;
  
			return this;
  
		},
  
		sub: function ( color ) {
  
			this.r = Math.max( 0, this.r - color.r );
			this.g = Math.max( 0, this.g - color.g );
			this.b = Math.max( 0, this.b - color.b );
  
			return this;
  
		},
  
		multiply: function ( color ) {
  
			this.r *= color.r;
			this.g *= color.g;
			this.b *= color.b;
  
			return this;
  
		},
  
		multiplyScalar: function ( s ) {
  
			this.r *= s;
			this.g *= s;
			this.b *= s;
  
			return this;
  
		},
  
		lerp: function ( color, alpha ) {
  
			this.r += ( color.r - this.r ) * alpha;
			this.g += ( color.g - this.g ) * alpha;
			this.b += ( color.b - this.b ) * alpha;
  
			return this;
  
		},
  
		equals: function ( c ) {
  
			return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b );
  
		},
  
		fromArray: function ( array, offset ) {
  
			if ( offset === undefined ) offset = 0;
  
			this.r = array[ offset ];
			this.g = array[ offset + 1 ];
			this.b = array[ offset + 2 ];
  
			return this;
  
		},
  
		toArray: function ( array, offset ) {
  
			if ( array === undefined ) array = [];
			if ( offset === undefined ) offset = 0;
  
			array[ offset ] = this.r;
			array[ offset + 1 ] = this.g;
			array[ offset + 2 ] = this.b;
  
			return array;
  
		},
  
		toJSON: function () {
  
			return this.getHex();
  
		}
  
	} );
  
	/**
	 * Uniforms library for shared webgl shaders
	 */
  
	var UniformsLib = {
  
		common: {
  
			diffuse: { value: new Color( 0xeeeeee ) },
			opacity: { value: 1.0 },
  
			map: { value: null },
			uvTransform: { value: new Matrix3() },
  
			alphaMap: { value: null },
  
		},
  
		specularmap: {
  
			specularMap: { value: null },
  
		},
  
		envmap: {
  
			envMap: { value: null },
			flipEnvMap: { value: - 1 },
			reflectivity: { value: 1.0 },
			refractionRatio: { value: 0.98 },
			maxMipLevel: { value: 0 }
  
		},
  
		aomap: {
  
			aoMap: { value: null },
			aoMapIntensity: { value: 1 }
  
		},
  
		lightmap: {
  
			lightMap: { value: null },
			lightMapIntensity: { value: 1 }
  
		},
  
		emissivemap: {
  
			emissiveMap: { value: null }
  
		},
  
		bumpmap: {
  
			bumpMap: { value: null },
			bumpScale: { value: 1 }
  
		},
  
		normalmap: {
  
			normalMap: { value: null },
			normalScale: { value: new Vector2( 1, 1 ) }
  
		},
  
		displacementmap: {
  
			displacementMap: { value: null },
			displacementScale: { value: 1 },
			displacementBias: { value: 0 }
  
		},
  
		roughnessmap: {
  
			roughnessMap: { value: null }
  
		},
  
		metalnessmap: {
  
			metalnessMap: { value: null }
  
		},
  
		gradientmap: {
  
			gradientMap: { value: null }
  
		},
  
		fog: {
  
			fogDensity: { value: 0.00025 },
			fogNear: { value: 1 },
			fogFar: { value: 2000 },
			fogColor: { value: new Color( 0xffffff ) }
  
		},
  
		lights: {
  
			ambientLightColor: { value: [] },
  
			directionalLights: { value: [], properties: {
				direction: {},
				color: {},
  
				shadow: {},
				shadowBias: {},
				shadowRadius: {},
				shadowMapSize: {}
			} },
  
			directionalShadowMap: { value: [] },
			directionalShadowMatrix: { value: [] },
  
			spotLights: { value: [], properties: {
				color: {},
				position: {},
				direction: {},
				distance: {},
				coneCos: {},
				penumbraCos: {},
				decay: {},
  
				shadow: {},
				shadowBias: {},
				shadowRadius: {},
				shadowMapSize: {}
			} },
  
			spotShadowMap: { value: [] },
			spotShadowMatrix: { value: [] },
  
			pointLights: { value: [], properties: {
				color: {},
				position: {},
				decay: {},
				distance: {},
  
				shadow: {},
				shadowBias: {},
				shadowRadius: {},
				shadowMapSize: {},
				shadowCameraNear: {},
				shadowCameraFar: {}
			} },
  
			pointShadowMap: { value: [] },
			pointShadowMatrix: { value: [] },
  
			hemisphereLights: { value: [], properties: {
				direction: {},
				skyColor: {},
				groundColor: {}
			} },
  
			// TODO (abelnation): RectAreaLight BRDF data needs to be moved from example to main src
			rectAreaLights: { value: [], properties: {
				color: {},
				position: {},
				width: {},
				height: {}
			} }
  
		},
  
		points: {
  
			diffuse: { value: new Color( 0xeeeeee ) },
			opacity: { value: 1.0 },
			size: { value: 1.0 },
			scale: { value: 1.0 },
			map: { value: null },
			uvTransform: { value: new Matrix3() }
  
		}
  
	};
  
	/**
	 * @author alteredq / http://alteredqualia.com/
	 * @author mrdoob / http://mrdoob.com/
	 * @author mikael emtinger / http://gomo.se/
	 */
  
	var ShaderLib = {
  
		basic: {
  
			uniforms: UniformsUtils.merge( [
				UniformsLib.common,
				UniformsLib.specularmap,
				UniformsLib.envmap,
				UniformsLib.aomap,
				UniformsLib.lightmap,
				UniformsLib.fog
			] ),
  
			vertexShader: ShaderChunk.meshbasic_vert,
			fragmentShader: ShaderChunk.meshbasic_frag
  
		},
  
		lambert: {
  
			uniforms: UniformsUtils.merge( [
				UniformsLib.common,
				UniformsLib.specularmap,
				UniformsLib.envmap,
				UniformsLib.aomap,
				UniformsLib.lightmap,
				UniformsLib.emissivemap,
				UniformsLib.fog,
				UniformsLib.lights,
				{
					emissive: { value: new Color( 0x000000 ) }
				}
			] ),
  
			vertexShader: ShaderChunk.meshlambert_vert,
			fragmentShader: ShaderChunk.meshlambert_frag
  
		},
  
		phong: {
  
			uniforms: UniformsUtils.merge( [
				UniformsLib.common,
				UniformsLib.specularmap,
				UniformsLib.envmap,
				UniformsLib.aomap,
				UniformsLib.lightmap,
				UniformsLib.emissivemap,
				UniformsLib.bumpmap,
				UniformsLib.normalmap,
				UniformsLib.displacementmap,
				UniformsLib.gradientmap,
				UniformsLib.fog,
				UniformsLib.lights,
				{
					emissive: { value: new Color( 0x000000 ) },
					specular: { value: new Color( 0x111111 ) },
					shininess: { value: 30 }
				}
			] ),
  
			vertexShader: ShaderChunk.meshphong_vert,
			fragmentShader: ShaderChunk.meshphong_frag
  
		},
  
		standard: {
  
			uniforms: UniformsUtils.merge( [
				UniformsLib.common,
				UniformsLib.envmap,
				UniformsLib.aomap,
				UniformsLib.lightmap,
				UniformsLib.emissivemap,
				UniformsLib.bumpmap,
				UniformsLib.normalmap,
				UniformsLib.displacementmap,
				UniformsLib.roughnessmap,
				UniformsLib.metalnessmap,
				UniformsLib.fog,
				UniformsLib.lights,
				{
					emissive: { value: new Color( 0x000000 ) },
					roughness: { value: 0.5 },
					metalness: { value: 0.5 },
					envMapIntensity: { value: 1 } // temporary
				}
			] ),
  
			vertexShader: ShaderChunk.meshphysical_vert,
			fragmentShader: ShaderChunk.meshphysical_frag
  
		},
  
		points: {
  
			uniforms: UniformsUtils.merge( [
				UniformsLib.points,
				UniformsLib.fog
			] ),
  
			vertexShader: ShaderChunk.points_vert,
			fragmentShader: ShaderChunk.points_frag
  
		},
  
		dashed: {
  
			uniforms: UniformsUtils.merge( [
				UniformsLib.common,
				UniformsLib.fog,
				{
					scale: { value: 1 },
					dashSize: { value: 1 },
					totalSize: { value: 2 }
				}
			] ),
  
			vertexShader: ShaderChunk.linedashed_vert,
			fragmentShader: ShaderChunk.linedashed_frag
  
		},
  
		depth: {
  
			uniforms: UniformsUtils.merge( [
				UniformsLib.common,
				UniformsLib.displacementmap
			] ),
  
			vertexShader: ShaderChunk.depth_vert,
			fragmentShader: ShaderChunk.depth_frag
  
		},
  
		normal: {
  
			uniforms: UniformsUtils.merge( [
				UniformsLib.common,
				UniformsLib.bumpmap,
				UniformsLib.normalmap,
				UniformsLib.displacementmap,
				{
					opacity: { value: 1.0 }
				}
			] ),
  
			vertexShader: ShaderChunk.normal_vert,
			fragmentShader: ShaderChunk.normal_frag
  
		},
  
		/* -------------------------------------------------------------------------
		//	Cube map shader
		 ------------------------------------------------------------------------- */
  
		cube: {
  
			uniforms: {
				tCube: { value: null },
				tFlip: { value: - 1 },
				opacity: { value: 1.0 }
			},
  
			vertexShader: ShaderChunk.cube_vert,
			fragmentShader: ShaderChunk.cube_frag
  
		},
  
		equirect: {
  
			uniforms: {
				tEquirect: { value: null },
			},
  
			vertexShader: ShaderChunk.equirect_vert,
			fragmentShader: ShaderChunk.equirect_frag
  
		},
  
		distanceRGBA: {
  
			uniforms: UniformsUtils.merge( [
				UniformsLib.common,
				UniformsLib.displacementmap,
				{
					referencePosition: { value: new Vector3() },
					nearDistance: { value: 1 },
					farDistance: { value: 1000 }
				}
			] ),
  
			vertexShader: ShaderChunk.distanceRGBA_vert,
			fragmentShader: ShaderChunk.distanceRGBA_frag
  
		},
  
		shadow: {
  
			uniforms: UniformsUtils.merge( [
				UniformsLib.lights,
				UniformsLib.fog,
				{
					color: { value: new Color( 0x00000 ) },
					opacity: { value: 1.0 }
				},
			] ),
  
			vertexShader: ShaderChunk.shadow_vert,
			fragmentShader: ShaderChunk.shadow_frag
  
		}
  
	};
  
	ShaderLib.physical = {
  
		uniforms: UniformsUtils.merge( [
			ShaderLib.standard.uniforms,
			{
				clearCoat: { value: 0 },
				clearCoatRoughness: { value: 0 }
			}
		] ),
  
		vertexShader: ShaderChunk.meshphysical_vert,
		fragmentShader: ShaderChunk.meshphysical_frag
  
	};
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function WebGLAnimation() {
  
		var context = null;
		var isAnimating = false;
		var animationLoop = null;
  
		function onAnimationFrame( time, frame ) {
  
			if ( isAnimating === false ) return;
  
			animationLoop( time, frame );
  
			context.requestAnimationFrame( onAnimationFrame );
  
		}
  
		return {
  
			start: function () {
  
				if ( isAnimating === true ) return;
				if ( animationLoop === null ) return;
  
				context.requestAnimationFrame( onAnimationFrame );
  
				isAnimating = true;
  
			},
  
			stop: function () {
  
				isAnimating = false;
  
			},
  
			setAnimationLoop: function ( callback ) {
  
				animationLoop = callback;
  
			},
  
			setContext: function ( value ) {
  
				context = value;
  
			}
  
		};
  
	}
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function WebGLAttributes( gl ) {
  
		var buffers = new WeakMap();
  
		function createBuffer( attribute, bufferType ) {
  
			var array = attribute.array;
			var usage = attribute.dynamic ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW;
  
			var buffer = gl.createBuffer();
  
			gl.bindBuffer( bufferType, buffer );
			gl.bufferData( bufferType, array, usage );
  
			attribute.onUploadCallback();
  
			var type = gl.FLOAT;
  
			if ( array instanceof Float32Array ) {
  
				type = gl.FLOAT;
  
			} else if ( array instanceof Float64Array ) {
  
				console.warn( 'THREE.WebGLAttributes: Unsupported data buffer format: Float64Array.' );
  
			} else if ( array instanceof Uint16Array ) {
  
				type = gl.UNSIGNED_SHORT;
  
			} else if ( array instanceof Int16Array ) {
  
				type = gl.SHORT;
  
			} else if ( array instanceof Uint32Array ) {
  
				type = gl.UNSIGNED_INT;
  
			} else if ( array instanceof Int32Array ) {
  
				type = gl.INT;
  
			} else if ( array instanceof Int8Array ) {
  
				type = gl.BYTE;
  
			} else if ( array instanceof Uint8Array ) {
  
				type = gl.UNSIGNED_BYTE;
  
			}
  
			return {
				buffer: buffer,
				type: type,
				bytesPerElement: array.BYTES_PER_ELEMENT,
				version: attribute.version
			};
  
		}
  
		function updateBuffer( buffer, attribute, bufferType ) {
  
			var array = attribute.array;
			var updateRange = attribute.updateRange;
  
			gl.bindBuffer( bufferType, buffer );
  
			if ( attribute.dynamic === false ) {
  
				gl.bufferData( bufferType, array, gl.STATIC_DRAW );
  
			} else if ( updateRange.count === - 1 ) {
  
				// Not using update ranges
  
				gl.bufferSubData( bufferType, 0, array );
  
			} else if ( updateRange.count === 0 ) {
  
				console.error( 'THREE.WebGLObjects.updateBuffer: dynamic THREE.BufferAttribute marked as needsUpdate but updateRange.count is 0, ensure you are using set methods or updating manually.' );
  
			} else {
  
				gl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT,
					array.subarray( updateRange.offset, updateRange.offset + updateRange.count ) );
  
				updateRange.count = - 1; // reset range
  
			}
  
		}
  
		//
  
		function get( attribute ) {
  
			if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;
  
			return buffers.get( attribute );
  
		}
  
		function remove( attribute ) {
  
			if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;
  
			var data = buffers.get( attribute );
  
			if ( data ) {
  
				gl.deleteBuffer( data.buffer );
  
				buffers.delete( attribute );
  
			}
  
		}
  
		function update( attribute, bufferType ) {
  
			if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;
  
			var data = buffers.get( attribute );
  
			if ( data === undefined ) {
  
				buffers.set( attribute, createBuffer( attribute, bufferType ) );
  
			} else if ( data.version < attribute.version ) {
  
				updateBuffer( data.buffer, attribute, bufferType );
  
				data.version = attribute.version;
  
			}
  
		}
  
		return {
  
			get: get,
			remove: remove,
			update: update
  
		};
  
	}
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 * @author WestLangley / http://github.com/WestLangley
	 * @author bhouston / http://clara.io
	 */
  
	function Euler( x, y, z, order ) {
  
		this._x = x || 0;
		this._y = y || 0;
		this._z = z || 0;
		this._order = order || Euler.DefaultOrder;
  
	}
  
	Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ];
  
	Euler.DefaultOrder = 'XYZ';
  
	Object.defineProperties( Euler.prototype, {
  
		x: {
  
			get: function () {
  
				return this._x;
  
			},
  
			set: function ( value ) {
  
				this._x = value;
				this.onChangeCallback();
  
			}
  
		},
  
		y: {
  
			get: function () {
  
				return this._y;
  
			},
  
			set: function ( value ) {
  
				this._y = value;
				this.onChangeCallback();
  
			}
  
		},
  
		z: {
  
			get: function () {
  
				return this._z;
  
			},
  
			set: function ( value ) {
  
				this._z = value;
				this.onChangeCallback();
  
			}
  
		},
  
		order: {
  
			get: function () {
  
				return this._order;
  
			},
  
			set: function ( value ) {
  
				this._order = value;
				this.onChangeCallback();
  
			}
  
		}
  
	} );
  
	Object.assign( Euler.prototype, {
  
		isEuler: true,
  
		set: function ( x, y, z, order ) {
  
			this._x = x;
			this._y = y;
			this._z = z;
			this._order = order || this._order;
  
			this.onChangeCallback();
  
			return this;
  
		},
  
		clone: function () {
  
			return new this.constructor( this._x, this._y, this._z, this._order );
  
		},
  
		copy: function ( euler ) {
  
			this._x = euler._x;
			this._y = euler._y;
			this._z = euler._z;
			this._order = euler._order;
  
			this.onChangeCallback();
  
			return this;
  
		},
  
		setFromRotationMatrix: function ( m, order, update ) {
  
			var clamp = _Math.clamp;
  
			// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
  
			var te = m.elements;
			var m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ];
			var m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ];
			var m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ];
  
			order = order || this._order;
  
			if ( order === 'XYZ' ) {
  
				this._y = Math.asin( clamp( m13, - 1, 1 ) );
  
				if ( Math.abs( m13 ) < 0.99999 ) {
  
					this._x = Math.atan2( - m23, m33 );
					this._z = Math.atan2( - m12, m11 );
  
				} else {
  
					this._x = Math.atan2( m32, m22 );
					this._z = 0;
  
				}
  
			} else if ( order === 'YXZ' ) {
  
				this._x = Math.asin( - clamp( m23, - 1, 1 ) );
  
				if ( Math.abs( m23 ) < 0.99999 ) {
  
					this._y = Math.atan2( m13, m33 );
					this._z = Math.atan2( m21, m22 );
  
				} else {
  
					this._y = Math.atan2( - m31, m11 );
					this._z = 0;
  
				}
  
			} else if ( order === 'ZXY' ) {
  
				this._x = Math.asin( clamp( m32, - 1, 1 ) );
  
				if ( Math.abs( m32 ) < 0.99999 ) {
  
					this._y = Math.atan2( - m31, m33 );
					this._z = Math.atan2( - m12, m22 );
  
				} else {
  
					this._y = 0;
					this._z = Math.atan2( m21, m11 );
  
				}
  
			} else if ( order === 'ZYX' ) {
  
				this._y = Math.asin( - clamp( m31, - 1, 1 ) );
  
				if ( Math.abs( m31 ) < 0.99999 ) {
  
					this._x = Math.atan2( m32, m33 );
					this._z = Math.atan2( m21, m11 );
  
				} else {
  
					this._x = 0;
					this._z = Math.atan2( - m12, m22 );
  
				}
  
			} else if ( order === 'YZX' ) {
  
				this._z = Math.asin( clamp( m21, - 1, 1 ) );
  
				if ( Math.abs( m21 ) < 0.99999 ) {
  
					this._x = Math.atan2( - m23, m22 );
					this._y = Math.atan2( - m31, m11 );
  
				} else {
  
					this._x = 0;
					this._y = Math.atan2( m13, m33 );
  
				}
  
			} else if ( order === 'XZY' ) {
  
				this._z = Math.asin( - clamp( m12, - 1, 1 ) );
  
				if ( Math.abs( m12 ) < 0.99999 ) {
  
					this._x = Math.atan2( m32, m22 );
					this._y = Math.atan2( m13, m11 );
  
				} else {
  
					this._x = Math.atan2( - m23, m33 );
					this._y = 0;
  
				}
  
			} else {
  
				console.warn( 'THREE.Euler: .setFromRotationMatrix() given unsupported order: ' + order );
  
			}
  
			this._order = order;
  
			if ( update !== false ) this.onChangeCallback();
  
			return this;
  
		},
  
		setFromQuaternion: function () {
  
			var matrix = new Matrix4();
  
			return function setFromQuaternion( q, order, update ) {
  
				matrix.makeRotationFromQuaternion( q );
  
				return this.setFromRotationMatrix( matrix, order, update );
  
			};
  
		}(),
  
		setFromVector3: function ( v, order ) {
  
			return this.set( v.x, v.y, v.z, order || this._order );
  
		},
  
		reorder: function () {
  
			// WARNING: this discards revolution information -bhouston
  
			var q = new Quaternion();
  
			return function reorder( newOrder ) {
  
				q.setFromEuler( this );
  
				return this.setFromQuaternion( q, newOrder );
  
			};
  
		}(),
  
		equals: function ( euler ) {
  
			return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order );
  
		},
  
		fromArray: function ( array ) {
  
			this._x = array[ 0 ];
			this._y = array[ 1 ];
			this._z = array[ 2 ];
			if ( array[ 3 ] !== undefined ) this._order = array[ 3 ];
  
			this.onChangeCallback();
  
			return this;
  
		},
  
		toArray: function ( array, offset ) {
  
			if ( array === undefined ) array = [];
			if ( offset === undefined ) offset = 0;
  
			array[ offset ] = this._x;
			array[ offset + 1 ] = this._y;
			array[ offset + 2 ] = this._z;
			array[ offset + 3 ] = this._order;
  
			return array;
  
		},
  
		toVector3: function ( optionalResult ) {
  
			if ( optionalResult ) {
  
				return optionalResult.set( this._x, this._y, this._z );
  
			} else {
  
				return new Vector3( this._x, this._y, this._z );
  
			}
  
		},
  
		onChange: function ( callback ) {
  
			this.onChangeCallback = callback;
  
			return this;
  
		},
  
		onChangeCallback: function () {}
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function Layers() {
  
		this.mask = 1 | 0;
  
	}
  
	Object.assign( Layers.prototype, {
  
		set: function ( channel ) {
  
			this.mask = 1 << channel | 0;
  
		},
  
		enable: function ( channel ) {
  
			this.mask |= 1 << channel | 0;
  
		},
  
		toggle: function ( channel ) {
  
			this.mask ^= 1 << channel | 0;
  
		},
  
		disable: function ( channel ) {
  
			this.mask &= ~ ( 1 << channel | 0 );
  
		},
  
		test: function ( layers ) {
  
			return ( this.mask & layers.mask ) !== 0;
  
		}
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 * @author mikael emtinger / http://gomo.se/
	 * @author alteredq / http://alteredqualia.com/
	 * @author WestLangley / http://github.com/WestLangley
	 * @author elephantatwork / www.elephantatwork.ch
	 */
  
	var object3DId = 0;
  
	function Object3D() {
  
		Object.defineProperty( this, 'id', { value: object3DId ++ } );
  
		this.uuid = _Math.generateUUID();
  
		this.name = '';
		this.type = 'Object3D';
  
		this.parent = null;
		this.children = [];
  
		this.up = Object3D.DefaultUp.clone();
  
		var position = new Vector3();
		var rotation = new Euler();
		var quaternion = new Quaternion();
		var scale = new Vector3( 1, 1, 1 );
  
		function onRotationChange() {
  
			quaternion.setFromEuler( rotation, false );
  
		}
  
		function onQuaternionChange() {
  
			rotation.setFromQuaternion( quaternion, undefined, false );
  
		}
  
		rotation.onChange( onRotationChange );
		quaternion.onChange( onQuaternionChange );
  
		Object.defineProperties( this, {
			position: {
				enumerable: true,
				value: position
			},
			rotation: {
				enumerable: true,
				value: rotation
			},
			quaternion: {
				enumerable: true,
				value: quaternion
			},
			scale: {
				enumerable: true,
				value: scale
			},
			modelViewMatrix: {
				value: new Matrix4()
			},
			normalMatrix: {
				value: new Matrix3()
			}
		} );
  
		this.matrix = new Matrix4();
		this.matrixWorld = new Matrix4();
  
		this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate;
		this.matrixWorldNeedsUpdate = false;
  
		this.layers = new Layers();
		this.visible = true;
  
		this.castShadow = false;
		this.receiveShadow = false;
  
		this.frustumCulled = true;
		this.renderOrder = 0;
  
		this.userData = {};
  
	}
  
	Object3D.DefaultUp = new Vector3( 0, 1, 0 );
	Object3D.DefaultMatrixAutoUpdate = true;
  
	Object3D.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {
  
		constructor: Object3D,
  
		isObject3D: true,
  
		onBeforeRender: function () {},
		onAfterRender: function () {},
  
		applyMatrix: function ( matrix ) {
  
			this.matrix.multiplyMatrices( matrix, this.matrix );
  
			this.matrix.decompose( this.position, this.quaternion, this.scale );
  
		},
  
		applyQuaternion: function ( q ) {
  
			this.quaternion.premultiply( q );
  
			return this;
  
		},
  
		setRotationFromAxisAngle: function ( axis, angle ) {
  
			// assumes axis is normalized
  
			this.quaternion.setFromAxisAngle( axis, angle );
  
		},
  
		setRotationFromEuler: function ( euler ) {
  
			this.quaternion.setFromEuler( euler, true );
  
		},
  
		setRotationFromMatrix: function ( m ) {
  
			// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
  
			this.quaternion.setFromRotationMatrix( m );
  
		},
  
		setRotationFromQuaternion: function ( q ) {
  
			// assumes q is normalized
  
			this.quaternion.copy( q );
  
		},
  
		rotateOnAxis: function () {
  
			// rotate object on axis in object space
			// axis is assumed to be normalized
  
			var q1 = new Quaternion();
  
			return function rotateOnAxis( axis, angle ) {
  
				q1.setFromAxisAngle( axis, angle );
  
				this.quaternion.multiply( q1 );
  
				return this;
  
			};
  
		}(),
  
		rotateOnWorldAxis: function () {
  
			// rotate object on axis in world space
			// axis is assumed to be normalized
			// method assumes no rotated parent
  
			var q1 = new Quaternion();
  
			return function rotateOnWorldAxis( axis, angle ) {
  
				q1.setFromAxisAngle( axis, angle );
  
				this.quaternion.premultiply( q1 );
  
				return this;
  
			};
  
		}(),
  
		rotateX: function () {
  
			var v1 = new Vector3( 1, 0, 0 );
  
			return function rotateX( angle ) {
  
				return this.rotateOnAxis( v1, angle );
  
			};
  
		}(),
  
		rotateY: function () {
  
			var v1 = new Vector3( 0, 1, 0 );
  
			return function rotateY( angle ) {
  
				return this.rotateOnAxis( v1, angle );
  
			};
  
		}(),
  
		rotateZ: function () {
  
			var v1 = new Vector3( 0, 0, 1 );
  
			return function rotateZ( angle ) {
  
				return this.rotateOnAxis( v1, angle );
  
			};
  
		}(),
  
		translateOnAxis: function () {
  
			// translate object by distance along axis in object space
			// axis is assumed to be normalized
  
			var v1 = new Vector3();
  
			return function translateOnAxis( axis, distance ) {
  
				v1.copy( axis ).applyQuaternion( this.quaternion );
  
				this.position.add( v1.multiplyScalar( distance ) );
  
				return this;
  
			};
  
		}(),
  
		translateX: function () {
  
			var v1 = new Vector3( 1, 0, 0 );
  
			return function translateX( distance ) {
  
				return this.translateOnAxis( v1, distance );
  
			};
  
		}(),
  
		translateY: function () {
  
			var v1 = new Vector3( 0, 1, 0 );
  
			return function translateY( distance ) {
  
				return this.translateOnAxis( v1, distance );
  
			};
  
		}(),
  
		translateZ: function () {
  
			var v1 = new Vector3( 0, 0, 1 );
  
			return function translateZ( distance ) {
  
				return this.translateOnAxis( v1, distance );
  
			};
  
		}(),
  
		localToWorld: function ( vector ) {
  
			return vector.applyMatrix4( this.matrixWorld );
  
		},
  
		worldToLocal: function () {
  
			var m1 = new Matrix4();
  
			return function worldToLocal( vector ) {
  
				return vector.applyMatrix4( m1.getInverse( this.matrixWorld ) );
  
			};
  
		}(),
  
		lookAt: function () {
  
			// This method does not support objects with rotated and/or translated parent(s)
  
			var m1 = new Matrix4();
			var vector = new Vector3();
  
			return function lookAt( x, y, z ) {
  
				if ( x.isVector3 ) {
  
					vector.copy( x );
  
				} else {
  
					vector.set( x, y, z );
  
				}
  
				if ( this.isCamera ) {
  
					m1.lookAt( this.position, vector, this.up );
  
				} else {
  
					m1.lookAt( vector, this.position, this.up );
  
				}
  
				this.quaternion.setFromRotationMatrix( m1 );
  
			};
  
		}(),
  
		add: function ( object ) {
  
			if ( arguments.length > 1 ) {
  
				for ( var i = 0; i < arguments.length; i ++ ) {
  
					this.add( arguments[ i ] );
  
				}
  
				return this;
  
			}
  
			if ( object === this ) {
  
				console.error( "THREE.Object3D.add: object can't be added as a child of itself.", object );
				return this;
  
			}
  
			if ( ( object && object.isObject3D ) ) {
  
				if ( object.parent !== null ) {
  
					object.parent.remove( object );
  
				}
  
				object.parent = this;
				object.dispatchEvent( { type: 'added' } );
  
				this.children.push( object );
  
			} else {
  
				console.error( "THREE.Object3D.add: object not an instance of THREE.Object3D.", object );
  
			}
  
			return this;
  
		},
  
		remove: function ( object ) {
  
			if ( arguments.length > 1 ) {
  
				for ( var i = 0; i < arguments.length; i ++ ) {
  
					this.remove( arguments[ i ] );
  
				}
  
				return this;
  
			}
  
			var index = this.children.indexOf( object );
  
			if ( index !== - 1 ) {
  
				object.parent = null;
  
				object.dispatchEvent( { type: 'removed' } );
  
				this.children.splice( index, 1 );
  
			}
  
			return this;
  
		},
  
		getObjectById: function ( id ) {
  
			return this.getObjectByProperty( 'id', id );
  
		},
  
		getObjectByName: function ( name ) {
  
			return this.getObjectByProperty( 'name', name );
  
		},
  
		getObjectByProperty: function ( name, value ) {
  
			if ( this[ name ] === value ) return this;
  
			for ( var i = 0, l = this.children.length; i < l; i ++ ) {
  
				var child = this.children[ i ];
				var object = child.getObjectByProperty( name, value );
  
				if ( object !== undefined ) {
  
					return object;
  
				}
  
			}
  
			return undefined;
  
		},
  
		getWorldPosition: function ( target ) {
  
			if ( target === undefined ) {
  
				console.warn( 'THREE.Object3D: .getWorldPosition() target is now required' );
				target = new Vector3();
  
			}
  
			this.updateMatrixWorld( true );
  
			return target.setFromMatrixPosition( this.matrixWorld );
  
		},
  
		getWorldQuaternion: function () {
  
			var position = new Vector3();
			var scale = new Vector3();
  
			return function getWorldQuaternion( target ) {
  
				if ( target === undefined ) {
  
					console.warn( 'THREE.Object3D: .getWorldQuaternion() target is now required' );
					target = new Quaternion();
  
				}
  
				this.updateMatrixWorld( true );
  
				this.matrixWorld.decompose( position, target, scale );
  
				return target;
  
			};
  
		}(),
  
		getWorldScale: function () {
  
			var position = new Vector3();
			var quaternion = new Quaternion();
  
			return function getWorldScale( target ) {
  
				if ( target === undefined ) {
  
					console.warn( 'THREE.Object3D: .getWorldScale() target is now required' );
					target = new Vector3();
  
				}
  
				this.updateMatrixWorld( true );
  
				this.matrixWorld.decompose( position, quaternion, target );
  
				return target;
  
			};
  
		}(),
  
		getWorldDirection: function () {
  
			var quaternion = new Quaternion();
  
			return function getWorldDirection( target ) {
  
				if ( target === undefined ) {
  
					console.warn( 'THREE.Object3D: .getWorldDirection() target is now required' );
					target = new Vector3();
  
				}
  
				this.getWorldQuaternion( quaternion );
  
				return target.set( 0, 0, 1 ).applyQuaternion( quaternion );
  
			};
  
		}(),
  
		raycast: function () {},
  
		traverse: function ( callback ) {
  
			callback( this );
  
			var children = this.children;
  
			for ( var i = 0, l = children.length; i < l; i ++ ) {
  
				children[ i ].traverse( callback );
  
			}
  
		},
  
		traverseVisible: function ( callback ) {
  
			if ( this.visible === false ) return;
  
			callback( this );
  
			var children = this.children;
  
			for ( var i = 0, l = children.length; i < l; i ++ ) {
  
				children[ i ].traverseVisible( callback );
  
			}
  
		},
  
		traverseAncestors: function ( callback ) {
  
			var parent = this.parent;
  
			if ( parent !== null ) {
  
				callback( parent );
  
				parent.traverseAncestors( callback );
  
			}
  
		},
  
		updateMatrix: function () {
  
			this.matrix.compose( this.position, this.quaternion, this.scale );
  
			this.matrixWorldNeedsUpdate = true;
  
		},
  
		updateMatrixWorld: function ( force ) {
  
			if ( this.matrixAutoUpdate ) this.updateMatrix();
  
			if ( this.matrixWorldNeedsUpdate || force ) {
  
				if ( this.parent === null ) {
  
					this.matrixWorld.copy( this.matrix );
  
				} else {
  
					this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );
  
				}
  
				this.matrixWorldNeedsUpdate = false;
  
				force = true;
  
			}
  
			// update children
  
			var children = this.children;
  
			for ( var i = 0, l = children.length; i < l; i ++ ) {
  
				children[ i ].updateMatrixWorld( force );
  
			}
  
		},
  
		toJSON: function ( meta ) {
  
			// meta is a string when called from JSON.stringify
			var isRootObject = ( meta === undefined || typeof meta === 'string' );
  
			var output = {};
  
			// meta is a hash used to collect geometries, materials.
			// not providing it implies that this is the root object
			// being serialized.
			if ( isRootObject ) {
  
				// initialize meta obj
				meta = {
					geometries: {},
					materials: {},
					textures: {},
					images: {},
					shapes: {}
				};
  
				output.metadata = {
					version: 4.5,
					type: 'Object',
					generator: 'Object3D.toJSON'
				};
  
			}
  
			// standard Object3D serialization
  
			var object = {};
  
			object.uuid = this.uuid;
			object.type = this.type;
  
			if ( this.name !== '' ) object.name = this.name;
			if ( this.castShadow === true ) object.castShadow = true;
			if ( this.receiveShadow === true ) object.receiveShadow = true;
			if ( this.visible === false ) object.visible = false;
			if ( this.frustumCulled === false ) object.frustumCulled = false;
			if ( this.renderOrder !== 0 ) object.renderOrder = this.renderOrder;
			if ( JSON.stringify( this.userData ) !== '{}' ) object.userData = this.userData;
  
			object.matrix = this.matrix.toArray();
  
			if ( this.matrixAutoUpdate === false ) object.matrixAutoUpdate = false;
  
			//
  
			function serialize( library, element ) {
  
				if ( library[ element.uuid ] === undefined ) {
  
					library[ element.uuid ] = element.toJSON( meta );
  
				}
  
				return element.uuid;
  
			}
  
			if ( this.geometry !== undefined ) {
  
				object.geometry = serialize( meta.geometries, this.geometry );
  
				var parameters = this.geometry.parameters;
  
				if ( parameters !== undefined && parameters.shapes !== undefined ) {
  
					var shapes = parameters.shapes;
  
					if ( Array.isArray( shapes ) ) {
  
						for ( var i = 0, l = shapes.length; i < l; i ++ ) {
  
							var shape = shapes[ i ];
  
							serialize( meta.shapes, shape );
  
						}
  
					} else {
  
						serialize( meta.shapes, shapes );
  
					}
  
				}
  
			}
  
			if ( this.material !== undefined ) {
  
				if ( Array.isArray( this.material ) ) {
  
					var uuids = [];
  
					for ( var i = 0, l = this.material.length; i < l; i ++ ) {
  
						uuids.push( serialize( meta.materials, this.material[ i ] ) );
  
					}
  
					object.material = uuids;
  
				} else {
  
					object.material = serialize( meta.materials, this.material );
  
				}
  
			}
  
			//
  
			if ( this.children.length > 0 ) {
  
				object.children = [];
  
				for ( var i = 0; i < this.children.length; i ++ ) {
  
					object.children.push( this.children[ i ].toJSON( meta ).object );
  
				}
  
			}
  
			if ( isRootObject ) {
  
				var geometries = extractFromCache( meta.geometries );
				var materials = extractFromCache( meta.materials );
				var textures = extractFromCache( meta.textures );
				var images = extractFromCache( meta.images );
				var shapes = extractFromCache( meta.shapes );
  
				if ( geometries.length > 0 ) output.geometries = geometries;
				if ( materials.length > 0 ) output.materials = materials;
				if ( textures.length > 0 ) output.textures = textures;
				if ( images.length > 0 ) output.images = images;
				if ( shapes.length > 0 ) output.shapes = shapes;
  
			}
  
			output.object = object;
  
			return output;
  
			// extract data from the cache hash
			// remove metadata on each item
			// and return as array
			function extractFromCache( cache ) {
  
				var values = [];
				for ( var key in cache ) {
  
					var data = cache[ key ];
					delete data.metadata;
					values.push( data );
  
				}
				return values;
  
			}
  
		},
  
		clone: function ( recursive ) {
  
			return new this.constructor().copy( this, recursive );
  
		},
  
		copy: function ( source, recursive ) {
  
			if ( recursive === undefined ) recursive = true;
  
			this.name = source.name;
  
			this.up.copy( source.up );
  
			this.position.copy( source.position );
			this.quaternion.copy( source.quaternion );
			this.scale.copy( source.scale );
  
			this.matrix.copy( source.matrix );
			this.matrixWorld.copy( source.matrixWorld );
  
			this.matrixAutoUpdate = source.matrixAutoUpdate;
			this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate;
  
			this.layers.mask = source.layers.mask;
			this.visible = source.visible;
  
			this.castShadow = source.castShadow;
			this.receiveShadow = source.receiveShadow;
  
			this.frustumCulled = source.frustumCulled;
			this.renderOrder = source.renderOrder;
  
			this.userData = JSON.parse( JSON.stringify( source.userData ) );
  
			if ( recursive === true ) {
  
				for ( var i = 0; i < source.children.length; i ++ ) {
  
					var child = source.children[ i ];
					this.add( child.clone() );
  
				}
  
			}
  
			return this;
  
		}
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 * @author mikael emtinger / http://gomo.se/
	 * @author WestLangley / http://github.com/WestLangley
	*/
  
	function Camera() {
  
		Object3D.call( this );
  
		this.type = 'Camera';
  
		this.matrixWorldInverse = new Matrix4();
		this.projectionMatrix = new Matrix4();
  
	}
  
	Camera.prototype = Object.assign( Object.create( Object3D.prototype ), {
  
		constructor: Camera,
  
		isCamera: true,
  
		copy: function ( source, recursive ) {
  
			Object3D.prototype.copy.call( this, source, recursive );
  
			this.matrixWorldInverse.copy( source.matrixWorldInverse );
			this.projectionMatrix.copy( source.projectionMatrix );
  
			return this;
  
		},
  
		getWorldDirection: function () {
  
			var quaternion = new Quaternion();
  
			return function getWorldDirection( target ) {
  
				if ( target === undefined ) {
  
					console.warn( 'THREE.Camera: .getWorldDirection() target is now required' );
					target = new Vector3();
  
				}
  
				this.getWorldQuaternion( quaternion );
  
				return target.set( 0, 0, - 1 ).applyQuaternion( quaternion );
  
			};
  
		}(),
  
		updateMatrixWorld: function ( force ) {
  
			Object3D.prototype.updateMatrixWorld.call( this, force );
  
			this.matrixWorldInverse.getInverse( this.matrixWorld );
  
		},
  
		clone: function () {
  
			return new this.constructor().copy( this );
  
		}
  
	} );
  
	/**
	 * @author alteredq / http://alteredqualia.com/
	 * @author arose / http://github.com/arose
	 */
  
	function OrthographicCamera( left, right, top, bottom, near, far ) {
  
		Camera.call( this );
  
		this.type = 'OrthographicCamera';
  
		this.zoom = 1;
		this.view = null;
  
		this.left = left;
		this.right = right;
		this.top = top;
		this.bottom = bottom;
  
		this.near = ( near !== undefined ) ? near : 0.1;
		this.far = ( far !== undefined ) ? far : 2000;
  
		this.updateProjectionMatrix();
  
	}
  
	OrthographicCamera.prototype = Object.assign( Object.create( Camera.prototype ), {
  
		constructor: OrthographicCamera,
  
		isOrthographicCamera: true,
  
		copy: function ( source, recursive ) {
  
			Camera.prototype.copy.call( this, source, recursive );
  
			this.left = source.left;
			this.right = source.right;
			this.top = source.top;
			this.bottom = source.bottom;
			this.near = source.near;
			this.far = source.far;
  
			this.zoom = source.zoom;
			this.view = source.view === null ? null : Object.assign( {}, source.view );
  
			return this;
  
		},
  
		setViewOffset: function ( fullWidth, fullHeight, x, y, width, height ) {
  
			if ( this.view === null ) {
  
				this.view = {
					enabled: true,
					fullWidth: 1,
					fullHeight: 1,
					offsetX: 0,
					offsetY: 0,
					width: 1,
					height: 1
				};
  
			}
  
			this.view.enabled = true;
			this.view.fullWidth = fullWidth;
			this.view.fullHeight = fullHeight;
			this.view.offsetX = x;
			this.view.offsetY = y;
			this.view.width = width;
			this.view.height = height;
  
			this.updateProjectionMatrix();
  
		},
  
		clearViewOffset: function () {
  
			if ( this.view !== null ) {
  
				this.view.enabled = false;
  
			}
  
			this.updateProjectionMatrix();
  
		},
  
		updateProjectionMatrix: function () {
  
			var dx = ( this.right - this.left ) / ( 2 * this.zoom );
			var dy = ( this.top - this.bottom ) / ( 2 * this.zoom );
			var cx = ( this.right + this.left ) / 2;
			var cy = ( this.top + this.bottom ) / 2;
  
			var left = cx - dx;
			var right = cx + dx;
			var top = cy + dy;
			var bottom = cy - dy;
  
			if ( this.view !== null && this.view.enabled ) {
  
				var zoomW = this.zoom / ( this.view.width / this.view.fullWidth );
				var zoomH = this.zoom / ( this.view.height / this.view.fullHeight );
				var scaleW = ( this.right - this.left ) / this.view.width;
				var scaleH = ( this.top - this.bottom ) / this.view.height;
  
				left += scaleW * ( this.view.offsetX / zoomW );
				right = left + scaleW * ( this.view.width / zoomW );
				top -= scaleH * ( this.view.offsetY / zoomH );
				bottom = top - scaleH * ( this.view.height / zoomH );
  
			}
  
			this.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far );
  
		},
  
		toJSON: function ( meta ) {
  
			var data = Object3D.prototype.toJSON.call( this, meta );
  
			data.object.zoom = this.zoom;
			data.object.left = this.left;
			data.object.right = this.right;
			data.object.top = this.top;
			data.object.bottom = this.bottom;
			data.object.near = this.near;
			data.object.far = this.far;
  
			if ( this.view !== null ) data.object.view = Object.assign( {}, this.view );
  
			return data;
  
		}
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 * @author alteredq / http://alteredqualia.com/
	 */
  
	function Face3( a, b, c, normal, color, materialIndex ) {
  
		this.a = a;
		this.b = b;
		this.c = c;
  
		this.normal = ( normal && normal.isVector3 ) ? normal : new Vector3();
		this.vertexNormals = Array.isArray( normal ) ? normal : [];
  
		this.color = ( color && color.isColor ) ? color : new Color();
		this.vertexColors = Array.isArray( color ) ? color : [];
  
		this.materialIndex = materialIndex !== undefined ? materialIndex : 0;
  
	}
  
	Object.assign( Face3.prototype, {
  
		clone: function () {
  
			return new this.constructor().copy( this );
  
		},
  
		copy: function ( source ) {
  
			this.a = source.a;
			this.b = source.b;
			this.c = source.c;
  
			this.normal.copy( source.normal );
			this.color.copy( source.color );
  
			this.materialIndex = source.materialIndex;
  
			for ( var i = 0, il = source.vertexNormals.length; i < il; i ++ ) {
  
				this.vertexNormals[ i ] = source.vertexNormals[ i ].clone();
  
			}
  
			for ( var i = 0, il = source.vertexColors.length; i < il; i ++ ) {
  
				this.vertexColors[ i ] = source.vertexColors[ i ].clone();
  
			}
  
			return this;
  
		}
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 * @author kile / http://kile.stravaganza.org/
	 * @author alteredq / http://alteredqualia.com/
	 * @author mikael emtinger / http://gomo.se/
	 * @author zz85 / http://www.lab4games.net/zz85/blog
	 * @author bhouston / http://clara.io
	 */
  
	var geometryId = 0; // Geometry uses even numbers as Id
  
	function Geometry() {
  
		Object.defineProperty( this, 'id', { value: geometryId += 2 } );
  
		this.uuid = _Math.generateUUID();
  
		this.name = '';
		this.type = 'Geometry';
  
		this.vertices = [];
		this.colors = [];
		this.faces = [];
		this.faceVertexUvs = [[]];
  
		this.morphTargets = [];
		this.morphNormals = [];
  
		this.skinWeights = [];
		this.skinIndices = [];
  
		this.lineDistances = [];
  
		this.boundingBox = null;
		this.boundingSphere = null;
  
		// update flags
  
		this.elementsNeedUpdate = false;
		this.verticesNeedUpdate = false;
		this.uvsNeedUpdate = false;
		this.normalsNeedUpdate = false;
		this.colorsNeedUpdate = false;
		this.lineDistancesNeedUpdate = false;
		this.groupsNeedUpdate = false;
  
	}
  
	Geometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {
  
		constructor: Geometry,
  
		isGeometry: true,
  
		applyMatrix: function ( matrix ) {
  
			var normalMatrix = new Matrix3().getNormalMatrix( matrix );
  
			for ( var i = 0, il = this.vertices.length; i < il; i ++ ) {
  
				var vertex = this.vertices[ i ];
				vertex.applyMatrix4( matrix );
  
			}
  
			for ( var i = 0, il = this.faces.length; i < il; i ++ ) {
  
				var face = this.faces[ i ];
				face.normal.applyMatrix3( normalMatrix ).normalize();
  
				for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) {
  
					face.vertexNormals[ j ].applyMatrix3( normalMatrix ).normalize();
  
				}
  
			}
  
			if ( this.boundingBox !== null ) {
  
				this.computeBoundingBox();
  
			}
  
			if ( this.boundingSphere !== null ) {
  
				this.computeBoundingSphere();
  
			}
  
			this.verticesNeedUpdate = true;
			this.normalsNeedUpdate = true;
  
			return this;
  
		},
  
		rotateX: function () {
  
			// rotate geometry around world x-axis
  
			var m1 = new Matrix4();
  
			return function rotateX( angle ) {
  
				m1.makeRotationX( angle );
  
				this.applyMatrix( m1 );
  
				return this;
  
			};
  
		}(),
  
		rotateY: function () {
  
			// rotate geometry around world y-axis
  
			var m1 = new Matrix4();
  
			return function rotateY( angle ) {
  
				m1.makeRotationY( angle );
  
				this.applyMatrix( m1 );
  
				return this;
  
			};
  
		}(),
  
		rotateZ: function () {
  
			// rotate geometry around world z-axis
  
			var m1 = new Matrix4();
  
			return function rotateZ( angle ) {
  
				m1.makeRotationZ( angle );
  
				this.applyMatrix( m1 );
  
				return this;
  
			};
  
		}(),
  
		translate: function () {
  
			// translate geometry
  
			var m1 = new Matrix4();
  
			return function translate( x, y, z ) {
  
				m1.makeTranslation( x, y, z );
  
				this.applyMatrix( m1 );
  
				return this;
  
			};
  
		}(),
  
		scale: function () {
  
			// scale geometry
  
			var m1 = new Matrix4();
  
			return function scale( x, y, z ) {
  
				m1.makeScale( x, y, z );
  
				this.applyMatrix( m1 );
  
				return this;
  
			};
  
		}(),
  
		lookAt: function () {
  
			var obj = new Object3D();
  
			return function lookAt( vector ) {
  
				obj.lookAt( vector );
  
				obj.updateMatrix();
  
				this.applyMatrix( obj.matrix );
  
			};
  
		}(),
  
		fromBufferGeometry: function ( geometry ) {
  
			var scope = this;
  
			var indices = geometry.index !== null ? geometry.index.array : undefined;
			var attributes = geometry.attributes;
  
			var positions = attributes.position.array;
			var normals = attributes.normal !== undefined ? attributes.normal.array : undefined;
			var colors = attributes.color !== undefined ? attributes.color.array : undefined;
			var uvs = attributes.uv !== undefined ? attributes.uv.array : undefined;
			var uvs2 = attributes.uv2 !== undefined ? attributes.uv2.array : undefined;
  
			if ( uvs2 !== undefined ) this.faceVertexUvs[ 1 ] = [];
  
			var tempNormals = [];
			var tempUVs = [];
			var tempUVs2 = [];
  
			for ( var i = 0, j = 0; i < positions.length; i += 3, j += 2 ) {
  
				scope.vertices.push( new Vector3( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ) );
  
				if ( normals !== undefined ) {
  
					tempNormals.push( new Vector3( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] ) );
  
				}
  
				if ( colors !== undefined ) {
  
					scope.colors.push( new Color( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ) );
  
				}
  
				if ( uvs !== undefined ) {
  
					tempUVs.push( new Vector2( uvs[ j ], uvs[ j + 1 ] ) );
  
				}
  
				if ( uvs2 !== undefined ) {
  
					tempUVs2.push( new Vector2( uvs2[ j ], uvs2[ j + 1 ] ) );
  
				}
  
			}
  
			function addFace( a, b, c, materialIndex ) {
  
				var vertexNormals = normals !== undefined ? [ tempNormals[ a ].clone(), tempNormals[ b ].clone(), tempNormals[ c ].clone() ] : [];
				var vertexColors = colors !== undefined ? [ scope.colors[ a ].clone(), scope.colors[ b ].clone(), scope.colors[ c ].clone() ] : [];
  
				var face = new Face3( a, b, c, vertexNormals, vertexColors, materialIndex );
  
				scope.faces.push( face );
  
				if ( uvs !== undefined ) {
  
					scope.faceVertexUvs[ 0 ].push( [ tempUVs[ a ].clone(), tempUVs[ b ].clone(), tempUVs[ c ].clone() ] );
  
				}
  
				if ( uvs2 !== undefined ) {
  
					scope.faceVertexUvs[ 1 ].push( [ tempUVs2[ a ].clone(), tempUVs2[ b ].clone(), tempUVs2[ c ].clone() ] );
  
				}
  
			}
  
			var groups = geometry.groups;
  
			if ( groups.length > 0 ) {
  
				for ( var i = 0; i < groups.length; i ++ ) {
  
					var group = groups[ i ];
  
					var start = group.start;
					var count = group.count;
  
					for ( var j = start, jl = start + count; j < jl; j += 3 ) {
  
						if ( indices !== undefined ) {
  
							addFace( indices[ j ], indices[ j + 1 ], indices[ j + 2 ], group.materialIndex );
  
						} else {
  
							addFace( j, j + 1, j + 2, group.materialIndex );
  
						}
  
					}
  
				}
  
			} else {
  
				if ( indices !== undefined ) {
  
					for ( var i = 0; i < indices.length; i += 3 ) {
  
						addFace( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] );
  
					}
  
				} else {
  
					for ( var i = 0; i < positions.length / 3; i += 3 ) {
  
						addFace( i, i + 1, i + 2 );
  
					}
  
				}
  
			}
  
			this.computeFaceNormals();
  
			if ( geometry.boundingBox !== null ) {
  
				this.boundingBox = geometry.boundingBox.clone();
  
			}
  
			if ( geometry.boundingSphere !== null ) {
  
				this.boundingSphere = geometry.boundingSphere.clone();
  
			}
  
			return this;
  
		},
  
		center: function () {
  
			var offset = new Vector3();
  
			return function center() {
  
				this.computeBoundingBox();
  
				this.boundingBox.getCenter( offset ).negate();
  
				this.translate( offset.x, offset.y, offset.z );
  
				return this;
  
			};
  
		}(),
  
		normalize: function () {
  
			this.computeBoundingSphere();
  
			var center = this.boundingSphere.center;
			var radius = this.boundingSphere.radius;
  
			var s = radius === 0 ? 1 : 1.0 / radius;
  
			var matrix = new Matrix4();
			matrix.set(
				s, 0, 0, - s * center.x,
				0, s, 0, - s * center.y,
				0, 0, s, - s * center.z,
				0, 0, 0, 1
			);
  
			this.applyMatrix( matrix );
  
			return this;
  
		},
  
		computeFaceNormals: function () {
  
			var cb = new Vector3(), ab = new Vector3();
  
			for ( var f = 0, fl = this.faces.length; f < fl; f ++ ) {
  
				var face = this.faces[ f ];
  
				var vA = this.vertices[ face.a ];
				var vB = this.vertices[ face.b ];
				var vC = this.vertices[ face.c ];
  
				cb.subVectors( vC, vB );
				ab.subVectors( vA, vB );
				cb.cross( ab );
  
				cb.normalize();
  
				face.normal.copy( cb );
  
			}
  
		},
  
		computeVertexNormals: function ( areaWeighted ) {
  
			if ( areaWeighted === undefined ) areaWeighted = true;
  
			var v, vl, f, fl, face, vertices;
  
			vertices = new Array( this.vertices.length );
  
			for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) {
  
				vertices[ v ] = new Vector3();
  
			}
  
			if ( areaWeighted ) {
  
				// vertex normals weighted by triangle areas
				// http://www.iquilezles.org/www/articles/normals/normals.htm
  
				var vA, vB, vC;
				var cb = new Vector3(), ab = new Vector3();
  
				for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
  
					face = this.faces[ f ];
  
					vA = this.vertices[ face.a ];
					vB = this.vertices[ face.b ];
					vC = this.vertices[ face.c ];
  
					cb.subVectors( vC, vB );
					ab.subVectors( vA, vB );
					cb.cross( ab );
  
					vertices[ face.a ].add( cb );
					vertices[ face.b ].add( cb );
					vertices[ face.c ].add( cb );
  
				}
  
			} else {
  
				this.computeFaceNormals();
  
				for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
  
					face = this.faces[ f ];
  
					vertices[ face.a ].add( face.normal );
					vertices[ face.b ].add( face.normal );
					vertices[ face.c ].add( face.normal );
  
				}
  
			}
  
			for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) {
  
				vertices[ v ].normalize();
  
			}
  
			for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
  
				face = this.faces[ f ];
  
				var vertexNormals = face.vertexNormals;
  
				if ( vertexNormals.length === 3 ) {
  
					vertexNormals[ 0 ].copy( vertices[ face.a ] );
					vertexNormals[ 1 ].copy( vertices[ face.b ] );
					vertexNormals[ 2 ].copy( vertices[ face.c ] );
  
				} else {
  
					vertexNormals[ 0 ] = vertices[ face.a ].clone();
					vertexNormals[ 1 ] = vertices[ face.b ].clone();
					vertexNormals[ 2 ] = vertices[ face.c ].clone();
  
				}
  
			}
  
			if ( this.faces.length > 0 ) {
  
				this.normalsNeedUpdate = true;
  
			}
  
		},
  
		computeFlatVertexNormals: function () {
  
			var f, fl, face;
  
			this.computeFaceNormals();
  
			for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
  
				face = this.faces[ f ];
  
				var vertexNormals = face.vertexNormals;
  
				if ( vertexNormals.length === 3 ) {
  
					vertexNormals[ 0 ].copy( face.normal );
					vertexNormals[ 1 ].copy( face.normal );
					vertexNormals[ 2 ].copy( face.normal );
  
				} else {
  
					vertexNormals[ 0 ] = face.normal.clone();
					vertexNormals[ 1 ] = face.normal.clone();
					vertexNormals[ 2 ] = face.normal.clone();
  
				}
  
			}
  
			if ( this.faces.length > 0 ) {
  
				this.normalsNeedUpdate = true;
  
			}
  
		},
  
		computeMorphNormals: function () {
  
			var i, il, f, fl, face;
  
			// save original normals
			// - create temp variables on first access
			//   otherwise just copy (for faster repeated calls)
  
			for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
  
				face = this.faces[ f ];
  
				if ( ! face.__originalFaceNormal ) {
  
					face.__originalFaceNormal = face.normal.clone();
  
				} else {
  
					face.__originalFaceNormal.copy( face.normal );
  
				}
  
				if ( ! face.__originalVertexNormals ) face.__originalVertexNormals = [];
  
				for ( i = 0, il = face.vertexNormals.length; i < il; i ++ ) {
  
					if ( ! face.__originalVertexNormals[ i ] ) {
  
						face.__originalVertexNormals[ i ] = face.vertexNormals[ i ].clone();
  
					} else {
  
						face.__originalVertexNormals[ i ].copy( face.vertexNormals[ i ] );
  
					}
  
				}
  
			}
  
			// use temp geometry to compute face and vertex normals for each morph
  
			var tmpGeo = new Geometry();
			tmpGeo.faces = this.faces;
  
			for ( i = 0, il = this.morphTargets.length; i < il; i ++ ) {
  
				// create on first access
  
				if ( ! this.morphNormals[ i ] ) {
  
					this.morphNormals[ i ] = {};
					this.morphNormals[ i ].faceNormals = [];
					this.morphNormals[ i ].vertexNormals = [];
  
					var dstNormalsFace = this.morphNormals[ i ].faceNormals;
					var dstNormalsVertex = this.morphNormals[ i ].vertexNormals;
  
					var faceNormal, vertexNormals;
  
					for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
  
						faceNormal = new Vector3();
						vertexNormals = { a: new Vector3(), b: new Vector3(), c: new Vector3() };
  
						dstNormalsFace.push( faceNormal );
						dstNormalsVertex.push( vertexNormals );
  
					}
  
				}
  
				var morphNormals = this.morphNormals[ i ];
  
				// set vertices to morph target
  
				tmpGeo.vertices = this.morphTargets[ i ].vertices;
  
				// compute morph normals
  
				tmpGeo.computeFaceNormals();
				tmpGeo.computeVertexNormals();
  
				// store morph normals
  
				var faceNormal, vertexNormals;
  
				for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
  
					face = this.faces[ f ];
  
					faceNormal = morphNormals.faceNormals[ f ];
					vertexNormals = morphNormals.vertexNormals[ f ];
  
					faceNormal.copy( face.normal );
  
					vertexNormals.a.copy( face.vertexNormals[ 0 ] );
					vertexNormals.b.copy( face.vertexNormals[ 1 ] );
					vertexNormals.c.copy( face.vertexNormals[ 2 ] );
  
				}
  
			}
  
			// restore original normals
  
			for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
  
				face = this.faces[ f ];
  
				face.normal = face.__originalFaceNormal;
				face.vertexNormals = face.__originalVertexNormals;
  
			}
  
		},
  
		computeBoundingBox: function () {
  
			if ( this.boundingBox === null ) {
  
				this.boundingBox = new Box3();
  
			}
  
			this.boundingBox.setFromPoints( this.vertices );
  
		},
  
		computeBoundingSphere: function () {
  
			if ( this.boundingSphere === null ) {
  
				this.boundingSphere = new Sphere();
  
			}
  
			this.boundingSphere.setFromPoints( this.vertices );
  
		},
  
		merge: function ( geometry, matrix, materialIndexOffset ) {
  
			if ( ! ( geometry && geometry.isGeometry ) ) {
  
				console.error( 'THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.', geometry );
				return;
  
			}
  
			var normalMatrix,
				vertexOffset = this.vertices.length,
				vertices1 = this.vertices,
				vertices2 = geometry.vertices,
				faces1 = this.faces,
				faces2 = geometry.faces,
				uvs1 = this.faceVertexUvs[ 0 ],
				uvs2 = geometry.faceVertexUvs[ 0 ],
				colors1 = this.colors,
				colors2 = geometry.colors;
  
			if ( materialIndexOffset === undefined ) materialIndexOffset = 0;
  
			if ( matrix !== undefined ) {
  
				normalMatrix = new Matrix3().getNormalMatrix( matrix );
  
			}
  
			// vertices
  
			for ( var i = 0, il = vertices2.length; i < il; i ++ ) {
  
				var vertex = vertices2[ i ];
  
				var vertexCopy = vertex.clone();
  
				if ( matrix !== undefined ) vertexCopy.applyMatrix4( matrix );
  
				vertices1.push( vertexCopy );
  
			}
  
			// colors
  
			for ( var i = 0, il = colors2.length; i < il; i ++ ) {
  
				colors1.push( colors2[ i ].clone() );
  
			}
  
			// faces
  
			for ( i = 0, il = faces2.length; i < il; i ++ ) {
  
				var face = faces2[ i ], faceCopy, normal, color,
					faceVertexNormals = face.vertexNormals,
					faceVertexColors = face.vertexColors;
  
				faceCopy = new Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset );
				faceCopy.normal.copy( face.normal );
  
				if ( normalMatrix !== undefined ) {
  
					faceCopy.normal.applyMatrix3( normalMatrix ).normalize();
  
				}
  
				for ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) {
  
					normal = faceVertexNormals[ j ].clone();
  
					if ( normalMatrix !== undefined ) {
  
						normal.applyMatrix3( normalMatrix ).normalize();
  
					}
  
					faceCopy.vertexNormals.push( normal );
  
				}
  
				faceCopy.color.copy( face.color );
  
				for ( var j = 0, jl = faceVertexColors.length; j < jl; j ++ ) {
  
					color = faceVertexColors[ j ];
					faceCopy.vertexColors.push( color.clone() );
  
				}
  
				faceCopy.materialIndex = face.materialIndex + materialIndexOffset;
  
				faces1.push( faceCopy );
  
			}
  
			// uvs
  
			for ( i = 0, il = uvs2.length; i < il; i ++ ) {
  
				var uv = uvs2[ i ], uvCopy = [];
  
				if ( uv === undefined ) {
  
					continue;
  
				}
  
				for ( var j = 0, jl = uv.length; j < jl; j ++ ) {
  
					uvCopy.push( uv[ j ].clone() );
  
				}
  
				uvs1.push( uvCopy );
  
			}
  
		},
  
		mergeMesh: function ( mesh ) {
  
			if ( ! ( mesh && mesh.isMesh ) ) {
  
				console.error( 'THREE.Geometry.mergeMesh(): mesh not an instance of THREE.Mesh.', mesh );
				return;
  
			}
  
			if ( mesh.matrixAutoUpdate ) mesh.updateMatrix();
  
			this.merge( mesh.geometry, mesh.matrix );
  
		},
  
		/*
		 * Checks for duplicate vertices with hashmap.
		 * Duplicated vertices are removed
		 * and faces' vertices are updated.
		 */
  
		mergeVertices: function () {
  
			var verticesMap = {}; // Hashmap for looking up vertices by position coordinates (and making sure they are unique)
			var unique = [], changes = [];
  
			var v, key;
			var precisionPoints = 4; // number of decimal points, e.g. 4 for epsilon of 0.0001
			var precision = Math.pow( 10, precisionPoints );
			var i, il, face;
			var indices, j, jl;
  
			for ( i = 0, il = this.vertices.length; i < il; i ++ ) {
  
				v = this.vertices[ i ];
				key = Math.round( v.x * precision ) + '_' + Math.round( v.y * precision ) + '_' + Math.round( v.z * precision );
  
				if ( verticesMap[ key ] === undefined ) {
  
					verticesMap[ key ] = i;
					unique.push( this.vertices[ i ] );
					changes[ i ] = unique.length - 1;
  
				} else {
  
					//console.log('Duplicate vertex found. ', i, ' could be using ', verticesMap[key]);
					changes[ i ] = changes[ verticesMap[ key ] ];
  
				}
  
			}
  
  
			// if faces are completely degenerate after merging vertices, we
			// have to remove them from the geometry.
			var faceIndicesToRemove = [];
  
			for ( i = 0, il = this.faces.length; i < il; i ++ ) {
  
				face = this.faces[ i ];
  
				face.a = changes[ face.a ];
				face.b = changes[ face.b ];
				face.c = changes[ face.c ];
  
				indices = [ face.a, face.b, face.c ];
  
				// if any duplicate vertices are found in a Face3
				// we have to remove the face as nothing can be saved
				for ( var n = 0; n < 3; n ++ ) {
  
					if ( indices[ n ] === indices[ ( n + 1 ) % 3 ] ) {
  
						faceIndicesToRemove.push( i );
						break;
  
					}
  
				}
  
			}
  
			for ( i = faceIndicesToRemove.length - 1; i >= 0; i -- ) {
  
				var idx = faceIndicesToRemove[ i ];
  
				this.faces.splice( idx, 1 );
  
				for ( j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) {
  
					this.faceVertexUvs[ j ].splice( idx, 1 );
  
				}
  
			}
  
			// Use unique set of vertices
  
			var diff = this.vertices.length - unique.length;
			this.vertices = unique;
			return diff;
  
		},
  
		setFromPoints: function ( points ) {
  
			this.vertices = [];
  
			for ( var i = 0, l = points.length; i < l; i ++ ) {
  
				var point = points[ i ];
				this.vertices.push( new Vector3( point.x, point.y, point.z || 0 ) );
  
			}
  
			return this;
  
		},
  
		sortFacesByMaterialIndex: function () {
  
			var faces = this.faces;
			var length = faces.length;
  
			// tag faces
  
			for ( var i = 0; i < length; i ++ ) {
  
				faces[ i ]._id = i;
  
			}
  
			// sort faces
  
			function materialIndexSort( a, b ) {
  
				return a.materialIndex - b.materialIndex;
  
			}
  
			faces.sort( materialIndexSort );
  
			// sort uvs
  
			var uvs1 = this.faceVertexUvs[ 0 ];
			var uvs2 = this.faceVertexUvs[ 1 ];
  
			var newUvs1, newUvs2;
  
			if ( uvs1 && uvs1.length === length ) newUvs1 = [];
			if ( uvs2 && uvs2.length === length ) newUvs2 = [];
  
			for ( var i = 0; i < length; i ++ ) {
  
				var id = faces[ i ]._id;
  
				if ( newUvs1 ) newUvs1.push( uvs1[ id ] );
				if ( newUvs2 ) newUvs2.push( uvs2[ id ] );
  
			}
  
			if ( newUvs1 ) this.faceVertexUvs[ 0 ] = newUvs1;
			if ( newUvs2 ) this.faceVertexUvs[ 1 ] = newUvs2;
  
		},
  
		toJSON: function () {
  
			var data = {
				metadata: {
					version: 4.5,
					type: 'Geometry',
					generator: 'Geometry.toJSON'
				}
			};
  
			// standard Geometry serialization
  
			data.uuid = this.uuid;
			data.type = this.type;
			if ( this.name !== '' ) data.name = this.name;
  
			if ( this.parameters !== undefined ) {
  
				var parameters = this.parameters;
  
				for ( var key in parameters ) {
  
					if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ];
  
				}
  
				return data;
  
			}
  
			var vertices = [];
  
			for ( var i = 0; i < this.vertices.length; i ++ ) {
  
				var vertex = this.vertices[ i ];
				vertices.push( vertex.x, vertex.y, vertex.z );
  
			}
  
			var faces = [];
			var normals = [];
			var normalsHash = {};
			var colors = [];
			var colorsHash = {};
			var uvs = [];
			var uvsHash = {};
  
			for ( var i = 0; i < this.faces.length; i ++ ) {
  
				var face = this.faces[ i ];
  
				var hasMaterial = true;
				var hasFaceUv = false; // deprecated
				var hasFaceVertexUv = this.faceVertexUvs[ 0 ][ i ] !== undefined;
				var hasFaceNormal = face.normal.length() > 0;
				var hasFaceVertexNormal = face.vertexNormals.length > 0;
				var hasFaceColor = face.color.r !== 1 || face.color.g !== 1 || face.color.b !== 1;
				var hasFaceVertexColor = face.vertexColors.length > 0;
  
				var faceType = 0;
  
				faceType = setBit( faceType, 0, 0 ); // isQuad
				faceType = setBit( faceType, 1, hasMaterial );
				faceType = setBit( faceType, 2, hasFaceUv );
				faceType = setBit( faceType, 3, hasFaceVertexUv );
				faceType = setBit( faceType, 4, hasFaceNormal );
				faceType = setBit( faceType, 5, hasFaceVertexNormal );
				faceType = setBit( faceType, 6, hasFaceColor );
				faceType = setBit( faceType, 7, hasFaceVertexColor );
  
				faces.push( faceType );
				faces.push( face.a, face.b, face.c );
				faces.push( face.materialIndex );
  
				if ( hasFaceVertexUv ) {
  
					var faceVertexUvs = this.faceVertexUvs[ 0 ][ i ];
  
					faces.push(
						getUvIndex( faceVertexUvs[ 0 ] ),
						getUvIndex( faceVertexUvs[ 1 ] ),
						getUvIndex( faceVertexUvs[ 2 ] )
					);
  
				}
  
				if ( hasFaceNormal ) {
  
					faces.push( getNormalIndex( face.normal ) );
  
				}
  
				if ( hasFaceVertexNormal ) {
  
					var vertexNormals = face.vertexNormals;
  
					faces.push(
						getNormalIndex( vertexNormals[ 0 ] ),
						getNormalIndex( vertexNormals[ 1 ] ),
						getNormalIndex( vertexNormals[ 2 ] )
					);
  
				}
  
				if ( hasFaceColor ) {
  
					faces.push( getColorIndex( face.color ) );
  
				}
  
				if ( hasFaceVertexColor ) {
  
					var vertexColors = face.vertexColors;
  
					faces.push(
						getColorIndex( vertexColors[ 0 ] ),
						getColorIndex( vertexColors[ 1 ] ),
						getColorIndex( vertexColors[ 2 ] )
					);
  
				}
  
			}
  
			function setBit( value, position, enabled ) {
  
				return enabled ? value | ( 1 << position ) : value & ( ~ ( 1 << position ) );
  
			}
  
			function getNormalIndex( normal ) {
  
				var hash = normal.x.toString() + normal.y.toString() + normal.z.toString();
  
				if ( normalsHash[ hash ] !== undefined ) {
  
					return normalsHash[ hash ];
  
				}
  
				normalsHash[ hash ] = normals.length / 3;
				normals.push( normal.x, normal.y, normal.z );
  
				return normalsHash[ hash ];
  
			}
  
			function getColorIndex( color ) {
  
				var hash = color.r.toString() + color.g.toString() + color.b.toString();
  
				if ( colorsHash[ hash ] !== undefined ) {
  
					return colorsHash[ hash ];
  
				}
  
				colorsHash[ hash ] = colors.length;
				colors.push( color.getHex() );
  
				return colorsHash[ hash ];
  
			}
  
			function getUvIndex( uv ) {
  
				var hash = uv.x.toString() + uv.y.toString();
  
				if ( uvsHash[ hash ] !== undefined ) {
  
					return uvsHash[ hash ];
  
				}
  
				uvsHash[ hash ] = uvs.length / 2;
				uvs.push( uv.x, uv.y );
  
				return uvsHash[ hash ];
  
			}
  
			data.data = {};
  
			data.data.vertices = vertices;
			data.data.normals = normals;
			if ( colors.length > 0 ) data.data.colors = colors;
			if ( uvs.length > 0 ) data.data.uvs = [ uvs ]; // temporal backward compatibility
			data.data.faces = faces;
  
			return data;
  
		},
  
		clone: function () {
  
			/*
			 // Handle primitives
  
			 var parameters = this.parameters;
  
			 if ( parameters !== undefined ) {
  
			 var values = [];
  
			 for ( var key in parameters ) {
  
			 values.push( parameters[ key ] );
  
			 }
  
			 var geometry = Object.create( this.constructor.prototype );
			 this.constructor.apply( geometry, values );
			 return geometry;
  
			 }
  
			 return new this.constructor().copy( this );
			 */
  
			return new Geometry().copy( this );
  
		},
  
		copy: function ( source ) {
  
			var i, il, j, jl, k, kl;
  
			// reset
  
			this.vertices = [];
			this.colors = [];
			this.faces = [];
			this.faceVertexUvs = [[]];
			this.morphTargets = [];
			this.morphNormals = [];
			this.skinWeights = [];
			this.skinIndices = [];
			this.lineDistances = [];
			this.boundingBox = null;
			this.boundingSphere = null;
  
			// name
  
			this.name = source.name;
  
			// vertices
  
			var vertices = source.vertices;
  
			for ( i = 0, il = vertices.length; i < il; i ++ ) {
  
				this.vertices.push( vertices[ i ].clone() );
  
			}
  
			// colors
  
			var colors = source.colors;
  
			for ( i = 0, il = colors.length; i < il; i ++ ) {
  
				this.colors.push( colors[ i ].clone() );
  
			}
  
			// faces
  
			var faces = source.faces;
  
			for ( i = 0, il = faces.length; i < il; i ++ ) {
  
				this.faces.push( faces[ i ].clone() );
  
			}
  
			// face vertex uvs
  
			for ( i = 0, il = source.faceVertexUvs.length; i < il; i ++ ) {
  
				var faceVertexUvs = source.faceVertexUvs[ i ];
  
				if ( this.faceVertexUvs[ i ] === undefined ) {
  
					this.faceVertexUvs[ i ] = [];
  
				}
  
				for ( j = 0, jl = faceVertexUvs.length; j < jl; j ++ ) {
  
					var uvs = faceVertexUvs[ j ], uvsCopy = [];
  
					for ( k = 0, kl = uvs.length; k < kl; k ++ ) {
  
						var uv = uvs[ k ];
  
						uvsCopy.push( uv.clone() );
  
					}
  
					this.faceVertexUvs[ i ].push( uvsCopy );
  
				}
  
			}
  
			// morph targets
  
			var morphTargets = source.morphTargets;
  
			for ( i = 0, il = morphTargets.length; i < il; i ++ ) {
  
				var morphTarget = {};
				morphTarget.name = morphTargets[ i ].name;
  
				// vertices
  
				if ( morphTargets[ i ].vertices !== undefined ) {
  
					morphTarget.vertices = [];
  
					for ( j = 0, jl = morphTargets[ i ].vertices.length; j < jl; j ++ ) {
  
						morphTarget.vertices.push( morphTargets[ i ].vertices[ j ].clone() );
  
					}
  
				}
  
				// normals
  
				if ( morphTargets[ i ].normals !== undefined ) {
  
					morphTarget.normals = [];
  
					for ( j = 0, jl = morphTargets[ i ].normals.length; j < jl; j ++ ) {
  
						morphTarget.normals.push( morphTargets[ i ].normals[ j ].clone() );
  
					}
  
				}
  
				this.morphTargets.push( morphTarget );
  
			}
  
			// morph normals
  
			var morphNormals = source.morphNormals;
  
			for ( i = 0, il = morphNormals.length; i < il; i ++ ) {
  
				var morphNormal = {};
  
				// vertex normals
  
				if ( morphNormals[ i ].vertexNormals !== undefined ) {
  
					morphNormal.vertexNormals = [];
  
					for ( j = 0, jl = morphNormals[ i ].vertexNormals.length; j < jl; j ++ ) {
  
						var srcVertexNormal = morphNormals[ i ].vertexNormals[ j ];
						var destVertexNormal = {};
  
						destVertexNormal.a = srcVertexNormal.a.clone();
						destVertexNormal.b = srcVertexNormal.b.clone();
						destVertexNormal.c = srcVertexNormal.c.clone();
  
						morphNormal.vertexNormals.push( destVertexNormal );
  
					}
  
				}
  
				// face normals
  
				if ( morphNormals[ i ].faceNormals !== undefined ) {
  
					morphNormal.faceNormals = [];
  
					for ( j = 0, jl = morphNormals[ i ].faceNormals.length; j < jl; j ++ ) {
  
						morphNormal.faceNormals.push( morphNormals[ i ].faceNormals[ j ].clone() );
  
					}
  
				}
  
				this.morphNormals.push( morphNormal );
  
			}
  
			// skin weights
  
			var skinWeights = source.skinWeights;
  
			for ( i = 0, il = skinWeights.length; i < il; i ++ ) {
  
				this.skinWeights.push( skinWeights[ i ].clone() );
  
			}
  
			// skin indices
  
			var skinIndices = source.skinIndices;
  
			for ( i = 0, il = skinIndices.length; i < il; i ++ ) {
  
				this.skinIndices.push( skinIndices[ i ].clone() );
  
			}
  
			// line distances
  
			var lineDistances = source.lineDistances;
  
			for ( i = 0, il = lineDistances.length; i < il; i ++ ) {
  
				this.lineDistances.push( lineDistances[ i ] );
  
			}
  
			// bounding box
  
			var boundingBox = source.boundingBox;
  
			if ( boundingBox !== null ) {
  
				this.boundingBox = boundingBox.clone();
  
			}
  
			// bounding sphere
  
			var boundingSphere = source.boundingSphere;
  
			if ( boundingSphere !== null ) {
  
				this.boundingSphere = boundingSphere.clone();
  
			}
  
			// update flags
  
			this.elementsNeedUpdate = source.elementsNeedUpdate;
			this.verticesNeedUpdate = source.verticesNeedUpdate;
			this.uvsNeedUpdate = source.uvsNeedUpdate;
			this.normalsNeedUpdate = source.normalsNeedUpdate;
			this.colorsNeedUpdate = source.colorsNeedUpdate;
			this.lineDistancesNeedUpdate = source.lineDistancesNeedUpdate;
			this.groupsNeedUpdate = source.groupsNeedUpdate;
  
			return this;
  
		},
  
		dispose: function () {
  
			this.dispatchEvent( { type: 'dispose' } );
  
		}
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function BufferAttribute( array, itemSize, normalized ) {
  
		if ( Array.isArray( array ) ) {
  
			throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' );
  
		}
  
		this.name = '';
  
		this.array = array;
		this.itemSize = itemSize;
		this.count = array !== undefined ? array.length / itemSize : 0;
		this.normalized = normalized === true;
  
		this.dynamic = false;
		this.updateRange = { offset: 0, count: - 1 };
  
		this.version = 0;
  
	}
  
	Object.defineProperty( BufferAttribute.prototype, 'needsUpdate', {
  
		set: function ( value ) {
  
			if ( value === true ) this.version ++;
  
		}
  
	} );
  
	Object.assign( BufferAttribute.prototype, {
  
		isBufferAttribute: true,
  
		onUploadCallback: function () {},
  
		setArray: function ( array ) {
  
			if ( Array.isArray( array ) ) {
  
				throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' );
  
			}
  
			this.count = array !== undefined ? array.length / this.itemSize : 0;
			this.array = array;
  
			return this;
  
		},
  
		setDynamic: function ( value ) {
  
			this.dynamic = value;
  
			return this;
  
		},
  
		copy: function ( source ) {
  
			this.name = source.name;
			this.array = new source.array.constructor( source.array );
			this.itemSize = source.itemSize;
			this.count = source.count;
			this.normalized = source.normalized;
  
			this.dynamic = source.dynamic;
  
			return this;
  
		},
  
		copyAt: function ( index1, attribute, index2 ) {
  
			index1 *= this.itemSize;
			index2 *= attribute.itemSize;
  
			for ( var i = 0, l = this.itemSize; i < l; i ++ ) {
  
				this.array[ index1 + i ] = attribute.array[ index2 + i ];
  
			}
  
			return this;
  
		},
  
		copyArray: function ( array ) {
  
			this.array.set( array );
  
			return this;
  
		},
  
		copyColorsArray: function ( colors ) {
  
			var array = this.array, offset = 0;
  
			for ( var i = 0, l = colors.length; i < l; i ++ ) {
  
				var color = colors[ i ];
  
				if ( color === undefined ) {
  
					console.warn( 'THREE.BufferAttribute.copyColorsArray(): color is undefined', i );
					color = new Color();
  
				}
  
				array[ offset ++ ] = color.r;
				array[ offset ++ ] = color.g;
				array[ offset ++ ] = color.b;
  
			}
  
			return this;
  
		},
  
		copyVector2sArray: function ( vectors ) {
  
			var array = this.array, offset = 0;
  
			for ( var i = 0, l = vectors.length; i < l; i ++ ) {
  
				var vector = vectors[ i ];
  
				if ( vector === undefined ) {
  
					console.warn( 'THREE.BufferAttribute.copyVector2sArray(): vector is undefined', i );
					vector = new Vector2();
  
				}
  
				array[ offset ++ ] = vector.x;
				array[ offset ++ ] = vector.y;
  
			}
  
			return this;
  
		},
  
		copyVector3sArray: function ( vectors ) {
  
			var array = this.array, offset = 0;
  
			for ( var i = 0, l = vectors.length; i < l; i ++ ) {
  
				var vector = vectors[ i ];
  
				if ( vector === undefined ) {
  
					console.warn( 'THREE.BufferAttribute.copyVector3sArray(): vector is undefined', i );
					vector = new Vector3();
  
				}
  
				array[ offset ++ ] = vector.x;
				array[ offset ++ ] = vector.y;
				array[ offset ++ ] = vector.z;
  
			}
  
			return this;
  
		},
  
		copyVector4sArray: function ( vectors ) {
  
			var array = this.array, offset = 0;
  
			for ( var i = 0, l = vectors.length; i < l; i ++ ) {
  
				var vector = vectors[ i ];
  
				if ( vector === undefined ) {
  
					console.warn( 'THREE.BufferAttribute.copyVector4sArray(): vector is undefined', i );
					vector = new Vector4();
  
				}
  
				array[ offset ++ ] = vector.x;
				array[ offset ++ ] = vector.y;
				array[ offset ++ ] = vector.z;
				array[ offset ++ ] = vector.w;
  
			}
  
			return this;
  
		},
  
		set: function ( value, offset ) {
  
			if ( offset === undefined ) offset = 0;
  
			this.array.set( value, offset );
  
			return this;
  
		},
  
		getX: function ( index ) {
  
			return this.array[ index * this.itemSize ];
  
		},
  
		setX: function ( index, x ) {
  
			this.array[ index * this.itemSize ] = x;
  
			return this;
  
		},
  
		getY: function ( index ) {
  
			return this.array[ index * this.itemSize + 1 ];
  
		},
  
		setY: function ( index, y ) {
  
			this.array[ index * this.itemSize + 1 ] = y;
  
			return this;
  
		},
  
		getZ: function ( index ) {
  
			return this.array[ index * this.itemSize + 2 ];
  
		},
  
		setZ: function ( index, z ) {
  
			this.array[ index * this.itemSize + 2 ] = z;
  
			return this;
  
		},
  
		getW: function ( index ) {
  
			return this.array[ index * this.itemSize + 3 ];
  
		},
  
		setW: function ( index, w ) {
  
			this.array[ index * this.itemSize + 3 ] = w;
  
			return this;
  
		},
  
		setXY: function ( index, x, y ) {
  
			index *= this.itemSize;
  
			this.array[ index + 0 ] = x;
			this.array[ index + 1 ] = y;
  
			return this;
  
		},
  
		setXYZ: function ( index, x, y, z ) {
  
			index *= this.itemSize;
  
			this.array[ index + 0 ] = x;
			this.array[ index + 1 ] = y;
			this.array[ index + 2 ] = z;
  
			return this;
  
		},
  
		setXYZW: function ( index, x, y, z, w ) {
  
			index *= this.itemSize;
  
			this.array[ index + 0 ] = x;
			this.array[ index + 1 ] = y;
			this.array[ index + 2 ] = z;
			this.array[ index + 3 ] = w;
  
			return this;
  
		},
  
		onUpload: function ( callback ) {
  
			this.onUploadCallback = callback;
  
			return this;
  
		},
  
		clone: function () {
  
			return new this.constructor( this.array, this.itemSize ).copy( this );
  
		}
  
	} );
  
	//
  
	function Int8BufferAttribute( array, itemSize, normalized ) {
  
		BufferAttribute.call( this, new Int8Array( array ), itemSize, normalized );
  
	}
  
	Int8BufferAttribute.prototype = Object.create( BufferAttribute.prototype );
	Int8BufferAttribute.prototype.constructor = Int8BufferAttribute;
  
  
	function Uint8BufferAttribute( array, itemSize, normalized ) {
  
		BufferAttribute.call( this, new Uint8Array( array ), itemSize, normalized );
  
	}
  
	Uint8BufferAttribute.prototype = Object.create( BufferAttribute.prototype );
	Uint8BufferAttribute.prototype.constructor = Uint8BufferAttribute;
  
  
	function Uint8ClampedBufferAttribute( array, itemSize, normalized ) {
  
		BufferAttribute.call( this, new Uint8ClampedArray( array ), itemSize, normalized );
  
	}
  
	Uint8ClampedBufferAttribute.prototype = Object.create( BufferAttribute.prototype );
	Uint8ClampedBufferAttribute.prototype.constructor = Uint8ClampedBufferAttribute;
  
  
	function Int16BufferAttribute( array, itemSize, normalized ) {
  
		BufferAttribute.call( this, new Int16Array( array ), itemSize, normalized );
  
	}
  
	Int16BufferAttribute.prototype = Object.create( BufferAttribute.prototype );
	Int16BufferAttribute.prototype.constructor = Int16BufferAttribute;
  
  
	function Uint16BufferAttribute( array, itemSize, normalized ) {
  
		BufferAttribute.call( this, new Uint16Array( array ), itemSize, normalized );
  
	}
  
	Uint16BufferAttribute.prototype = Object.create( BufferAttribute.prototype );
	Uint16BufferAttribute.prototype.constructor = Uint16BufferAttribute;
  
  
	function Int32BufferAttribute( array, itemSize, normalized ) {
  
		BufferAttribute.call( this, new Int32Array( array ), itemSize, normalized );
  
	}
  
	Int32BufferAttribute.prototype = Object.create( BufferAttribute.prototype );
	Int32BufferAttribute.prototype.constructor = Int32BufferAttribute;
  
  
	function Uint32BufferAttribute( array, itemSize, normalized ) {
  
		BufferAttribute.call( this, new Uint32Array( array ), itemSize, normalized );
  
	}
  
	Uint32BufferAttribute.prototype = Object.create( BufferAttribute.prototype );
	Uint32BufferAttribute.prototype.constructor = Uint32BufferAttribute;
  
  
	function Float32BufferAttribute( array, itemSize, normalized ) {
  
		BufferAttribute.call( this, new Float32Array( array ), itemSize, normalized );
  
	}
  
	Float32BufferAttribute.prototype = Object.create( BufferAttribute.prototype );
	Float32BufferAttribute.prototype.constructor = Float32BufferAttribute;
  
  
	function Float64BufferAttribute( array, itemSize, normalized ) {
  
		BufferAttribute.call( this, new Float64Array( array ), itemSize, normalized );
  
	}
  
	Float64BufferAttribute.prototype = Object.create( BufferAttribute.prototype );
	Float64BufferAttribute.prototype.constructor = Float64BufferAttribute;
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function DirectGeometry() {
  
		this.vertices = [];
		this.normals = [];
		this.colors = [];
		this.uvs = [];
		this.uvs2 = [];
  
		this.groups = [];
  
		this.morphTargets = {};
  
		this.skinWeights = [];
		this.skinIndices = [];
  
		// this.lineDistances = [];
  
		this.boundingBox = null;
		this.boundingSphere = null;
  
		// update flags
  
		this.verticesNeedUpdate = false;
		this.normalsNeedUpdate = false;
		this.colorsNeedUpdate = false;
		this.uvsNeedUpdate = false;
		this.groupsNeedUpdate = false;
  
	}
  
	Object.assign( DirectGeometry.prototype, {
  
		computeGroups: function ( geometry ) {
  
			var group;
			var groups = [];
			var materialIndex = undefined;
  
			var faces = geometry.faces;
  
			for ( var i = 0; i < faces.length; i ++ ) {
  
				var face = faces[ i ];
  
				// materials
  
				if ( face.materialIndex !== materialIndex ) {
  
					materialIndex = face.materialIndex;
  
					if ( group !== undefined ) {
  
						group.count = ( i * 3 ) - group.start;
						groups.push( group );
  
					}
  
					group = {
						start: i * 3,
						materialIndex: materialIndex
					};
  
				}
  
			}
  
			if ( group !== undefined ) {
  
				group.count = ( i * 3 ) - group.start;
				groups.push( group );
  
			}
  
			this.groups = groups;
  
		},
  
		fromGeometry: function ( geometry ) {
  
			var faces = geometry.faces;
			var vertices = geometry.vertices;
			var faceVertexUvs = geometry.faceVertexUvs;
  
			var hasFaceVertexUv = faceVertexUvs[ 0 ] && faceVertexUvs[ 0 ].length > 0;
			var hasFaceVertexUv2 = faceVertexUvs[ 1 ] && faceVertexUvs[ 1 ].length > 0;
  
			// morphs
  
			var morphTargets = geometry.morphTargets;
			var morphTargetsLength = morphTargets.length;
  
			var morphTargetsPosition;
  
			if ( morphTargetsLength > 0 ) {
  
				morphTargetsPosition = [];
  
				for ( var i = 0; i < morphTargetsLength; i ++ ) {
  
					morphTargetsPosition[ i ] = [];
  
				}
  
				this.morphTargets.position = morphTargetsPosition;
  
			}
  
			var morphNormals = geometry.morphNormals;
			var morphNormalsLength = morphNormals.length;
  
			var morphTargetsNormal;
  
			if ( morphNormalsLength > 0 ) {
  
				morphTargetsNormal = [];
  
				for ( var i = 0; i < morphNormalsLength; i ++ ) {
  
					morphTargetsNormal[ i ] = [];
  
				}
  
				this.morphTargets.normal = morphTargetsNormal;
  
			}
  
			// skins
  
			var skinIndices = geometry.skinIndices;
			var skinWeights = geometry.skinWeights;
  
			var hasSkinIndices = skinIndices.length === vertices.length;
			var hasSkinWeights = skinWeights.length === vertices.length;
  
			//
  
			if ( faces.length === 0 ) {
  
				console.error( 'THREE.DirectGeometry: Faceless geometries are not supported.' );
  
			}
  
			for ( var i = 0; i < faces.length; i ++ ) {
  
				var face = faces[ i ];
  
				this.vertices.push( vertices[ face.a ], vertices[ face.b ], vertices[ face.c ] );
  
				var vertexNormals = face.vertexNormals;
  
				if ( vertexNormals.length === 3 ) {
  
					this.normals.push( vertexNormals[ 0 ], vertexNormals[ 1 ], vertexNormals[ 2 ] );
  
				} else {
  
					var normal = face.normal;
  
					this.normals.push( normal, normal, normal );
  
				}
  
				var vertexColors = face.vertexColors;
  
				if ( vertexColors.length === 3 ) {
  
					this.colors.push( vertexColors[ 0 ], vertexColors[ 1 ], vertexColors[ 2 ] );
  
				} else {
  
					var color = face.color;
  
					this.colors.push( color, color, color );
  
				}
  
				if ( hasFaceVertexUv === true ) {
  
					var vertexUvs = faceVertexUvs[ 0 ][ i ];
  
					if ( vertexUvs !== undefined ) {
  
						this.uvs.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] );
  
					} else {
  
						console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv ', i );
  
						this.uvs.push( new Vector2(), new Vector2(), new Vector2() );
  
					}
  
				}
  
				if ( hasFaceVertexUv2 === true ) {
  
					var vertexUvs = faceVertexUvs[ 1 ][ i ];
  
					if ( vertexUvs !== undefined ) {
  
						this.uvs2.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] );
  
					} else {
  
						console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv2 ', i );
  
						this.uvs2.push( new Vector2(), new Vector2(), new Vector2() );
  
					}
  
				}
  
				// morphs
  
				for ( var j = 0; j < morphTargetsLength; j ++ ) {
  
					var morphTarget = morphTargets[ j ].vertices;
  
					morphTargetsPosition[ j ].push( morphTarget[ face.a ], morphTarget[ face.b ], morphTarget[ face.c ] );
  
				}
  
				for ( var j = 0; j < morphNormalsLength; j ++ ) {
  
					var morphNormal = morphNormals[ j ].vertexNormals[ i ];
  
					morphTargetsNormal[ j ].push( morphNormal.a, morphNormal.b, morphNormal.c );
  
				}
  
				// skins
  
				if ( hasSkinIndices ) {
  
					this.skinIndices.push( skinIndices[ face.a ], skinIndices[ face.b ], skinIndices[ face.c ] );
  
				}
  
				if ( hasSkinWeights ) {
  
					this.skinWeights.push( skinWeights[ face.a ], skinWeights[ face.b ], skinWeights[ face.c ] );
  
				}
  
			}
  
			this.computeGroups( geometry );
  
			this.verticesNeedUpdate = geometry.verticesNeedUpdate;
			this.normalsNeedUpdate = geometry.normalsNeedUpdate;
			this.colorsNeedUpdate = geometry.colorsNeedUpdate;
			this.uvsNeedUpdate = geometry.uvsNeedUpdate;
			this.groupsNeedUpdate = geometry.groupsNeedUpdate;
  
			return this;
  
		}
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function arrayMax( array ) {
  
		if ( array.length === 0 ) return - Infinity;
  
		var max = array[ 0 ];
  
		for ( var i = 1, l = array.length; i < l; ++ i ) {
  
			if ( array[ i ] > max ) max = array[ i ];
  
		}
  
		return max;
  
	}
  
	/**
	 * @author alteredq / http://alteredqualia.com/
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	var bufferGeometryId = 1; // BufferGeometry uses odd numbers as Id
  
	function BufferGeometry() {
  
		Object.defineProperty( this, 'id', { value: bufferGeometryId += 2 } );
  
		this.uuid = _Math.generateUUID();
  
		this.name = '';
		this.type = 'BufferGeometry';
  
		this.index = null;
		this.attributes = {};
  
		this.morphAttributes = {};
  
		this.groups = [];
  
		this.boundingBox = null;
		this.boundingSphere = null;
  
		this.drawRange = { start: 0, count: Infinity };
  
		this.userData = {};
  
	}
  
	BufferGeometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {
  
		constructor: BufferGeometry,
  
		isBufferGeometry: true,
  
		getIndex: function () {
  
			return this.index;
  
		},
  
		setIndex: function ( index ) {
  
			if ( Array.isArray( index ) ) {
  
				this.index = new ( arrayMax( index ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( index, 1 );
  
			} else {
  
				this.index = index;
  
			}
  
		},
  
		addAttribute: function ( name, attribute ) {
  
			if ( ! ( attribute && attribute.isBufferAttribute ) && ! ( attribute && attribute.isInterleavedBufferAttribute ) ) {
  
				console.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' );
  
				return this.addAttribute( name, new BufferAttribute( arguments[ 1 ], arguments[ 2 ] ) );
  
			}
  
			if ( name === 'index' ) {
  
				console.warn( 'THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute.' );
				this.setIndex( attribute );
  
				return this;
  
			}
  
			this.attributes[ name ] = attribute;
  
			return this;
  
		},
  
		getAttribute: function ( name ) {
  
			return this.attributes[ name ];
  
		},
  
		removeAttribute: function ( name ) {
  
			delete this.attributes[ name ];
  
			return this;
  
		},
  
		addGroup: function ( start, count, materialIndex ) {
  
			this.groups.push( {
  
				start: start,
				count: count,
				materialIndex: materialIndex !== undefined ? materialIndex : 0
  
			} );
  
		},
  
		clearGroups: function () {
  
			this.groups = [];
  
		},
  
		setDrawRange: function ( start, count ) {
  
			this.drawRange.start = start;
			this.drawRange.count = count;
  
		},
  
		applyMatrix: function ( matrix ) {
  
			var position = this.attributes.position;
  
			if ( position !== undefined ) {
  
				matrix.applyToBufferAttribute( position );
				position.needsUpdate = true;
  
			}
  
			var normal = this.attributes.normal;
  
			if ( normal !== undefined ) {
  
				var normalMatrix = new Matrix3().getNormalMatrix( matrix );
  
				normalMatrix.applyToBufferAttribute( normal );
				normal.needsUpdate = true;
  
			}
  
			if ( this.boundingBox !== null ) {
  
				this.computeBoundingBox();
  
			}
  
			if ( this.boundingSphere !== null ) {
  
				this.computeBoundingSphere();
  
			}
  
			return this;
  
		},
  
		rotateX: function () {
  
			// rotate geometry around world x-axis
  
			var m1 = new Matrix4();
  
			return function rotateX( angle ) {
  
				m1.makeRotationX( angle );
  
				this.applyMatrix( m1 );
  
				return this;
  
			};
  
		}(),
  
		rotateY: function () {
  
			// rotate geometry around world y-axis
  
			var m1 = new Matrix4();
  
			return function rotateY( angle ) {
  
				m1.makeRotationY( angle );
  
				this.applyMatrix( m1 );
  
				return this;
  
			};
  
		}(),
  
		rotateZ: function () {
  
			// rotate geometry around world z-axis
  
			var m1 = new Matrix4();
  
			return function rotateZ( angle ) {
  
				m1.makeRotationZ( angle );
  
				this.applyMatrix( m1 );
  
				return this;
  
			};
  
		}(),
  
		translate: function () {
  
			// translate geometry
  
			var m1 = new Matrix4();
  
			return function translate( x, y, z ) {
  
				m1.makeTranslation( x, y, z );
  
				this.applyMatrix( m1 );
  
				return this;
  
			};
  
		}(),
  
		scale: function () {
  
			// scale geometry
  
			var m1 = new Matrix4();
  
			return function scale( x, y, z ) {
  
				m1.makeScale( x, y, z );
  
				this.applyMatrix( m1 );
  
				return this;
  
			};
  
		}(),
  
		lookAt: function () {
  
			var obj = new Object3D();
  
			return function lookAt( vector ) {
  
				obj.lookAt( vector );
  
				obj.updateMatrix();
  
				this.applyMatrix( obj.matrix );
  
			};
  
		}(),
  
		center: function () {
  
			var offset = new Vector3();
  
			return function center() {
  
				this.computeBoundingBox();
  
				this.boundingBox.getCenter( offset ).negate();
  
				this.translate( offset.x, offset.y, offset.z );
  
				return this;
  
			};
  
		}(),
  
		setFromObject: function ( object ) {
  
			// console.log( 'THREE.BufferGeometry.setFromObject(). Converting', object, this );
  
			var geometry = object.geometry;
  
			if ( object.isPoints || object.isLine ) {
  
				var positions = new Float32BufferAttribute( geometry.vertices.length * 3, 3 );
				var colors = new Float32BufferAttribute( geometry.colors.length * 3, 3 );
  
				this.addAttribute( 'position', positions.copyVector3sArray( geometry.vertices ) );
				this.addAttribute( 'color', colors.copyColorsArray( geometry.colors ) );
  
				if ( geometry.lineDistances && geometry.lineDistances.length === geometry.vertices.length ) {
  
					var lineDistances = new Float32BufferAttribute( geometry.lineDistances.length, 1 );
  
					this.addAttribute( 'lineDistance', lineDistances.copyArray( geometry.lineDistances ) );
  
				}
  
				if ( geometry.boundingSphere !== null ) {
  
					this.boundingSphere = geometry.boundingSphere.clone();
  
				}
  
				if ( geometry.boundingBox !== null ) {
  
					this.boundingBox = geometry.boundingBox.clone();
  
				}
  
			} else if ( object.isMesh ) {
  
				if ( geometry && geometry.isGeometry ) {
  
					this.fromGeometry( geometry );
  
				}
  
			}
  
			return this;
  
		},
  
		setFromPoints: function ( points ) {
  
			var position = [];
  
			for ( var i = 0, l = points.length; i < l; i ++ ) {
  
				var point = points[ i ];
				position.push( point.x, point.y, point.z || 0 );
  
			}
  
			this.addAttribute( 'position', new Float32BufferAttribute( position, 3 ) );
  
			return this;
  
		},
  
		updateFromObject: function ( object ) {
  
			var geometry = object.geometry;
  
			if ( object.isMesh ) {
  
				var direct = geometry.__directGeometry;
  
				if ( geometry.elementsNeedUpdate === true ) {
  
					direct = undefined;
					geometry.elementsNeedUpdate = false;
  
				}
  
				if ( direct === undefined ) {
  
					return this.fromGeometry( geometry );
  
				}
  
				direct.verticesNeedUpdate = geometry.verticesNeedUpdate;
				direct.normalsNeedUpdate = geometry.normalsNeedUpdate;
				direct.colorsNeedUpdate = geometry.colorsNeedUpdate;
				direct.uvsNeedUpdate = geometry.uvsNeedUpdate;
				direct.groupsNeedUpdate = geometry.groupsNeedUpdate;
  
				geometry.verticesNeedUpdate = false;
				geometry.normalsNeedUpdate = false;
				geometry.colorsNeedUpdate = false;
				geometry.uvsNeedUpdate = false;
				geometry.groupsNeedUpdate = false;
  
				geometry = direct;
  
			}
  
			var attribute;
  
			if ( geometry.verticesNeedUpdate === true ) {
  
				attribute = this.attributes.position;
  
				if ( attribute !== undefined ) {
  
					attribute.copyVector3sArray( geometry.vertices );
					attribute.needsUpdate = true;
  
				}
  
				geometry.verticesNeedUpdate = false;
  
			}
  
			if ( geometry.normalsNeedUpdate === true ) {
  
				attribute = this.attributes.normal;
  
				if ( attribute !== undefined ) {
  
					attribute.copyVector3sArray( geometry.normals );
					attribute.needsUpdate = true;
  
				}
  
				geometry.normalsNeedUpdate = false;
  
			}
  
			if ( geometry.colorsNeedUpdate === true ) {
  
				attribute = this.attributes.color;
  
				if ( attribute !== undefined ) {
  
					attribute.copyColorsArray( geometry.colors );
					attribute.needsUpdate = true;
  
				}
  
				geometry.colorsNeedUpdate = false;
  
			}
  
			if ( geometry.uvsNeedUpdate ) {
  
				attribute = this.attributes.uv;
  
				if ( attribute !== undefined ) {
  
					attribute.copyVector2sArray( geometry.uvs );
					attribute.needsUpdate = true;
  
				}
  
				geometry.uvsNeedUpdate = false;
  
			}
  
			if ( geometry.lineDistancesNeedUpdate ) {
  
				attribute = this.attributes.lineDistance;
  
				if ( attribute !== undefined ) {
  
					attribute.copyArray( geometry.lineDistances );
					attribute.needsUpdate = true;
  
				}
  
				geometry.lineDistancesNeedUpdate = false;
  
			}
  
			if ( geometry.groupsNeedUpdate ) {
  
				geometry.computeGroups( object.geometry );
				this.groups = geometry.groups;
  
				geometry.groupsNeedUpdate = false;
  
			}
  
			return this;
  
		},
  
		fromGeometry: function ( geometry ) {
  
			geometry.__directGeometry = new DirectGeometry().fromGeometry( geometry );
  
			return this.fromDirectGeometry( geometry.__directGeometry );
  
		},
  
		fromDirectGeometry: function ( geometry ) {
  
			var positions = new Float32Array( geometry.vertices.length * 3 );
			this.addAttribute( 'position', new BufferAttribute( positions, 3 ).copyVector3sArray( geometry.vertices ) );
  
			if ( geometry.normals.length > 0 ) {
  
				var normals = new Float32Array( geometry.normals.length * 3 );
				this.addAttribute( 'normal', new BufferAttribute( normals, 3 ).copyVector3sArray( geometry.normals ) );
  
			}
  
			if ( geometry.colors.length > 0 ) {
  
				var colors = new Float32Array( geometry.colors.length * 3 );
				this.addAttribute( 'color', new BufferAttribute( colors, 3 ).copyColorsArray( geometry.colors ) );
  
			}
  
			if ( geometry.uvs.length > 0 ) {
  
				var uvs = new Float32Array( geometry.uvs.length * 2 );
				this.addAttribute( 'uv', new BufferAttribute( uvs, 2 ).copyVector2sArray( geometry.uvs ) );
  
			}
  
			if ( geometry.uvs2.length > 0 ) {
  
				var uvs2 = new Float32Array( geometry.uvs2.length * 2 );
				this.addAttribute( 'uv2', new BufferAttribute( uvs2, 2 ).copyVector2sArray( geometry.uvs2 ) );
  
			}
  
			// groups
  
			this.groups = geometry.groups;
  
			// morphs
  
			for ( var name in geometry.morphTargets ) {
  
				var array = [];
				var morphTargets = geometry.morphTargets[ name ];
  
				for ( var i = 0, l = morphTargets.length; i < l; i ++ ) {
  
					var morphTarget = morphTargets[ i ];
  
					var attribute = new Float32BufferAttribute( morphTarget.length * 3, 3 );
  
					array.push( attribute.copyVector3sArray( morphTarget ) );
  
				}
  
				this.morphAttributes[ name ] = array;
  
			}
  
			// skinning
  
			if ( geometry.skinIndices.length > 0 ) {
  
				var skinIndices = new Float32BufferAttribute( geometry.skinIndices.length * 4, 4 );
				this.addAttribute( 'skinIndex', skinIndices.copyVector4sArray( geometry.skinIndices ) );
  
			}
  
			if ( geometry.skinWeights.length > 0 ) {
  
				var skinWeights = new Float32BufferAttribute( geometry.skinWeights.length * 4, 4 );
				this.addAttribute( 'skinWeight', skinWeights.copyVector4sArray( geometry.skinWeights ) );
  
			}
  
			//
  
			if ( geometry.boundingSphere !== null ) {
  
				this.boundingSphere = geometry.boundingSphere.clone();
  
			}
  
			if ( geometry.boundingBox !== null ) {
  
				this.boundingBox = geometry.boundingBox.clone();
  
			}
  
			return this;
  
		},
  
		computeBoundingBox: function () {
  
			if ( this.boundingBox === null ) {
  
				this.boundingBox = new Box3();
  
			}
  
			var position = this.attributes.position;
  
			if ( position !== undefined ) {
  
				this.boundingBox.setFromBufferAttribute( position );
  
			} else {
  
				this.boundingBox.makeEmpty();
  
			}
  
			if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) {
  
				console.error( 'THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.', this );
  
			}
  
		},
  
		computeBoundingSphere: function () {
  
			var box = new Box3();
			var vector = new Vector3();
  
			return function computeBoundingSphere() {
  
				if ( this.boundingSphere === null ) {
  
					this.boundingSphere = new Sphere();
  
				}
  
				var position = this.attributes.position;
  
				if ( position ) {
  
					var center = this.boundingSphere.center;
  
					box.setFromBufferAttribute( position );
					box.getCenter( center );
  
					// hoping to find a boundingSphere with a radius smaller than the
					// boundingSphere of the boundingBox: sqrt(3) smaller in the best case
  
					var maxRadiusSq = 0;
  
					for ( var i = 0, il = position.count; i < il; i ++ ) {
  
						vector.x = position.getX( i );
						vector.y = position.getY( i );
						vector.z = position.getZ( i );
						maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) );
  
					}
  
					this.boundingSphere.radius = Math.sqrt( maxRadiusSq );
  
					if ( isNaN( this.boundingSphere.radius ) ) {
  
						console.error( 'THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.', this );
  
					}
  
				}
  
			};
  
		}(),
  
		computeFaceNormals: function () {
  
			// backwards compatibility
  
		},
  
		computeVertexNormals: function () {
  
			var index = this.index;
			var attributes = this.attributes;
			var groups = this.groups;
  
			if ( attributes.position ) {
  
				var positions = attributes.position.array;
  
				if ( attributes.normal === undefined ) {
  
					this.addAttribute( 'normal', new BufferAttribute( new Float32Array( positions.length ), 3 ) );
  
				} else {
  
					// reset existing normals to zero
  
					var array = attributes.normal.array;
  
					for ( var i = 0, il = array.length; i < il; i ++ ) {
  
						array[ i ] = 0;
  
					}
  
				}
  
				var normals = attributes.normal.array;
  
				var vA, vB, vC;
				var pA = new Vector3(), pB = new Vector3(), pC = new Vector3();
				var cb = new Vector3(), ab = new Vector3();
  
				// indexed elements
  
				if ( index ) {
  
					var indices = index.array;
  
					if ( groups.length === 0 ) {
  
						this.addGroup( 0, indices.length );
  
					}
  
					for ( var j = 0, jl = groups.length; j < jl; ++ j ) {
  
						var group = groups[ j ];
  
						var start = group.start;
						var count = group.count;
  
						for ( var i = start, il = start + count; i < il; i += 3 ) {
  
							vA = indices[ i + 0 ] * 3;
							vB = indices[ i + 1 ] * 3;
							vC = indices[ i + 2 ] * 3;
  
							pA.fromArray( positions, vA );
							pB.fromArray( positions, vB );
							pC.fromArray( positions, vC );
  
							cb.subVectors( pC, pB );
							ab.subVectors( pA, pB );
							cb.cross( ab );
  
							normals[ vA ] += cb.x;
							normals[ vA + 1 ] += cb.y;
							normals[ vA + 2 ] += cb.z;
  
							normals[ vB ] += cb.x;
							normals[ vB + 1 ] += cb.y;
							normals[ vB + 2 ] += cb.z;
  
							normals[ vC ] += cb.x;
							normals[ vC + 1 ] += cb.y;
							normals[ vC + 2 ] += cb.z;
  
						}
  
					}
  
				} else {
  
					// non-indexed elements (unconnected triangle soup)
  
					for ( var i = 0, il = positions.length; i < il; i += 9 ) {
  
						pA.fromArray( positions, i );
						pB.fromArray( positions, i + 3 );
						pC.fromArray( positions, i + 6 );
  
						cb.subVectors( pC, pB );
						ab.subVectors( pA, pB );
						cb.cross( ab );
  
						normals[ i ] = cb.x;
						normals[ i + 1 ] = cb.y;
						normals[ i + 2 ] = cb.z;
  
						normals[ i + 3 ] = cb.x;
						normals[ i + 4 ] = cb.y;
						normals[ i + 5 ] = cb.z;
  
						normals[ i + 6 ] = cb.x;
						normals[ i + 7 ] = cb.y;
						normals[ i + 8 ] = cb.z;
  
					}
  
				}
  
				this.normalizeNormals();
  
				attributes.normal.needsUpdate = true;
  
			}
  
		},
  
		merge: function ( geometry, offset ) {
  
			if ( ! ( geometry && geometry.isBufferGeometry ) ) {
  
				console.error( 'THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.', geometry );
				return;
  
			}
  
			if ( offset === undefined ) {
  
				offset = 0;
  
				console.warn(
					'THREE.BufferGeometry.merge(): Overwriting original geometry, starting at offset=0. '
					+ 'Use BufferGeometryUtils.mergeBufferGeometries() for lossless merge.'
				);
  
			}
  
			var attributes = this.attributes;
  
			for ( var key in attributes ) {
  
				if ( geometry.attributes[ key ] === undefined ) continue;
  
				var attribute1 = attributes[ key ];
				var attributeArray1 = attribute1.array;
  
				var attribute2 = geometry.attributes[ key ];
				var attributeArray2 = attribute2.array;
  
				var attributeSize = attribute2.itemSize;
  
				for ( var i = 0, j = attributeSize * offset; i < attributeArray2.length; i ++, j ++ ) {
  
					attributeArray1[ j ] = attributeArray2[ i ];
  
				}
  
			}
  
			return this;
  
		},
  
		normalizeNormals: function () {
  
			var vector = new Vector3();
  
			return function normalizeNormals() {
  
				var normals = this.attributes.normal;
  
				for ( var i = 0, il = normals.count; i < il; i ++ ) {
  
					vector.x = normals.getX( i );
					vector.y = normals.getY( i );
					vector.z = normals.getZ( i );
  
					vector.normalize();
  
					normals.setXYZ( i, vector.x, vector.y, vector.z );
  
				}
  
			};
  
		}(),
  
		toNonIndexed: function () {
  
			if ( this.index === null ) {
  
				console.warn( 'THREE.BufferGeometry.toNonIndexed(): Geometry is already non-indexed.' );
				return this;
  
			}
  
			var geometry2 = new BufferGeometry();
  
			var indices = this.index.array;
			var attributes = this.attributes;
  
			for ( var name in attributes ) {
  
				var attribute = attributes[ name ];
  
				var array = attribute.array;
				var itemSize = attribute.itemSize;
  
				var array2 = new array.constructor( indices.length * itemSize );
  
				var index = 0, index2 = 0;
  
				for ( var i = 0, l = indices.length; i < l; i ++ ) {
  
					index = indices[ i ] * itemSize;
  
					for ( var j = 0; j < itemSize; j ++ ) {
  
						array2[ index2 ++ ] = array[ index ++ ];
  
					}
  
				}
  
				geometry2.addAttribute( name, new BufferAttribute( array2, itemSize ) );
  
			}
  
			var groups = this.groups;
  
			for ( var i = 0, l = groups.length; i < l; i ++ ) {
  
				var group = groups[ i ];
				geometry2.addGroup( group.start, group.count, group.materialIndex );
  
			}
  
			return geometry2;
  
		},
  
		toJSON: function () {
  
			var data = {
				metadata: {
					version: 4.5,
					type: 'BufferGeometry',
					generator: 'BufferGeometry.toJSON'
				}
			};
  
			// standard BufferGeometry serialization
  
			data.uuid = this.uuid;
			data.type = this.type;
			if ( this.name !== '' ) data.name = this.name;
			if ( Object.keys( this.userData ).length > 0 ) data.userData = this.userData;
  
			if ( this.parameters !== undefined ) {
  
				var parameters = this.parameters;
  
				for ( var key in parameters ) {
  
					if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ];
  
				}
  
				return data;
  
			}
  
			data.data = { attributes: {} };
  
			var index = this.index;
  
			if ( index !== null ) {
  
				var array = Array.prototype.slice.call( index.array );
  
				data.data.index = {
					type: index.array.constructor.name,
					array: array
				};
  
			}
  
			var attributes = this.attributes;
  
			for ( var key in attributes ) {
  
				var attribute = attributes[ key ];
  
				var array = Array.prototype.slice.call( attribute.array );
  
				data.data.attributes[ key ] = {
					itemSize: attribute.itemSize,
					type: attribute.array.constructor.name,
					array: array,
					normalized: attribute.normalized
				};
  
			}
  
			var groups = this.groups;
  
			if ( groups.length > 0 ) {
  
				data.data.groups = JSON.parse( JSON.stringify( groups ) );
  
			}
  
			var boundingSphere = this.boundingSphere;
  
			if ( boundingSphere !== null ) {
  
				data.data.boundingSphere = {
					center: boundingSphere.center.toArray(),
					radius: boundingSphere.radius
				};
  
			}
  
			return data;
  
		},
  
		clone: function () {
  
			/*
			 // Handle primitives
  
			 var parameters = this.parameters;
  
			 if ( parameters !== undefined ) {
  
			 var values = [];
  
			 for ( var key in parameters ) {
  
			 values.push( parameters[ key ] );
  
			 }
  
			 var geometry = Object.create( this.constructor.prototype );
			 this.constructor.apply( geometry, values );
			 return geometry;
  
			 }
  
			 return new this.constructor().copy( this );
			 */
  
			return new BufferGeometry().copy( this );
  
		},
  
		copy: function ( source ) {
  
			var name, i, l;
  
			// reset
  
			this.index = null;
			this.attributes = {};
			this.morphAttributes = {};
			this.groups = [];
			this.boundingBox = null;
			this.boundingSphere = null;
  
			// name
  
			this.name = source.name;
  
			// index
  
			var index = source.index;
  
			if ( index !== null ) {
  
				this.setIndex( index.clone() );
  
			}
  
			// attributes
  
			var attributes = source.attributes;
  
			for ( name in attributes ) {
  
				var attribute = attributes[ name ];
				this.addAttribute( name, attribute.clone() );
  
			}
  
			// morph attributes
  
			var morphAttributes = source.morphAttributes;
  
			for ( name in morphAttributes ) {
  
				var array = [];
				var morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes
  
				for ( i = 0, l = morphAttribute.length; i < l; i ++ ) {
  
					array.push( morphAttribute[ i ].clone() );
  
				}
  
				this.morphAttributes[ name ] = array;
  
			}
  
			// groups
  
			var groups = source.groups;
  
			for ( i = 0, l = groups.length; i < l; i ++ ) {
  
				var group = groups[ i ];
				this.addGroup( group.start, group.count, group.materialIndex );
  
			}
  
			// bounding box
  
			var boundingBox = source.boundingBox;
  
			if ( boundingBox !== null ) {
  
				this.boundingBox = boundingBox.clone();
  
			}
  
			// bounding sphere
  
			var boundingSphere = source.boundingSphere;
  
			if ( boundingSphere !== null ) {
  
				this.boundingSphere = boundingSphere.clone();
  
			}
  
			// draw range
  
			this.drawRange.start = source.drawRange.start;
			this.drawRange.count = source.drawRange.count;
  
			// user data
  
			this.userData = source.userData;
  
			return this;
  
		},
  
		dispose: function () {
  
			this.dispatchEvent( { type: 'dispose' } );
  
		}
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 * @author Mugen87 / https://github.com/Mugen87
	 */
  
	// BoxGeometry
  
	function BoxGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) {
  
		Geometry.call( this );
  
		this.type = 'BoxGeometry';
  
		this.parameters = {
			width: width,
			height: height,
			depth: depth,
			widthSegments: widthSegments,
			heightSegments: heightSegments,
			depthSegments: depthSegments
		};
  
		this.fromBufferGeometry( new BoxBufferGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) );
		this.mergeVertices();
  
	}
  
	BoxGeometry.prototype = Object.create( Geometry.prototype );
	BoxGeometry.prototype.constructor = BoxGeometry;
  
	// BoxBufferGeometry
  
	function BoxBufferGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) {
  
		BufferGeometry.call( this );
  
		this.type = 'BoxBufferGeometry';
  
		this.parameters = {
			width: width,
			height: height,
			depth: depth,
			widthSegments: widthSegments,
			heightSegments: heightSegments,
			depthSegments: depthSegments
		};
  
		var scope = this;
  
		width = width || 1;
		height = height || 1;
		depth = depth || 1;
  
		// segments
  
		widthSegments = Math.floor( widthSegments ) || 1;
		heightSegments = Math.floor( heightSegments ) || 1;
		depthSegments = Math.floor( depthSegments ) || 1;
  
		// buffers
  
		var indices = [];
		var vertices = [];
		var normals = [];
		var uvs = [];
  
		// helper variables
  
		var numberOfVertices = 0;
		var groupStart = 0;
  
		// build each side of the box geometry
  
		buildPlane( 'z', 'y', 'x', - 1, - 1, depth, height, width, depthSegments, heightSegments, 0 ); // px
		buildPlane( 'z', 'y', 'x', 1, - 1, depth, height, - width, depthSegments, heightSegments, 1 ); // nx
		buildPlane( 'x', 'z', 'y', 1, 1, width, depth, height, widthSegments, depthSegments, 2 ); // py
		buildPlane( 'x', 'z', 'y', 1, - 1, width, depth, - height, widthSegments, depthSegments, 3 ); // ny
		buildPlane( 'x', 'y', 'z', 1, - 1, width, height, depth, widthSegments, heightSegments, 4 ); // pz
		buildPlane( 'x', 'y', 'z', - 1, - 1, width, height, - depth, widthSegments, heightSegments, 5 ); // nz
  
		// build geometry
  
		this.setIndex( indices );
		this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
		this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
		this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
  
		function buildPlane( u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex ) {
  
			var segmentWidth = width / gridX;
			var segmentHeight = height / gridY;
  
			var widthHalf = width / 2;
			var heightHalf = height / 2;
			var depthHalf = depth / 2;
  
			var gridX1 = gridX + 1;
			var gridY1 = gridY + 1;
  
			var vertexCounter = 0;
			var groupCount = 0;
  
			var ix, iy;
  
			var vector = new Vector3();
  
			// generate vertices, normals and uvs
  
			for ( iy = 0; iy < gridY1; iy ++ ) {
  
				var y = iy * segmentHeight - heightHalf;
  
				for ( ix = 0; ix < gridX1; ix ++ ) {
  
					var x = ix * segmentWidth - widthHalf;
  
					// set values to correct vector component
  
					vector[ u ] = x * udir;
					vector[ v ] = y * vdir;
					vector[ w ] = depthHalf;
  
					// now apply vector to vertex buffer
  
					vertices.push( vector.x, vector.y, vector.z );
  
					// set values to correct vector component
  
					vector[ u ] = 0;
					vector[ v ] = 0;
					vector[ w ] = depth > 0 ? 1 : - 1;
  
					// now apply vector to normal buffer
  
					normals.push( vector.x, vector.y, vector.z );
  
					// uvs
  
					uvs.push( ix / gridX );
					uvs.push( 1 - ( iy / gridY ) );
  
					// counters
  
					vertexCounter += 1;
  
				}
  
			}
  
			// indices
  
			// 1. you need three indices to draw a single face
			// 2. a single segment consists of two faces
			// 3. so we need to generate six (2*3) indices per segment
  
			for ( iy = 0; iy < gridY; iy ++ ) {
  
				for ( ix = 0; ix < gridX; ix ++ ) {
  
					var a = numberOfVertices + ix + gridX1 * iy;
					var b = numberOfVertices + ix + gridX1 * ( iy + 1 );
					var c = numberOfVertices + ( ix + 1 ) + gridX1 * ( iy + 1 );
					var d = numberOfVertices + ( ix + 1 ) + gridX1 * iy;
  
					// faces
  
					indices.push( a, b, d );
					indices.push( b, c, d );
  
					// increase counter
  
					groupCount += 6;
  
				}
  
			}
  
			// add a group to the geometry. this will ensure multi material support
  
			scope.addGroup( groupStart, groupCount, materialIndex );
  
			// calculate new start value for groups
  
			groupStart += groupCount;
  
			// update total number of vertices
  
			numberOfVertices += vertexCounter;
  
		}
  
	}
  
	BoxBufferGeometry.prototype = Object.create( BufferGeometry.prototype );
	BoxBufferGeometry.prototype.constructor = BoxBufferGeometry;
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 * @author Mugen87 / https://github.com/Mugen87
	 */
  
	// PlaneGeometry
  
	function PlaneGeometry( width, height, widthSegments, heightSegments ) {
  
		Geometry.call( this );
  
		this.type = 'PlaneGeometry';
  
		this.parameters = {
			width: width,
			height: height,
			widthSegments: widthSegments,
			heightSegments: heightSegments
		};
  
		this.fromBufferGeometry( new PlaneBufferGeometry( width, height, widthSegments, heightSegments ) );
		this.mergeVertices();
  
	}
  
	PlaneGeometry.prototype = Object.create( Geometry.prototype );
	PlaneGeometry.prototype.constructor = PlaneGeometry;
  
	// PlaneBufferGeometry
  
	function PlaneBufferGeometry( width, height, widthSegments, heightSegments ) {
  
		BufferGeometry.call( this );
  
		this.type = 'PlaneBufferGeometry';
  
		this.parameters = {
			width: width,
			height: height,
			widthSegments: widthSegments,
			heightSegments: heightSegments
		};
  
		width = width || 1;
		height = height || 1;
  
		var width_half = width / 2;
		var height_half = height / 2;
  
		var gridX = Math.floor( widthSegments ) || 1;
		var gridY = Math.floor( heightSegments ) || 1;
  
		var gridX1 = gridX + 1;
		var gridY1 = gridY + 1;
  
		var segment_width = width / gridX;
		var segment_height = height / gridY;
  
		var ix, iy;
  
		// buffers
  
		var indices = [];
		var vertices = [];
		var normals = [];
		var uvs = [];
  
		// generate vertices, normals and uvs
  
		for ( iy = 0; iy < gridY1; iy ++ ) {
  
			var y = iy * segment_height - height_half;
  
			for ( ix = 0; ix < gridX1; ix ++ ) {
  
				var x = ix * segment_width - width_half;
  
				vertices.push( x, - y, 0 );
  
				normals.push( 0, 0, 1 );
  
				uvs.push( ix / gridX );
				uvs.push( 1 - ( iy / gridY ) );
  
			}
  
		}
  
		// indices
  
		for ( iy = 0; iy < gridY; iy ++ ) {
  
			for ( ix = 0; ix < gridX; ix ++ ) {
  
				var a = ix + gridX1 * iy;
				var b = ix + gridX1 * ( iy + 1 );
				var c = ( ix + 1 ) + gridX1 * ( iy + 1 );
				var d = ( ix + 1 ) + gridX1 * iy;
  
				// faces
  
				indices.push( a, b, d );
				indices.push( b, c, d );
  
			}
  
		}
  
		// build geometry
  
		this.setIndex( indices );
		this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
		this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
		this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
  
	}
  
	PlaneBufferGeometry.prototype = Object.create( BufferGeometry.prototype );
	PlaneBufferGeometry.prototype.constructor = PlaneBufferGeometry;
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 * @author alteredq / http://alteredqualia.com/
	 */
  
	var materialId = 0;
  
	function Material() {
  
		Object.defineProperty( this, 'id', { value: materialId ++ } );
  
		this.uuid = _Math.generateUUID();
  
		this.name = '';
		this.type = 'Material';
  
		this.fog = true;
		this.lights = true;
  
		this.blending = NormalBlending;
		this.side = FrontSide;
		this.flatShading = false;
		this.vertexColors = NoColors; // THREE.NoColors, THREE.VertexColors, THREE.FaceColors
  
		this.opacity = 1;
		this.transparent = false;
  
		this.blendSrc = SrcAlphaFactor;
		this.blendDst = OneMinusSrcAlphaFactor;
		this.blendEquation = AddEquation;
		this.blendSrcAlpha = null;
		this.blendDstAlpha = null;
		this.blendEquationAlpha = null;
  
		this.depthFunc = LessEqualDepth;
		this.depthTest = true;
		this.depthWrite = true;
  
		this.clippingPlanes = null;
		this.clipIntersection = false;
		this.clipShadows = false;
  
		this.shadowSide = null;
  
		this.colorWrite = true;
  
		this.precision = null; // override the renderer's default precision for this material
  
		this.polygonOffset = false;
		this.polygonOffsetFactor = 0;
		this.polygonOffsetUnits = 0;
  
		this.dithering = false;
  
		this.alphaTest = 0;
		this.premultipliedAlpha = false;
  
		this.overdraw = 0; // Overdrawn pixels (typically between 0 and 1) for fixing antialiasing gaps in CanvasRenderer
  
		this.visible = true;
  
		this.userData = {};
  
		this.needsUpdate = true;
  
	}
  
	Material.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {
  
		constructor: Material,
  
		isMaterial: true,
  
		onBeforeCompile: function () {},
  
		setValues: function ( values ) {
  
			if ( values === undefined ) return;
  
			for ( var key in values ) {
  
				var newValue = values[ key ];
  
				if ( newValue === undefined ) {
  
					console.warn( "THREE.Material: '" + key + "' parameter is undefined." );
					continue;
  
				}
  
				// for backward compatability if shading is set in the constructor
				if ( key === 'shading' ) {
  
					console.warn( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' );
					this.flatShading = ( newValue === FlatShading ) ? true : false;
					continue;
  
				}
  
				var currentValue = this[ key ];
  
				if ( currentValue === undefined ) {
  
					console.warn( "THREE." + this.type + ": '" + key + "' is not a property of this material." );
					continue;
  
				}
  
				if ( currentValue && currentValue.isColor ) {
  
					currentValue.set( newValue );
  
				} else if ( ( currentValue && currentValue.isVector3 ) && ( newValue && newValue.isVector3 ) ) {
  
					currentValue.copy( newValue );
  
				} else if ( key === 'overdraw' ) {
  
					// ensure overdraw is backwards-compatible with legacy boolean type
					this[ key ] = Number( newValue );
  
				} else {
  
					this[ key ] = newValue;
  
				}
  
			}
  
		},
  
		toJSON: function ( meta ) {
  
			var isRoot = ( meta === undefined || typeof meta === 'string' );
  
			if ( isRoot ) {
  
				meta = {
					textures: {},
					images: {}
				};
  
			}
  
			var data = {
				metadata: {
					version: 4.5,
					type: 'Material',
					generator: 'Material.toJSON'
				}
			};
  
			// standard Material serialization
			data.uuid = this.uuid;
			data.type = this.type;
  
			if ( this.name !== '' ) data.name = this.name;
  
			if ( this.color && this.color.isColor ) data.color = this.color.getHex();
  
			if ( this.roughness !== undefined ) data.roughness = this.roughness;
			if ( this.metalness !== undefined ) data.metalness = this.metalness;
  
			if ( this.emissive && this.emissive.isColor ) data.emissive = this.emissive.getHex();
			if ( this.emissiveIntensity !== 1 ) data.emissiveIntensity = this.emissiveIntensity;
  
			if ( this.specular && this.specular.isColor ) data.specular = this.specular.getHex();
			if ( this.shininess !== undefined ) data.shininess = this.shininess;
			if ( this.clearCoat !== undefined ) data.clearCoat = this.clearCoat;
			if ( this.clearCoatRoughness !== undefined ) data.clearCoatRoughness = this.clearCoatRoughness;
  
			if ( this.map && this.map.isTexture ) data.map = this.map.toJSON( meta ).uuid;
			if ( this.alphaMap && this.alphaMap.isTexture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid;
			if ( this.lightMap && this.lightMap.isTexture ) data.lightMap = this.lightMap.toJSON( meta ).uuid;
  
			if ( this.aoMap && this.aoMap.isTexture ) {
  
				data.aoMap = this.aoMap.toJSON( meta ).uuid;
				data.aoMapIntensity = this.aoMapIntensity;
  
			}
  
			if ( this.bumpMap && this.bumpMap.isTexture ) {
  
				data.bumpMap = this.bumpMap.toJSON( meta ).uuid;
				data.bumpScale = this.bumpScale;
  
			}
  
			if ( this.normalMap && this.normalMap.isTexture ) {
  
				data.normalMap = this.normalMap.toJSON( meta ).uuid;
				data.normalScale = this.normalScale.toArray();
  
			}
  
			if ( this.displacementMap && this.displacementMap.isTexture ) {
  
				data.displacementMap = this.displacementMap.toJSON( meta ).uuid;
				data.displacementScale = this.displacementScale;
				data.displacementBias = this.displacementBias;
  
			}
  
			if ( this.roughnessMap && this.roughnessMap.isTexture ) data.roughnessMap = this.roughnessMap.toJSON( meta ).uuid;
			if ( this.metalnessMap && this.metalnessMap.isTexture ) data.metalnessMap = this.metalnessMap.toJSON( meta ).uuid;
  
			if ( this.emissiveMap && this.emissiveMap.isTexture ) data.emissiveMap = this.emissiveMap.toJSON( meta ).uuid;
			if ( this.specularMap && this.specularMap.isTexture ) data.specularMap = this.specularMap.toJSON( meta ).uuid;
  
			if ( this.envMap && this.envMap.isTexture ) {
  
				data.envMap = this.envMap.toJSON( meta ).uuid;
				data.reflectivity = this.reflectivity; // Scale behind envMap
  
			}
  
			if ( this.gradientMap && this.gradientMap.isTexture ) {
  
				data.gradientMap = this.gradientMap.toJSON( meta ).uuid;
  
			}
  
			if ( this.size !== undefined ) data.size = this.size;
			if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation;
  
			if ( this.blending !== NormalBlending ) data.blending = this.blending;
			if ( this.flatShading === true ) data.flatShading = this.flatShading;
			if ( this.side !== FrontSide ) data.side = this.side;
			if ( this.vertexColors !== NoColors ) data.vertexColors = this.vertexColors;
  
			if ( this.opacity < 1 ) data.opacity = this.opacity;
			if ( this.transparent === true ) data.transparent = this.transparent;
  
			data.depthFunc = this.depthFunc;
			data.depthTest = this.depthTest;
			data.depthWrite = this.depthWrite;
  
			// rotation (SpriteMaterial)
			if ( this.rotation !== 0 ) data.rotation = this.rotation;
  
			if ( this.linewidth !== 1 ) data.linewidth = this.linewidth;
			if ( this.dashSize !== undefined ) data.dashSize = this.dashSize;
			if ( this.gapSize !== undefined ) data.gapSize = this.gapSize;
			if ( this.scale !== undefined ) data.scale = this.scale;
  
			if ( this.dithering === true ) data.dithering = true;
  
			if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest;
			if ( this.premultipliedAlpha === true ) data.premultipliedAlpha = this.premultipliedAlpha;
  
			if ( this.wireframe === true ) data.wireframe = this.wireframe;
			if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth;
			if ( this.wireframeLinecap !== 'round' ) data.wireframeLinecap = this.wireframeLinecap;
			if ( this.wireframeLinejoin !== 'round' ) data.wireframeLinejoin = this.wireframeLinejoin;
  
			if ( this.morphTargets === true ) data.morphTargets = true;
			if ( this.skinning === true ) data.skinning = true;
  
			if ( this.visible === false ) data.visible = false;
			if ( JSON.stringify( this.userData ) !== '{}' ) data.userData = this.userData;
  
			// TODO: Copied from Object3D.toJSON
  
			function extractFromCache( cache ) {
  
				var values = [];
  
				for ( var key in cache ) {
  
					var data = cache[ key ];
					delete data.metadata;
					values.push( data );
  
				}
  
				return values;
  
			}
  
			if ( isRoot ) {
  
				var textures = extractFromCache( meta.textures );
				var images = extractFromCache( meta.images );
  
				if ( textures.length > 0 ) data.textures = textures;
				if ( images.length > 0 ) data.images = images;
  
			}
  
			return data;
  
		},
  
		clone: function () {
  
			return new this.constructor().copy( this );
  
		},
  
		copy: function ( source ) {
  
			this.name = source.name;
  
			this.fog = source.fog;
			this.lights = source.lights;
  
			this.blending = source.blending;
			this.side = source.side;
			this.flatShading = source.flatShading;
			this.vertexColors = source.vertexColors;
  
			this.opacity = source.opacity;
			this.transparent = source.transparent;
  
			this.blendSrc = source.blendSrc;
			this.blendDst = source.blendDst;
			this.blendEquation = source.blendEquation;
			this.blendSrcAlpha = source.blendSrcAlpha;
			this.blendDstAlpha = source.blendDstAlpha;
			this.blendEquationAlpha = source.blendEquationAlpha;
  
			this.depthFunc = source.depthFunc;
			this.depthTest = source.depthTest;
			this.depthWrite = source.depthWrite;
  
			this.colorWrite = source.colorWrite;
  
			this.precision = source.precision;
  
			this.polygonOffset = source.polygonOffset;
			this.polygonOffsetFactor = source.polygonOffsetFactor;
			this.polygonOffsetUnits = source.polygonOffsetUnits;
  
			this.dithering = source.dithering;
  
			this.alphaTest = source.alphaTest;
			this.premultipliedAlpha = source.premultipliedAlpha;
  
			this.overdraw = source.overdraw;
  
			this.visible = source.visible;
			this.userData = JSON.parse( JSON.stringify( source.userData ) );
  
			this.clipShadows = source.clipShadows;
			this.clipIntersection = source.clipIntersection;
  
			var srcPlanes = source.clippingPlanes,
				dstPlanes = null;
  
			if ( srcPlanes !== null ) {
  
				var n = srcPlanes.length;
				dstPlanes = new Array( n );
  
				for ( var i = 0; i !== n; ++ i )
					dstPlanes[ i ] = srcPlanes[ i ].clone();
  
			}
  
			this.clippingPlanes = dstPlanes;
  
			this.shadowSide = source.shadowSide;
  
			return this;
  
		},
  
		dispose: function () {
  
			this.dispatchEvent( { type: 'dispose' } );
  
		}
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 * @author alteredq / http://alteredqualia.com/
	 *
	 * parameters = {
	 *  color: <hex>,
	 *  opacity: <float>,
	 *  map: new THREE.Texture( <Image> ),
	 *
	 *  lightMap: new THREE.Texture( <Image> ),
	 *  lightMapIntensity: <float>
	 *
	 *  aoMap: new THREE.Texture( <Image> ),
	 *  aoMapIntensity: <float>
	 *
	 *  specularMap: new THREE.Texture( <Image> ),
	 *
	 *  alphaMap: new THREE.Texture( <Image> ),
	 *
	 *  envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ),
	 *  combine: THREE.Multiply,
	 *  reflectivity: <float>,
	 *  refractionRatio: <float>,
	 *
	 *  depthTest: <bool>,
	 *  depthWrite: <bool>,
	 *
	 *  wireframe: <boolean>,
	 *  wireframeLinewidth: <float>,
	 *
	 *  skinning: <bool>,
	 *  morphTargets: <bool>
	 * }
	 */
  
	function MeshBasicMaterial( parameters ) {
  
		Material.call( this );
  
		this.type = 'MeshBasicMaterial';
  
		this.color = new Color( 0xffffff ); // emissive
  
		this.map = null;
  
		this.lightMap = null;
		this.lightMapIntensity = 1.0;
  
		this.aoMap = null;
		this.aoMapIntensity = 1.0;
  
		this.specularMap = null;
  
		this.alphaMap = null;
  
		this.envMap = null;
		this.combine = MultiplyOperation;
		this.reflectivity = 1;
		this.refractionRatio = 0.98;
  
		this.wireframe = false;
		this.wireframeLinewidth = 1;
		this.wireframeLinecap = 'round';
		this.wireframeLinejoin = 'round';
  
		this.skinning = false;
		this.morphTargets = false;
  
		this.lights = false;
  
		this.setValues( parameters );
  
	}
  
	MeshBasicMaterial.prototype = Object.create( Material.prototype );
	MeshBasicMaterial.prototype.constructor = MeshBasicMaterial;
  
	MeshBasicMaterial.prototype.isMeshBasicMaterial = true;
  
	MeshBasicMaterial.prototype.copy = function ( source ) {
  
		Material.prototype.copy.call( this, source );
  
		this.color.copy( source.color );
  
		this.map = source.map;
  
		this.lightMap = source.lightMap;
		this.lightMapIntensity = source.lightMapIntensity;
  
		this.aoMap = source.aoMap;
		this.aoMapIntensity = source.aoMapIntensity;
  
		this.specularMap = source.specularMap;
  
		this.alphaMap = source.alphaMap;
  
		this.envMap = source.envMap;
		this.combine = source.combine;
		this.reflectivity = source.reflectivity;
		this.refractionRatio = source.refractionRatio;
  
		this.wireframe = source.wireframe;
		this.wireframeLinewidth = source.wireframeLinewidth;
		this.wireframeLinecap = source.wireframeLinecap;
		this.wireframeLinejoin = source.wireframeLinejoin;
  
		this.skinning = source.skinning;
		this.morphTargets = source.morphTargets;
  
		return this;
  
	};
  
	/**
	 * @author alteredq / http://alteredqualia.com/
	 *
	 * parameters = {
	 *  defines: { "label" : "value" },
	 *  uniforms: { "parameter1": { value: 1.0 }, "parameter2": { value2: 2 } },
	 *
	 *  fragmentShader: <string>,
	 *  vertexShader: <string>,
	 *
	 *  wireframe: <boolean>,
	 *  wireframeLinewidth: <float>,
	 *
	 *  lights: <bool>,
	 *
	 *  skinning: <bool>,
	 *  morphTargets: <bool>,
	 *  morphNormals: <bool>
	 * }
	 */
  
	function ShaderMaterial( parameters ) {
  
		Material.call( this );
  
		this.type = 'ShaderMaterial';
  
		this.defines = {};
		this.uniforms = {};
  
		this.vertexShader = 'void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}';
		this.fragmentShader = 'void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}';
  
		this.linewidth = 1;
  
		this.wireframe = false;
		this.wireframeLinewidth = 1;
  
		this.fog = false; // set to use scene fog
		this.lights = false; // set to use scene lights
		this.clipping = false; // set to use user-defined clipping planes
  
		this.skinning = false; // set to use skinning attribute streams
		this.morphTargets = false; // set to use morph targets
		this.morphNormals = false; // set to use morph normals
  
		this.extensions = {
			derivatives: false, // set to use derivatives
			fragDepth: false, // set to use fragment depth values
			drawBuffers: false, // set to use draw buffers
			shaderTextureLOD: false // set to use shader texture LOD
		};
  
		// When rendered geometry doesn't include these attributes but the material does,
		// use these default values in WebGL. This avoids errors when buffer data is missing.
		this.defaultAttributeValues = {
			'color': [ 1, 1, 1 ],
			'uv': [ 0, 0 ],
			'uv2': [ 0, 0 ]
		};
  
		this.index0AttributeName = undefined;
		this.uniformsNeedUpdate = false;
  
		if ( parameters !== undefined ) {
  
			if ( parameters.attributes !== undefined ) {
  
				console.error( 'THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead.' );
  
			}
  
			this.setValues( parameters );
  
		}
  
	}
  
	ShaderMaterial.prototype = Object.create( Material.prototype );
	ShaderMaterial.prototype.constructor = ShaderMaterial;
  
	ShaderMaterial.prototype.isShaderMaterial = true;
  
	ShaderMaterial.prototype.copy = function ( source ) {
  
		Material.prototype.copy.call( this, source );
  
		this.fragmentShader = source.fragmentShader;
		this.vertexShader = source.vertexShader;
  
		this.uniforms = UniformsUtils.clone( source.uniforms );
  
		this.defines = Object.assign( {}, source.defines );
  
		this.wireframe = source.wireframe;
		this.wireframeLinewidth = source.wireframeLinewidth;
  
		this.lights = source.lights;
		this.clipping = source.clipping;
  
		this.skinning = source.skinning;
  
		this.morphTargets = source.morphTargets;
		this.morphNormals = source.morphNormals;
  
		this.extensions = source.extensions;
  
		return this;
  
	};
  
	ShaderMaterial.prototype.toJSON = function ( meta ) {
  
		var data = Material.prototype.toJSON.call( this, meta );
  
		data.uniforms = this.uniforms;
		data.vertexShader = this.vertexShader;
		data.fragmentShader = this.fragmentShader;
  
		return data;
  
	};
  
	/**
	 * @author bhouston / http://clara.io
	 */
  
	function Ray( origin, direction ) {
  
		this.origin = ( origin !== undefined ) ? origin : new Vector3();
		this.direction = ( direction !== undefined ) ? direction : new Vector3();
  
	}
  
	Object.assign( Ray.prototype, {
  
		set: function ( origin, direction ) {
  
			this.origin.copy( origin );
			this.direction.copy( direction );
  
			return this;
  
		},
  
		clone: function () {
  
			return new this.constructor().copy( this );
  
		},
  
		copy: function ( ray ) {
  
			this.origin.copy( ray.origin );
			this.direction.copy( ray.direction );
  
			return this;
  
		},
  
		at: function ( t, target ) {
  
			if ( target === undefined ) {
  
				console.warn( 'THREE.Ray: .at() target is now required' );
				target = new Vector3();
  
			}
  
			return target.copy( this.direction ).multiplyScalar( t ).add( this.origin );
  
		},
  
		lookAt: function ( v ) {
  
			this.direction.copy( v ).sub( this.origin ).normalize();
  
			return this;
  
		},
  
		recast: function () {
  
			var v1 = new Vector3();
  
			return function recast( t ) {
  
				this.origin.copy( this.at( t, v1 ) );
  
				return this;
  
			};
  
		}(),
  
		closestPointToPoint: function ( point, target ) {
  
			if ( target === undefined ) {
  
				console.warn( 'THREE.Ray: .closestPointToPoint() target is now required' );
				target = new Vector3();
  
			}
  
			target.subVectors( point, this.origin );
  
			var directionDistance = target.dot( this.direction );
  
			if ( directionDistance < 0 ) {
  
				return target.copy( this.origin );
  
			}
  
			return target.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin );
  
		},
  
		distanceToPoint: function ( point ) {
  
			return Math.sqrt( this.distanceSqToPoint( point ) );
  
		},
  
		distanceSqToPoint: function () {
  
			var v1 = new Vector3();
  
			return function distanceSqToPoint( point ) {
  
				var directionDistance = v1.subVectors( point, this.origin ).dot( this.direction );
  
				// point behind the ray
  
				if ( directionDistance < 0 ) {
  
					return this.origin.distanceToSquared( point );
  
				}
  
				v1.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin );
  
				return v1.distanceToSquared( point );
  
			};
  
		}(),
  
		distanceSqToSegment: function () {
  
			var segCenter = new Vector3();
			var segDir = new Vector3();
			var diff = new Vector3();
  
			return function distanceSqToSegment( v0, v1, optionalPointOnRay, optionalPointOnSegment ) {
  
				// from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteDistRaySegment.h
				// It returns the min distance between the ray and the segment
				// defined by v0 and v1
				// It can also set two optional targets :
				// - The closest point on the ray
				// - The closest point on the segment
  
				segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 );
				segDir.copy( v1 ).sub( v0 ).normalize();
				diff.copy( this.origin ).sub( segCenter );
  
				var segExtent = v0.distanceTo( v1 ) * 0.5;
				var a01 = - this.direction.dot( segDir );
				var b0 = diff.dot( this.direction );
				var b1 = - diff.dot( segDir );
				var c = diff.lengthSq();
				var det = Math.abs( 1 - a01 * a01 );
				var s0, s1, sqrDist, extDet;
  
				if ( det > 0 ) {
  
					// The ray and segment are not parallel.
  
					s0 = a01 * b1 - b0;
					s1 = a01 * b0 - b1;
					extDet = segExtent * det;
  
					if ( s0 >= 0 ) {
  
						if ( s1 >= - extDet ) {
  
							if ( s1 <= extDet ) {
  
								// region 0
								// Minimum at interior points of ray and segment.
  
								var invDet = 1 / det;
								s0 *= invDet;
								s1 *= invDet;
								sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c;
  
							} else {
  
								// region 1
  
								s1 = segExtent;
								s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
								sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
  
							}
  
						} else {
  
							// region 5
  
							s1 = - segExtent;
							s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
							sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
  
						}
  
					} else {
  
						if ( s1 <= - extDet ) {
  
							// region 4
  
							s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) );
							s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );
							sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
  
						} else if ( s1 <= extDet ) {
  
							// region 3
  
							s0 = 0;
							s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent );
							sqrDist = s1 * ( s1 + 2 * b1 ) + c;
  
						} else {
  
							// region 2
  
							s0 = Math.max( 0, - ( a01 * segExtent + b0 ) );
							s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );
							sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
  
						}
  
					}
  
				} else {
  
					// Ray and segment are parallel.
  
					s1 = ( a01 > 0 ) ? - segExtent : segExtent;
					s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
					sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
  
				}
  
				if ( optionalPointOnRay ) {
  
					optionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin );
  
				}
  
				if ( optionalPointOnSegment ) {
  
					optionalPointOnSegment.copy( segDir ).multiplyScalar( s1 ).add( segCenter );
  
				}
  
				return sqrDist;
  
			};
  
		}(),
  
		intersectSphere: function () {
  
			var v1 = new Vector3();
  
			return function intersectSphere( sphere, target ) {
  
				v1.subVectors( sphere.center, this.origin );
				var tca = v1.dot( this.direction );
				var d2 = v1.dot( v1 ) - tca * tca;
				var radius2 = sphere.radius * sphere.radius;
  
				if ( d2 > radius2 ) return null;
  
				var thc = Math.sqrt( radius2 - d2 );
  
				// t0 = first intersect point - entrance on front of sphere
				var t0 = tca - thc;
  
				// t1 = second intersect point - exit point on back of sphere
				var t1 = tca + thc;
  
				// test to see if both t0 and t1 are behind the ray - if so, return null
				if ( t0 < 0 && t1 < 0 ) return null;
  
				// test to see if t0 is behind the ray:
				// if it is, the ray is inside the sphere, so return the second exit point scaled by t1,
				// in order to always return an intersect point that is in front of the ray.
				if ( t0 < 0 ) return this.at( t1, target );
  
				// else t0 is in front of the ray, so return the first collision point scaled by t0
				return this.at( t0, target );
  
			};
  
		}(),
  
		intersectsSphere: function ( sphere ) {
  
			return this.distanceToPoint( sphere.center ) <= sphere.radius;
  
		},
  
		distanceToPlane: function ( plane ) {
  
			var denominator = plane.normal.dot( this.direction );
  
			if ( denominator === 0 ) {
  
				// line is coplanar, return origin
				if ( plane.distanceToPoint( this.origin ) === 0 ) {
  
					return 0;
  
				}
  
				// Null is preferable to undefined since undefined means.... it is undefined
  
				return null;
  
			}
  
			var t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator;
  
			// Return if the ray never intersects the plane
  
			return t >= 0 ? t : null;
  
		},
  
		intersectPlane: function ( plane, target ) {
  
			var t = this.distanceToPlane( plane );
  
			if ( t === null ) {
  
				return null;
  
			}
  
			return this.at( t, target );
  
		},
  
		intersectsPlane: function ( plane ) {
  
			// check if the ray lies on the plane first
  
			var distToPoint = plane.distanceToPoint( this.origin );
  
			if ( distToPoint === 0 ) {
  
				return true;
  
			}
  
			var denominator = plane.normal.dot( this.direction );
  
			if ( denominator * distToPoint < 0 ) {
  
				return true;
  
			}
  
			// ray origin is behind the plane (and is pointing behind it)
  
			return false;
  
		},
  
		intersectBox: function ( box, target ) {
  
			var tmin, tmax, tymin, tymax, tzmin, tzmax;
  
			var invdirx = 1 / this.direction.x,
				invdiry = 1 / this.direction.y,
				invdirz = 1 / this.direction.z;
  
			var origin = this.origin;
  
			if ( invdirx >= 0 ) {
  
				tmin = ( box.min.x - origin.x ) * invdirx;
				tmax = ( box.max.x - origin.x ) * invdirx;
  
			} else {
  
				tmin = ( box.max.x - origin.x ) * invdirx;
				tmax = ( box.min.x - origin.x ) * invdirx;
  
			}
  
			if ( invdiry >= 0 ) {
  
				tymin = ( box.min.y - origin.y ) * invdiry;
				tymax = ( box.max.y - origin.y ) * invdiry;
  
			} else {
  
				tymin = ( box.max.y - origin.y ) * invdiry;
				tymax = ( box.min.y - origin.y ) * invdiry;
  
			}
  
			if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null;
  
			// These lines also handle the case where tmin or tmax is NaN
			// (result of 0 * Infinity). x !== x returns true if x is NaN
  
			if ( tymin > tmin || tmin !== tmin ) tmin = tymin;
  
			if ( tymax < tmax || tmax !== tmax ) tmax = tymax;
  
			if ( invdirz >= 0 ) {
  
				tzmin = ( box.min.z - origin.z ) * invdirz;
				tzmax = ( box.max.z - origin.z ) * invdirz;
  
			} else {
  
				tzmin = ( box.max.z - origin.z ) * invdirz;
				tzmax = ( box.min.z - origin.z ) * invdirz;
  
			}
  
			if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null;
  
			if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin;
  
			if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax;
  
			//return point closest to the ray (positive side)
  
			if ( tmax < 0 ) return null;
  
			return this.at( tmin >= 0 ? tmin : tmax, target );
  
		},
  
		intersectsBox: ( function () {
  
			var v = new Vector3();
  
			return function intersectsBox( box ) {
  
				return this.intersectBox( box, v ) !== null;
  
			};
  
		} )(),
  
		intersectTriangle: function () {
  
			// Compute the offset origin, edges, and normal.
			var diff = new Vector3();
			var edge1 = new Vector3();
			var edge2 = new Vector3();
			var normal = new Vector3();
  
			return function intersectTriangle( a, b, c, backfaceCulling, target ) {
  
				// from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h
  
				edge1.subVectors( b, a );
				edge2.subVectors( c, a );
				normal.crossVectors( edge1, edge2 );
  
				// Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction,
				// E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by
				//   |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2))
				//   |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q))
				//   |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N)
				var DdN = this.direction.dot( normal );
				var sign;
  
				if ( DdN > 0 ) {
  
					if ( backfaceCulling ) return null;
					sign = 1;
  
				} else if ( DdN < 0 ) {
  
					sign = - 1;
					DdN = - DdN;
  
				} else {
  
					return null;
  
				}
  
				diff.subVectors( this.origin, a );
				var DdQxE2 = sign * this.direction.dot( edge2.crossVectors( diff, edge2 ) );
  
				// b1 < 0, no intersection
				if ( DdQxE2 < 0 ) {
  
					return null;
  
				}
  
				var DdE1xQ = sign * this.direction.dot( edge1.cross( diff ) );
  
				// b2 < 0, no intersection
				if ( DdE1xQ < 0 ) {
  
					return null;
  
				}
  
				// b1+b2 > 1, no intersection
				if ( DdQxE2 + DdE1xQ > DdN ) {
  
					return null;
  
				}
  
				// Line intersects triangle, check if ray does.
				var QdN = - sign * diff.dot( normal );
  
				// t < 0, no intersection
				if ( QdN < 0 ) {
  
					return null;
  
				}
  
				// Ray intersects triangle.
				return this.at( QdN / DdN, target );
  
			};
  
		}(),
  
		applyMatrix4: function ( matrix4 ) {
  
			this.origin.applyMatrix4( matrix4 );
			this.direction.transformDirection( matrix4 );
  
			return this;
  
		},
  
		equals: function ( ray ) {
  
			return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction );
  
		}
  
	} );
  
	/**
	 * @author bhouston / http://clara.io
	 */
  
	function Line3( start, end ) {
  
		this.start = ( start !== undefined ) ? start : new Vector3();
		this.end = ( end !== undefined ) ? end : new Vector3();
  
	}
  
	Object.assign( Line3.prototype, {
  
		set: function ( start, end ) {
  
			this.start.copy( start );
			this.end.copy( end );
  
			return this;
  
		},
  
		clone: function () {
  
			return new this.constructor().copy( this );
  
		},
  
		copy: function ( line ) {
  
			this.start.copy( line.start );
			this.end.copy( line.end );
  
			return this;
  
		},
  
		getCenter: function ( target ) {
  
			if ( target === undefined ) {
  
				console.warn( 'THREE.Line3: .getCenter() target is now required' );
				target = new Vector3();
  
			}
  
			return target.addVectors( this.start, this.end ).multiplyScalar( 0.5 );
  
		},
  
		delta: function ( target ) {
  
			if ( target === undefined ) {
  
				console.warn( 'THREE.Line3: .delta() target is now required' );
				target = new Vector3();
  
			}
  
			return target.subVectors( this.end, this.start );
  
		},
  
		distanceSq: function () {
  
			return this.start.distanceToSquared( this.end );
  
		},
  
		distance: function () {
  
			return this.start.distanceTo( this.end );
  
		},
  
		at: function ( t, target ) {
  
			if ( target === undefined ) {
  
				console.warn( 'THREE.Line3: .at() target is now required' );
				target = new Vector3();
  
			}
  
			return this.delta( target ).multiplyScalar( t ).add( this.start );
  
		},
  
		closestPointToPointParameter: function () {
  
			var startP = new Vector3();
			var startEnd = new Vector3();
  
			return function closestPointToPointParameter( point, clampToLine ) {
  
				startP.subVectors( point, this.start );
				startEnd.subVectors( this.end, this.start );
  
				var startEnd2 = startEnd.dot( startEnd );
				var startEnd_startP = startEnd.dot( startP );
  
				var t = startEnd_startP / startEnd2;
  
				if ( clampToLine ) {
  
					t = _Math.clamp( t, 0, 1 );
  
				}
  
				return t;
  
			};
  
		}(),
  
		closestPointToPoint: function ( point, clampToLine, target ) {
  
			var t = this.closestPointToPointParameter( point, clampToLine );
  
			if ( target === undefined ) {
  
				console.warn( 'THREE.Line3: .closestPointToPoint() target is now required' );
				target = new Vector3();
  
			}
  
			return this.delta( target ).multiplyScalar( t ).add( this.start );
  
		},
  
		applyMatrix4: function ( matrix ) {
  
			this.start.applyMatrix4( matrix );
			this.end.applyMatrix4( matrix );
  
			return this;
  
		},
  
		equals: function ( line ) {
  
			return line.start.equals( this.start ) && line.end.equals( this.end );
  
		}
  
	} );
  
	/**
	 * @author bhouston / http://clara.io
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function Triangle( a, b, c ) {
  
		this.a = ( a !== undefined ) ? a : new Vector3();
		this.b = ( b !== undefined ) ? b : new Vector3();
		this.c = ( c !== undefined ) ? c : new Vector3();
  
	}
  
	Object.assign( Triangle, {
  
		getNormal: function () {
  
			var v0 = new Vector3();
  
			return function getNormal( a, b, c, target ) {
  
				if ( target === undefined ) {
  
					console.warn( 'THREE.Triangle: .getNormal() target is now required' );
					target = new Vector3();
  
				}
  
				target.subVectors( c, b );
				v0.subVectors( a, b );
				target.cross( v0 );
  
				var targetLengthSq = target.lengthSq();
				if ( targetLengthSq > 0 ) {
  
					return target.multiplyScalar( 1 / Math.sqrt( targetLengthSq ) );
  
				}
  
				return target.set( 0, 0, 0 );
  
			};
  
		}(),
  
		// static/instance method to calculate barycentric coordinates
		// based on: http://www.blackpawn.com/texts/pointinpoly/default.html
		getBarycoord: function () {
  
			var v0 = new Vector3();
			var v1 = new Vector3();
			var v2 = new Vector3();
  
			return function getBarycoord( point, a, b, c, target ) {
  
				v0.subVectors( c, a );
				v1.subVectors( b, a );
				v2.subVectors( point, a );
  
				var dot00 = v0.dot( v0 );
				var dot01 = v0.dot( v1 );
				var dot02 = v0.dot( v2 );
				var dot11 = v1.dot( v1 );
				var dot12 = v1.dot( v2 );
  
				var denom = ( dot00 * dot11 - dot01 * dot01 );
  
				if ( target === undefined ) {
  
					console.warn( 'THREE.Triangle: .getBarycoord() target is now required' );
					target = new Vector3();
  
				}
  
				// collinear or singular triangle
				if ( denom === 0 ) {
  
					// arbitrary location outside of triangle?
					// not sure if this is the best idea, maybe should be returning undefined
					return target.set( - 2, - 1, - 1 );
  
				}
  
				var invDenom = 1 / denom;
				var u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom;
				var v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom;
  
				// barycentric coordinates must always sum to 1
				return target.set( 1 - u - v, v, u );
  
			};
  
		}(),
  
		containsPoint: function () {
  
			var v1 = new Vector3();
  
			return function containsPoint( point, a, b, c ) {
  
				Triangle.getBarycoord( point, a, b, c, v1 );
  
				return ( v1.x >= 0 ) && ( v1.y >= 0 ) && ( ( v1.x + v1.y ) <= 1 );
  
			};
  
		}()
  
	} );
  
	Object.assign( Triangle.prototype, {
  
		set: function ( a, b, c ) {
  
			this.a.copy( a );
			this.b.copy( b );
			this.c.copy( c );
  
			return this;
  
		},
  
		setFromPointsAndIndices: function ( points, i0, i1, i2 ) {
  
			this.a.copy( points[ i0 ] );
			this.b.copy( points[ i1 ] );
			this.c.copy( points[ i2 ] );
  
			return this;
  
		},
  
		clone: function () {
  
			return new this.constructor().copy( this );
  
		},
  
		copy: function ( triangle ) {
  
			this.a.copy( triangle.a );
			this.b.copy( triangle.b );
			this.c.copy( triangle.c );
  
			return this;
  
		},
  
		getArea: function () {
  
			var v0 = new Vector3();
			var v1 = new Vector3();
  
			return function getArea() {
  
				v0.subVectors( this.c, this.b );
				v1.subVectors( this.a, this.b );
  
				return v0.cross( v1 ).length() * 0.5;
  
			};
  
		}(),
  
		getMidpoint: function ( target ) {
  
			if ( target === undefined ) {
  
				console.warn( 'THREE.Triangle: .getMidpoint() target is now required' );
				target = new Vector3();
  
			}
  
			return target.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 );
  
		},
  
		getNormal: function ( target ) {
  
			return Triangle.getNormal( this.a, this.b, this.c, target );
  
		},
  
		getPlane: function ( target ) {
  
			if ( target === undefined ) {
  
				console.warn( 'THREE.Triangle: .getPlane() target is now required' );
				target = new Vector3();
  
			}
  
			return target.setFromCoplanarPoints( this.a, this.b, this.c );
  
		},
  
		getBarycoord: function ( point, target ) {
  
			return Triangle.getBarycoord( point, this.a, this.b, this.c, target );
  
		},
  
		containsPoint: function ( point ) {
  
			return Triangle.containsPoint( point, this.a, this.b, this.c );
  
		},
  
		intersectsBox: function ( box ) {
  
			return box.intersectsTriangle( this );
  
		},
  
		closestPointToPoint: function () {
  
			var plane = new Plane();
			var edgeList = [ new Line3(), new Line3(), new Line3() ];
			var projectedPoint = new Vector3();
			var closestPoint = new Vector3();
  
			return function closestPointToPoint( point, target ) {
  
				if ( target === undefined ) {
  
					console.warn( 'THREE.Triangle: .closestPointToPoint() target is now required' );
					target = new Vector3();
  
				}
  
				var minDistance = Infinity;
  
				// project the point onto the plane of the triangle
  
				plane.setFromCoplanarPoints( this.a, this.b, this.c );
				plane.projectPoint( point, projectedPoint );
  
				// check if the projection lies within the triangle
  
				if ( this.containsPoint( projectedPoint ) === true ) {
  
					// if so, this is the closest point
  
					target.copy( projectedPoint );
  
				} else {
  
					// if not, the point falls outside the triangle. the target is the closest point to the triangle's edges or vertices
  
					edgeList[ 0 ].set( this.a, this.b );
					edgeList[ 1 ].set( this.b, this.c );
					edgeList[ 2 ].set( this.c, this.a );
  
					for ( var i = 0; i < edgeList.length; i ++ ) {
  
						edgeList[ i ].closestPointToPoint( projectedPoint, true, closestPoint );
  
						var distance = projectedPoint.distanceToSquared( closestPoint );
  
						if ( distance < minDistance ) {
  
							minDistance = distance;
  
							target.copy( closestPoint );
  
						}
  
					}
  
				}
  
				return target;
  
			};
  
		}(),
  
		equals: function ( triangle ) {
  
			return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c );
  
		}
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 * @author alteredq / http://alteredqualia.com/
	 * @author mikael emtinger / http://gomo.se/
	 * @author jonobr1 / http://jonobr1.com/
	 */
  
	function Mesh( geometry, material ) {
  
		Object3D.call( this );
  
		this.type = 'Mesh';
  
		this.geometry = geometry !== undefined ? geometry : new BufferGeometry();
		this.material = material !== undefined ? material : new MeshBasicMaterial( { color: Math.random() * 0xffffff } );
  
		this.drawMode = TrianglesDrawMode;
  
		this.updateMorphTargets();
  
	}
  
	Mesh.prototype = Object.assign( Object.create( Object3D.prototype ), {
  
		constructor: Mesh,
  
		isMesh: true,
  
		setDrawMode: function ( value ) {
  
			this.drawMode = value;
  
		},
  
		copy: function ( source ) {
  
			Object3D.prototype.copy.call( this, source );
  
			this.drawMode = source.drawMode;
  
			if ( source.morphTargetInfluences !== undefined ) {
  
				this.morphTargetInfluences = source.morphTargetInfluences.slice();
  
			}
  
			if ( source.morphTargetDictionary !== undefined ) {
  
				this.morphTargetDictionary = Object.assign( {}, source.morphTargetDictionary );
  
			}
  
			return this;
  
		},
  
		updateMorphTargets: function () {
  
			var geometry = this.geometry;
			var m, ml, name;
  
			if ( geometry.isBufferGeometry ) {
  
				var morphAttributes = geometry.morphAttributes;
				var keys = Object.keys( morphAttributes );
  
				if ( keys.length > 0 ) {
  
					var morphAttribute = morphAttributes[ keys[ 0 ] ];
  
					if ( morphAttribute !== undefined ) {
  
						this.morphTargetInfluences = [];
						this.morphTargetDictionary = {};
  
						for ( m = 0, ml = morphAttribute.length; m < ml; m ++ ) {
  
							name = morphAttribute[ m ].name || String( m );
  
							this.morphTargetInfluences.push( 0 );
							this.morphTargetDictionary[ name ] = m;
  
						}
  
					}
  
				}
  
			} else {
  
				var morphTargets = geometry.morphTargets;
  
				if ( morphTargets !== undefined && morphTargets.length > 0 ) {
  
					this.morphTargetInfluences = [];
					this.morphTargetDictionary = {};
  
					for ( m = 0, ml = morphTargets.length; m < ml; m ++ ) {
  
						name = morphTargets[ m ].name || String( m );
  
						this.morphTargetInfluences.push( 0 );
						this.morphTargetDictionary[ name ] = m;
  
					}
  
				}
  
			}
  
		},
  
		raycast: ( function () {
  
			var inverseMatrix = new Matrix4();
			var ray = new Ray();
			var sphere = new Sphere();
  
			var vA = new Vector3();
			var vB = new Vector3();
			var vC = new Vector3();
  
			var tempA = new Vector3();
			var tempB = new Vector3();
			var tempC = new Vector3();
  
			var uvA = new Vector2();
			var uvB = new Vector2();
			var uvC = new Vector2();
  
			var barycoord = new Vector3();
  
			var intersectionPoint = new Vector3();
			var intersectionPointWorld = new Vector3();
  
			function uvIntersection( point, p1, p2, p3, uv1, uv2, uv3 ) {
  
				Triangle.getBarycoord( point, p1, p2, p3, barycoord );
  
				uv1.multiplyScalar( barycoord.x );
				uv2.multiplyScalar( barycoord.y );
				uv3.multiplyScalar( barycoord.z );
  
				uv1.add( uv2 ).add( uv3 );
  
				return uv1.clone();
  
			}
  
			function checkIntersection( object, material, raycaster, ray, pA, pB, pC, point ) {
  
				var intersect;
  
				if ( material.side === BackSide ) {
  
					intersect = ray.intersectTriangle( pC, pB, pA, true, point );
  
				} else {
  
					intersect = ray.intersectTriangle( pA, pB, pC, material.side !== DoubleSide, point );
  
				}
  
				if ( intersect === null ) return null;
  
				intersectionPointWorld.copy( point );
				intersectionPointWorld.applyMatrix4( object.matrixWorld );
  
				var distance = raycaster.ray.origin.distanceTo( intersectionPointWorld );
  
				if ( distance < raycaster.near || distance > raycaster.far ) return null;
  
				return {
					distance: distance,
					point: intersectionPointWorld.clone(),
					object: object
				};
  
			}
  
			function checkBufferGeometryIntersection( object, raycaster, ray, position, uv, a, b, c ) {
  
				vA.fromBufferAttribute( position, a );
				vB.fromBufferAttribute( position, b );
				vC.fromBufferAttribute( position, c );
  
				var intersection = checkIntersection( object, object.material, raycaster, ray, vA, vB, vC, intersectionPoint );
  
				if ( intersection ) {
  
					if ( uv ) {
  
						uvA.fromBufferAttribute( uv, a );
						uvB.fromBufferAttribute( uv, b );
						uvC.fromBufferAttribute( uv, c );
  
						intersection.uv = uvIntersection( intersectionPoint, vA, vB, vC, uvA, uvB, uvC );
  
					}
  
					var face = new Face3( a, b, c );
					Triangle.getNormal( vA, vB, vC, face.normal );
  
					intersection.face = face;
  
				}
  
				return intersection;
  
			}
  
			return function raycast( raycaster, intersects ) {
  
				var geometry = this.geometry;
				var material = this.material;
				var matrixWorld = this.matrixWorld;
  
				if ( material === undefined ) return;
  
				// Checking boundingSphere distance to ray
  
				if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();
  
				sphere.copy( geometry.boundingSphere );
				sphere.applyMatrix4( matrixWorld );
  
				if ( raycaster.ray.intersectsSphere( sphere ) === false ) return;
  
				//
  
				inverseMatrix.getInverse( matrixWorld );
				ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix );
  
				// Check boundingBox before continuing
  
				if ( geometry.boundingBox !== null ) {
  
					if ( ray.intersectsBox( geometry.boundingBox ) === false ) return;
  
				}
  
				var intersection;
  
				if ( geometry.isBufferGeometry ) {
  
					var a, b, c;
					var index = geometry.index;
					var position = geometry.attributes.position;
					var uv = geometry.attributes.uv;
					var i, l;
  
					if ( index !== null ) {
  
						// indexed buffer geometry
  
						for ( i = 0, l = index.count; i < l; i += 3 ) {
  
							a = index.getX( i );
							b = index.getX( i + 1 );
							c = index.getX( i + 2 );
  
							intersection = checkBufferGeometryIntersection( this, raycaster, ray, position, uv, a, b, c );
  
							if ( intersection ) {
  
								intersection.faceIndex = Math.floor( i / 3 ); // triangle number in indexed buffer semantics
								intersects.push( intersection );
  
							}
  
						}
  
					} else if ( position !== undefined ) {
  
						// non-indexed buffer geometry
  
						for ( i = 0, l = position.count; i < l; i += 3 ) {
  
							a = i;
							b = i + 1;
							c = i + 2;
  
							intersection = checkBufferGeometryIntersection( this, raycaster, ray, position, uv, a, b, c );
  
							if ( intersection ) {
  
								intersection.faceIndex = Math.floor( i / 3 ); // triangle number in non-indexed buffer semantics
								intersects.push( intersection );
  
							}
  
						}
  
					}
  
				} else if ( geometry.isGeometry ) {
  
					var fvA, fvB, fvC;
					var isMultiMaterial = Array.isArray( material );
  
					var vertices = geometry.vertices;
					var faces = geometry.faces;
					var uvs;
  
					var faceVertexUvs = geometry.faceVertexUvs[ 0 ];
					if ( faceVertexUvs.length > 0 ) uvs = faceVertexUvs;
  
					for ( var f = 0, fl = faces.length; f < fl; f ++ ) {
  
						var face = faces[ f ];
						var faceMaterial = isMultiMaterial ? material[ face.materialIndex ] : material;
  
						if ( faceMaterial === undefined ) continue;
  
						fvA = vertices[ face.a ];
						fvB = vertices[ face.b ];
						fvC = vertices[ face.c ];
  
						if ( faceMaterial.morphTargets === true ) {
  
							var morphTargets = geometry.morphTargets;
							var morphInfluences = this.morphTargetInfluences;
  
							vA.set( 0, 0, 0 );
							vB.set( 0, 0, 0 );
							vC.set( 0, 0, 0 );
  
							for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) {
  
								var influence = morphInfluences[ t ];
  
								if ( influence === 0 ) continue;
  
								var targets = morphTargets[ t ].vertices;
  
								vA.addScaledVector( tempA.subVectors( targets[ face.a ], fvA ), influence );
								vB.addScaledVector( tempB.subVectors( targets[ face.b ], fvB ), influence );
								vC.addScaledVector( tempC.subVectors( targets[ face.c ], fvC ), influence );
  
							}
  
							vA.add( fvA );
							vB.add( fvB );
							vC.add( fvC );
  
							fvA = vA;
							fvB = vB;
							fvC = vC;
  
						}
  
						intersection = checkIntersection( this, faceMaterial, raycaster, ray, fvA, fvB, fvC, intersectionPoint );
  
						if ( intersection ) {
  
							if ( uvs && uvs[ f ] ) {
  
								var uvs_f = uvs[ f ];
								uvA.copy( uvs_f[ 0 ] );
								uvB.copy( uvs_f[ 1 ] );
								uvC.copy( uvs_f[ 2 ] );
  
								intersection.uv = uvIntersection( intersectionPoint, fvA, fvB, fvC, uvA, uvB, uvC );
  
							}
  
							intersection.face = face;
							intersection.faceIndex = f;
							intersects.push( intersection );
  
						}
  
					}
  
				}
  
			};
  
		}() ),
  
		clone: function () {
  
			return new this.constructor( this.geometry, this.material ).copy( this );
  
		}
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function WebGLBackground( renderer, state, objects, premultipliedAlpha ) {
  
		var clearColor = new Color( 0x000000 );
		var clearAlpha = 0;
  
		var planeCamera, planeMesh;
		var boxMesh;
  
		function render( renderList, scene, camera, forceClear ) {
  
			var background = scene.background;
  
			if ( background === null ) {
  
				setClear( clearColor, clearAlpha );
  
			} else if ( background && background.isColor ) {
  
				setClear( background, 1 );
				forceClear = true;
  
			}
  
			if ( renderer.autoClear || forceClear ) {
  
				renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil );
  
			}
  
			if ( background && background.isCubeTexture ) {
  
				if ( boxMesh === undefined ) {
  
					boxMesh = new Mesh(
						new BoxBufferGeometry( 1, 1, 1 ),
						new ShaderMaterial( {
							uniforms: ShaderLib.cube.uniforms,
							vertexShader: ShaderLib.cube.vertexShader,
							fragmentShader: ShaderLib.cube.fragmentShader,
							side: BackSide,
							depthTest: true,
							depthWrite: false,
							fog: false
						} )
					);
  
					boxMesh.geometry.removeAttribute( 'normal' );
					boxMesh.geometry.removeAttribute( 'uv' );
  
					boxMesh.onBeforeRender = function ( renderer, scene, camera ) {
  
						this.matrixWorld.copyPosition( camera.matrixWorld );
  
					};
  
					objects.update( boxMesh );
  
				}
  
				boxMesh.material.uniforms.tCube.value = background;
  
				renderList.push( boxMesh, boxMesh.geometry, boxMesh.material, 0, null );
  
			} else if ( background && background.isTexture ) {
  
				if ( planeCamera === undefined ) {
  
					planeCamera = new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
  
					planeMesh = new Mesh(
						new PlaneBufferGeometry( 2, 2 ),
						new MeshBasicMaterial( { depthTest: false, depthWrite: false, fog: false } )
					);
  
					objects.update( planeMesh );
  
				}
  
				planeMesh.material.map = background;
  
				// TODO Push this to renderList
  
				renderer.renderBufferDirect( planeCamera, null, planeMesh.geometry, planeMesh.material, planeMesh, null );
  
			}
  
		}
  
		function setClear( color, alpha ) {
  
			state.buffers.color.setClear( color.r, color.g, color.b, alpha, premultipliedAlpha );
  
		}
  
		return {
  
			getClearColor: function () {
  
				return clearColor;
  
			},
			setClearColor: function ( color, alpha ) {
  
				clearColor.set( color );
				clearAlpha = alpha !== undefined ? alpha : 1;
				setClear( clearColor, clearAlpha );
  
			},
			getClearAlpha: function () {
  
				return clearAlpha;
  
			},
			setClearAlpha: function ( alpha ) {
  
				clearAlpha = alpha;
				setClear( clearColor, clearAlpha );
  
			},
			render: render
  
		};
  
	}
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function WebGLBufferRenderer( gl, extensions, info ) {
  
		var mode;
  
		function setMode( value ) {
  
			mode = value;
  
		}
  
		function render( start, count ) {
  
			gl.drawArrays( mode, start, count );
  
			info.update( count, mode );
  
		}
  
		function renderInstances( geometry, start, count ) {
  
			var extension = extensions.get( 'ANGLE_instanced_arrays' );
  
			if ( extension === null ) {
  
				console.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' );
				return;
  
			}
  
			extension.drawArraysInstancedANGLE( mode, start, count, geometry.maxInstancedCount );
  
			info.update( count, mode, geometry.maxInstancedCount );
  
		}
  
		//
  
		this.setMode = setMode;
		this.render = render;
		this.renderInstances = renderInstances;
  
	}
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function WebGLCapabilities( gl, extensions, parameters ) {
  
		var maxAnisotropy;
  
		function getMaxAnisotropy() {
  
			if ( maxAnisotropy !== undefined ) return maxAnisotropy;
  
			var extension = extensions.get( 'EXT_texture_filter_anisotropic' );
  
			if ( extension !== null ) {
  
				maxAnisotropy = gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT );
  
			} else {
  
				maxAnisotropy = 0;
  
			}
  
			return maxAnisotropy;
  
		}
  
		function getMaxPrecision( precision ) {
  
			if ( precision === 'highp' ) {
  
				if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.HIGH_FLOAT ).precision > 0 &&
					 gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.HIGH_FLOAT ).precision > 0 ) {
  
					return 'highp';
  
				}
  
				precision = 'mediump';
  
			}
  
			if ( precision === 'mediump' ) {
  
				if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.MEDIUM_FLOAT ).precision > 0 &&
					 gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT ).precision > 0 ) {
  
					return 'mediump';
  
				}
  
			}
  
			return 'lowp';
  
		}
  
		var precision = parameters.precision !== undefined ? parameters.precision : 'highp';
		var maxPrecision = getMaxPrecision( precision );
  
		if ( maxPrecision !== precision ) {
  
			console.warn( 'THREE.WebGLRenderer:', precision, 'not supported, using', maxPrecision, 'instead.' );
			precision = maxPrecision;
  
		}
  
		var logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true;
  
		var maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS );
		var maxVertexTextures = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS );
		var maxTextureSize = gl.getParameter( gl.MAX_TEXTURE_SIZE );
		var maxCubemapSize = gl.getParameter( gl.MAX_CUBE_MAP_TEXTURE_SIZE );
  
		var maxAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS );
		var maxVertexUniforms = gl.getParameter( gl.MAX_VERTEX_UNIFORM_VECTORS );
		var maxVaryings = gl.getParameter( gl.MAX_VARYING_VECTORS );
		var maxFragmentUniforms = gl.getParameter( gl.MAX_FRAGMENT_UNIFORM_VECTORS );
  
		var vertexTextures = maxVertexTextures > 0;
		var floatFragmentTextures = !! extensions.get( 'OES_texture_float' );
		var floatVertexTextures = vertexTextures && floatFragmentTextures;
  
		return {
  
			getMaxAnisotropy: getMaxAnisotropy,
			getMaxPrecision: getMaxPrecision,
  
			precision: precision,
			logarithmicDepthBuffer: logarithmicDepthBuffer,
  
			maxTextures: maxTextures,
			maxVertexTextures: maxVertexTextures,
			maxTextureSize: maxTextureSize,
			maxCubemapSize: maxCubemapSize,
  
			maxAttributes: maxAttributes,
			maxVertexUniforms: maxVertexUniforms,
			maxVaryings: maxVaryings,
			maxFragmentUniforms: maxFragmentUniforms,
  
			vertexTextures: vertexTextures,
			floatFragmentTextures: floatFragmentTextures,
			floatVertexTextures: floatVertexTextures
  
		};
  
	}
  
	/**
	 * @author tschw
	 */
  
	function WebGLClipping() {
  
		var scope = this,
  
			globalState = null,
			numGlobalPlanes = 0,
			localClippingEnabled = false,
			renderingShadows = false,
  
			plane = new Plane(),
			viewNormalMatrix = new Matrix3(),
  
			uniform = { value: null, needsUpdate: false };
  
		this.uniform = uniform;
		this.numPlanes = 0;
		this.numIntersection = 0;
  
		this.init = function ( planes, enableLocalClipping, camera ) {
  
			var enabled =
				planes.length !== 0 ||
				enableLocalClipping ||
				// enable state of previous frame - the clipping code has to
				// run another frame in order to reset the state:
				numGlobalPlanes !== 0 ||
				localClippingEnabled;
  
			localClippingEnabled = enableLocalClipping;
  
			globalState = projectPlanes( planes, camera, 0 );
			numGlobalPlanes = planes.length;
  
			return enabled;
  
		};
  
		this.beginShadows = function () {
  
			renderingShadows = true;
			projectPlanes( null );
  
		};
  
		this.endShadows = function () {
  
			renderingShadows = false;
			resetGlobalState();
  
		};
  
		this.setState = function ( planes, clipIntersection, clipShadows, camera, cache, fromCache ) {
  
			if ( ! localClippingEnabled || planes === null || planes.length === 0 || renderingShadows && ! clipShadows ) {
  
				// there's no local clipping
  
				if ( renderingShadows ) {
  
					// there's no global clipping
  
					projectPlanes( null );
  
				} else {
  
					resetGlobalState();
  
				}
  
			} else {
  
				var nGlobal = renderingShadows ? 0 : numGlobalPlanes,
					lGlobal = nGlobal * 4,
  
					dstArray = cache.clippingState || null;
  
				uniform.value = dstArray; // ensure unique state
  
				dstArray = projectPlanes( planes, camera, lGlobal, fromCache );
  
				for ( var i = 0; i !== lGlobal; ++ i ) {
  
					dstArray[ i ] = globalState[ i ];
  
				}
  
				cache.clippingState = dstArray;
				this.numIntersection = clipIntersection ? this.numPlanes : 0;
				this.numPlanes += nGlobal;
  
			}
  
  
		};
  
		function resetGlobalState() {
  
			if ( uniform.value !== globalState ) {
  
				uniform.value = globalState;
				uniform.needsUpdate = numGlobalPlanes > 0;
  
			}
  
			scope.numPlanes = numGlobalPlanes;
			scope.numIntersection = 0;
  
		}
  
		function projectPlanes( planes, camera, dstOffset, skipTransform ) {
  
			var nPlanes = planes !== null ? planes.length : 0,
				dstArray = null;
  
			if ( nPlanes !== 0 ) {
  
				dstArray = uniform.value;
  
				if ( skipTransform !== true || dstArray === null ) {
  
					var flatSize = dstOffset + nPlanes * 4,
						viewMatrix = camera.matrixWorldInverse;
  
					viewNormalMatrix.getNormalMatrix( viewMatrix );
  
					if ( dstArray === null || dstArray.length < flatSize ) {
  
						dstArray = new Float32Array( flatSize );
  
					}
  
					for ( var i = 0, i4 = dstOffset; i !== nPlanes; ++ i, i4 += 4 ) {
  
						plane.copy( planes[ i ] ).applyMatrix4( viewMatrix, viewNormalMatrix );
  
						plane.normal.toArray( dstArray, i4 );
						dstArray[ i4 + 3 ] = plane.constant;
  
					}
  
				}
  
				uniform.value = dstArray;
				uniform.needsUpdate = true;
  
			}
  
			scope.numPlanes = nPlanes;
  
			return dstArray;
  
		}
  
	}
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function WebGLExtensions( gl ) {
  
		var extensions = {};
  
		return {
  
			get: function ( name ) {
  
				if ( extensions[ name ] !== undefined ) {
  
					return extensions[ name ];
  
				}
  
				var extension;
  
				switch ( name ) {
  
					case 'WEBGL_depth_texture':
						extension = gl.getExtension( 'WEBGL_depth_texture' ) || gl.getExtension( 'MOZ_WEBGL_depth_texture' ) || gl.getExtension( 'WEBKIT_WEBGL_depth_texture' );
						break;
  
					case 'EXT_texture_filter_anisotropic':
						extension = gl.getExtension( 'EXT_texture_filter_anisotropic' ) || gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' );
						break;
  
					case 'WEBGL_compressed_texture_s3tc':
						extension = gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' );
						break;
  
					case 'WEBGL_compressed_texture_pvrtc':
						extension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' );
						break;
  
					default:
						extension = gl.getExtension( name );
  
				}
  
				if ( extension === null ) {
  
					console.warn( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' );
  
				}
  
				extensions[ name ] = extension;
  
				return extension;
  
			}
  
		};
  
	}
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function WebGLGeometries( gl, attributes, info ) {
  
		var geometries = {};
		var wireframeAttributes = {};
  
		function onGeometryDispose( event ) {
  
			var geometry = event.target;
			var buffergeometry = geometries[ geometry.id ];
  
			if ( buffergeometry.index !== null ) {
  
				attributes.remove( buffergeometry.index );
  
			}
  
			for ( var name in buffergeometry.attributes ) {
  
				attributes.remove( buffergeometry.attributes[ name ] );
  
			}
  
			geometry.removeEventListener( 'dispose', onGeometryDispose );
  
			delete geometries[ geometry.id ];
  
			// TODO Remove duplicate code
  
			var attribute = wireframeAttributes[ geometry.id ];
  
			if ( attribute ) {
  
				attributes.remove( attribute );
				delete wireframeAttributes[ geometry.id ];
  
			}
  
			attribute = wireframeAttributes[ buffergeometry.id ];
  
			if ( attribute ) {
  
				attributes.remove( attribute );
				delete wireframeAttributes[ buffergeometry.id ];
  
			}
  
			//
  
			info.memory.geometries --;
  
		}
  
		function get( object, geometry ) {
  
			var buffergeometry = geometries[ geometry.id ];
  
			if ( buffergeometry ) return buffergeometry;
  
			geometry.addEventListener( 'dispose', onGeometryDispose );
  
			if ( geometry.isBufferGeometry ) {
  
				buffergeometry = geometry;
  
			} else if ( geometry.isGeometry ) {
  
				if ( geometry._bufferGeometry === undefined ) {
  
					geometry._bufferGeometry = new BufferGeometry().setFromObject( object );
  
				}
  
				buffergeometry = geometry._bufferGeometry;
  
			}
  
			geometries[ geometry.id ] = buffergeometry;
  
			info.memory.geometries ++;
  
			return buffergeometry;
  
		}
  
		function update( geometry ) {
  
			var index = geometry.index;
			var geometryAttributes = geometry.attributes;
  
			if ( index !== null ) {
  
				attributes.update( index, gl.ELEMENT_ARRAY_BUFFER );
  
			}
  
			for ( var name in geometryAttributes ) {
  
				attributes.update( geometryAttributes[ name ], gl.ARRAY_BUFFER );
  
			}
  
			// morph targets
  
			var morphAttributes = geometry.morphAttributes;
  
			for ( var name in morphAttributes ) {
  
				var array = morphAttributes[ name ];
  
				for ( var i = 0, l = array.length; i < l; i ++ ) {
  
					attributes.update( array[ i ], gl.ARRAY_BUFFER );
  
				}
  
			}
  
		}
  
		function getWireframeAttribute( geometry ) {
  
			var attribute = wireframeAttributes[ geometry.id ];
  
			if ( attribute ) return attribute;
  
			var indices = [];
  
			var geometryIndex = geometry.index;
			var geometryAttributes = geometry.attributes;
  
			// console.time( 'wireframe' );
  
			if ( geometryIndex !== null ) {
  
				var array = geometryIndex.array;
  
				for ( var i = 0, l = array.length; i < l; i += 3 ) {
  
					var a = array[ i + 0 ];
					var b = array[ i + 1 ];
					var c = array[ i + 2 ];
  
					indices.push( a, b, b, c, c, a );
  
				}
  
			} else {
  
				var array = geometryAttributes.position.array;
  
				for ( var i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) {
  
					var a = i + 0;
					var b = i + 1;
					var c = i + 2;
  
					indices.push( a, b, b, c, c, a );
  
				}
  
			}
  
			// console.timeEnd( 'wireframe' );
  
			attribute = new ( arrayMax( indices ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 );
  
			attributes.update( attribute, gl.ELEMENT_ARRAY_BUFFER );
  
			wireframeAttributes[ geometry.id ] = attribute;
  
			return attribute;
  
		}
  
		return {
  
			get: get,
			update: update,
  
			getWireframeAttribute: getWireframeAttribute
  
		};
  
	}
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function WebGLIndexedBufferRenderer( gl, extensions, info ) {
  
		var mode;
  
		function setMode( value ) {
  
			mode = value;
  
		}
  
		var type, bytesPerElement;
  
		function setIndex( value ) {
  
			type = value.type;
			bytesPerElement = value.bytesPerElement;
  
		}
  
		function render( start, count ) {
  
			gl.drawElements( mode, count, type, start * bytesPerElement );
  
			info.update( count, mode );
  
		}
  
		function renderInstances( geometry, start, count ) {
  
			var extension = extensions.get( 'ANGLE_instanced_arrays' );
  
			if ( extension === null ) {
  
				console.error( 'THREE.WebGLIndexedBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' );
				return;
  
			}
  
			extension.drawElementsInstancedANGLE( mode, count, type, start * bytesPerElement, geometry.maxInstancedCount );
  
			info.update( count, mode, geometry.maxInstancedCount );
  
		}
  
		//
  
		this.setMode = setMode;
		this.setIndex = setIndex;
		this.render = render;
		this.renderInstances = renderInstances;
  
	}
  
	/**
	 * @author Mugen87 / https://github.com/Mugen87
	 */
  
	function WebGLInfo( gl ) {
  
		var memory = {
			geometries: 0,
			textures: 0
		};
  
		var render = {
			frame: 0,
			calls: 0,
			triangles: 0,
			points: 0,
			lines: 0
		};
  
		function update( count, mode, instanceCount ) {
  
			instanceCount = instanceCount || 1;
  
			render.calls ++;
  
			switch ( mode ) {
  
				case gl.TRIANGLES:
					render.triangles += instanceCount * ( count / 3 );
					break;
  
				case gl.TRIANGLE_STRIP:
				case gl.TRIANGLE_FAN:
					render.triangles += instanceCount * ( count - 2 );
					break;
  
				case gl.LINES:
					render.lines += instanceCount * ( count / 2 );
					break;
  
				case gl.LINE_STRIP:
					render.lines += instanceCount * ( count - 1 );
					break;
  
				case gl.LINE_LOOP:
					render.lines += instanceCount * count;
					break;
  
				case gl.POINTS:
					render.points += instanceCount * count;
					break;
  
				default:
					console.error( 'THREE.WebGLInfo: Unknown draw mode:', mode );
					break;
  
			}
  
		}
  
		function reset() {
  
			render.frame ++;
			render.calls = 0;
			render.triangles = 0;
			render.points = 0;
			render.lines = 0;
  
		}
  
		return {
			memory: memory,
			render: render,
			programs: null,
			autoReset: true,
			reset: reset,
			update: update
		};
  
	}
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function absNumericalSort( a, b ) {
  
		return Math.abs( b[ 1 ] ) - Math.abs( a[ 1 ] );
  
	}
  
	function WebGLMorphtargets( gl ) {
  
		var influencesList = {};
		var morphInfluences = new Float32Array( 8 );
  
		function update( object, geometry, material, program ) {
  
			var objectInfluences = object.morphTargetInfluences;
  
			var length = objectInfluences.length;
  
			var influences = influencesList[ geometry.id ];
  
			if ( influences === undefined ) {
  
				// initialise list
  
				influences = [];
  
				for ( var i = 0; i < length; i ++ ) {
  
					influences[ i ] = [ i, 0 ];
  
				}
  
				influencesList[ geometry.id ] = influences;
  
			}
  
			var morphTargets = material.morphTargets && geometry.morphAttributes.position;
			var morphNormals = material.morphNormals && geometry.morphAttributes.normal;
  
			// Remove current morphAttributes
  
			for ( var i = 0; i < length; i ++ ) {
  
				var influence = influences[ i ];
  
				if ( influence[ 1 ] !== 0 ) {
  
					if ( morphTargets ) geometry.removeAttribute( 'morphTarget' + i );
					if ( morphNormals ) geometry.removeAttribute( 'morphNormal' + i );
  
				}
  
			}
  
			// Collect influences
  
			for ( var i = 0; i < length; i ++ ) {
  
				var influence = influences[ i ];
  
				influence[ 0 ] = i;
				influence[ 1 ] = objectInfluences[ i ];
  
			}
  
			influences.sort( absNumericalSort );
  
			// Add morphAttributes
  
			for ( var i = 0; i < 8; i ++ ) {
  
				var influence = influences[ i ];
  
				if ( influence ) {
  
					var index = influence[ 0 ];
					var value = influence[ 1 ];
  
					if ( value ) {
  
						if ( morphTargets ) geometry.addAttribute( 'morphTarget' + i, morphTargets[ index ] );
						if ( morphNormals ) geometry.addAttribute( 'morphNormal' + i, morphNormals[ index ] );
  
						morphInfluences[ i ] = value;
						continue;
  
					}
  
				}
  
				morphInfluences[ i ] = 0;
  
			}
  
			program.getUniforms().setValue( gl, 'morphTargetInfluences', morphInfluences );
  
		}
  
		return {
  
			update: update
  
		};
  
	}
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function WebGLObjects( geometries, info ) {
  
		var updateList = {};
  
		function update( object ) {
  
			var frame = info.render.frame;
  
			var geometry = object.geometry;
			var buffergeometry = geometries.get( object, geometry );
  
			// Update once per frame
  
			if ( updateList[ buffergeometry.id ] !== frame ) {
  
				if ( geometry.isGeometry ) {
  
					buffergeometry.updateFromObject( object );
  
				}
  
				geometries.update( buffergeometry );
  
				updateList[ buffergeometry.id ] = frame;
  
			}
  
			return buffergeometry;
  
		}
  
		function dispose() {
  
			updateList = {};
  
		}
  
		return {
  
			update: update,
			dispose: dispose
  
		};
  
	}
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function CubeTexture( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) {
  
		images = images !== undefined ? images : [];
		mapping = mapping !== undefined ? mapping : CubeReflectionMapping;
  
		Texture.call( this, images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding );
  
		this.flipY = false;
  
	}
  
	CubeTexture.prototype = Object.create( Texture.prototype );
	CubeTexture.prototype.constructor = CubeTexture;
  
	CubeTexture.prototype.isCubeTexture = true;
  
	Object.defineProperty( CubeTexture.prototype, 'images', {
  
		get: function () {
  
			return this.image;
  
		},
  
		set: function ( value ) {
  
			this.image = value;
  
		}
  
	} );
  
	/**
	 * @author tschw
	 *
	 * Uniforms of a program.
	 * Those form a tree structure with a special top-level container for the root,
	 * which you get by calling 'new WebGLUniforms( gl, program, renderer )'.
	 *
	 *
	 * Properties of inner nodes including the top-level container:
	 *
	 * .seq - array of nested uniforms
	 * .map - nested uniforms by name
	 *
	 *
	 * Methods of all nodes except the top-level container:
	 *
	 * .setValue( gl, value, [renderer] )
	 *
	 * 		uploads a uniform value(s)
	 *  	the 'renderer' parameter is needed for sampler uniforms
	 *
	 *
	 * Static methods of the top-level container (renderer factorizations):
	 *
	 * .upload( gl, seq, values, renderer )
	 *
	 * 		sets uniforms in 'seq' to 'values[id].value'
	 *
	 * .seqWithValue( seq, values ) : filteredSeq
	 *
	 * 		filters 'seq' entries with corresponding entry in values
	 *
	 *
	 * Methods of the top-level container (renderer factorizations):
	 *
	 * .setValue( gl, name, value )
	 *
	 * 		sets uniform with  name 'name' to 'value'
	 *
	 * .set( gl, obj, prop )
	 *
	 * 		sets uniform from object and property with same name than uniform
	 *
	 * .setOptional( gl, obj, prop )
	 *
	 * 		like .set for an optional property of the object
	 *
	 */
  
	var emptyTexture = new Texture();
	var emptyCubeTexture = new CubeTexture();
  
	// --- Base for inner nodes (including the root) ---
  
	function UniformContainer() {
  
		this.seq = [];
		this.map = {};
  
	}
  
	// --- Utilities ---
  
	// Array Caches (provide typed arrays for temporary by size)
  
	var arrayCacheF32 = [];
	var arrayCacheI32 = [];
  
	// Float32Array caches used for uploading Matrix uniforms
  
	var mat4array = new Float32Array( 16 );
	var mat3array = new Float32Array( 9 );
	var mat2array = new Float32Array( 4 );
  
	// Flattening for arrays of vectors and matrices
  
	function flatten( array, nBlocks, blockSize ) {
  
		var firstElem = array[ 0 ];
  
		if ( firstElem <= 0 || firstElem > 0 ) return array;
		// unoptimized: ! isNaN( firstElem )
		// see http://jacksondunstan.com/articles/983
  
		var n = nBlocks * blockSize,
			r = arrayCacheF32[ n ];
  
		if ( r === undefined ) {
  
			r = new Float32Array( n );
			arrayCacheF32[ n ] = r;
  
		}
  
		if ( nBlocks !== 0 ) {
  
			firstElem.toArray( r, 0 );
  
			for ( var i = 1, offset = 0; i !== nBlocks; ++ i ) {
  
				offset += blockSize;
				array[ i ].toArray( r, offset );
  
			}
  
		}
  
		return r;
  
	}
  
	function arraysEqual( a, b ) {
  
		if ( a.length !== b.length ) return false;
  
		for ( var i = 0, l = a.length; i < l; i ++ ) {
  
			if ( a[ i ] !== b[ i ] ) return false;
  
		}
  
		return true;
  
	}
  
	function copyArray( a, b ) {
  
		for ( var i = 0, l = b.length; i < l; i ++ ) {
  
			a[ i ] = b[ i ];
  
		}
  
	}
  
	// Texture unit allocation
  
	function allocTexUnits( renderer, n ) {
  
		var r = arrayCacheI32[ n ];
  
		if ( r === undefined ) {
  
			r = new Int32Array( n );
			arrayCacheI32[ n ] = r;
  
		}
  
		for ( var i = 0; i !== n; ++ i )
			r[ i ] = renderer.allocTextureUnit();
  
		return r;
  
	}
  
	// --- Setters ---
  
	// Note: Defining these methods externally, because they come in a bunch
	// and this way their names minify.
  
	// Single scalar
  
	function setValue1f( gl, v ) {
  
		var cache = this.cache;
  
		if ( cache[ 0 ] === v ) return;
  
		gl.uniform1f( this.addr, v );
  
		cache[ 0 ] = v;
  
	}
  
	function setValue1i( gl, v ) {
  
		var cache = this.cache;
  
		if ( cache[ 0 ] === v ) return;
  
		gl.uniform1i( this.addr, v );
  
		cache[ 0 ] = v;
  
	}
  
	// Single float vector (from flat array or THREE.VectorN)
  
	function setValue2fv( gl, v ) {
  
		var cache = this.cache;
  
		if ( v.x !== undefined ) {
  
			if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) {
  
				gl.uniform2f( this.addr, v.x, v.y );
  
				cache[ 0 ] = v.x;
				cache[ 1 ] = v.y;
  
			}
  
		} else {
  
			if ( arraysEqual( cache, v ) ) return;
  
			gl.uniform2fv( this.addr, v );
  
			copyArray( cache, v );
  
		}
  
	}
  
	function setValue3fv( gl, v ) {
  
		var cache = this.cache;
  
		if ( v.x !== undefined ) {
  
			if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) {
  
				gl.uniform3f( this.addr, v.x, v.y, v.z );
  
				cache[ 0 ] = v.x;
				cache[ 1 ] = v.y;
				cache[ 2 ] = v.z;
  
			}
  
		} else if ( v.r !== undefined ) {
  
			if ( cache[ 0 ] !== v.r || cache[ 1 ] !== v.g || cache[ 2 ] !== v.b ) {
  
				gl.uniform3f( this.addr, v.r, v.g, v.b );
  
				cache[ 0 ] = v.r;
				cache[ 1 ] = v.g;
				cache[ 2 ] = v.b;
  
			}
  
		} else {
  
			if ( arraysEqual( cache, v ) ) return;
  
			gl.uniform3fv( this.addr, v );
  
			copyArray( cache, v );
  
		}
  
	}
  
	function setValue4fv( gl, v ) {
  
		var cache = this.cache;
  
		if ( v.x !== undefined ) {
  
			if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) {
  
				gl.uniform4f( this.addr, v.x, v.y, v.z, v.w );
  
				cache[ 0 ] = v.x;
				cache[ 1 ] = v.y;
				cache[ 2 ] = v.z;
				cache[ 3 ] = v.w;
  
			}
  
		} else {
  
			if ( arraysEqual( cache, v ) ) return;
  
			gl.uniform4fv( this.addr, v );
  
			copyArray( cache, v );
  
		}
  
	}
  
	// Single matrix (from flat array or MatrixN)
  
	function setValue2fm( gl, v ) {
  
		var cache = this.cache;
		var elements = v.elements;
  
		if ( elements === undefined ) {
  
			if ( arraysEqual( cache, v ) ) return;
  
			gl.uniformMatrix2fv( this.addr, false, v );
  
			copyArray( cache, v );
  
		} else {
  
			if ( arraysEqual( cache, elements ) ) return;
  
			mat2array.set( elements );
  
			gl.uniformMatrix2fv( this.addr, false, mat2array );
  
			copyArray( cache, elements );
  
		}
  
	}
  
	function setValue3fm( gl, v ) {
  
		var cache = this.cache;
		var elements = v.elements;
  
		if ( elements === undefined ) {
  
			if ( arraysEqual( cache, v ) ) return;
  
			gl.uniformMatrix3fv( this.addr, false, v );
  
			copyArray( cache, v );
  
		} else {
  
			if ( arraysEqual( cache, elements ) ) return;
  
			mat3array.set( elements );
  
			gl.uniformMatrix3fv( this.addr, false, mat3array );
  
			copyArray( cache, elements );
  
		}
  
	}
  
	function setValue4fm( gl, v ) {
  
		var cache = this.cache;
		var elements = v.elements;
  
		if ( elements === undefined ) {
  
			if ( arraysEqual( cache, v ) ) return;
  
			gl.uniformMatrix4fv( this.addr, false, v );
  
			copyArray( cache, v );
  
		} else {
  
			if ( arraysEqual( cache, elements ) ) return;
  
			mat4array.set( elements );
  
			gl.uniformMatrix4fv( this.addr, false, mat4array );
  
			copyArray( cache, elements );
  
		}
  
	}
  
	// Single texture (2D / Cube)
  
	function setValueT1( gl, v, renderer ) {
  
		var cache = this.cache;
		var unit = renderer.allocTextureUnit();
  
		if ( cache[ 0 ] !== unit ) {
  
			gl.uniform1i( this.addr, unit );
			cache[ 0 ] = unit;
  
		}
  
		renderer.setTexture2D( v || emptyTexture, unit );
  
	}
  
	function setValueT6( gl, v, renderer ) {
  
		var cache = this.cache;
		var unit = renderer.allocTextureUnit();
  
		if ( cache[ 0 ] !== unit ) {
  
			gl.uniform1i( this.addr, unit );
			cache[ 0 ] = unit;
  
		}
  
		renderer.setTextureCube( v || emptyCubeTexture, unit );
  
	}
  
	// Integer / Boolean vectors or arrays thereof (always flat arrays)
  
	function setValue2iv( gl, v ) {
  
		var cache = this.cache;
  
		if ( arraysEqual( cache, v ) ) return;
  
		gl.uniform2iv( this.addr, v );
  
		copyArray( cache, v );
  
	}
  
	function setValue3iv( gl, v ) {
  
		var cache = this.cache;
  
		if ( arraysEqual( cache, v ) ) return;
  
		gl.uniform3iv( this.addr, v );
  
		copyArray( cache, v );
  
	}
  
	function setValue4iv( gl, v ) {
  
		var cache = this.cache;
  
		if ( arraysEqual( cache, v ) ) return;
  
		gl.uniform4iv( this.addr, v );
  
		copyArray( cache, v );
  
	}
  
	// Helper to pick the right setter for the singular case
  
	function getSingularSetter( type ) {
  
		switch ( type ) {
  
			case 0x1406: return setValue1f; // FLOAT
			case 0x8b50: return setValue2fv; // _VEC2
			case 0x8b51: return setValue3fv; // _VEC3
			case 0x8b52: return setValue4fv; // _VEC4
  
			case 0x8b5a: return setValue2fm; // _MAT2
			case 0x8b5b: return setValue3fm; // _MAT3
			case 0x8b5c: return setValue4fm; // _MAT4
  
			case 0x8b5e: case 0x8d66: return setValueT1; // SAMPLER_2D, SAMPLER_EXTERNAL_OES
			case 0x8b60: return setValueT6; // SAMPLER_CUBE
  
			case 0x1404: case 0x8b56: return setValue1i; // INT, BOOL
			case 0x8b53: case 0x8b57: return setValue2iv; // _VEC2
			case 0x8b54: case 0x8b58: return setValue3iv; // _VEC3
			case 0x8b55: case 0x8b59: return setValue4iv; // _VEC4
  
		}
  
	}
  
	// Array of scalars
  
	function setValue1fv( gl, v ) {
  
		var cache = this.cache;
  
		if ( arraysEqual( cache, v ) ) return;
  
		gl.uniform1fv( this.addr, v );
  
		copyArray( cache, v );
  
	}
	function setValue1iv( gl, v ) {
  
		var cache = this.cache;
  
		if ( arraysEqual( cache, v ) ) return;
  
		gl.uniform1iv( this.addr, v );
  
		copyArray( cache, v );
  
	}
  
	// Array of vectors (flat or from THREE classes)
  
	function setValueV2a( gl, v ) {
  
		var cache = this.cache;
		var data = flatten( v, this.size, 2 );
  
		if ( arraysEqual( cache, data ) ) return;
  
		gl.uniform2fv( this.addr, data );
  
		this.updateCache( data );
  
	}
  
	function setValueV3a( gl, v ) {
  
		var cache = this.cache;
		var data = flatten( v, this.size, 3 );
  
		if ( arraysEqual( cache, data ) ) return;
  
		gl.uniform3fv( this.addr, data );
  
		this.updateCache( data );
  
	}
  
	function setValueV4a( gl, v ) {
  
		var cache = this.cache;
		var data = flatten( v, this.size, 4 );
  
		if ( arraysEqual( cache, data ) ) return;
  
		gl.uniform4fv( this.addr, data );
  
		this.updateCache( data );
  
	}
  
	// Array of matrices (flat or from THREE clases)
  
	function setValueM2a( gl, v ) {
  
		var cache = this.cache;
		var data = flatten( v, this.size, 4 );
  
		if ( arraysEqual( cache, data ) ) return;
  
		gl.uniformMatrix2fv( this.addr, false, data );
  
		this.updateCache( data );
  
	}
  
	function setValueM3a( gl, v ) {
  
		var cache = this.cache;
		var data = flatten( v, this.size, 9 );
  
		if ( arraysEqual( cache, data ) ) return;
  
		gl.uniformMatrix3fv( this.addr, false, data );
  
		this.updateCache( data );
  
	}
  
	function setValueM4a( gl, v ) {
  
		var cache = this.cache;
		var data = flatten( v, this.size, 16 );
  
		if ( arraysEqual( cache, data ) ) return;
  
		gl.uniformMatrix4fv( this.addr, false, data );
  
		this.updateCache( data );
  
	}
  
	// Array of textures (2D / Cube)
  
	function setValueT1a( gl, v, renderer ) {
  
		var cache = this.cache;
		var n = v.length;
  
		var units = allocTexUnits( renderer, n );
  
		if ( arraysEqual( cache, units ) === false ) {
  
			gl.uniform1iv( this.addr, units );
			copyArray( cache, units );
  
		}
  
		for ( var i = 0; i !== n; ++ i ) {
  
			renderer.setTexture2D( v[ i ] || emptyTexture, units[ i ] );
  
		}
  
	}
  
	function setValueT6a( gl, v, renderer ) {
  
		var cache = this.cache;
		var n = v.length;
  
		var units = allocTexUnits( renderer, n );
  
		if ( arraysEqual( cache, units ) === false ) {
  
			gl.uniform1iv( this.addr, units );
			copyArray( cache, units );
  
		}
  
		for ( var i = 0; i !== n; ++ i ) {
  
			renderer.setTextureCube( v[ i ] || emptyCubeTexture, units[ i ] );
  
		}
  
	}
  
	// Helper to pick the right setter for a pure (bottom-level) array
  
	function getPureArraySetter( type ) {
  
		switch ( type ) {
  
			case 0x1406: return setValue1fv; // FLOAT
			case 0x8b50: return setValueV2a; // _VEC2
			case 0x8b51: return setValueV3a; // _VEC3
			case 0x8b52: return setValueV4a; // _VEC4
  
			case 0x8b5a: return setValueM2a; // _MAT2
			case 0x8b5b: return setValueM3a; // _MAT3
			case 0x8b5c: return setValueM4a; // _MAT4
  
			case 0x8b5e: return setValueT1a; // SAMPLER_2D
			case 0x8b60: return setValueT6a; // SAMPLER_CUBE
  
			case 0x1404: case 0x8b56: return setValue1iv; // INT, BOOL
			case 0x8b53: case 0x8b57: return setValue2iv; // _VEC2
			case 0x8b54: case 0x8b58: return setValue3iv; // _VEC3
			case 0x8b55: case 0x8b59: return setValue4iv; // _VEC4
  
		}
  
	}
  
	// --- Uniform Classes ---
  
	function SingleUniform( id, activeInfo, addr ) {
  
		this.id = id;
		this.addr = addr;
		this.cache = [];
		this.setValue = getSingularSetter( activeInfo.type );
  
		// this.path = activeInfo.name; // DEBUG
  
	}
  
	function PureArrayUniform( id, activeInfo, addr ) {
  
		this.id = id;
		this.addr = addr;
		this.cache = [];
		this.size = activeInfo.size;
		this.setValue = getPureArraySetter( activeInfo.type );
  
		// this.path = activeInfo.name; // DEBUG
  
	}
  
	PureArrayUniform.prototype.updateCache = function ( data ) {
  
		var cache = this.cache;
  
		if ( data instanceof Float32Array && cache.length !== data.length ) {
  
			this.cache = new Float32Array( data.length );
  
		}
  
		copyArray( cache, data );
  
	};
  
	function StructuredUniform( id ) {
  
		this.id = id;
  
		UniformContainer.call( this ); // mix-in
  
	}
  
	StructuredUniform.prototype.setValue = function ( gl, value ) {
  
		// Note: Don't need an extra 'renderer' parameter, since samplers
		// are not allowed in structured uniforms.
  
		var seq = this.seq;
  
		for ( var i = 0, n = seq.length; i !== n; ++ i ) {
  
			var u = seq[ i ];
			u.setValue( gl, value[ u.id ] );
  
		}
  
	};
  
	// --- Top-level ---
  
	// Parser - builds up the property tree from the path strings
  
	var RePathPart = /([\w\d_]+)(\])?(\[|\.)?/g;
  
	// extracts
	// 	- the identifier (member name or array index)
	//  - followed by an optional right bracket (found when array index)
	//  - followed by an optional left bracket or dot (type of subscript)
	//
	// Note: These portions can be read in a non-overlapping fashion and
	// allow straightforward parsing of the hierarchy that WebGL encodes
	// in the uniform names.
  
	function addUniform( container, uniformObject ) {
  
		container.seq.push( uniformObject );
		container.map[ uniformObject.id ] = uniformObject;
  
	}
  
	function parseUniform( activeInfo, addr, container ) {
  
		var path = activeInfo.name,
			pathLength = path.length;
  
		// reset RegExp object, because of the early exit of a previous run
		RePathPart.lastIndex = 0;
  
		while ( true ) {
  
			var match = RePathPart.exec( path ),
				matchEnd = RePathPart.lastIndex,
  
				id = match[ 1 ],
				idIsIndex = match[ 2 ] === ']',
				subscript = match[ 3 ];
  
			if ( idIsIndex ) id = id | 0; // convert to integer
  
			if ( subscript === undefined || subscript === '[' && matchEnd + 2 === pathLength ) {
  
				// bare name or "pure" bottom-level array "[0]" suffix
  
				addUniform( container, subscript === undefined ?
					new SingleUniform( id, activeInfo, addr ) :
					new PureArrayUniform( id, activeInfo, addr ) );
  
				break;
  
			} else {
  
				// step into inner node / create it in case it doesn't exist
  
				var map = container.map, next = map[ id ];
  
				if ( next === undefined ) {
  
					next = new StructuredUniform( id );
					addUniform( container, next );
  
				}
  
				container = next;
  
			}
  
		}
  
	}
  
	// Root Container
  
	function WebGLUniforms( gl, program, renderer ) {
  
		UniformContainer.call( this );
  
		this.renderer = renderer;
  
		var n = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS );
  
		for ( var i = 0; i < n; ++ i ) {
  
			var info = gl.getActiveUniform( program, i ),
				addr = gl.getUniformLocation( program, info.name );
  
			parseUniform( info, addr, this );
  
		}
  
	}
  
	WebGLUniforms.prototype.setValue = function ( gl, name, value ) {
  
		var u = this.map[ name ];
  
		if ( u !== undefined ) u.setValue( gl, value, this.renderer );
  
	};
  
	WebGLUniforms.prototype.setOptional = function ( gl, object, name ) {
  
		var v = object[ name ];
  
		if ( v !== undefined ) this.setValue( gl, name, v );
  
	};
  
  
	// Static interface
  
	WebGLUniforms.upload = function ( gl, seq, values, renderer ) {
  
		for ( var i = 0, n = seq.length; i !== n; ++ i ) {
  
			var u = seq[ i ],
				v = values[ u.id ];
  
			if ( v.needsUpdate !== false ) {
  
				// note: always updating when .needsUpdate is undefined
				u.setValue( gl, v.value, renderer );
  
			}
  
		}
  
	};
  
	WebGLUniforms.seqWithValue = function ( seq, values ) {
  
		var r = [];
  
		for ( var i = 0, n = seq.length; i !== n; ++ i ) {
  
			var u = seq[ i ];
			if ( u.id in values ) r.push( u );
  
		}
  
		return r;
  
	};
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function addLineNumbers( string ) {
  
		var lines = string.split( '\n' );
  
		for ( var i = 0; i < lines.length; i ++ ) {
  
			lines[ i ] = ( i + 1 ) + ': ' + lines[ i ];
  
		}
  
		return lines.join( '\n' );
  
	}
  
	function WebGLShader( gl, type, string ) {
  
		var shader = gl.createShader( type );
  
		gl.shaderSource( shader, string );
		gl.compileShader( shader );
  
		if ( gl.getShaderParameter( shader, gl.COMPILE_STATUS ) === false ) {
  
			console.error( 'THREE.WebGLShader: Shader couldn\'t compile.' );
  
		}
  
		if ( gl.getShaderInfoLog( shader ) !== '' ) {
  
			console.warn( 'THREE.WebGLShader: gl.getShaderInfoLog()', type === gl.VERTEX_SHADER ? 'vertex' : 'fragment', gl.getShaderInfoLog( shader ), addLineNumbers( string ) );
  
		}
  
		// --enable-privileged-webgl-extension
		// console.log( type, gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) );
  
		return shader;
  
	}
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	var programIdCount = 0;
  
	function getEncodingComponents( encoding ) {
  
		switch ( encoding ) {
  
			case LinearEncoding:
				return [ 'Linear', '( value )' ];
			case sRGBEncoding:
				return [ 'sRGB', '( value )' ];
			case RGBEEncoding:
				return [ 'RGBE', '( value )' ];
			case RGBM7Encoding:
				return [ 'RGBM', '( value, 7.0 )' ];
			case RGBM16Encoding:
				return [ 'RGBM', '( value, 16.0 )' ];
			case RGBDEncoding:
				return [ 'RGBD', '( value, 256.0 )' ];
			case GammaEncoding:
				return [ 'Gamma', '( value, float( GAMMA_FACTOR ) )' ];
			default:
				throw new Error( 'unsupported encoding: ' + encoding );
  
		}
  
	}
  
	function getTexelDecodingFunction( functionName, encoding ) {
  
		var components = getEncodingComponents( encoding );
		return 'vec4 ' + functionName + '( vec4 value ) { return ' + components[ 0 ] + 'ToLinear' + components[ 1 ] + '; }';
  
	}
  
	function getTexelEncodingFunction( functionName, encoding ) {
  
		var components = getEncodingComponents( encoding );
		return 'vec4 ' + functionName + '( vec4 value ) { return LinearTo' + components[ 0 ] + components[ 1 ] + '; }';
  
	}
  
	function getToneMappingFunction( functionName, toneMapping ) {
  
		var toneMappingName;
  
		switch ( toneMapping ) {
  
			case LinearToneMapping:
				toneMappingName = 'Linear';
				break;
  
			case ReinhardToneMapping:
				toneMappingName = 'Reinhard';
				break;
  
			case Uncharted2ToneMapping:
				toneMappingName = 'Uncharted2';
				break;
  
			case CineonToneMapping:
				toneMappingName = 'OptimizedCineon';
				break;
  
			default:
				throw new Error( 'unsupported toneMapping: ' + toneMapping );
  
		}
  
		return 'vec3 ' + functionName + '( vec3 color ) { return ' + toneMappingName + 'ToneMapping( color ); }';
  
	}
  
	function generateExtensions( extensions, parameters, rendererExtensions ) {
  
		extensions = extensions || {};
  
		var chunks = [
			( extensions.derivatives || parameters.envMapCubeUV || parameters.bumpMap || parameters.normalMap || parameters.flatShading ) ? '#extension GL_OES_standard_derivatives : enable' : '',
			( extensions.fragDepth || parameters.logarithmicDepthBuffer ) && rendererExtensions.get( 'EXT_frag_depth' ) ? '#extension GL_EXT_frag_depth : enable' : '',
			( extensions.drawBuffers ) && rendererExtensions.get( 'WEBGL_draw_buffers' ) ? '#extension GL_EXT_draw_buffers : require' : '',
			( extensions.shaderTextureLOD || parameters.envMap ) && rendererExtensions.get( 'EXT_shader_texture_lod' ) ? '#extension GL_EXT_shader_texture_lod : enable' : ''
		];
  
		return chunks.filter( filterEmptyLine ).join( '\n' );
  
	}
  
	function generateDefines( defines ) {
  
		var chunks = [];
  
		for ( var name in defines ) {
  
			var value = defines[ name ];
  
			if ( value === false ) continue;
  
			chunks.push( '#define ' + name + ' ' + value );
  
		}
  
		return chunks.join( '\n' );
  
	}
  
	function fetchAttributeLocations( gl, program ) {
  
		var attributes = {};
  
		var n = gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES );
  
		for ( var i = 0; i < n; i ++ ) {
  
			var info = gl.getActiveAttrib( program, i );
			var name = info.name;
  
			// console.log( 'THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:', name, i );
  
			attributes[ name ] = gl.getAttribLocation( program, name );
  
		}
  
		return attributes;
  
	}
  
	function filterEmptyLine( string ) {
  
		return string !== '';
  
	}
  
	function replaceLightNums( string, parameters ) {
  
		return string
			.replace( /NUM_DIR_LIGHTS/g, parameters.numDirLights )
			.replace( /NUM_SPOT_LIGHTS/g, parameters.numSpotLights )
			.replace( /NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights )
			.replace( /NUM_POINT_LIGHTS/g, parameters.numPointLights )
			.replace( /NUM_HEMI_LIGHTS/g, parameters.numHemiLights );
  
	}
  
	function replaceClippingPlaneNums( string, parameters ) {
  
		return string
			.replace( /NUM_CLIPPING_PLANES/g, parameters.numClippingPlanes )
			.replace( /UNION_CLIPPING_PLANES/g, ( parameters.numClippingPlanes - parameters.numClipIntersection ) );
  
	}
  
	function parseIncludes( string ) {
  
		var pattern = /^[ \t]*#include +<([\w\d.]+)>/gm;
  
		function replace( match, include ) {
  
			var replace = ShaderChunk[ include ];
  
			if ( replace === undefined ) {
  
				throw new Error( 'Can not resolve #include <' + include + '>' );
  
			}
  
			return parseIncludes( replace );
  
		}
  
		return string.replace( pattern, replace );
  
	}
  
	function unrollLoops( string ) {
  
		var pattern = /#pragma unroll_loop[\s]+?for \( int i \= (\d+)\; i < (\d+)\; i \+\+ \) \{([\s\S]+?)(?=\})\}/g;
  
		function replace( match, start, end, snippet ) {
  
			var unroll = '';
  
			for ( var i = parseInt( start ); i < parseInt( end ); i ++ ) {
  
				unroll += snippet.replace( /\[ i \]/g, '[ ' + i + ' ]' );
  
			}
  
			return unroll;
  
		}
  
		return string.replace( pattern, replace );
  
	}
  
	function WebGLProgram( renderer, extensions, code, material, shader, parameters ) {
  
		var gl = renderer.context;
  
		var defines = material.defines;
  
		var vertexShader = shader.vertexShader;
		var fragmentShader = shader.fragmentShader;
  
		var shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC';
  
		if ( parameters.shadowMapType === PCFShadowMap ) {
  
			shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF';
  
		} else if ( parameters.shadowMapType === PCFSoftShadowMap ) {
  
			shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT';
  
		}
  
		var envMapTypeDefine = 'ENVMAP_TYPE_CUBE';
		var envMapModeDefine = 'ENVMAP_MODE_REFLECTION';
		var envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY';
  
		if ( parameters.envMap ) {
  
			switch ( material.envMap.mapping ) {
  
				case CubeReflectionMapping:
				case CubeRefractionMapping:
					envMapTypeDefine = 'ENVMAP_TYPE_CUBE';
					break;
  
				case CubeUVReflectionMapping:
				case CubeUVRefractionMapping:
					envMapTypeDefine = 'ENVMAP_TYPE_CUBE_UV';
					break;
  
				case EquirectangularReflectionMapping:
				case EquirectangularRefractionMapping:
					envMapTypeDefine = 'ENVMAP_TYPE_EQUIREC';
					break;
  
				case SphericalReflectionMapping:
					envMapTypeDefine = 'ENVMAP_TYPE_SPHERE';
					break;
  
			}
  
			switch ( material.envMap.mapping ) {
  
				case CubeRefractionMapping:
				case EquirectangularRefractionMapping:
					envMapModeDefine = 'ENVMAP_MODE_REFRACTION';
					break;
  
			}
  
			switch ( material.combine ) {
  
				case MultiplyOperation:
					envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY';
					break;
  
				case MixOperation:
					envMapBlendingDefine = 'ENVMAP_BLENDING_MIX';
					break;
  
				case AddOperation:
					envMapBlendingDefine = 'ENVMAP_BLENDING_ADD';
					break;
  
			}
  
		}
  
		var gammaFactorDefine = ( renderer.gammaFactor > 0 ) ? renderer.gammaFactor : 1.0;
  
		// console.log( 'building new program ' );
  
		//
  
		var customExtensions = generateExtensions( material.extensions, parameters, extensions );
  
		var customDefines = generateDefines( defines );
  
		//
  
		var program = gl.createProgram();
  
		var prefixVertex, prefixFragment;
  
		if ( material.isRawShaderMaterial ) {
  
			prefixVertex = [
  
				customDefines
  
			].filter( filterEmptyLine ).join( '\n' );
  
			if ( prefixVertex.length > 0 ) {
  
				prefixVertex += '\n';
  
			}
  
			prefixFragment = [
  
				customExtensions,
				customDefines
  
			].filter( filterEmptyLine ).join( '\n' );
  
			if ( prefixFragment.length > 0 ) {
  
				prefixFragment += '\n';
  
			}
  
		} else {
  
			prefixVertex = [
  
				'precision ' + parameters.precision + ' float;',
				'precision ' + parameters.precision + ' int;',
  
				'#define SHADER_NAME ' + shader.name,
  
				customDefines,
  
				parameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '',
  
				'#define GAMMA_FACTOR ' + gammaFactorDefine,
  
				'#define MAX_BONES ' + parameters.maxBones,
				( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '',
				( parameters.useFog && parameters.fogExp ) ? '#define FOG_EXP2' : '',
  
				parameters.map ? '#define USE_MAP' : '',
				parameters.envMap ? '#define USE_ENVMAP' : '',
				parameters.envMap ? '#define ' + envMapModeDefine : '',
				parameters.lightMap ? '#define USE_LIGHTMAP' : '',
				parameters.aoMap ? '#define USE_AOMAP' : '',
				parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '',
				parameters.bumpMap ? '#define USE_BUMPMAP' : '',
				parameters.normalMap ? '#define USE_NORMALMAP' : '',
				parameters.displacementMap && parameters.supportsVertexTextures ? '#define USE_DISPLACEMENTMAP' : '',
				parameters.specularMap ? '#define USE_SPECULARMAP' : '',
				parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '',
				parameters.metalnessMap ? '#define USE_METALNESSMAP' : '',
				parameters.alphaMap ? '#define USE_ALPHAMAP' : '',
				parameters.vertexColors ? '#define USE_COLOR' : '',
  
				parameters.flatShading ? '#define FLAT_SHADED' : '',
  
				parameters.skinning ? '#define USE_SKINNING' : '',
				parameters.useVertexTexture ? '#define BONE_TEXTURE' : '',
  
				parameters.morphTargets ? '#define USE_MORPHTARGETS' : '',
				parameters.morphNormals && parameters.flatShading === false ? '#define USE_MORPHNORMALS' : '',
				parameters.doubleSided ? '#define DOUBLE_SIDED' : '',
				parameters.flipSided ? '#define FLIP_SIDED' : '',
  
				parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '',
				parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '',
  
				parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '',
  
				parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '',
				parameters.logarithmicDepthBuffer && extensions.get( 'EXT_frag_depth' ) ? '#define USE_LOGDEPTHBUF_EXT' : '',
  
				'uniform mat4 modelMatrix;',
				'uniform mat4 modelViewMatrix;',
				'uniform mat4 projectionMatrix;',
				'uniform mat4 viewMatrix;',
				'uniform mat3 normalMatrix;',
				'uniform vec3 cameraPosition;',
  
				'attribute vec3 position;',
				'attribute vec3 normal;',
				'attribute vec2 uv;',
  
				'#ifdef USE_COLOR',
  
				'	attribute vec3 color;',
  
				'#endif',
  
				'#ifdef USE_MORPHTARGETS',
  
				'	attribute vec3 morphTarget0;',
				'	attribute vec3 morphTarget1;',
				'	attribute vec3 morphTarget2;',
				'	attribute vec3 morphTarget3;',
  
				'	#ifdef USE_MORPHNORMALS',
  
				'		attribute vec3 morphNormal0;',
				'		attribute vec3 morphNormal1;',
				'		attribute vec3 morphNormal2;',
				'		attribute vec3 morphNormal3;',
  
				'	#else',
  
				'		attribute vec3 morphTarget4;',
				'		attribute vec3 morphTarget5;',
				'		attribute vec3 morphTarget6;',
				'		attribute vec3 morphTarget7;',
  
				'	#endif',
  
				'#endif',
  
				'#ifdef USE_SKINNING',
  
				'	attribute vec4 skinIndex;',
				'	attribute vec4 skinWeight;',
  
				'#endif',
  
				'\n'
  
			].filter( filterEmptyLine ).join( '\n' );
  
			prefixFragment = [
  
				customExtensions,
  
				'precision ' + parameters.precision + ' float;',
				'precision ' + parameters.precision + ' int;',
  
				'#define SHADER_NAME ' + shader.name,
  
				customDefines,
  
				parameters.alphaTest ? '#define ALPHATEST ' + parameters.alphaTest + ( parameters.alphaTest % 1 ? '' : '.0' ) : '', // add '.0' if integer
  
				'#define GAMMA_FACTOR ' + gammaFactorDefine,
  
				( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '',
				( parameters.useFog && parameters.fogExp ) ? '#define FOG_EXP2' : '',
  
				parameters.map ? '#define USE_MAP' : '',
				parameters.envMap ? '#define USE_ENVMAP' : '',
				parameters.envMap ? '#define ' + envMapTypeDefine : '',
				parameters.envMap ? '#define ' + envMapModeDefine : '',
				parameters.envMap ? '#define ' + envMapBlendingDefine : '',
				parameters.lightMap ? '#define USE_LIGHTMAP' : '',
				parameters.aoMap ? '#define USE_AOMAP' : '',
				parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '',
				parameters.bumpMap ? '#define USE_BUMPMAP' : '',
				parameters.normalMap ? '#define USE_NORMALMAP' : '',
				parameters.specularMap ? '#define USE_SPECULARMAP' : '',
				parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '',
				parameters.metalnessMap ? '#define USE_METALNESSMAP' : '',
				parameters.alphaMap ? '#define USE_ALPHAMAP' : '',
				parameters.vertexColors ? '#define USE_COLOR' : '',
  
				parameters.gradientMap ? '#define USE_GRADIENTMAP' : '',
  
				parameters.flatShading ? '#define FLAT_SHADED' : '',
  
				parameters.doubleSided ? '#define DOUBLE_SIDED' : '',
				parameters.flipSided ? '#define FLIP_SIDED' : '',
  
				parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '',
				parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '',
  
				parameters.premultipliedAlpha ? '#define PREMULTIPLIED_ALPHA' : '',
  
				parameters.physicallyCorrectLights ? '#define PHYSICALLY_CORRECT_LIGHTS' : '',
  
				parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '',
				parameters.logarithmicDepthBuffer && extensions.get( 'EXT_frag_depth' ) ? '#define USE_LOGDEPTHBUF_EXT' : '',
  
				parameters.envMap && extensions.get( 'EXT_shader_texture_lod' ) ? '#define TEXTURE_LOD_EXT' : '',
  
				'uniform mat4 viewMatrix;',
				'uniform vec3 cameraPosition;',
  
				( parameters.toneMapping !== NoToneMapping ) ? '#define TONE_MAPPING' : '',
				( parameters.toneMapping !== NoToneMapping ) ? ShaderChunk[ 'tonemapping_pars_fragment' ] : '', // this code is required here because it is used by the toneMapping() function defined below
				( parameters.toneMapping !== NoToneMapping ) ? getToneMappingFunction( 'toneMapping', parameters.toneMapping ) : '',
  
				parameters.dithering ? '#define DITHERING' : '',
  
				( parameters.outputEncoding || parameters.mapEncoding || parameters.envMapEncoding || parameters.emissiveMapEncoding ) ? ShaderChunk[ 'encodings_pars_fragment' ] : '', // this code is required here because it is used by the various encoding/decoding function defined below
				parameters.mapEncoding ? getTexelDecodingFunction( 'mapTexelToLinear', parameters.mapEncoding ) : '',
				parameters.envMapEncoding ? getTexelDecodingFunction( 'envMapTexelToLinear', parameters.envMapEncoding ) : '',
				parameters.emissiveMapEncoding ? getTexelDecodingFunction( 'emissiveMapTexelToLinear', parameters.emissiveMapEncoding ) : '',
				parameters.outputEncoding ? getTexelEncodingFunction( 'linearToOutputTexel', parameters.outputEncoding ) : '',
  
				parameters.depthPacking ? '#define DEPTH_PACKING ' + material.depthPacking : '',
  
				'\n'
  
			].filter( filterEmptyLine ).join( '\n' );
  
		}
  
		vertexShader = parseIncludes( vertexShader );
		vertexShader = replaceLightNums( vertexShader, parameters );
		vertexShader = replaceClippingPlaneNums( vertexShader, parameters );
  
		fragmentShader = parseIncludes( fragmentShader );
		fragmentShader = replaceLightNums( fragmentShader, parameters );
		fragmentShader = replaceClippingPlaneNums( fragmentShader, parameters );
  
		vertexShader = unrollLoops( vertexShader );
		fragmentShader = unrollLoops( fragmentShader );
  
		var vertexGlsl = prefixVertex + vertexShader;
		var fragmentGlsl = prefixFragment + fragmentShader;
  
		// console.log( '*VERTEX*', vertexGlsl );
		// console.log( '*FRAGMENT*', fragmentGlsl );
  
		var glVertexShader = WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl );
		var glFragmentShader = WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl );
  
		gl.attachShader( program, glVertexShader );
		gl.attachShader( program, glFragmentShader );
  
		// Force a particular attribute to index 0.
  
		if ( material.index0AttributeName !== undefined ) {
  
			gl.bindAttribLocation( program, 0, material.index0AttributeName );
  
		} else if ( parameters.morphTargets === true ) {
  
			// programs with morphTargets displace position out of attribute 0
			gl.bindAttribLocation( program, 0, 'position' );
  
		}
  
		gl.linkProgram( program );
  
		var programLog = gl.getProgramInfoLog( program ).trim();
		var vertexLog = gl.getShaderInfoLog( glVertexShader ).trim();
		var fragmentLog = gl.getShaderInfoLog( glFragmentShader ).trim();
  
		var runnable = true;
		var haveDiagnostics = true;
  
		// console.log( '**VERTEX**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( glVertexShader ) );
		// console.log( '**FRAGMENT**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( glFragmentShader ) );
  
		if ( gl.getProgramParameter( program, gl.LINK_STATUS ) === false ) {
  
			runnable = false;
  
			console.error( 'THREE.WebGLProgram: shader error: ', gl.getError(), 'gl.VALIDATE_STATUS', gl.getProgramParameter( program, gl.VALIDATE_STATUS ), 'gl.getProgramInfoLog', programLog, vertexLog, fragmentLog );
  
		} else if ( programLog !== '' ) {
  
			console.warn( 'THREE.WebGLProgram: gl.getProgramInfoLog()', programLog );
  
		} else if ( vertexLog === '' || fragmentLog === '' ) {
  
			haveDiagnostics = false;
  
		}
  
		if ( haveDiagnostics ) {
  
			this.diagnostics = {
  
				runnable: runnable,
				material: material,
  
				programLog: programLog,
  
				vertexShader: {
  
					log: vertexLog,
					prefix: prefixVertex
  
				},
  
				fragmentShader: {
  
					log: fragmentLog,
					prefix: prefixFragment
  
				}
  
			};
  
		}
  
		// clean up
  
		gl.deleteShader( glVertexShader );
		gl.deleteShader( glFragmentShader );
  
		// set up caching for uniform locations
  
		var cachedUniforms;
  
		this.getUniforms = function () {
  
			if ( cachedUniforms === undefined ) {
  
				cachedUniforms = new WebGLUniforms( gl, program, renderer );
  
			}
  
			return cachedUniforms;
  
		};
  
		// set up caching for attribute locations
  
		var cachedAttributes;
  
		this.getAttributes = function () {
  
			if ( cachedAttributes === undefined ) {
  
				cachedAttributes = fetchAttributeLocations( gl, program );
  
			}
  
			return cachedAttributes;
  
		};
  
		// free resource
  
		this.destroy = function () {
  
			gl.deleteProgram( program );
			this.program = undefined;
  
		};
  
		// DEPRECATED
  
		Object.defineProperties( this, {
  
			uniforms: {
				get: function () {
  
					console.warn( 'THREE.WebGLProgram: .uniforms is now .getUniforms().' );
					return this.getUniforms();
  
				}
			},
  
			attributes: {
				get: function () {
  
					console.warn( 'THREE.WebGLProgram: .attributes is now .getAttributes().' );
					return this.getAttributes();
  
				}
			}
  
		} );
  
  
		//
  
		this.name = shader.name;
		this.id = programIdCount ++;
		this.code = code;
		this.usedTimes = 1;
		this.program = program;
		this.vertexShader = glVertexShader;
		this.fragmentShader = glFragmentShader;
  
		return this;
  
	}
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function WebGLPrograms( renderer, extensions, capabilities ) {
  
		var programs = [];
  
		var shaderIDs = {
			MeshDepthMaterial: 'depth',
			MeshDistanceMaterial: 'distanceRGBA',
			MeshNormalMaterial: 'normal',
			MeshBasicMaterial: 'basic',
			MeshLambertMaterial: 'lambert',
			MeshPhongMaterial: 'phong',
			MeshToonMaterial: 'phong',
			MeshStandardMaterial: 'physical',
			MeshPhysicalMaterial: 'physical',
			LineBasicMaterial: 'basic',
			LineDashedMaterial: 'dashed',
			PointsMaterial: 'points',
			ShadowMaterial: 'shadow'
		};
  
		var parameterNames = [
			"precision", "supportsVertexTextures", "map", "mapEncoding", "envMap", "envMapMode", "envMapEncoding",
			"lightMap", "aoMap", "emissiveMap", "emissiveMapEncoding", "bumpMap", "normalMap", "displacementMap", "specularMap",
			"roughnessMap", "metalnessMap", "gradientMap",
			"alphaMap", "combine", "vertexColors", "fog", "useFog", "fogExp",
			"flatShading", "sizeAttenuation", "logarithmicDepthBuffer", "skinning",
			"maxBones", "useVertexTexture", "morphTargets", "morphNormals",
			"maxMorphTargets", "maxMorphNormals", "premultipliedAlpha",
			"numDirLights", "numPointLights", "numSpotLights", "numHemiLights", "numRectAreaLights",
			"shadowMapEnabled", "shadowMapType", "toneMapping", 'physicallyCorrectLights',
			"alphaTest", "doubleSided", "flipSided", "numClippingPlanes", "numClipIntersection", "depthPacking", "dithering"
		];
  
  
		function allocateBones( object ) {
  
			var skeleton = object.skeleton;
			var bones = skeleton.bones;
  
			if ( capabilities.floatVertexTextures ) {
  
				return 1024;
  
			} else {
  
				// default for when object is not specified
				// ( for example when prebuilding shader to be used with multiple objects )
				//
				//  - leave some extra space for other uniforms
				//  - limit here is ANGLE's 254 max uniform vectors
				//    (up to 54 should be safe)
  
				var nVertexUniforms = capabilities.maxVertexUniforms;
				var nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 );
  
				var maxBones = Math.min( nVertexMatrices, bones.length );
  
				if ( maxBones < bones.length ) {
  
					console.warn( 'THREE.WebGLRenderer: Skeleton has ' + bones.length + ' bones. This GPU supports ' + maxBones + '.' );
					return 0;
  
				}
  
				return maxBones;
  
			}
  
		}
  
		function getTextureEncodingFromMap( map, gammaOverrideLinear ) {
  
			var encoding;
  
			if ( ! map ) {
  
				encoding = LinearEncoding;
  
			} else if ( map.isTexture ) {
  
				encoding = map.encoding;
  
			} else if ( map.isWebGLRenderTarget ) {
  
				console.warn( "THREE.WebGLPrograms.getTextureEncodingFromMap: don't use render targets as textures. Use their .texture property instead." );
				encoding = map.texture.encoding;
  
			}
  
			// add backwards compatibility for WebGLRenderer.gammaInput/gammaOutput parameter, should probably be removed at some point.
			if ( encoding === LinearEncoding && gammaOverrideLinear ) {
  
				encoding = GammaEncoding;
  
			}
  
			return encoding;
  
		}
  
		this.getParameters = function ( material, lights, shadows, fog, nClipPlanes, nClipIntersection, object ) {
  
			var shaderID = shaderIDs[ material.type ];
  
			// heuristics to create shader parameters according to lights in the scene
			// (not to blow over maxLights budget)
  
			var maxBones = object.isSkinnedMesh ? allocateBones( object ) : 0;
			var precision = capabilities.precision;
  
			if ( material.precision !== null ) {
  
				precision = capabilities.getMaxPrecision( material.precision );
  
				if ( precision !== material.precision ) {
  
					console.warn( 'THREE.WebGLProgram.getParameters:', material.precision, 'not supported, using', precision, 'instead.' );
  
				}
  
			}
  
			var currentRenderTarget = renderer.getRenderTarget();
  
			var parameters = {
  
				shaderID: shaderID,
  
				precision: precision,
				supportsVertexTextures: capabilities.vertexTextures,
				outputEncoding: getTextureEncodingFromMap( ( ! currentRenderTarget ) ? null : currentRenderTarget.texture, renderer.gammaOutput ),
				map: !! material.map,
				mapEncoding: getTextureEncodingFromMap( material.map, renderer.gammaInput ),
				envMap: !! material.envMap,
				envMapMode: material.envMap && material.envMap.mapping,
				envMapEncoding: getTextureEncodingFromMap( material.envMap, renderer.gammaInput ),
				envMapCubeUV: ( !! material.envMap ) && ( ( material.envMap.mapping === CubeUVReflectionMapping ) || ( material.envMap.mapping === CubeUVRefractionMapping ) ),
				lightMap: !! material.lightMap,
				aoMap: !! material.aoMap,
				emissiveMap: !! material.emissiveMap,
				emissiveMapEncoding: getTextureEncodingFromMap( material.emissiveMap, renderer.gammaInput ),
				bumpMap: !! material.bumpMap,
				normalMap: !! material.normalMap,
				displacementMap: !! material.displacementMap,
				roughnessMap: !! material.roughnessMap,
				metalnessMap: !! material.metalnessMap,
				specularMap: !! material.specularMap,
				alphaMap: !! material.alphaMap,
  
				gradientMap: !! material.gradientMap,
  
				combine: material.combine,
  
				vertexColors: material.vertexColors,
  
				fog: !! fog,
				useFog: material.fog,
				fogExp: ( fog && fog.isFogExp2 ),
  
				flatShading: material.flatShading,
  
				sizeAttenuation: material.sizeAttenuation,
				logarithmicDepthBuffer: capabilities.logarithmicDepthBuffer,
  
				skinning: material.skinning && maxBones > 0,
				maxBones: maxBones,
				useVertexTexture: capabilities.floatVertexTextures,
  
				morphTargets: material.morphTargets,
				morphNormals: material.morphNormals,
				maxMorphTargets: renderer.maxMorphTargets,
				maxMorphNormals: renderer.maxMorphNormals,
  
				numDirLights: lights.directional.length,
				numPointLights: lights.point.length,
				numSpotLights: lights.spot.length,
				numRectAreaLights: lights.rectArea.length,
				numHemiLights: lights.hemi.length,
  
				numClippingPlanes: nClipPlanes,
				numClipIntersection: nClipIntersection,
  
				dithering: material.dithering,
  
				shadowMapEnabled: renderer.shadowMap.enabled && object.receiveShadow && shadows.length > 0,
				shadowMapType: renderer.shadowMap.type,
  
				toneMapping: renderer.toneMapping,
				physicallyCorrectLights: renderer.physicallyCorrectLights,
  
				premultipliedAlpha: material.premultipliedAlpha,
  
				alphaTest: material.alphaTest,
				doubleSided: material.side === DoubleSide,
				flipSided: material.side === BackSide,
  
				depthPacking: ( material.depthPacking !== undefined ) ? material.depthPacking : false
  
			};
  
			return parameters;
  
		};
  
		this.getProgramCode = function ( material, parameters ) {
  
			var array = [];
  
			if ( parameters.shaderID ) {
  
				array.push( parameters.shaderID );
  
			} else {
  
				array.push( material.fragmentShader );
				array.push( material.vertexShader );
  
			}
  
			if ( material.defines !== undefined ) {
  
				for ( var name in material.defines ) {
  
					array.push( name );
					array.push( material.defines[ name ] );
  
				}
  
			}
  
			for ( var i = 0; i < parameterNames.length; i ++ ) {
  
				array.push( parameters[ parameterNames[ i ] ] );
  
			}
  
			array.push( material.onBeforeCompile.toString() );
  
			array.push( renderer.gammaOutput );
  
			return array.join();
  
		};
  
		this.acquireProgram = function ( material, shader, parameters, code ) {
  
			var program;
  
			// Check if code has been already compiled
			for ( var p = 0, pl = programs.length; p < pl; p ++ ) {
  
				var programInfo = programs[ p ];
  
				if ( programInfo.code === code ) {
  
					program = programInfo;
					++ program.usedTimes;
  
					break;
  
				}
  
			}
  
			if ( program === undefined ) {
  
				program = new WebGLProgram( renderer, extensions, code, material, shader, parameters );
				programs.push( program );
  
			}
  
			return program;
  
		};
  
		this.releaseProgram = function ( program ) {
  
			if ( -- program.usedTimes === 0 ) {
  
				// Remove from unordered set
				var i = programs.indexOf( program );
				programs[ i ] = programs[ programs.length - 1 ];
				programs.pop();
  
				// Free WebGL resources
				program.destroy();
  
			}
  
		};
  
		// Exposed for resource monitoring & error feedback via renderer.info:
		this.programs = programs;
  
	}
  
	/**
	 * @author fordacious / fordacious.github.io
	 */
  
	function WebGLProperties() {
  
		var properties = new WeakMap();
  
		function get( object ) {
  
			var map = properties.get( object );
  
			if ( map === undefined ) {
  
				map = {};
				properties.set( object, map );
  
			}
  
			return map;
  
		}
  
		function remove( object ) {
  
			properties.delete( object );
  
		}
  
		function update( object, key, value ) {
  
			properties.get( object )[ key ] = value;
  
		}
  
		function dispose() {
  
			properties = new WeakMap();
  
		}
  
		return {
			get: get,
			remove: remove,
			update: update,
			dispose: dispose
		};
  
	}
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function painterSortStable( a, b ) {
  
		if ( a.renderOrder !== b.renderOrder ) {
  
			return a.renderOrder - b.renderOrder;
  
		} else if ( a.program && b.program && a.program !== b.program ) {
  
			return a.program.id - b.program.id;
  
		} else if ( a.material.id !== b.material.id ) {
  
			return a.material.id - b.material.id;
  
		} else if ( a.z !== b.z ) {
  
			return a.z - b.z;
  
		} else {
  
			return a.id - b.id;
  
		}
  
	}
  
	function reversePainterSortStable( a, b ) {
  
		if ( a.renderOrder !== b.renderOrder ) {
  
			return a.renderOrder - b.renderOrder;
  
		} if ( a.z !== b.z ) {
  
			return b.z - a.z;
  
		} else {
  
			return a.id - b.id;
  
		}
  
	}
  
	function WebGLRenderList() {
  
		var renderItems = [];
		var renderItemsIndex = 0;
  
		var opaque = [];
		var transparent = [];
  
		function init() {
  
			renderItemsIndex = 0;
  
			opaque.length = 0;
			transparent.length = 0;
  
		}
  
		function push( object, geometry, material, z, group ) {
  
			var renderItem = renderItems[ renderItemsIndex ];
  
			if ( renderItem === undefined ) {
  
				renderItem = {
					id: object.id,
					object: object,
					geometry: geometry,
					material: material,
					program: material.program,
					renderOrder: object.renderOrder,
					z: z,
					group: group
				};
  
				renderItems[ renderItemsIndex ] = renderItem;
  
			} else {
  
				renderItem.id = object.id;
				renderItem.object = object;
				renderItem.geometry = geometry;
				renderItem.material = material;
				renderItem.program = material.program;
				renderItem.renderOrder = object.renderOrder;
				renderItem.z = z;
				renderItem.group = group;
  
			}
  
			( material.transparent === true ? transparent : opaque ).push( renderItem );
  
			renderItemsIndex ++;
  
		}
  
		function sort() {
  
			if ( opaque.length > 1 ) opaque.sort( painterSortStable );
			if ( transparent.length > 1 ) transparent.sort( reversePainterSortStable );
  
		}
  
		return {
			opaque: opaque,
			transparent: transparent,
  
			init: init,
			push: push,
  
			sort: sort
		};
  
	}
  
	function WebGLRenderLists() {
  
		var lists = {};
  
		function get( scene, camera ) {
  
			var hash = scene.id + ',' + camera.id;
			var list = lists[ hash ];
  
			if ( list === undefined ) {
  
				// console.log( 'THREE.WebGLRenderLists:', hash );
  
				list = new WebGLRenderList();
				lists[ hash ] = list;
  
			}
  
			return list;
  
		}
  
		function dispose() {
  
			lists = {};
  
		}
  
		return {
			get: get,
			dispose: dispose
		};
  
	}
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function UniformsCache() {
  
		var lights = {};
  
		return {
  
			get: function ( light ) {
  
				if ( lights[ light.id ] !== undefined ) {
  
					return lights[ light.id ];
  
				}
  
				var uniforms;
  
				switch ( light.type ) {
  
					case 'DirectionalLight':
						uniforms = {
							direction: new Vector3(),
							color: new Color(),
  
							shadow: false,
							shadowBias: 0,
							shadowRadius: 1,
							shadowMapSize: new Vector2()
						};
						break;
  
					case 'SpotLight':
						uniforms = {
							position: new Vector3(),
							direction: new Vector3(),
							color: new Color(),
							distance: 0,
							coneCos: 0,
							penumbraCos: 0,
							decay: 0,
  
							shadow: false,
							shadowBias: 0,
							shadowRadius: 1,
							shadowMapSize: new Vector2()
						};
						break;
  
					case 'PointLight':
						uniforms = {
							position: new Vector3(),
							color: new Color(),
							distance: 0,
							decay: 0,
  
							shadow: false,
							shadowBias: 0,
							shadowRadius: 1,
							shadowMapSize: new Vector2(),
							shadowCameraNear: 1,
							shadowCameraFar: 1000
						};
						break;
  
					case 'HemisphereLight':
						uniforms = {
							direction: new Vector3(),
							skyColor: new Color(),
							groundColor: new Color()
						};
						break;
  
					case 'RectAreaLight':
						uniforms = {
							color: new Color(),
							position: new Vector3(),
							halfWidth: new Vector3(),
							halfHeight: new Vector3()
							// TODO (abelnation): set RectAreaLight shadow uniforms
						};
						break;
  
				}
  
				lights[ light.id ] = uniforms;
  
				return uniforms;
  
			}
  
		};
  
	}
  
	var count = 0;
  
	function WebGLLights() {
  
		var cache = new UniformsCache();
  
		var state = {
  
			id: count ++,
  
			hash: '',
  
			ambient: [ 0, 0, 0 ],
			directional: [],
			directionalShadowMap: [],
			directionalShadowMatrix: [],
			spot: [],
			spotShadowMap: [],
			spotShadowMatrix: [],
			rectArea: [],
			point: [],
			pointShadowMap: [],
			pointShadowMatrix: [],
			hemi: []
  
		};
  
		var vector3 = new Vector3();
		var matrix4 = new Matrix4();
		var matrix42 = new Matrix4();
  
		function setup( lights, shadows, camera ) {
  
			var r = 0, g = 0, b = 0;
  
			var directionalLength = 0;
			var pointLength = 0;
			var spotLength = 0;
			var rectAreaLength = 0;
			var hemiLength = 0;
  
			var viewMatrix = camera.matrixWorldInverse;
  
			for ( var i = 0, l = lights.length; i < l; i ++ ) {
  
				var light = lights[ i ];
  
				var color = light.color;
				var intensity = light.intensity;
				var distance = light.distance;
  
				var shadowMap = ( light.shadow && light.shadow.map ) ? light.shadow.map.texture : null;
  
				if ( light.isAmbientLight ) {
  
					r += color.r * intensity;
					g += color.g * intensity;
					b += color.b * intensity;
  
				} else if ( light.isDirectionalLight ) {
  
					var uniforms = cache.get( light );
  
					uniforms.color.copy( light.color ).multiplyScalar( light.intensity );
					uniforms.direction.setFromMatrixPosition( light.matrixWorld );
					vector3.setFromMatrixPosition( light.target.matrixWorld );
					uniforms.direction.sub( vector3 );
					uniforms.direction.transformDirection( viewMatrix );
  
					uniforms.shadow = light.castShadow;
  
					if ( light.castShadow ) {
  
						var shadow = light.shadow;
  
						uniforms.shadowBias = shadow.bias;
						uniforms.shadowRadius = shadow.radius;
						uniforms.shadowMapSize = shadow.mapSize;
  
					}
  
					state.directionalShadowMap[ directionalLength ] = shadowMap;
					state.directionalShadowMatrix[ directionalLength ] = light.shadow.matrix;
					state.directional[ directionalLength ] = uniforms;
  
					directionalLength ++;
  
				} else if ( light.isSpotLight ) {
  
					var uniforms = cache.get( light );
  
					uniforms.position.setFromMatrixPosition( light.matrixWorld );
					uniforms.position.applyMatrix4( viewMatrix );
  
					uniforms.color.copy( color ).multiplyScalar( intensity );
					uniforms.distance = distance;
  
					uniforms.direction.setFromMatrixPosition( light.matrixWorld );
					vector3.setFromMatrixPosition( light.target.matrixWorld );
					uniforms.direction.sub( vector3 );
					uniforms.direction.transformDirection( viewMatrix );
  
					uniforms.coneCos = Math.cos( light.angle );
					uniforms.penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) );
					uniforms.decay = ( light.distance === 0 ) ? 0.0 : light.decay;
  
					uniforms.shadow = light.castShadow;
  
					if ( light.castShadow ) {
  
						var shadow = light.shadow;
  
						uniforms.shadowBias = shadow.bias;
						uniforms.shadowRadius = shadow.radius;
						uniforms.shadowMapSize = shadow.mapSize;
  
					}
  
					state.spotShadowMap[ spotLength ] = shadowMap;
					state.spotShadowMatrix[ spotLength ] = light.shadow.matrix;
					state.spot[ spotLength ] = uniforms;
  
					spotLength ++;
  
				} else if ( light.isRectAreaLight ) {
  
					var uniforms = cache.get( light );
  
					// (a) intensity is the total visible light emitted
					//uniforms.color.copy( color ).multiplyScalar( intensity / ( light.width * light.height * Math.PI ) );
  
					// (b) intensity is the brightness of the light
					uniforms.color.copy( color ).multiplyScalar( intensity );
  
					uniforms.position.setFromMatrixPosition( light.matrixWorld );
					uniforms.position.applyMatrix4( viewMatrix );
  
					// extract local rotation of light to derive width/height half vectors
					matrix42.identity();
					matrix4.copy( light.matrixWorld );
					matrix4.premultiply( viewMatrix );
					matrix42.extractRotation( matrix4 );
  
					uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 );
					uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 );
  
					uniforms.halfWidth.applyMatrix4( matrix42 );
					uniforms.halfHeight.applyMatrix4( matrix42 );
  
					// TODO (abelnation): RectAreaLight distance?
					// uniforms.distance = distance;
  
					state.rectArea[ rectAreaLength ] = uniforms;
  
					rectAreaLength ++;
  
				} else if ( light.isPointLight ) {
  
					var uniforms = cache.get( light );
  
					uniforms.position.setFromMatrixPosition( light.matrixWorld );
					uniforms.position.applyMatrix4( viewMatrix );
  
					uniforms.color.copy( light.color ).multiplyScalar( light.intensity );
					uniforms.distance = light.distance;
					uniforms.decay = ( light.distance === 0 ) ? 0.0 : light.decay;
  
					uniforms.shadow = light.castShadow;
  
					if ( light.castShadow ) {
  
						var shadow = light.shadow;
  
						uniforms.shadowBias = shadow.bias;
						uniforms.shadowRadius = shadow.radius;
						uniforms.shadowMapSize = shadow.mapSize;
						uniforms.shadowCameraNear = shadow.camera.near;
						uniforms.shadowCameraFar = shadow.camera.far;
  
					}
  
					state.pointShadowMap[ pointLength ] = shadowMap;
					state.pointShadowMatrix[ pointLength ] = light.shadow.matrix;
					state.point[ pointLength ] = uniforms;
  
					pointLength ++;
  
				} else if ( light.isHemisphereLight ) {
  
					var uniforms = cache.get( light );
  
					uniforms.direction.setFromMatrixPosition( light.matrixWorld );
					uniforms.direction.transformDirection( viewMatrix );
					uniforms.direction.normalize();
  
					uniforms.skyColor.copy( light.color ).multiplyScalar( intensity );
					uniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity );
  
					state.hemi[ hemiLength ] = uniforms;
  
					hemiLength ++;
  
				}
  
			}
  
			state.ambient[ 0 ] = r;
			state.ambient[ 1 ] = g;
			state.ambient[ 2 ] = b;
  
			state.directional.length = directionalLength;
			state.spot.length = spotLength;
			state.rectArea.length = rectAreaLength;
			state.point.length = pointLength;
			state.hemi.length = hemiLength;
  
			state.hash = state.id + ',' + directionalLength + ',' + pointLength + ',' + spotLength + ',' + rectAreaLength + ',' + hemiLength + ',' + shadows.length;
  
		}
  
		return {
			setup: setup,
			state: state
		};
  
	}
  
	/**
	 * @author Mugen87 / https://github.com/Mugen87
	 */
  
	function WebGLRenderState() {
  
		var lights = new WebGLLights();
  
		var lightsArray = [];
		var shadowsArray = [];
		var spritesArray = [];
  
		function init() {
  
			lightsArray.length = 0;
			shadowsArray.length = 0;
			spritesArray.length = 0;
  
		}
  
		function pushLight( light ) {
  
			lightsArray.push( light );
  
		}
  
		function pushShadow( shadowLight ) {
  
			shadowsArray.push( shadowLight );
  
		}
  
		function pushSprite( shadowLight ) {
  
			spritesArray.push( shadowLight );
  
		}
  
		function setupLights( camera ) {
  
			lights.setup( lightsArray, shadowsArray, camera );
  
		}
  
		var state = {
			lightsArray: lightsArray,
			shadowsArray: shadowsArray,
			spritesArray: spritesArray,
  
			lights: lights
		};
  
		return {
			init: init,
			state: state,
			setupLights: setupLights,
  
			pushLight: pushLight,
			pushShadow: pushShadow,
			pushSprite: pushSprite
		};
  
	}
  
	function WebGLRenderStates() {
  
		var renderStates = {};
  
		function get( scene, camera ) {
  
			var hash = scene.id + ',' + camera.id;
  
			var renderState = renderStates[ hash ];
  
			if ( renderState === undefined ) {
  
				renderState = new WebGLRenderState();
				renderStates[ hash ] = renderState;
  
			}
  
			return renderState;
  
		}
  
		function dispose() {
  
			renderStates = {};
  
		}
  
		return {
			get: get,
			dispose: dispose
		};
  
	}
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 * @author alteredq / http://alteredqualia.com/
	 * @author bhouston / https://clara.io
	 * @author WestLangley / http://github.com/WestLangley
	 *
	 * parameters = {
	 *
	 *  opacity: <float>,
	 *
	 *  map: new THREE.Texture( <Image> ),
	 *
	 *  alphaMap: new THREE.Texture( <Image> ),
	 *
	 *  displacementMap: new THREE.Texture( <Image> ),
	 *  displacementScale: <float>,
	 *  displacementBias: <float>,
	 *
	 *  wireframe: <boolean>,
	 *  wireframeLinewidth: <float>
	 * }
	 */
  
	function MeshDepthMaterial( parameters ) {
  
		Material.call( this );
  
		this.type = 'MeshDepthMaterial';
  
		this.depthPacking = BasicDepthPacking;
  
		this.skinning = false;
		this.morphTargets = false;
  
		this.map = null;
  
		this.alphaMap = null;
  
		this.displacementMap = null;
		this.displacementScale = 1;
		this.displacementBias = 0;
  
		this.wireframe = false;
		this.wireframeLinewidth = 1;
  
		this.fog = false;
		this.lights = false;
  
		this.setValues( parameters );
  
	}
  
	MeshDepthMaterial.prototype = Object.create( Material.prototype );
	MeshDepthMaterial.prototype.constructor = MeshDepthMaterial;
  
	MeshDepthMaterial.prototype.isMeshDepthMaterial = true;
  
	MeshDepthMaterial.prototype.copy = function ( source ) {
  
		Material.prototype.copy.call( this, source );
  
		this.depthPacking = source.depthPacking;
  
		this.skinning = source.skinning;
		this.morphTargets = source.morphTargets;
  
		this.map = source.map;
  
		this.alphaMap = source.alphaMap;
  
		this.displacementMap = source.displacementMap;
		this.displacementScale = source.displacementScale;
		this.displacementBias = source.displacementBias;
  
		this.wireframe = source.wireframe;
		this.wireframeLinewidth = source.wireframeLinewidth;
  
		return this;
  
	};
  
	/**
	 * @author WestLangley / http://github.com/WestLangley
	 *
	 * parameters = {
	 *
	 *  referencePosition: <float>,
	 *  nearDistance: <float>,
	 *  farDistance: <float>,
	 *
	 *  skinning: <bool>,
	 *  morphTargets: <bool>,
	 *
	 *  map: new THREE.Texture( <Image> ),
	 *
	 *  alphaMap: new THREE.Texture( <Image> ),
	 *
	 *  displacementMap: new THREE.Texture( <Image> ),
	 *  displacementScale: <float>,
	 *  displacementBias: <float>
	 *
	 * }
	 */
  
	function MeshDistanceMaterial( parameters ) {
  
		Material.call( this );
  
		this.type = 'MeshDistanceMaterial';
  
		this.referencePosition = new Vector3();
		this.nearDistance = 1;
		this.farDistance = 1000;
  
		this.skinning = false;
		this.morphTargets = false;
  
		this.map = null;
  
		this.alphaMap = null;
  
		this.displacementMap = null;
		this.displacementScale = 1;
		this.displacementBias = 0;
  
		this.fog = false;
		this.lights = false;
  
		this.setValues( parameters );
  
	}
  
	MeshDistanceMaterial.prototype = Object.create( Material.prototype );
	MeshDistanceMaterial.prototype.constructor = MeshDistanceMaterial;
  
	MeshDistanceMaterial.prototype.isMeshDistanceMaterial = true;
  
	MeshDistanceMaterial.prototype.copy = function ( source ) {
  
		Material.prototype.copy.call( this, source );
  
		this.referencePosition.copy( source.referencePosition );
		this.nearDistance = source.nearDistance;
		this.farDistance = source.farDistance;
  
		this.skinning = source.skinning;
		this.morphTargets = source.morphTargets;
  
		this.map = source.map;
  
		this.alphaMap = source.alphaMap;
  
		this.displacementMap = source.displacementMap;
		this.displacementScale = source.displacementScale;
		this.displacementBias = source.displacementBias;
  
		return this;
  
	};
  
	/**
	 * @author alteredq / http://alteredqualia.com/
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function WebGLShadowMap( _renderer, _objects, maxTextureSize ) {
  
		var _frustum = new Frustum(),
			_projScreenMatrix = new Matrix4(),
  
			_shadowMapSize = new Vector2(),
			_maxShadowMapSize = new Vector2( maxTextureSize, maxTextureSize ),
  
			_lookTarget = new Vector3(),
			_lightPositionWorld = new Vector3(),
  
			_MorphingFlag = 1,
			_SkinningFlag = 2,
  
			_NumberOfMaterialVariants = ( _MorphingFlag | _SkinningFlag ) + 1,
  
			_depthMaterials = new Array( _NumberOfMaterialVariants ),
			_distanceMaterials = new Array( _NumberOfMaterialVariants ),
  
			_materialCache = {};
  
		var shadowSide = { 0: BackSide, 1: FrontSide, 2: DoubleSide };
  
		var cubeDirections = [
			new Vector3( 1, 0, 0 ), new Vector3( - 1, 0, 0 ), new Vector3( 0, 0, 1 ),
			new Vector3( 0, 0, - 1 ), new Vector3( 0, 1, 0 ), new Vector3( 0, - 1, 0 )
		];
  
		var cubeUps = [
			new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ),
			new Vector3( 0, 1, 0 ), new Vector3( 0, 0, 1 ),	new Vector3( 0, 0, - 1 )
		];
  
		var cube2DViewPorts = [
			new Vector4(), new Vector4(), new Vector4(),
			new Vector4(), new Vector4(), new Vector4()
		];
  
		// init
  
		for ( var i = 0; i !== _NumberOfMaterialVariants; ++ i ) {
  
			var useMorphing = ( i & _MorphingFlag ) !== 0;
			var useSkinning = ( i & _SkinningFlag ) !== 0;
  
			var depthMaterial = new MeshDepthMaterial( {
  
				depthPacking: RGBADepthPacking,
  
				morphTargets: useMorphing,
				skinning: useSkinning
  
			} );
  
			_depthMaterials[ i ] = depthMaterial;
  
			//
  
			var distanceMaterial = new MeshDistanceMaterial( {
  
				morphTargets: useMorphing,
				skinning: useSkinning
  
			} );
  
			_distanceMaterials[ i ] = distanceMaterial;
  
		}
  
		//
  
		var scope = this;
  
		this.enabled = false;
  
		this.autoUpdate = true;
		this.needsUpdate = false;
  
		this.type = PCFShadowMap;
  
		this.render = function ( lights, scene, camera ) {
  
			if ( scope.enabled === false ) return;
			if ( scope.autoUpdate === false && scope.needsUpdate === false ) return;
  
			if ( lights.length === 0 ) return;
  
			// TODO Clean up (needed in case of contextlost)
			var _gl = _renderer.context;
			var _state = _renderer.state;
  
			// Set GL state for depth map.
			_state.disable( _gl.BLEND );
			_state.buffers.color.setClear( 1, 1, 1, 1 );
			_state.buffers.depth.setTest( true );
			_state.setScissorTest( false );
  
			// render depth map
  
			var faceCount;
  
			for ( var i = 0, il = lights.length; i < il; i ++ ) {
  
				var light = lights[ i ];
				var shadow = light.shadow;
				var isPointLight = light && light.isPointLight;
  
				if ( shadow === undefined ) {
  
					console.warn( 'THREE.WebGLShadowMap:', light, 'has no shadow.' );
					continue;
  
				}
  
				var shadowCamera = shadow.camera;
  
				_shadowMapSize.copy( shadow.mapSize );
				_shadowMapSize.min( _maxShadowMapSize );
  
				if ( isPointLight ) {
  
					var vpWidth = _shadowMapSize.x;
					var vpHeight = _shadowMapSize.y;
  
					// These viewports map a cube-map onto a 2D texture with the
					// following orientation:
					//
					//  xzXZ
					//   y Y
					//
					// X - Positive x direction
					// x - Negative x direction
					// Y - Positive y direction
					// y - Negative y direction
					// Z - Positive z direction
					// z - Negative z direction
  
					// positive X
					cube2DViewPorts[ 0 ].set( vpWidth * 2, vpHeight, vpWidth, vpHeight );
					// negative X
					cube2DViewPorts[ 1 ].set( 0, vpHeight, vpWidth, vpHeight );
					// positive Z
					cube2DViewPorts[ 2 ].set( vpWidth * 3, vpHeight, vpWidth, vpHeight );
					// negative Z
					cube2DViewPorts[ 3 ].set( vpWidth, vpHeight, vpWidth, vpHeight );
					// positive Y
					cube2DViewPorts[ 4 ].set( vpWidth * 3, 0, vpWidth, vpHeight );
					// negative Y
					cube2DViewPorts[ 5 ].set( vpWidth, 0, vpWidth, vpHeight );
  
					_shadowMapSize.x *= 4.0;
					_shadowMapSize.y *= 2.0;
  
				}
  
				if ( shadow.map === null ) {
  
					var pars = { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat };
  
					shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars );
					shadow.map.texture.name = light.name + ".shadowMap";
  
					shadowCamera.updateProjectionMatrix();
  
				}
  
				if ( shadow.isSpotLightShadow ) {
  
					shadow.update( light );
  
				}
  
				var shadowMap = shadow.map;
				var shadowMatrix = shadow.matrix;
  
				_lightPositionWorld.setFromMatrixPosition( light.matrixWorld );
				shadowCamera.position.copy( _lightPositionWorld );
  
				if ( isPointLight ) {
  
					faceCount = 6;
  
					// for point lights we set the shadow matrix to be a translation-only matrix
					// equal to inverse of the light's position
  
					shadowMatrix.makeTranslation( - _lightPositionWorld.x, - _lightPositionWorld.y, - _lightPositionWorld.z );
  
				} else {
  
					faceCount = 1;
  
					_lookTarget.setFromMatrixPosition( light.target.matrixWorld );
					shadowCamera.lookAt( _lookTarget );
					shadowCamera.updateMatrixWorld();
  
					// compute shadow matrix
  
					shadowMatrix.set(
						0.5, 0.0, 0.0, 0.5,
						0.0, 0.5, 0.0, 0.5,
						0.0, 0.0, 0.5, 0.5,
						0.0, 0.0, 0.0, 1.0
					);
  
					shadowMatrix.multiply( shadowCamera.projectionMatrix );
					shadowMatrix.multiply( shadowCamera.matrixWorldInverse );
  
				}
  
				_renderer.setRenderTarget( shadowMap );
				_renderer.clear();
  
				// render shadow map for each cube face (if omni-directional) or
				// run a single pass if not
  
				for ( var face = 0; face < faceCount; face ++ ) {
  
					if ( isPointLight ) {
  
						_lookTarget.copy( shadowCamera.position );
						_lookTarget.add( cubeDirections[ face ] );
						shadowCamera.up.copy( cubeUps[ face ] );
						shadowCamera.lookAt( _lookTarget );
						shadowCamera.updateMatrixWorld();
  
						var vpDimensions = cube2DViewPorts[ face ];
						_state.viewport( vpDimensions );
  
					}
  
					// update camera matrices and frustum
  
					_projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse );
					_frustum.setFromMatrix( _projScreenMatrix );
  
					// set object matrices & frustum culling
  
					renderObject( scene, camera, shadowCamera, isPointLight );
  
				}
  
			}
  
			scope.needsUpdate = false;
  
		};
  
		function getDepthMaterial( object, material, isPointLight, lightPositionWorld, shadowCameraNear, shadowCameraFar ) {
  
			var geometry = object.geometry;
  
			var result = null;
  
			var materialVariants = _depthMaterials;
			var customMaterial = object.customDepthMaterial;
  
			if ( isPointLight ) {
  
				materialVariants = _distanceMaterials;
				customMaterial = object.customDistanceMaterial;
  
			}
  
			if ( ! customMaterial ) {
  
				var useMorphing = false;
  
				if ( material.morphTargets ) {
  
					if ( geometry && geometry.isBufferGeometry ) {
  
						useMorphing = geometry.morphAttributes && geometry.morphAttributes.position && geometry.morphAttributes.position.length > 0;
  
					} else if ( geometry && geometry.isGeometry ) {
  
						useMorphing = geometry.morphTargets && geometry.morphTargets.length > 0;
  
					}
  
				}
  
				if ( object.isSkinnedMesh && material.skinning === false ) {
  
					console.warn( 'THREE.WebGLShadowMap: THREE.SkinnedMesh with material.skinning set to false:', object );
  
				}
  
				var useSkinning = object.isSkinnedMesh && material.skinning;
  
				var variantIndex = 0;
  
				if ( useMorphing ) variantIndex |= _MorphingFlag;
				if ( useSkinning ) variantIndex |= _SkinningFlag;
  
				result = materialVariants[ variantIndex ];
  
			} else {
  
				result = customMaterial;
  
			}
  
			if ( _renderer.localClippingEnabled &&
					material.clipShadows === true &&
					material.clippingPlanes.length !== 0 ) {
  
				// in this case we need a unique material instance reflecting the
				// appropriate state
  
				var keyA = result.uuid, keyB = material.uuid;
  
				var materialsForVariant = _materialCache[ keyA ];
  
				if ( materialsForVariant === undefined ) {
  
					materialsForVariant = {};
					_materialCache[ keyA ] = materialsForVariant;
  
				}
  
				var cachedMaterial = materialsForVariant[ keyB ];
  
				if ( cachedMaterial === undefined ) {
  
					cachedMaterial = result.clone();
					materialsForVariant[ keyB ] = cachedMaterial;
  
				}
  
				result = cachedMaterial;
  
			}
  
			result.visible = material.visible;
			result.wireframe = material.wireframe;
  
			result.side = ( material.shadowSide != null ) ? material.shadowSide : shadowSide[ material.side ];
  
			result.clipShadows = material.clipShadows;
			result.clippingPlanes = material.clippingPlanes;
			result.clipIntersection = material.clipIntersection;
  
			result.wireframeLinewidth = material.wireframeLinewidth;
			result.linewidth = material.linewidth;
  
			if ( isPointLight && result.isMeshDistanceMaterial ) {
  
				result.referencePosition.copy( lightPositionWorld );
				result.nearDistance = shadowCameraNear;
				result.farDistance = shadowCameraFar;
  
			}
  
			return result;
  
		}
  
		function renderObject( object, camera, shadowCamera, isPointLight ) {
  
			if ( object.visible === false ) return;
  
			var visible = object.layers.test( camera.layers );
  
			if ( visible && ( object.isMesh || object.isLine || object.isPoints ) ) {
  
				if ( object.castShadow && ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) ) {
  
					object.modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld );
  
					var geometry = _objects.update( object );
					var material = object.material;
  
					if ( Array.isArray( material ) ) {
  
						var groups = geometry.groups;
  
						for ( var k = 0, kl = groups.length; k < kl; k ++ ) {
  
							var group = groups[ k ];
							var groupMaterial = material[ group.materialIndex ];
  
							if ( groupMaterial && groupMaterial.visible ) {
  
								var depthMaterial = getDepthMaterial( object, groupMaterial, isPointLight, _lightPositionWorld, shadowCamera.near, shadowCamera.far );
								_renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, group );
  
							}
  
						}
  
					} else if ( material.visible ) {
  
						var depthMaterial = getDepthMaterial( object, material, isPointLight, _lightPositionWorld, shadowCamera.near, shadowCamera.far );
						_renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, null );
  
					}
  
				}
  
			}
  
			var children = object.children;
  
			for ( var i = 0, l = children.length; i < l; i ++ ) {
  
				renderObject( children[ i ], camera, shadowCamera, isPointLight );
  
			}
  
		}
  
	}
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function CanvasTexture( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {
  
		Texture.call( this, canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );
  
		this.needsUpdate = true;
  
	}
  
	CanvasTexture.prototype = Object.create( Texture.prototype );
	CanvasTexture.prototype.constructor = CanvasTexture;
	CanvasTexture.prototype.isCanvasTexture = true;
  
	/**
	 * @author mikael emtinger / http://gomo.se/
	 * @author alteredq / http://alteredqualia.com/
	 */
  
	function WebGLSpriteRenderer( renderer, gl, state, textures, capabilities ) {
  
		var vertexBuffer, elementBuffer;
		var program, attributes, uniforms;
  
		var texture;
  
		// decompose matrixWorld
  
		var spritePosition = new Vector3();
		var spriteRotation = new Quaternion();
		var spriteScale = new Vector3();
  
		function init() {
  
			var vertices = new Float32Array( [
				- 0.5, - 0.5, 0, 0,
				  0.5, - 0.5, 1, 0,
				  0.5, 0.5, 1, 1,
				- 0.5, 0.5, 0, 1
			] );
  
			var faces = new Uint16Array( [
				0, 1, 2,
				0, 2, 3
			] );
  
			vertexBuffer = gl.createBuffer();
			elementBuffer = gl.createBuffer();
  
			gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );
			gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW );
  
			gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );
			gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW );
  
			program = createProgram();
  
			attributes = {
				position: gl.getAttribLocation( program, 'position' ),
				uv: gl.getAttribLocation( program, 'uv' )
			};
  
			uniforms = {
				uvOffset: gl.getUniformLocation( program, 'uvOffset' ),
				uvScale: gl.getUniformLocation( program, 'uvScale' ),
  
				rotation: gl.getUniformLocation( program, 'rotation' ),
				center: gl.getUniformLocation( program, 'center' ),
				scale: gl.getUniformLocation( program, 'scale' ),
  
				color: gl.getUniformLocation( program, 'color' ),
				map: gl.getUniformLocation( program, 'map' ),
				opacity: gl.getUniformLocation( program, 'opacity' ),
  
				modelViewMatrix: gl.getUniformLocation( program, 'modelViewMatrix' ),
				projectionMatrix: gl.getUniformLocation( program, 'projectionMatrix' ),
  
				fogType: gl.getUniformLocation( program, 'fogType' ),
				fogDensity: gl.getUniformLocation( program, 'fogDensity' ),
				fogNear: gl.getUniformLocation( program, 'fogNear' ),
				fogFar: gl.getUniformLocation( program, 'fogFar' ),
				fogColor: gl.getUniformLocation( program, 'fogColor' ),
				fogDepth: gl.getUniformLocation( program, 'fogDepth' ),
  
				alphaTest: gl.getUniformLocation( program, 'alphaTest' )
			};
  
			var canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' );
			canvas.width = 8;
			canvas.height = 8;
  
			var context = canvas.getContext( '2d' );
			context.fillStyle = 'white';
			context.fillRect( 0, 0, 8, 8 );
  
			texture = new CanvasTexture( canvas );
  
		}
  
		this.render = function ( sprites, scene, camera ) {
  
			if ( sprites.length === 0 ) return;
  
			// setup gl
  
			if ( program === undefined ) {
  
				init();
  
			}
  
			state.useProgram( program );
  
			state.initAttributes();
			state.enableAttribute( attributes.position );
			state.enableAttribute( attributes.uv );
			state.disableUnusedAttributes();
  
			state.disable( gl.CULL_FACE );
			state.enable( gl.BLEND );
  
			gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );
			gl.vertexAttribPointer( attributes.position, 2, gl.FLOAT, false, 2 * 8, 0 );
			gl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 );
  
			gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );
  
			gl.uniformMatrix4fv( uniforms.projectionMatrix, false, camera.projectionMatrix.elements );
  
			state.activeTexture( gl.TEXTURE0 );
			gl.uniform1i( uniforms.map, 0 );
  
			var oldFogType = 0;
			var sceneFogType = 0;
			var fog = scene.fog;
  
			if ( fog ) {
  
				gl.uniform3f( uniforms.fogColor, fog.color.r, fog.color.g, fog.color.b );
  
				if ( fog.isFog ) {
  
					gl.uniform1f( uniforms.fogNear, fog.near );
					gl.uniform1f( uniforms.fogFar, fog.far );
  
					gl.uniform1i( uniforms.fogType, 1 );
					oldFogType = 1;
					sceneFogType = 1;
  
				} else if ( fog.isFogExp2 ) {
  
					gl.uniform1f( uniforms.fogDensity, fog.density );
  
					gl.uniform1i( uniforms.fogType, 2 );
					oldFogType = 2;
					sceneFogType = 2;
  
				}
  
			} else {
  
				gl.uniform1i( uniforms.fogType, 0 );
				oldFogType = 0;
				sceneFogType = 0;
  
			}
  
  
			// update positions and sort
  
			for ( var i = 0, l = sprites.length; i < l; i ++ ) {
  
				var sprite = sprites[ i ];
  
				sprite.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld );
				sprite.z = - sprite.modelViewMatrix.elements[ 14 ];
  
			}
  
			sprites.sort( painterSortStable );
  
			// render all sprites
  
			var scale = [];
			var center = [];
  
			for ( var i = 0, l = sprites.length; i < l; i ++ ) {
  
				var sprite = sprites[ i ];
				var material = sprite.material;
  
				if ( material.visible === false ) continue;
  
				sprite.onBeforeRender( renderer, scene, camera, undefined, material, undefined );
  
				gl.uniform1f( uniforms.alphaTest, material.alphaTest );
				gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite.modelViewMatrix.elements );
  
				sprite.matrixWorld.decompose( spritePosition, spriteRotation, spriteScale );
  
				scale[ 0 ] = spriteScale.x;
				scale[ 1 ] = spriteScale.y;
  
				center[ 0 ] = sprite.center.x - 0.5;
				center[ 1 ] = sprite.center.y - 0.5;
  
				var fogType = 0;
  
				if ( scene.fog && material.fog ) {
  
					fogType = sceneFogType;
  
				}
  
				if ( oldFogType !== fogType ) {
  
					gl.uniform1i( uniforms.fogType, fogType );
					oldFogType = fogType;
  
				}
  
				if ( material.map !== null ) {
  
					gl.uniform2f( uniforms.uvOffset, material.map.offset.x, material.map.offset.y );
					gl.uniform2f( uniforms.uvScale, material.map.repeat.x, material.map.repeat.y );
  
				} else {
  
					gl.uniform2f( uniforms.uvOffset, 0, 0 );
					gl.uniform2f( uniforms.uvScale, 1, 1 );
  
				}
  
				gl.uniform1f( uniforms.opacity, material.opacity );
				gl.uniform3f( uniforms.color, material.color.r, material.color.g, material.color.b );
  
				gl.uniform1f( uniforms.rotation, material.rotation );
				gl.uniform2fv( uniforms.center, center );
				gl.uniform2fv( uniforms.scale, scale );
  
				state.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha );
				state.buffers.depth.setTest( material.depthTest );
				state.buffers.depth.setMask( material.depthWrite );
				state.buffers.color.setMask( material.colorWrite );
  
				textures.setTexture2D( material.map || texture, 0 );
  
				gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
  
				sprite.onAfterRender( renderer, scene, camera, undefined, material, undefined );
  
			}
  
			// restore gl
  
			state.enable( gl.CULL_FACE );
  
			state.reset();
  
		};
  
		function createProgram() {
  
			var program = gl.createProgram();
  
			var vertexShader = gl.createShader( gl.VERTEX_SHADER );
			var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER );
  
			gl.shaderSource( vertexShader, [
  
				'precision ' + capabilities.precision + ' float;',
  
				'#define SHADER_NAME ' + 'SpriteMaterial',
  
				'uniform mat4 modelViewMatrix;',
				'uniform mat4 projectionMatrix;',
				'uniform float rotation;',
				'uniform vec2 center;',
				'uniform vec2 scale;',
				'uniform vec2 uvOffset;',
				'uniform vec2 uvScale;',
  
				'attribute vec2 position;',
				'attribute vec2 uv;',
  
				'varying vec2 vUV;',
				'varying float fogDepth;',
  
				'void main() {',
  
				'	vUV = uvOffset + uv * uvScale;',
  
				'	vec2 alignedPosition = ( position - center ) * scale;',
  
				'	vec2 rotatedPosition;',
				'	rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;',
				'	rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;',
  
				'	vec4 mvPosition;',
  
				'	mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );',
				'	mvPosition.xy += rotatedPosition;',
  
				'	gl_Position = projectionMatrix * mvPosition;',
  
				'	fogDepth = - mvPosition.z;',
  
				'}'
  
			].join( '\n' ) );
  
			gl.shaderSource( fragmentShader, [
  
				'precision ' + capabilities.precision + ' float;',
  
				'#define SHADER_NAME ' + 'SpriteMaterial',
  
				'uniform vec3 color;',
				'uniform sampler2D map;',
				'uniform float opacity;',
  
				'uniform int fogType;',
				'uniform vec3 fogColor;',
				'uniform float fogDensity;',
				'uniform float fogNear;',
				'uniform float fogFar;',
				'uniform float alphaTest;',
  
				'varying vec2 vUV;',
				'varying float fogDepth;',
  
				'void main() {',
  
				'	vec4 texture = texture2D( map, vUV );',
  
				'	gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );',
  
				'	if ( gl_FragColor.a < alphaTest ) discard;',
  
				'	if ( fogType > 0 ) {',
  
				'		float fogFactor = 0.0;',
  
				'		if ( fogType == 1 ) {',
  
				'			fogFactor = smoothstep( fogNear, fogFar, fogDepth );',
  
				'		} else {',
  
				'			const float LOG2 = 1.442695;',
				'			fogFactor = exp2( - fogDensity * fogDensity * fogDepth * fogDepth * LOG2 );',
				'			fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );',
  
				'		}',
  
				'		gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );',
  
				'	}',
  
				'}'
  
			].join( '\n' ) );
  
			gl.compileShader( vertexShader );
			gl.compileShader( fragmentShader );
  
			gl.attachShader( program, vertexShader );
			gl.attachShader( program, fragmentShader );
  
			gl.linkProgram( program );
  
			return program;
  
		}
  
		function painterSortStable( a, b ) {
  
			if ( a.renderOrder !== b.renderOrder ) {
  
				return a.renderOrder - b.renderOrder;
  
			} else if ( a.z !== b.z ) {
  
				return b.z - a.z;
  
			} else {
  
				return b.id - a.id;
  
			}
  
		}
  
	}
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function WebGLState( gl, extensions, utils ) {
  
		function ColorBuffer() {
  
			var locked = false;
  
			var color = new Vector4();
			var currentColorMask = null;
			var currentColorClear = new Vector4( 0, 0, 0, 0 );
  
			return {
  
				setMask: function ( colorMask ) {
  
					if ( currentColorMask !== colorMask && ! locked ) {
  
						gl.colorMask( colorMask, colorMask, colorMask, colorMask );
						currentColorMask = colorMask;
  
					}
  
				},
  
				setLocked: function ( lock ) {
  
					locked = lock;
  
				},
  
				setClear: function ( r, g, b, a, premultipliedAlpha ) {
  
					if ( premultipliedAlpha === true ) {
  
						r *= a; g *= a; b *= a;
  
					}
  
					color.set( r, g, b, a );
  
					if ( currentColorClear.equals( color ) === false ) {
  
						gl.clearColor( r, g, b, a );
						currentColorClear.copy( color );
  
					}
  
				},
  
				reset: function () {
  
					locked = false;
  
					currentColorMask = null;
					currentColorClear.set( - 1, 0, 0, 0 ); // set to invalid state
  
				}
  
			};
  
		}
  
		function DepthBuffer() {
  
			var locked = false;
  
			var currentDepthMask = null;
			var currentDepthFunc = null;
			var currentDepthClear = null;
  
			return {
  
				setTest: function ( depthTest ) {
  
					if ( depthTest ) {
  
						enable( gl.DEPTH_TEST );
  
					} else {
  
						disable( gl.DEPTH_TEST );
  
					}
  
				},
  
				setMask: function ( depthMask ) {
  
					if ( currentDepthMask !== depthMask && ! locked ) {
  
						gl.depthMask( depthMask );
						currentDepthMask = depthMask;
  
					}
  
				},
  
				setFunc: function ( depthFunc ) {
  
					if ( currentDepthFunc !== depthFunc ) {
  
						if ( depthFunc ) {
  
							switch ( depthFunc ) {
  
								case NeverDepth:
  
									gl.depthFunc( gl.NEVER );
									break;
  
								case AlwaysDepth:
  
									gl.depthFunc( gl.ALWAYS );
									break;
  
								case LessDepth:
  
									gl.depthFunc( gl.LESS );
									break;
  
								case LessEqualDepth:
  
									gl.depthFunc( gl.LEQUAL );
									break;
  
								case EqualDepth:
  
									gl.depthFunc( gl.EQUAL );
									break;
  
								case GreaterEqualDepth:
  
									gl.depthFunc( gl.GEQUAL );
									break;
  
								case GreaterDepth:
  
									gl.depthFunc( gl.GREATER );
									break;
  
								case NotEqualDepth:
  
									gl.depthFunc( gl.NOTEQUAL );
									break;
  
								default:
  
									gl.depthFunc( gl.LEQUAL );
  
							}
  
						} else {
  
							gl.depthFunc( gl.LEQUAL );
  
						}
  
						currentDepthFunc = depthFunc;
  
					}
  
				},
  
				setLocked: function ( lock ) {
  
					locked = lock;
  
				},
  
				setClear: function ( depth ) {
  
					if ( currentDepthClear !== depth ) {
  
						gl.clearDepth( depth );
						currentDepthClear = depth;
  
					}
  
				},
  
				reset: function () {
  
					locked = false;
  
					currentDepthMask = null;
					currentDepthFunc = null;
					currentDepthClear = null;
  
				}
  
			};
  
		}
  
		function StencilBuffer() {
  
			var locked = false;
  
			var currentStencilMask = null;
			var currentStencilFunc = null;
			var currentStencilRef = null;
			var currentStencilFuncMask = null;
			var currentStencilFail = null;
			var currentStencilZFail = null;
			var currentStencilZPass = null;
			var currentStencilClear = null;
  
			return {
  
				setTest: function ( stencilTest ) {
  
					if ( stencilTest ) {
  
						enable( gl.STENCIL_TEST );
  
					} else {
  
						disable( gl.STENCIL_TEST );
  
					}
  
				},
  
				setMask: function ( stencilMask ) {
  
					if ( currentStencilMask !== stencilMask && ! locked ) {
  
						gl.stencilMask( stencilMask );
						currentStencilMask = stencilMask;
  
					}
  
				},
  
				setFunc: function ( stencilFunc, stencilRef, stencilMask ) {
  
					if ( currentStencilFunc !== stencilFunc ||
						 currentStencilRef 	!== stencilRef 	||
						 currentStencilFuncMask !== stencilMask ) {
  
						gl.stencilFunc( stencilFunc, stencilRef, stencilMask );
  
						currentStencilFunc = stencilFunc;
						currentStencilRef = stencilRef;
						currentStencilFuncMask = stencilMask;
  
					}
  
				},
  
				setOp: function ( stencilFail, stencilZFail, stencilZPass ) {
  
					if ( currentStencilFail	 !== stencilFail 	||
						 currentStencilZFail !== stencilZFail ||
						 currentStencilZPass !== stencilZPass ) {
  
						gl.stencilOp( stencilFail, stencilZFail, stencilZPass );
  
						currentStencilFail = stencilFail;
						currentStencilZFail = stencilZFail;
						currentStencilZPass = stencilZPass;
  
					}
  
				},
  
				setLocked: function ( lock ) {
  
					locked = lock;
  
				},
  
				setClear: function ( stencil ) {
  
					if ( currentStencilClear !== stencil ) {
  
						gl.clearStencil( stencil );
						currentStencilClear = stencil;
  
					}
  
				},
  
				reset: function () {
  
					locked = false;
  
					currentStencilMask = null;
					currentStencilFunc = null;
					currentStencilRef = null;
					currentStencilFuncMask = null;
					currentStencilFail = null;
					currentStencilZFail = null;
					currentStencilZPass = null;
					currentStencilClear = null;
  
				}
  
			};
  
		}
  
		//
  
		var colorBuffer = new ColorBuffer();
		var depthBuffer = new DepthBuffer();
		var stencilBuffer = new StencilBuffer();
  
		var maxVertexAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS );
		var newAttributes = new Uint8Array( maxVertexAttributes );
		var enabledAttributes = new Uint8Array( maxVertexAttributes );
		var attributeDivisors = new Uint8Array( maxVertexAttributes );
  
		var capabilities = {};
  
		var compressedTextureFormats = null;
  
		var currentProgram = null;
  
		var currentBlending = null;
		var currentBlendEquation = null;
		var currentBlendSrc = null;
		var currentBlendDst = null;
		var currentBlendEquationAlpha = null;
		var currentBlendSrcAlpha = null;
		var currentBlendDstAlpha = null;
		var currentPremultipledAlpha = false;
  
		var currentFlipSided = null;
		var currentCullFace = null;
  
		var currentLineWidth = null;
  
		var currentPolygonOffsetFactor = null;
		var currentPolygonOffsetUnits = null;
  
		var maxTextures = gl.getParameter( gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS );
  
		var lineWidthAvailable = false;
		var version = 0;
		var glVersion = gl.getParameter( gl.VERSION );
  
		if ( glVersion.indexOf( 'WebGL' ) !== - 1 ) {
  
			version = parseFloat( /^WebGL\ ([0-9])/.exec( glVersion )[ 1 ] );
			lineWidthAvailable = ( version >= 1.0 );
  
		} else if ( glVersion.indexOf( 'OpenGL ES' ) !== - 1 ) {
  
			version = parseFloat( /^OpenGL\ ES\ ([0-9])/.exec( glVersion )[ 1 ] );
			lineWidthAvailable = ( version >= 2.0 );
  
		}
  
		var currentTextureSlot = null;
		var currentBoundTextures = {};
  
		var currentScissor = new Vector4();
		var currentViewport = new Vector4();
  
		function createTexture( type, target, count ) {
  
			var data = new Uint8Array( 4 ); // 4 is required to match default unpack alignment of 4.
			var texture = gl.createTexture();
  
			gl.bindTexture( type, texture );
			gl.texParameteri( type, gl.TEXTURE_MIN_FILTER, gl.NEAREST );
			gl.texParameteri( type, gl.TEXTURE_MAG_FILTER, gl.NEAREST );
  
			for ( var i = 0; i < count; i ++ ) {
  
				gl.texImage2D( target + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data );
  
			}
  
			return texture;
  
		}
  
		var emptyTextures = {};
		emptyTextures[ gl.TEXTURE_2D ] = createTexture( gl.TEXTURE_2D, gl.TEXTURE_2D, 1 );
		emptyTextures[ gl.TEXTURE_CUBE_MAP ] = createTexture( gl.TEXTURE_CUBE_MAP, gl.TEXTURE_CUBE_MAP_POSITIVE_X, 6 );
  
		// init
  
		colorBuffer.setClear( 0, 0, 0, 1 );
		depthBuffer.setClear( 1 );
		stencilBuffer.setClear( 0 );
  
		enable( gl.DEPTH_TEST );
		depthBuffer.setFunc( LessEqualDepth );
  
		setFlipSided( false );
		setCullFace( CullFaceBack );
		enable( gl.CULL_FACE );
  
		enable( gl.BLEND );
		setBlending( NormalBlending );
  
		//
  
		function initAttributes() {
  
			for ( var i = 0, l = newAttributes.length; i < l; i ++ ) {
  
				newAttributes[ i ] = 0;
  
			}
  
		}
  
		function enableAttribute( attribute ) {
  
			enableAttributeAndDivisor( attribute, 0 );
  
		}
  
		function enableAttributeAndDivisor( attribute, meshPerAttribute ) {
  
			newAttributes[ attribute ] = 1;
  
			if ( enabledAttributes[ attribute ] === 0 ) {
  
				gl.enableVertexAttribArray( attribute );
				enabledAttributes[ attribute ] = 1;
  
			}
  
			if ( attributeDivisors[ attribute ] !== meshPerAttribute ) {
  
				var extension = extensions.get( 'ANGLE_instanced_arrays' );
  
				extension.vertexAttribDivisorANGLE( attribute, meshPerAttribute );
				attributeDivisors[ attribute ] = meshPerAttribute;
  
			}
  
		}
  
		function disableUnusedAttributes() {
  
			for ( var i = 0, l = enabledAttributes.length; i !== l; ++ i ) {
  
				if ( enabledAttributes[ i ] !== newAttributes[ i ] ) {
  
					gl.disableVertexAttribArray( i );
					enabledAttributes[ i ] = 0;
  
				}
  
			}
  
		}
  
		function enable( id ) {
  
			if ( capabilities[ id ] !== true ) {
  
				gl.enable( id );
				capabilities[ id ] = true;
  
			}
  
		}
  
		function disable( id ) {
  
			if ( capabilities[ id ] !== false ) {
  
				gl.disable( id );
				capabilities[ id ] = false;
  
			}
  
		}
  
		function getCompressedTextureFormats() {
  
			if ( compressedTextureFormats === null ) {
  
				compressedTextureFormats = [];
  
				if ( extensions.get( 'WEBGL_compressed_texture_pvrtc' ) ||
					 extensions.get( 'WEBGL_compressed_texture_s3tc' ) ||
					 extensions.get( 'WEBGL_compressed_texture_etc1' ) ||
					 extensions.get( 'WEBGL_compressed_texture_astc' ) ) {
  
					var formats = gl.getParameter( gl.COMPRESSED_TEXTURE_FORMATS );
  
					for ( var i = 0; i < formats.length; i ++ ) {
  
						compressedTextureFormats.push( formats[ i ] );
  
					}
  
				}
  
			}
  
			return compressedTextureFormats;
  
		}
  
		function useProgram( program ) {
  
			if ( currentProgram !== program ) {
  
				gl.useProgram( program );
  
				currentProgram = program;
  
				return true;
  
			}
  
			return false;
  
		}
  
		function setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) {
  
			if ( blending !== NoBlending ) {
  
				enable( gl.BLEND );
  
			} else {
  
				disable( gl.BLEND );
  
			}
  
			if ( blending !== CustomBlending ) {
  
				if ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) {
  
					switch ( blending ) {
  
						case AdditiveBlending:
  
							if ( premultipliedAlpha ) {
  
								gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD );
								gl.blendFuncSeparate( gl.ONE, gl.ONE, gl.ONE, gl.ONE );
  
							} else {
  
								gl.blendEquation( gl.FUNC_ADD );
								gl.blendFunc( gl.SRC_ALPHA, gl.ONE );
  
							}
							break;
  
						case SubtractiveBlending:
  
							if ( premultipliedAlpha ) {
  
								gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD );
								gl.blendFuncSeparate( gl.ZERO, gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ONE_MINUS_SRC_ALPHA );
  
							} else {
  
								gl.blendEquation( gl.FUNC_ADD );
								gl.blendFunc( gl.ZERO, gl.ONE_MINUS_SRC_COLOR );
  
							}
							break;
  
						case MultiplyBlending:
  
							if ( premultipliedAlpha ) {
  
								gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD );
								gl.blendFuncSeparate( gl.ZERO, gl.SRC_COLOR, gl.ZERO, gl.SRC_ALPHA );
  
							} else {
  
								gl.blendEquation( gl.FUNC_ADD );
								gl.blendFunc( gl.ZERO, gl.SRC_COLOR );
  
							}
							break;
  
						default:
  
							if ( premultipliedAlpha ) {
  
								gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD );
								gl.blendFuncSeparate( gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA );
  
							} else {
  
								gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD );
								gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA );
  
							}
  
					}
  
				}
  
				currentBlendEquation = null;
				currentBlendSrc = null;
				currentBlendDst = null;
				currentBlendEquationAlpha = null;
				currentBlendSrcAlpha = null;
				currentBlendDstAlpha = null;
  
			} else {
  
				blendEquationAlpha = blendEquationAlpha || blendEquation;
				blendSrcAlpha = blendSrcAlpha || blendSrc;
				blendDstAlpha = blendDstAlpha || blendDst;
  
				if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) {
  
					gl.blendEquationSeparate( utils.convert( blendEquation ), utils.convert( blendEquationAlpha ) );
  
					currentBlendEquation = blendEquation;
					currentBlendEquationAlpha = blendEquationAlpha;
  
				}
  
				if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) {
  
					gl.blendFuncSeparate( utils.convert( blendSrc ), utils.convert( blendDst ), utils.convert( blendSrcAlpha ), utils.convert( blendDstAlpha ) );
  
					currentBlendSrc = blendSrc;
					currentBlendDst = blendDst;
					currentBlendSrcAlpha = blendSrcAlpha;
					currentBlendDstAlpha = blendDstAlpha;
  
				}
  
			}
  
			currentBlending = blending;
			currentPremultipledAlpha = premultipliedAlpha;
  
		}
  
		function setMaterial( material, frontFaceCW ) {
  
			material.side === DoubleSide
				? disable( gl.CULL_FACE )
				: enable( gl.CULL_FACE );
  
			var flipSided = ( material.side === BackSide );
			if ( frontFaceCW ) flipSided = ! flipSided;
  
			setFlipSided( flipSided );
  
			material.transparent === true
				? setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha )
				: setBlending( NoBlending );
  
			depthBuffer.setFunc( material.depthFunc );
			depthBuffer.setTest( material.depthTest );
			depthBuffer.setMask( material.depthWrite );
			colorBuffer.setMask( material.colorWrite );
  
			setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits );
  
		}
  
		//
  
		function setFlipSided( flipSided ) {
  
			if ( currentFlipSided !== flipSided ) {
  
				if ( flipSided ) {
  
					gl.frontFace( gl.CW );
  
				} else {
  
					gl.frontFace( gl.CCW );
  
				}
  
				currentFlipSided = flipSided;
  
			}
  
		}
  
		function setCullFace( cullFace ) {
  
			if ( cullFace !== CullFaceNone ) {
  
				enable( gl.CULL_FACE );
  
				if ( cullFace !== currentCullFace ) {
  
					if ( cullFace === CullFaceBack ) {
  
						gl.cullFace( gl.BACK );
  
					} else if ( cullFace === CullFaceFront ) {
  
						gl.cullFace( gl.FRONT );
  
					} else {
  
						gl.cullFace( gl.FRONT_AND_BACK );
  
					}
  
				}
  
			} else {
  
				disable( gl.CULL_FACE );
  
			}
  
			currentCullFace = cullFace;
  
		}
  
		function setLineWidth( width ) {
  
			if ( width !== currentLineWidth ) {
  
				if ( lineWidthAvailable ) gl.lineWidth( width );
  
				currentLineWidth = width;
  
			}
  
		}
  
		function setPolygonOffset( polygonOffset, factor, units ) {
  
			if ( polygonOffset ) {
  
				enable( gl.POLYGON_OFFSET_FILL );
  
				if ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) {
  
					gl.polygonOffset( factor, units );
  
					currentPolygonOffsetFactor = factor;
					currentPolygonOffsetUnits = units;
  
				}
  
			} else {
  
				disable( gl.POLYGON_OFFSET_FILL );
  
			}
  
		}
  
		function setScissorTest( scissorTest ) {
  
			if ( scissorTest ) {
  
				enable( gl.SCISSOR_TEST );
  
			} else {
  
				disable( gl.SCISSOR_TEST );
  
			}
  
		}
  
		// texture
  
		function activeTexture( webglSlot ) {
  
			if ( webglSlot === undefined ) webglSlot = gl.TEXTURE0 + maxTextures - 1;
  
			if ( currentTextureSlot !== webglSlot ) {
  
				gl.activeTexture( webglSlot );
				currentTextureSlot = webglSlot;
  
			}
  
		}
  
		function bindTexture( webglType, webglTexture ) {
  
			if ( currentTextureSlot === null ) {
  
				activeTexture();
  
			}
  
			var boundTexture = currentBoundTextures[ currentTextureSlot ];
  
			if ( boundTexture === undefined ) {
  
				boundTexture = { type: undefined, texture: undefined };
				currentBoundTextures[ currentTextureSlot ] = boundTexture;
  
			}
  
			if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) {
  
				gl.bindTexture( webglType, webglTexture || emptyTextures[ webglType ] );
  
				boundTexture.type = webglType;
				boundTexture.texture = webglTexture;
  
			}
  
		}
  
		function compressedTexImage2D() {
  
			try {
  
				gl.compressedTexImage2D.apply( gl, arguments );
  
			} catch ( error ) {
  
				console.error( 'THREE.WebGLState:', error );
  
			}
  
		}
  
		function texImage2D() {
  
			try {
  
				gl.texImage2D.apply( gl, arguments );
  
			} catch ( error ) {
  
				console.error( 'THREE.WebGLState:', error );
  
			}
  
		}
  
		//
  
		function scissor( scissor ) {
  
			if ( currentScissor.equals( scissor ) === false ) {
  
				gl.scissor( scissor.x, scissor.y, scissor.z, scissor.w );
				currentScissor.copy( scissor );
  
			}
  
		}
  
		function viewport( viewport ) {
  
			if ( currentViewport.equals( viewport ) === false ) {
  
				gl.viewport( viewport.x, viewport.y, viewport.z, viewport.w );
				currentViewport.copy( viewport );
  
			}
  
		}
  
		//
  
		function reset() {
  
			for ( var i = 0; i < enabledAttributes.length; i ++ ) {
  
				if ( enabledAttributes[ i ] === 1 ) {
  
					gl.disableVertexAttribArray( i );
					enabledAttributes[ i ] = 0;
  
				}
  
			}
  
			capabilities = {};
  
			compressedTextureFormats = null;
  
			currentTextureSlot = null;
			currentBoundTextures = {};
  
			currentProgram = null;
  
			currentBlending = null;
  
			currentFlipSided = null;
			currentCullFace = null;
  
			colorBuffer.reset();
			depthBuffer.reset();
			stencilBuffer.reset();
  
		}
  
		return {
  
			buffers: {
				color: colorBuffer,
				depth: depthBuffer,
				stencil: stencilBuffer
			},
  
			initAttributes: initAttributes,
			enableAttribute: enableAttribute,
			enableAttributeAndDivisor: enableAttributeAndDivisor,
			disableUnusedAttributes: disableUnusedAttributes,
			enable: enable,
			disable: disable,
			getCompressedTextureFormats: getCompressedTextureFormats,
  
			useProgram: useProgram,
  
			setBlending: setBlending,
			setMaterial: setMaterial,
  
			setFlipSided: setFlipSided,
			setCullFace: setCullFace,
  
			setLineWidth: setLineWidth,
			setPolygonOffset: setPolygonOffset,
  
			setScissorTest: setScissorTest,
  
			activeTexture: activeTexture,
			bindTexture: bindTexture,
			compressedTexImage2D: compressedTexImage2D,
			texImage2D: texImage2D,
  
			scissor: scissor,
			viewport: viewport,
  
			reset: reset
  
		};
  
	}
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ) {
  
		var _isWebGL2 = ( typeof WebGL2RenderingContext !== 'undefined' && _gl instanceof WebGL2RenderingContext ); /* global WebGL2RenderingContext */
		var _videoTextures = {};
		var _canvas;
  
		//
  
		function clampToMaxSize( image, maxSize ) {
  
			if ( image.width > maxSize || image.height > maxSize ) {
  
				if ( 'data' in image ) {
  
					console.warn( 'THREE.WebGLRenderer: image in DataTexture is too big (' + image.width + 'x' + image.height + ').' );
					return;
  
				}
  
				// Warning: Scaling through the canvas will only work with images that use
				// premultiplied alpha.
  
				var scale = maxSize / Math.max( image.width, image.height );
  
				var canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' );
				canvas.width = Math.floor( image.width * scale );
				canvas.height = Math.floor( image.height * scale );
  
				var context = canvas.getContext( '2d' );
				context.drawImage( image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height );
  
				console.warn( 'THREE.WebGLRenderer: image is too big (' + image.width + 'x' + image.height + '). Resized to ' + canvas.width + 'x' + canvas.height, image );
  
				return canvas;
  
			}
  
			return image;
  
		}
  
		function isPowerOfTwo( image ) {
  
			return _Math.isPowerOfTwo( image.width ) && _Math.isPowerOfTwo( image.height );
  
		}
  
		function makePowerOfTwo( image ) {
  
			if ( image instanceof HTMLImageElement || image instanceof HTMLCanvasElement || image instanceof ImageBitmap ) {
  
				if ( _canvas === undefined ) _canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' );
  
				_canvas.width = _Math.floorPowerOfTwo( image.width );
				_canvas.height = _Math.floorPowerOfTwo( image.height );
  
				var context = _canvas.getContext( '2d' );
				context.drawImage( image, 0, 0, _canvas.width, _canvas.height );
  
				console.warn( 'THREE.WebGLRenderer: image is not power of two (' + image.width + 'x' + image.height + '). Resized to ' + _canvas.width + 'x' + _canvas.height, image );
  
				return _canvas;
  
			}
  
			return image;
  
		}
  
		function textureNeedsPowerOfTwo( texture ) {
  
			return ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) ||
				( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter );
  
		}
  
		function textureNeedsGenerateMipmaps( texture, isPowerOfTwo ) {
  
			return texture.generateMipmaps && isPowerOfTwo &&
				texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter;
  
		}
  
		function generateMipmap( target, texture, width, height ) {
  
			_gl.generateMipmap( target );
  
			var textureProperties = properties.get( texture );
  
			// Note: Math.log( x ) * Math.LOG2E used instead of Math.log2( x ) which is not supported by IE11
			textureProperties.__maxMipLevel = Math.log( Math.max( width, height ) ) * Math.LOG2E;
  
		}
  
		// Fallback filters for non-power-of-2 textures
  
		function filterFallback( f ) {
  
			if ( f === NearestFilter || f === NearestMipMapNearestFilter || f === NearestMipMapLinearFilter ) {
  
				return _gl.NEAREST;
  
			}
  
			return _gl.LINEAR;
  
		}
  
		//
  
		function onTextureDispose( event ) {
  
			var texture = event.target;
  
			texture.removeEventListener( 'dispose', onTextureDispose );
  
			deallocateTexture( texture );
  
			if ( texture.isVideoTexture ) {
  
				delete _videoTextures[ texture.id ];
  
			}
  
			info.memory.textures --;
  
		}
  
		function onRenderTargetDispose( event ) {
  
			var renderTarget = event.target;
  
			renderTarget.removeEventListener( 'dispose', onRenderTargetDispose );
  
			deallocateRenderTarget( renderTarget );
  
			info.memory.textures --;
  
		}
  
		//
  
		function deallocateTexture( texture ) {
  
			var textureProperties = properties.get( texture );
  
			if ( texture.image && textureProperties.__image__webglTextureCube ) {
  
				// cube texture
  
				_gl.deleteTexture( textureProperties.__image__webglTextureCube );
  
			} else {
  
				// 2D texture
  
				if ( textureProperties.__webglInit === undefined ) return;
  
				_gl.deleteTexture( textureProperties.__webglTexture );
  
			}
  
			// remove all webgl properties
			properties.remove( texture );
  
		}
  
		function deallocateRenderTarget( renderTarget ) {
  
			var renderTargetProperties = properties.get( renderTarget );
			var textureProperties = properties.get( renderTarget.texture );
  
			if ( ! renderTarget ) return;
  
			if ( textureProperties.__webglTexture !== undefined ) {
  
				_gl.deleteTexture( textureProperties.__webglTexture );
  
			}
  
			if ( renderTarget.depthTexture ) {
  
				renderTarget.depthTexture.dispose();
  
			}
  
			if ( renderTarget.isWebGLRenderTargetCube ) {
  
				for ( var i = 0; i < 6; i ++ ) {
  
					_gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] );
					if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer[ i ] );
  
				}
  
			} else {
  
				_gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer );
				if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer );
  
			}
  
			properties.remove( renderTarget.texture );
			properties.remove( renderTarget );
  
		}
  
		//
  
  
  
		function setTexture2D( texture, slot ) {
  
			var textureProperties = properties.get( texture );
  
			if ( texture.isVideoTexture ) updateVideoTexture( texture );
  
			if ( texture.version > 0 && textureProperties.__version !== texture.version ) {
  
				var image = texture.image;
  
				if ( image === undefined ) {
  
					console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is undefined', texture );
  
				} else if ( image.complete === false ) {
  
					console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is incomplete', texture );
  
				} else {
  
					uploadTexture( textureProperties, texture, slot );
					return;
  
				}
  
			}
  
			state.activeTexture( _gl.TEXTURE0 + slot );
			state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture );
  
		}
  
		function setTextureCube( texture, slot ) {
  
			var textureProperties = properties.get( texture );
  
			if ( texture.image.length === 6 ) {
  
				if ( texture.version > 0 && textureProperties.__version !== texture.version ) {
  
					if ( ! textureProperties.__image__webglTextureCube ) {
  
						texture.addEventListener( 'dispose', onTextureDispose );
  
						textureProperties.__image__webglTextureCube = _gl.createTexture();
  
						info.memory.textures ++;
  
					}
  
					state.activeTexture( _gl.TEXTURE0 + slot );
					state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube );
  
					_gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY );
  
					var isCompressed = ( texture && texture.isCompressedTexture );
					var isDataTexture = ( texture.image[ 0 ] && texture.image[ 0 ].isDataTexture );
  
					var cubeImage = [];
  
					for ( var i = 0; i < 6; i ++ ) {
  
						if ( ! isCompressed && ! isDataTexture ) {
  
							cubeImage[ i ] = clampToMaxSize( texture.image[ i ], capabilities.maxCubemapSize );
  
						} else {
  
							cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ];
  
						}
  
					}
  
					var image = cubeImage[ 0 ],
						isPowerOfTwoImage = isPowerOfTwo( image ),
						glFormat = utils.convert( texture.format ),
						glType = utils.convert( texture.type );
  
					setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, isPowerOfTwoImage );
  
					for ( var i = 0; i < 6; i ++ ) {
  
						if ( ! isCompressed ) {
  
							if ( isDataTexture ) {
  
								state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data );
  
							} else {
  
								state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] );
  
							}
  
						} else {
  
							var mipmap, mipmaps = cubeImage[ i ].mipmaps;
  
							for ( var j = 0, jl = mipmaps.length; j < jl; j ++ ) {
  
								mipmap = mipmaps[ j ];
  
								if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) {
  
									if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) {
  
										state.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data );
  
									} else {
  
										console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()' );
  
									}
  
								} else {
  
									state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );
  
								}
  
							}
  
						}
  
					}
  
					if ( ! isCompressed ) {
  
						textureProperties.__maxMipLevel = 0;
  
					} else {
  
						textureProperties.__maxMipLevel = mipmaps.length - 1;
  
					}
  
					if ( textureNeedsGenerateMipmaps( texture, isPowerOfTwoImage ) ) {
  
						// We assume images for cube map have the same size.
						generateMipmap( _gl.TEXTURE_CUBE_MAP, texture, image.width, image.height );
  
					}
  
					textureProperties.__version = texture.version;
  
					if ( texture.onUpdate ) texture.onUpdate( texture );
  
				} else {
  
					state.activeTexture( _gl.TEXTURE0 + slot );
					state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube );
  
				}
  
			}
  
		}
  
		function setTextureCubeDynamic( texture, slot ) {
  
			state.activeTexture( _gl.TEXTURE0 + slot );
			state.bindTexture( _gl.TEXTURE_CUBE_MAP, properties.get( texture ).__webglTexture );
  
		}
  
		function setTextureParameters( textureType, texture, isPowerOfTwoImage ) {
  
			var extension;
  
			if ( isPowerOfTwoImage ) {
  
				_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, utils.convert( texture.wrapS ) );
				_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, utils.convert( texture.wrapT ) );
  
				_gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, utils.convert( texture.magFilter ) );
				_gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, utils.convert( texture.minFilter ) );
  
			} else {
  
				_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE );
				_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE );
  
				if ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) {
  
					console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.', texture );
  
				}
  
				_gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) );
				_gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) );
  
				if ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) {
  
					console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.', texture );
  
				}
  
			}
  
			extension = extensions.get( 'EXT_texture_filter_anisotropic' );
  
			if ( extension ) {
  
				if ( texture.type === FloatType && extensions.get( 'OES_texture_float_linear' ) === null ) return;
				if ( texture.type === HalfFloatType && extensions.get( 'OES_texture_half_float_linear' ) === null ) return;
  
				if ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) {
  
					_gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, capabilities.getMaxAnisotropy() ) );
					properties.get( texture ).__currentAnisotropy = texture.anisotropy;
  
				}
  
			}
  
		}
  
		function uploadTexture( textureProperties, texture, slot ) {
  
			if ( textureProperties.__webglInit === undefined ) {
  
				textureProperties.__webglInit = true;
  
				texture.addEventListener( 'dispose', onTextureDispose );
  
				textureProperties.__webglTexture = _gl.createTexture();
  
				info.memory.textures ++;
  
			}
  
			state.activeTexture( _gl.TEXTURE0 + slot );
			state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture );
  
			_gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY );
			_gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha );
			_gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment );
  
			var image = clampToMaxSize( texture.image, capabilities.maxTextureSize );
  
			if ( textureNeedsPowerOfTwo( texture ) && isPowerOfTwo( image ) === false ) {
  
				image = makePowerOfTwo( image );
  
			}
  
			var isPowerOfTwoImage = isPowerOfTwo( image ),
				glFormat = utils.convert( texture.format ),
				glType = utils.convert( texture.type );
  
			setTextureParameters( _gl.TEXTURE_2D, texture, isPowerOfTwoImage );
  
			var mipmap, mipmaps = texture.mipmaps;
  
			if ( texture.isDepthTexture ) {
  
				// populate depth texture with dummy data
  
				var internalFormat = _gl.DEPTH_COMPONENT;
  
				if ( texture.type === FloatType ) {
  
					if ( ! _isWebGL2 ) throw new Error( 'Float Depth Texture only supported in WebGL2.0' );
					internalFormat = _gl.DEPTH_COMPONENT32F;
  
				} else if ( _isWebGL2 ) {
  
					// WebGL 2.0 requires signed internalformat for glTexImage2D
					internalFormat = _gl.DEPTH_COMPONENT16;
  
				}
  
				if ( texture.format === DepthFormat && internalFormat === _gl.DEPTH_COMPONENT ) {
  
					// The error INVALID_OPERATION is generated by texImage2D if format and internalformat are
					// DEPTH_COMPONENT and type is not UNSIGNED_SHORT or UNSIGNED_INT
					// (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/)
					if ( texture.type !== UnsignedShortType && texture.type !== UnsignedIntType ) {
  
						console.warn( 'THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture.' );
  
						texture.type = UnsignedShortType;
						glType = utils.convert( texture.type );
  
					}
  
				}
  
				// Depth stencil textures need the DEPTH_STENCIL internal format
				// (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/)
				if ( texture.format === DepthStencilFormat ) {
  
					internalFormat = _gl.DEPTH_STENCIL;
  
					// The error INVALID_OPERATION is generated by texImage2D if format and internalformat are
					// DEPTH_STENCIL and type is not UNSIGNED_INT_24_8_WEBGL.
					// (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/)
					if ( texture.type !== UnsignedInt248Type ) {
  
						console.warn( 'THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture.' );
  
						texture.type = UnsignedInt248Type;
						glType = utils.convert( texture.type );
  
					}
  
				}
  
				state.texImage2D( _gl.TEXTURE_2D, 0, internalFormat, image.width, image.height, 0, glFormat, glType, null );
  
			} else if ( texture.isDataTexture ) {
  
				// use manually created mipmaps if available
				// if there are no manual mipmaps
				// set 0 level mipmap and then use GL to generate other mipmap levels
  
				if ( mipmaps.length > 0 && isPowerOfTwoImage ) {
  
					for ( var i = 0, il = mipmaps.length; i < il; i ++ ) {
  
						mipmap = mipmaps[ i ];
						state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );
  
					}
  
					texture.generateMipmaps = false;
					textureProperties.__maxMipLevel = mipmaps.length - 1;
  
				} else {
  
					state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data );
					textureProperties.__maxMipLevel = 0;
  
				}
  
			} else if ( texture.isCompressedTexture ) {
  
				for ( var i = 0, il = mipmaps.length; i < il; i ++ ) {
  
					mipmap = mipmaps[ i ];
  
					if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) {
  
						if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) {
  
							state.compressedTexImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, mipmap.data );
  
						} else {
  
							console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()' );
  
						}
  
					} else {
  
						state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );
  
					}
  
				}
  
				textureProperties.__maxMipLevel = mipmaps.length - 1;
  
			} else {
  
				// regular Texture (image, video, canvas)
  
				// use manually created mipmaps if available
				// if there are no manual mipmaps
				// set 0 level mipmap and then use GL to generate other mipmap levels
  
				if ( mipmaps.length > 0 && isPowerOfTwoImage ) {
  
					for ( var i = 0, il = mipmaps.length; i < il; i ++ ) {
  
						mipmap = mipmaps[ i ];
						state.texImage2D( _gl.TEXTURE_2D, i, glFormat, glFormat, glType, mipmap );
  
					}
  
					texture.generateMipmaps = false;
					textureProperties.__maxMipLevel = mipmaps.length - 1;
  
				} else {
  
					state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, image );
					textureProperties.__maxMipLevel = 0;
  
				}
  
			}
  
			if ( textureNeedsGenerateMipmaps( texture, isPowerOfTwoImage ) ) {
  
				generateMipmap( _gl.TEXTURE_2D, texture, image.width, image.height );
  
			}
  
			textureProperties.__version = texture.version;
  
			if ( texture.onUpdate ) texture.onUpdate( texture );
  
		}
  
		// Render targets
  
		// Setup storage for target texture and bind it to correct framebuffer
		function setupFrameBufferTexture( framebuffer, renderTarget, attachment, textureTarget ) {
  
			var glFormat = utils.convert( renderTarget.texture.format );
			var glType = utils.convert( renderTarget.texture.type );
			state.texImage2D( textureTarget, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null );
			_gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );
			_gl.framebufferTexture2D( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get( renderTarget.texture ).__webglTexture, 0 );
			_gl.bindFramebuffer( _gl.FRAMEBUFFER, null );
  
		}
  
		// Setup storage for internal depth/stencil buffers and bind to correct framebuffer
		function setupRenderBufferStorage( renderbuffer, renderTarget ) {
  
			_gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer );
  
			if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) {
  
				_gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, renderTarget.width, renderTarget.height );
				_gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );
  
			} else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) {
  
				_gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height );
				_gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );
  
			} else {
  
				// FIXME: We don't support !depth !stencil
				_gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.RGBA4, renderTarget.width, renderTarget.height );
  
			}
  
			_gl.bindRenderbuffer( _gl.RENDERBUFFER, null );
  
		}
  
		// Setup resources for a Depth Texture for a FBO (needs an extension)
		function setupDepthTexture( framebuffer, renderTarget ) {
  
			var isCube = ( renderTarget && renderTarget.isWebGLRenderTargetCube );
			if ( isCube ) throw new Error( 'Depth Texture with cube render targets is not supported' );
  
			_gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );
  
			if ( ! ( renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture ) ) {
  
				throw new Error( 'renderTarget.depthTexture must be an instance of THREE.DepthTexture' );
  
			}
  
			// upload an empty depth texture with framebuffer size
			if ( ! properties.get( renderTarget.depthTexture ).__webglTexture ||
					renderTarget.depthTexture.image.width !== renderTarget.width ||
					renderTarget.depthTexture.image.height !== renderTarget.height ) {
  
				renderTarget.depthTexture.image.width = renderTarget.width;
				renderTarget.depthTexture.image.height = renderTarget.height;
				renderTarget.depthTexture.needsUpdate = true;
  
			}
  
			setTexture2D( renderTarget.depthTexture, 0 );
  
			var webglDepthTexture = properties.get( renderTarget.depthTexture ).__webglTexture;
  
			if ( renderTarget.depthTexture.format === DepthFormat ) {
  
				_gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 );
  
			} else if ( renderTarget.depthTexture.format === DepthStencilFormat ) {
  
				_gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 );
  
			} else {
  
				throw new Error( 'Unknown depthTexture format' );
  
			}
  
		}
  
		// Setup GL resources for a non-texture depth buffer
		function setupDepthRenderbuffer( renderTarget ) {
  
			var renderTargetProperties = properties.get( renderTarget );
  
			var isCube = ( renderTarget.isWebGLRenderTargetCube === true );
  
			if ( renderTarget.depthTexture ) {
  
				if ( isCube ) throw new Error( 'target.depthTexture not supported in Cube render targets' );
  
				setupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget );
  
			} else {
  
				if ( isCube ) {
  
					renderTargetProperties.__webglDepthbuffer = [];
  
					for ( var i = 0; i < 6; i ++ ) {
  
						_gl.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer[ i ] );
						renderTargetProperties.__webglDepthbuffer[ i ] = _gl.createRenderbuffer();
						setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer[ i ], renderTarget );
  
					}
  
				} else {
  
					_gl.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer );
					renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer();
					setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer, renderTarget );
  
				}
  
			}
  
			_gl.bindFramebuffer( _gl.FRAMEBUFFER, null );
  
		}
  
		// Set up GL resources for the render target
		function setupRenderTarget( renderTarget ) {
  
			var renderTargetProperties = properties.get( renderTarget );
			var textureProperties = properties.get( renderTarget.texture );
  
			renderTarget.addEventListener( 'dispose', onRenderTargetDispose );
  
			textureProperties.__webglTexture = _gl.createTexture();
  
			info.memory.textures ++;
  
			var isCube = ( renderTarget.isWebGLRenderTargetCube === true );
			var isTargetPowerOfTwo = isPowerOfTwo( renderTarget );
  
			// Setup framebuffer
  
			if ( isCube ) {
  
				renderTargetProperties.__webglFramebuffer = [];
  
				for ( var i = 0; i < 6; i ++ ) {
  
					renderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer();
  
				}
  
			} else {
  
				renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer();
  
			}
  
			// Setup color buffer
  
			if ( isCube ) {
  
				state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture );
				setTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget.texture, isTargetPowerOfTwo );
  
				for ( var i = 0; i < 6; i ++ ) {
  
					setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i );
  
				}
  
				if ( textureNeedsGenerateMipmaps( renderTarget.texture, isTargetPowerOfTwo ) ) {
  
					generateMipmap( _gl.TEXTURE_CUBE_MAP, renderTarget.texture, renderTarget.width, renderTarget.height );
  
				}
  
				state.bindTexture( _gl.TEXTURE_CUBE_MAP, null );
  
			} else {
  
				state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture );
				setTextureParameters( _gl.TEXTURE_2D, renderTarget.texture, isTargetPowerOfTwo );
				setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D );
  
				if ( textureNeedsGenerateMipmaps( renderTarget.texture, isTargetPowerOfTwo ) ) {
  
					generateMipmap( _gl.TEXTURE_2D, renderTarget.texture, renderTarget.width, renderTarget.height );
  
				}
  
				state.bindTexture( _gl.TEXTURE_2D, null );
  
			}
  
			// Setup depth and stencil buffers
  
			if ( renderTarget.depthBuffer ) {
  
				setupDepthRenderbuffer( renderTarget );
  
			}
  
		}
  
		function updateRenderTargetMipmap( renderTarget ) {
  
			var texture = renderTarget.texture;
			var isTargetPowerOfTwo = isPowerOfTwo( renderTarget );
  
			if ( textureNeedsGenerateMipmaps( texture, isTargetPowerOfTwo ) ) {
  
				var target = renderTarget.isWebGLRenderTargetCube ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D;
				var webglTexture = properties.get( texture ).__webglTexture;
  
				state.bindTexture( target, webglTexture );
				generateMipmap( target, texture, renderTarget.width, renderTarget.height );
				state.bindTexture( target, null );
  
			}
  
		}
  
		function updateVideoTexture( texture ) {
  
			var id = texture.id;
			var frame = info.render.frame;
  
			// Check the last frame we updated the VideoTexture
  
			if ( _videoTextures[ id ] !== frame ) {
  
				_videoTextures[ id ] = frame;
				texture.update();
  
			}
  
		}
  
		this.setTexture2D = setTexture2D;
		this.setTextureCube = setTextureCube;
		this.setTextureCubeDynamic = setTextureCubeDynamic;
		this.setupRenderTarget = setupRenderTarget;
		this.updateRenderTargetMipmap = updateRenderTargetMipmap;
  
	}
  
	/**
	 * @author thespite / http://www.twitter.com/thespite
	 */
  
	function WebGLUtils( gl, extensions ) {
  
		function convert( p ) {
  
			var extension;
  
			if ( p === RepeatWrapping ) return gl.REPEAT;
			if ( p === ClampToEdgeWrapping ) return gl.CLAMP_TO_EDGE;
			if ( p === MirroredRepeatWrapping ) return gl.MIRRORED_REPEAT;
  
			if ( p === NearestFilter ) return gl.NEAREST;
			if ( p === NearestMipMapNearestFilter ) return gl.NEAREST_MIPMAP_NEAREST;
			if ( p === NearestMipMapLinearFilter ) return gl.NEAREST_MIPMAP_LINEAR;
  
			if ( p === LinearFilter ) return gl.LINEAR;
			if ( p === LinearMipMapNearestFilter ) return gl.LINEAR_MIPMAP_NEAREST;
			if ( p === LinearMipMapLinearFilter ) return gl.LINEAR_MIPMAP_LINEAR;
  
			if ( p === UnsignedByteType ) return gl.UNSIGNED_BYTE;
			if ( p === UnsignedShort4444Type ) return gl.UNSIGNED_SHORT_4_4_4_4;
			if ( p === UnsignedShort5551Type ) return gl.UNSIGNED_SHORT_5_5_5_1;
			if ( p === UnsignedShort565Type ) return gl.UNSIGNED_SHORT_5_6_5;
  
			if ( p === ByteType ) return gl.BYTE;
			if ( p === ShortType ) return gl.SHORT;
			if ( p === UnsignedShortType ) return gl.UNSIGNED_SHORT;
			if ( p === IntType ) return gl.INT;
			if ( p === UnsignedIntType ) return gl.UNSIGNED_INT;
			if ( p === FloatType ) return gl.FLOAT;
  
			if ( p === HalfFloatType ) {
  
				extension = extensions.get( 'OES_texture_half_float' );
  
				if ( extension !== null ) return extension.HALF_FLOAT_OES;
  
			}
  
			if ( p === AlphaFormat ) return gl.ALPHA;
			if ( p === RGBFormat ) return gl.RGB;
			if ( p === RGBAFormat ) return gl.RGBA;
			if ( p === LuminanceFormat ) return gl.LUMINANCE;
			if ( p === LuminanceAlphaFormat ) return gl.LUMINANCE_ALPHA;
			if ( p === DepthFormat ) return gl.DEPTH_COMPONENT;
			if ( p === DepthStencilFormat ) return gl.DEPTH_STENCIL;
  
			if ( p === AddEquation ) return gl.FUNC_ADD;
			if ( p === SubtractEquation ) return gl.FUNC_SUBTRACT;
			if ( p === ReverseSubtractEquation ) return gl.FUNC_REVERSE_SUBTRACT;
  
			if ( p === ZeroFactor ) return gl.ZERO;
			if ( p === OneFactor ) return gl.ONE;
			if ( p === SrcColorFactor ) return gl.SRC_COLOR;
			if ( p === OneMinusSrcColorFactor ) return gl.ONE_MINUS_SRC_COLOR;
			if ( p === SrcAlphaFactor ) return gl.SRC_ALPHA;
			if ( p === OneMinusSrcAlphaFactor ) return gl.ONE_MINUS_SRC_ALPHA;
			if ( p === DstAlphaFactor ) return gl.DST_ALPHA;
			if ( p === OneMinusDstAlphaFactor ) return gl.ONE_MINUS_DST_ALPHA;
  
			if ( p === DstColorFactor ) return gl.DST_COLOR;
			if ( p === OneMinusDstColorFactor ) return gl.ONE_MINUS_DST_COLOR;
			if ( p === SrcAlphaSaturateFactor ) return gl.SRC_ALPHA_SATURATE;
  
			if ( p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format ||
				p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format ) {
  
				extension = extensions.get( 'WEBGL_compressed_texture_s3tc' );
  
				if ( extension !== null ) {
  
					if ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT;
					if ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT;
					if ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT;
					if ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT;
  
				}
  
			}
  
			if ( p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format ||
				p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format ) {
  
				extension = extensions.get( 'WEBGL_compressed_texture_pvrtc' );
  
				if ( extension !== null ) {
  
					if ( p === RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
					if ( p === RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
					if ( p === RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
					if ( p === RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
  
				}
  
			}
  
			if ( p === RGB_ETC1_Format ) {
  
				extension = extensions.get( 'WEBGL_compressed_texture_etc1' );
  
				if ( extension !== null ) return extension.COMPRESSED_RGB_ETC1_WEBGL;
  
			}
  
			if ( p === RGBA_ASTC_4x4_Format || p === RGBA_ASTC_5x4_Format || p === RGBA_ASTC_5x5_Format ||
				p === RGBA_ASTC_6x5_Format || p === RGBA_ASTC_6x6_Format || p === RGBA_ASTC_8x5_Format ||
				p === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format ||
				p === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format ||
				p === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format ) {
  
				extension = extensions.get( 'WEBGL_compressed_texture_astc' );
  
				if ( extension !== null ) {
  
					return p;
  
				}
  
			}
  
			if ( p === MinEquation || p === MaxEquation ) {
  
				extension = extensions.get( 'EXT_blend_minmax' );
  
				if ( extension !== null ) {
  
					if ( p === MinEquation ) return extension.MIN_EXT;
					if ( p === MaxEquation ) return extension.MAX_EXT;
  
				}
  
			}
  
			if ( p === UnsignedInt248Type ) {
  
				extension = extensions.get( 'WEBGL_depth_texture' );
  
				if ( extension !== null ) return extension.UNSIGNED_INT_24_8_WEBGL;
  
			}
  
			return 0;
  
		}
  
		return { convert: convert };
  
	}
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 * @author greggman / http://games.greggman.com/
	 * @author zz85 / http://www.lab4games.net/zz85/blog
	 * @author tschw
	 */
  
	function PerspectiveCamera( fov, aspect, near, far ) {
  
		Camera.call( this );
  
		this.type = 'PerspectiveCamera';
  
		this.fov = fov !== undefined ? fov : 50;
		this.zoom = 1;
  
		this.near = near !== undefined ? near : 0.1;
		this.far = far !== undefined ? far : 2000;
		this.focus = 10;
  
		this.aspect = aspect !== undefined ? aspect : 1;
		this.view = null;
  
		this.filmGauge = 35;	// width of the film (default in millimeters)
		this.filmOffset = 0;	// horizontal film offset (same unit as gauge)
  
		this.updateProjectionMatrix();
  
	}
  
	PerspectiveCamera.prototype = Object.assign( Object.create( Camera.prototype ), {
  
		constructor: PerspectiveCamera,
  
		isPerspectiveCamera: true,
  
		copy: function ( source, recursive ) {
  
			Camera.prototype.copy.call( this, source, recursive );
  
			this.fov = source.fov;
			this.zoom = source.zoom;
  
			this.near = source.near;
			this.far = source.far;
			this.focus = source.focus;
  
			this.aspect = source.aspect;
			this.view = source.view === null ? null : Object.assign( {}, source.view );
  
			this.filmGauge = source.filmGauge;
			this.filmOffset = source.filmOffset;
  
			return this;
  
		},
  
		/**
		 * Sets the FOV by focal length in respect to the current .filmGauge.
		 *
		 * The default film gauge is 35, so that the focal length can be specified for
		 * a 35mm (full frame) camera.
		 *
		 * Values for focal length and film gauge must have the same unit.
		 */
		setFocalLength: function ( focalLength ) {
  
			// see http://www.bobatkins.com/photography/technical/field_of_view.html
			var vExtentSlope = 0.5 * this.getFilmHeight() / focalLength;
  
			this.fov = _Math.RAD2DEG * 2 * Math.atan( vExtentSlope );
			this.updateProjectionMatrix();
  
		},
  
		/**
		 * Calculates the focal length from the current .fov and .filmGauge.
		 */
		getFocalLength: function () {
  
			var vExtentSlope = Math.tan( _Math.DEG2RAD * 0.5 * this.fov );
  
			return 0.5 * this.getFilmHeight() / vExtentSlope;
  
		},
  
		getEffectiveFOV: function () {
  
			return _Math.RAD2DEG * 2 * Math.atan(
				Math.tan( _Math.DEG2RAD * 0.5 * this.fov ) / this.zoom );
  
		},
  
		getFilmWidth: function () {
  
			// film not completely covered in portrait format (aspect < 1)
			return this.filmGauge * Math.min( this.aspect, 1 );
  
		},
  
		getFilmHeight: function () {
  
			// film not completely covered in landscape format (aspect > 1)
			return this.filmGauge / Math.max( this.aspect, 1 );
  
		},
  
		/**
		 * Sets an offset in a larger frustum. This is useful for multi-window or
		 * multi-monitor/multi-machine setups.
		 *
		 * For example, if you have 3x2 monitors and each monitor is 1920x1080 and
		 * the monitors are in grid like this
		 *
		 *   +---+---+---+
		 *   | A | B | C |
		 *   +---+---+---+
		 *   | D | E | F |
		 *   +---+---+---+
		 *
		 * then for each monitor you would call it like this
		 *
		 *   var w = 1920;
		 *   var h = 1080;
		 *   var fullWidth = w * 3;
		 *   var fullHeight = h * 2;
		 *
		 *   --A--
		 *   camera.setOffset( fullWidth, fullHeight, w * 0, h * 0, w, h );
		 *   --B--
		 *   camera.setOffset( fullWidth, fullHeight, w * 1, h * 0, w, h );
		 *   --C--
		 *   camera.setOffset( fullWidth, fullHeight, w * 2, h * 0, w, h );
		 *   --D--
		 *   camera.setOffset( fullWidth, fullHeight, w * 0, h * 1, w, h );
		 *   --E--
		 *   camera.setOffset( fullWidth, fullHeight, w * 1, h * 1, w, h );
		 *   --F--
		 *   camera.setOffset( fullWidth, fullHeight, w * 2, h * 1, w, h );
		 *
		 *   Note there is no reason monitors have to be the same size or in a grid.
		 */
		setViewOffset: function ( fullWidth, fullHeight, x, y, width, height ) {
  
			this.aspect = fullWidth / fullHeight;
  
			if ( this.view === null ) {
  
				this.view = {
					enabled: true,
					fullWidth: 1,
					fullHeight: 1,
					offsetX: 0,
					offsetY: 0,
					width: 1,
					height: 1
				};
  
			}
  
			this.view.enabled = true;
			this.view.fullWidth = fullWidth;
			this.view.fullHeight = fullHeight;
			this.view.offsetX = x;
			this.view.offsetY = y;
			this.view.width = width;
			this.view.height = height;
  
			this.updateProjectionMatrix();
  
		},
  
		clearViewOffset: function () {
  
			if ( this.view !== null ) {
  
				this.view.enabled = false;
  
			}
  
			this.updateProjectionMatrix();
  
		},
  
		updateProjectionMatrix: function () {
  
			var near = this.near,
				top = near * Math.tan(
					_Math.DEG2RAD * 0.5 * this.fov ) / this.zoom,
				height = 2 * top,
				width = this.aspect * height,
				left = - 0.5 * width,
				view = this.view;
  
			if ( this.view !== null && this.view.enabled ) {
  
				var fullWidth = view.fullWidth,
					fullHeight = view.fullHeight;
  
				left += view.offsetX * width / fullWidth;
				top -= view.offsetY * height / fullHeight;
				width *= view.width / fullWidth;
				height *= view.height / fullHeight;
  
			}
  
			var skew = this.filmOffset;
			if ( skew !== 0 ) left += near * skew / this.getFilmWidth();
  
			this.projectionMatrix.makePerspective( left, left + width, top, top - height, near, this.far );
  
		},
  
		toJSON: function ( meta ) {
  
			var data = Object3D.prototype.toJSON.call( this, meta );
  
			data.object.fov = this.fov;
			data.object.zoom = this.zoom;
  
			data.object.near = this.near;
			data.object.far = this.far;
			data.object.focus = this.focus;
  
			data.object.aspect = this.aspect;
  
			if ( this.view !== null ) data.object.view = Object.assign( {}, this.view );
  
			data.object.filmGauge = this.filmGauge;
			data.object.filmOffset = this.filmOffset;
  
			return data;
  
		}
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function ArrayCamera( array ) {
  
		PerspectiveCamera.call( this );
  
		this.cameras = array || [];
  
	}
  
	ArrayCamera.prototype = Object.assign( Object.create( PerspectiveCamera.prototype ), {
  
		constructor: ArrayCamera,
  
		isArrayCamera: true
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function WebVRManager( renderer ) {
  
		var scope = this;
  
		var device = null;
		var frameData = null;
  
		var poseTarget = null;
  
		var standingMatrix = new Matrix4();
		var standingMatrixInverse = new Matrix4();
  
		if ( typeof window !== 'undefined' && 'VRFrameData' in window ) {
  
			frameData = new window.VRFrameData();
			window.addEventListener( 'vrdisplaypresentchange', onVRDisplayPresentChange, false );
  
		}
  
		var matrixWorldInverse = new Matrix4();
		var tempQuaternion = new Quaternion();
		var tempPosition = new Vector3();
  
		var cameraL = new PerspectiveCamera();
		cameraL.bounds = new Vector4( 0.0, 0.0, 0.5, 1.0 );
		cameraL.layers.enable( 1 );
  
		var cameraR = new PerspectiveCamera();
		cameraR.bounds = new Vector4( 0.5, 0.0, 0.5, 1.0 );
		cameraR.layers.enable( 2 );
  
		var cameraVR = new ArrayCamera( [ cameraL, cameraR ] );
		cameraVR.layers.enable( 1 );
		cameraVR.layers.enable( 2 );
  
		//
  
		function isPresenting() {
  
			return device !== null && device.isPresenting === true;
  
		}
  
		var currentSize, currentPixelRatio;
  
		function onVRDisplayPresentChange() {
  
			if ( isPresenting() ) {
  
				var eyeParameters = device.getEyeParameters( 'left' );
				var renderWidth = eyeParameters.renderWidth;
				var renderHeight = eyeParameters.renderHeight;
  
				currentPixelRatio = renderer.getPixelRatio();
				currentSize = renderer.getSize();
  
				renderer.setDrawingBufferSize( renderWidth * 2, renderHeight, 1 );
  
				animation.start();
  
			} else if ( scope.enabled ) {
  
				renderer.setDrawingBufferSize( currentSize.width, currentSize.height, currentPixelRatio );
  
				animation.stop();
  
			}
  
		}
  
		//
  
		this.enabled = false;
		this.userHeight = 1.6;
  
		this.getDevice = function () {
  
			return device;
  
		};
  
		this.setDevice = function ( value ) {
  
			if ( value !== undefined ) device = value;
  
			animation.setContext( value );
  
		};
  
		this.setPoseTarget = function ( object ) {
  
			if ( object !== undefined ) poseTarget = object;
  
		};
  
		this.getCamera = function ( camera ) {
  
			if ( device === null ) return camera;
  
			device.depthNear = camera.near;
			device.depthFar = camera.far;
  
			device.getFrameData( frameData );
  
			//
  
			var stageParameters = device.stageParameters;
  
			if ( stageParameters ) {
  
				standingMatrix.fromArray( stageParameters.sittingToStandingTransform );
  
			} else {
  
				standingMatrix.makeTranslation( 0, scope.userHeight, 0 );
  
			}
  
  
			var pose = frameData.pose;
			var poseObject = poseTarget !== null ? poseTarget : camera;
  
			// We want to manipulate poseObject by its position and quaternion components since users may rely on them.
			poseObject.matrix.copy( standingMatrix );
			poseObject.matrix.decompose( poseObject.position, poseObject.quaternion, poseObject.scale );
  
			if ( pose.orientation !== null ) {
  
				tempQuaternion.fromArray( pose.orientation );
				poseObject.quaternion.multiply( tempQuaternion );
  
			}
  
			if ( pose.position !== null ) {
  
				tempQuaternion.setFromRotationMatrix( standingMatrix );
				tempPosition.fromArray( pose.position );
				tempPosition.applyQuaternion( tempQuaternion );
				poseObject.position.add( tempPosition );
  
			}
  
			poseObject.updateMatrixWorld();
  
			if ( device.isPresenting === false ) return camera;
  
			//
  
			cameraL.near = camera.near;
			cameraR.near = camera.near;
  
			cameraL.far = camera.far;
			cameraR.far = camera.far;
  
			cameraVR.matrixWorld.copy( camera.matrixWorld );
			cameraVR.matrixWorldInverse.copy( camera.matrixWorldInverse );
  
			cameraL.matrixWorldInverse.fromArray( frameData.leftViewMatrix );
			cameraR.matrixWorldInverse.fromArray( frameData.rightViewMatrix );
  
			// TODO (mrdoob) Double check this code
  
			standingMatrixInverse.getInverse( standingMatrix );
  
			cameraL.matrixWorldInverse.multiply( standingMatrixInverse );
			cameraR.matrixWorldInverse.multiply( standingMatrixInverse );
  
			var parent = poseObject.parent;
  
			if ( parent !== null ) {
  
				matrixWorldInverse.getInverse( parent.matrixWorld );
  
				cameraL.matrixWorldInverse.multiply( matrixWorldInverse );
				cameraR.matrixWorldInverse.multiply( matrixWorldInverse );
  
			}
  
			// envMap and Mirror needs camera.matrixWorld
  
			cameraL.matrixWorld.getInverse( cameraL.matrixWorldInverse );
			cameraR.matrixWorld.getInverse( cameraR.matrixWorldInverse );
  
			cameraL.projectionMatrix.fromArray( frameData.leftProjectionMatrix );
			cameraR.projectionMatrix.fromArray( frameData.rightProjectionMatrix );
  
			// HACK (mrdoob)
			// https://github.com/w3c/webvr/issues/203
  
			cameraVR.projectionMatrix.copy( cameraL.projectionMatrix );
  
			//
  
			var layers = device.getLayers();
  
			if ( layers.length ) {
  
				var layer = layers[ 0 ];
  
				if ( layer.leftBounds !== null && layer.leftBounds.length === 4 ) {
  
					cameraL.bounds.fromArray( layer.leftBounds );
  
				}
  
				if ( layer.rightBounds !== null && layer.rightBounds.length === 4 ) {
  
					cameraR.bounds.fromArray( layer.rightBounds );
  
				}
  
			}
  
			return cameraVR;
  
		};
  
		this.getStandingMatrix = function () {
  
			return standingMatrix;
  
		};
  
		this.isPresenting = isPresenting;
  
		// Animation Loop
  
		var animation = new WebGLAnimation();
  
		this.setAnimationLoop = function ( callback ) {
  
			animation.setAnimationLoop( callback );
  
		};
  
		this.submitFrame = function () {
  
			if ( isPresenting() ) device.submitFrame();
  
		};
  
		this.dispose = function () {
  
			if ( typeof window !== 'undefined' ) {
  
				window.removeEventListener( 'vrdisplaypresentchange', onVRDisplayPresentChange );
  
			}
  
		};
  
	}
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function WebXRManager( renderer ) {
  
		var gl = renderer.context;
  
		var device = null;
		var session = null;
  
		var frameOfRef = null;
  
		var pose = null;
  
		function isPresenting() {
  
			return session !== null && frameOfRef !== null;
  
		}
  
		//
  
		var cameraL = new PerspectiveCamera();
		cameraL.layers.enable( 1 );
		cameraL.viewport = new Vector4();
  
		var cameraR = new PerspectiveCamera();
		cameraR.layers.enable( 2 );
		cameraR.viewport = new Vector4();
  
		var cameraVR = new ArrayCamera( [ cameraL, cameraR ] );
		cameraVR.layers.enable( 1 );
		cameraVR.layers.enable( 2 );
  
		//
  
		this.enabled = false;
  
		this.getDevice = function () {
  
			return device;
  
		};
  
		this.setDevice = function ( value ) {
  
			if ( value !== undefined ) device = value;
  
			gl.setCompatibleXRDevice( value );
  
		};
  
		//
  
		this.setSession = function ( value, options ) {
  
			session = value;
  
			if ( session !== null ) {
  
				session.addEventListener( 'end', function () {
  
					renderer.setFramebuffer( null );
					animation.stop();
  
				} );
  
				session.baseLayer = new XRWebGLLayer( session, gl );
				session.requestFrameOfReference( options.frameOfReferenceType ).then( function ( value ) {
  
					frameOfRef = value;
  
					renderer.setFramebuffer( session.baseLayer.framebuffer );
  
					animation.setContext( session );
					animation.start();
  
				} );
  
			}
  
		};
  
		function updateCamera( camera, parent ) {
  
			if ( parent === null ) {
  
				camera.matrixWorld.copy( camera.matrix );
  
			} else {
  
				camera.matrixWorld.multiplyMatrices( parent.matrixWorld, camera.matrix );
  
			}
  
			camera.matrixWorldInverse.getInverse( camera.matrixWorld );
  
		}
  
		this.getCamera = function ( camera ) {
  
			if ( isPresenting() ) {
  
				var parent = camera.parent;
				var cameras = cameraVR.cameras;
  
				// apply camera.parent to cameraVR
  
				updateCamera( cameraVR, parent );
  
				for ( var i = 0; i < cameras.length; i ++ ) {
  
					updateCamera( cameras[ i ], parent );
  
				}
  
				// update camera and its children
  
				camera.matrixWorld.copy( cameraVR.matrixWorld );
  
				var children = camera.children;
  
				for ( var i = 0, l = children.length; i < l; i ++ ) {
  
					children[ i ].updateMatrixWorld( true );
  
				}
  
				return cameraVR;
  
			}
  
			return camera;
  
		};
  
		this.isPresenting = isPresenting;
  
		// Animation Loop
  
		var onAnimationFrameCallback = null;
  
		function onAnimationFrame( time, frame ) {
  
			pose = frame.getDevicePose( frameOfRef );
  
			var layer = session.baseLayer;
			var views = frame.views;
  
			for ( var i = 0; i < views.length; i ++ ) {
  
				var view = views[ i ];
				var viewport = layer.getViewport( view );
				var viewMatrix = pose.getViewMatrix( view );
  
				var camera = cameraVR.cameras[ i ];
				camera.matrix.fromArray( viewMatrix ).getInverse( camera.matrix );
				camera.projectionMatrix.fromArray( view.projectionMatrix );
				camera.viewport.set( viewport.x, viewport.y, viewport.width, viewport.height );
  
				if ( i === 0 ) {
  
					cameraVR.matrix.copy( camera.matrix );
  
					// HACK (mrdoob)
					// https://github.com/w3c/webvr/issues/203
  
					cameraVR.projectionMatrix.copy( camera.projectionMatrix );
  
				}
  
			}
  
			if ( onAnimationFrameCallback ) onAnimationFrameCallback();
  
		}
  
		var animation = new WebGLAnimation();
		animation.setAnimationLoop( onAnimationFrame );
  
		this.setAnimationLoop = function ( callback ) {
  
			onAnimationFrameCallback = callback;
  
		};
  
		// DEPRECATED
  
		this.getStandingMatrix = function () {
  
			console.warn( 'THREE.WebXRManager: getStandingMatrix() is no longer needed.' );
			return new THREE.Matrix4();
  
		};
  
		this.submitFrame = function () {};
  
	}
  
	/**
	 * @author supereggbert / http://www.paulbrunt.co.uk/
	 * @author mrdoob / http://mrdoob.com/
	 * @author alteredq / http://alteredqualia.com/
	 * @author szimek / https://github.com/szimek/
	 * @author tschw
	 */
  
	function WebGLRenderer( parameters ) {
  
		console.log( 'THREE.WebGLRenderer', REVISION );
  
		parameters = parameters || {};
  
		var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ),
			_context = parameters.context !== undefined ? parameters.context : null,
  
			_alpha = parameters.alpha !== undefined ? parameters.alpha : false,
			_depth = parameters.depth !== undefined ? parameters.depth : true,
			_stencil = parameters.stencil !== undefined ? parameters.stencil : true,
			_antialias = parameters.antialias !== undefined ? parameters.antialias : false,
			_premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true,
			_preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false,
			_powerPreference = parameters.powerPreference !== undefined ? parameters.powerPreference : 'default';
  
		var currentRenderList = null;
		var currentRenderState = null;
  
		// public properties
  
		this.domElement = _canvas;
		this.context = null;
  
		// clearing
  
		this.autoClear = true;
		this.autoClearColor = true;
		this.autoClearDepth = true;
		this.autoClearStencil = true;
  
		// scene graph
  
		this.sortObjects = true;
  
		// user-defined clipping
  
		this.clippingPlanes = [];
		this.localClippingEnabled = false;
  
		// physically based shading
  
		this.gammaFactor = 2.0;	// for backwards compatibility
		this.gammaInput = false;
		this.gammaOutput = false;
  
		// physical lights
  
		this.physicallyCorrectLights = false;
  
		// tone mapping
  
		this.toneMapping = LinearToneMapping;
		this.toneMappingExposure = 1.0;
		this.toneMappingWhitePoint = 1.0;
  
		// morphs
  
		this.maxMorphTargets = 8;
		this.maxMorphNormals = 4;
  
		// internal properties
  
		var _this = this,
  
			_isContextLost = false,
  
			// internal state cache
  
			_framebuffer = null,
  
			_currentRenderTarget = null,
			_currentFramebuffer = null,
			_currentMaterialId = - 1,
			_currentGeometryProgram = '',
  
			_currentCamera = null,
			_currentArrayCamera = null,
  
			_currentViewport = new Vector4(),
			_currentScissor = new Vector4(),
			_currentScissorTest = null,
  
			//
  
			_usedTextureUnits = 0,
  
			//
  
			_width = _canvas.width,
			_height = _canvas.height,
  
			_pixelRatio = 1,
  
			_viewport = new Vector4( 0, 0, _width, _height ),
			_scissor = new Vector4( 0, 0, _width, _height ),
			_scissorTest = false,
  
			// frustum
  
			_frustum = new Frustum(),
  
			// clipping
  
			_clipping = new WebGLClipping(),
			_clippingEnabled = false,
			_localClippingEnabled = false,
  
			// camera matrices cache
  
			_projScreenMatrix = new Matrix4(),
  
			_vector3 = new Vector3();
  
		function getTargetPixelRatio() {
  
			return _currentRenderTarget === null ? _pixelRatio : 1;
  
		}
  
		// initialize
  
		var _gl;
  
		try {
  
			var contextAttributes = {
				alpha: _alpha,
				depth: _depth,
				stencil: _stencil,
				antialias: _antialias,
				premultipliedAlpha: _premultipliedAlpha,
				preserveDrawingBuffer: _preserveDrawingBuffer,
				powerPreference: _powerPreference
			};
  
			// event listeners must be registered before WebGL context is created, see #12753
  
			_canvas.addEventListener( 'webglcontextlost', onContextLost, false );
			_canvas.addEventListener( 'webglcontextrestored', onContextRestore, false );
  
			_gl = _context || _canvas.getContext( 'webgl', contextAttributes ) || _canvas.getContext( 'experimental-webgl', contextAttributes );
  
			if ( _gl === null ) {
  
				if ( _canvas.getContext( 'webgl' ) !== null ) {
  
					throw new Error( 'Error creating WebGL context with your selected attributes.' );
  
				} else {
  
					throw new Error( 'Error creating WebGL context.' );
  
				}
  
			}
  
			// Some experimental-webgl implementations do not have getShaderPrecisionFormat
  
			if ( _gl.getShaderPrecisionFormat === undefined ) {
  
				_gl.getShaderPrecisionFormat = function () {
  
					return { 'rangeMin': 1, 'rangeMax': 1, 'precision': 1 };
  
				};
  
			}
  
		} catch ( error ) {
  
			console.error( 'THREE.WebGLRenderer: ' + error.message );
  
		}
  
		var extensions, capabilities, state, info;
		var properties, textures, attributes, geometries, objects;
		var programCache, renderLists, renderStates;
  
		var background, morphtargets, bufferRenderer, indexedBufferRenderer;
		var spriteRenderer;
  
		var utils;
  
		function initGLContext() {
  
			extensions = new WebGLExtensions( _gl );
			extensions.get( 'WEBGL_depth_texture' );
			extensions.get( 'OES_texture_float' );
			extensions.get( 'OES_texture_float_linear' );
			extensions.get( 'OES_texture_half_float' );
			extensions.get( 'OES_texture_half_float_linear' );
			extensions.get( 'OES_standard_derivatives' );
			extensions.get( 'OES_element_index_uint' );
			extensions.get( 'ANGLE_instanced_arrays' );
  
			utils = new WebGLUtils( _gl, extensions );
  
			capabilities = new WebGLCapabilities( _gl, extensions, parameters );
  
			state = new WebGLState( _gl, extensions, utils );
			state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ) );
			state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ) );
  
			info = new WebGLInfo( _gl );
			properties = new WebGLProperties();
			textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info );
			attributes = new WebGLAttributes( _gl );
			geometries = new WebGLGeometries( _gl, attributes, info );
			objects = new WebGLObjects( geometries, info );
			morphtargets = new WebGLMorphtargets( _gl );
			programCache = new WebGLPrograms( _this, extensions, capabilities );
			renderLists = new WebGLRenderLists();
			renderStates = new WebGLRenderStates();
  
			background = new WebGLBackground( _this, state, objects, _premultipliedAlpha );
  
			bufferRenderer = new WebGLBufferRenderer( _gl, extensions, info );
			indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info );
  
			spriteRenderer = new WebGLSpriteRenderer( _this, _gl, state, textures, capabilities );
  
			info.programs = programCache.programs;
  
			_this.context = _gl;
			_this.capabilities = capabilities;
			_this.extensions = extensions;
			_this.properties = properties;
			_this.renderLists = renderLists;
			_this.state = state;
			_this.info = info;
  
		}
  
		initGLContext();
  
		// vr
  
		var vr = ( 'xr' in navigator ) ? new WebXRManager( _this ) : new WebVRManager( _this );
  
		this.vr = vr;
  
		// shadow map
  
		var shadowMap = new WebGLShadowMap( _this, objects, capabilities.maxTextureSize );
  
		this.shadowMap = shadowMap;
  
		// API
  
		this.getContext = function () {
  
			return _gl;
  
		};
  
		this.getContextAttributes = function () {
  
			return _gl.getContextAttributes();
  
		};
  
		this.forceContextLoss = function () {
  
			var extension = extensions.get( 'WEBGL_lose_context' );
			if ( extension ) extension.loseContext();
  
		};
  
		this.forceContextRestore = function () {
  
			var extension = extensions.get( 'WEBGL_lose_context' );
			if ( extension ) extension.restoreContext();
  
		};
  
		this.getPixelRatio = function () {
  
			return _pixelRatio;
  
		};
  
		this.setPixelRatio = function ( value ) {
  
			if ( value === undefined ) return;
  
			_pixelRatio = value;
  
			this.setSize( _width, _height, false );
  
		};
  
		this.getSize = function () {
  
			return {
				width: _width,
				height: _height
			};
  
		};
  
		this.setSize = function ( width, height, updateStyle ) {
  
			if ( vr.isPresenting() ) {
  
				console.warn( 'THREE.WebGLRenderer: Can\'t change size while VR device is presenting.' );
				return;
  
			}
  
			_width = width;
			_height = height;
  
			_canvas.width = width * _pixelRatio;
			_canvas.height = height * _pixelRatio;
  
			if ( updateStyle !== false ) {
  
				_canvas.style.width = width + 'px';
				_canvas.style.height = height + 'px';
  
			}
  
			this.setViewport( 0, 0, width, height );
  
		};
  
		this.getDrawingBufferSize = function () {
  
			return {
				width: _width * _pixelRatio,
				height: _height * _pixelRatio
			};
  
		};
  
		this.setDrawingBufferSize = function ( width, height, pixelRatio ) {
  
			_width = width;
			_height = height;
  
			_pixelRatio = pixelRatio;
  
			_canvas.width = width * pixelRatio;
			_canvas.height = height * pixelRatio;
  
			this.setViewport( 0, 0, width, height );
  
		};
  
		this.getCurrentViewport = function () {
  
			return _currentViewport;
  
		};
  
		this.setViewport = function ( x, y, width, height ) {
  
			_viewport.set( x, _height - y - height, width, height );
			state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ) );
  
		};
  
		this.setScissor = function ( x, y, width, height ) {
  
			_scissor.set( x, _height - y - height, width, height );
			state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ) );
  
		};
  
		this.setScissorTest = function ( boolean ) {
  
			state.setScissorTest( _scissorTest = boolean );
  
		};
  
		// Clearing
  
		this.getClearColor = function () {
  
			return background.getClearColor();
  
		};
  
		this.setClearColor = function () {
  
			background.setClearColor.apply( background, arguments );
  
		};
  
		this.getClearAlpha = function () {
  
			return background.getClearAlpha();
  
		};
  
		this.setClearAlpha = function () {
  
			background.setClearAlpha.apply( background, arguments );
  
		};
  
		this.clear = function ( color, depth, stencil ) {
  
			var bits = 0;
  
			if ( color === undefined || color ) bits |= _gl.COLOR_BUFFER_BIT;
			if ( depth === undefined || depth ) bits |= _gl.DEPTH_BUFFER_BIT;
			if ( stencil === undefined || stencil ) bits |= _gl.STENCIL_BUFFER_BIT;
  
			_gl.clear( bits );
  
		};
  
		this.clearColor = function () {
  
			this.clear( true, false, false );
  
		};
  
		this.clearDepth = function () {
  
			this.clear( false, true, false );
  
		};
  
		this.clearStencil = function () {
  
			this.clear( false, false, true );
  
		};
  
		this.clearTarget = function ( renderTarget, color, depth, stencil ) {
  
			this.setRenderTarget( renderTarget );
			this.clear( color, depth, stencil );
  
		};
  
		//
  
		this.dispose = function () {
  
			_canvas.removeEventListener( 'webglcontextlost', onContextLost, false );
			_canvas.removeEventListener( 'webglcontextrestored', onContextRestore, false );
  
			renderLists.dispose();
			renderStates.dispose();
			properties.dispose();
			objects.dispose();
  
			vr.dispose();
  
			animation.stop();
  
		};
  
		// Events
  
		function onContextLost( event ) {
  
			event.preventDefault();
  
			console.log( 'THREE.WebGLRenderer: Context Lost.' );
  
			_isContextLost = true;
  
		}
  
		function onContextRestore( /* event */ ) {
  
			console.log( 'THREE.WebGLRenderer: Context Restored.' );
  
			_isContextLost = false;
  
			initGLContext();
  
		}
  
		function onMaterialDispose( event ) {
  
			var material = event.target;
  
			material.removeEventListener( 'dispose', onMaterialDispose );
  
			deallocateMaterial( material );
  
		}
  
		// Buffer deallocation
  
		function deallocateMaterial( material ) {
  
			releaseMaterialProgramReference( material );
  
			properties.remove( material );
  
		}
  
  
		function releaseMaterialProgramReference( material ) {
  
			var programInfo = properties.get( material ).program;
  
			material.program = undefined;
  
			if ( programInfo !== undefined ) {
  
				programCache.releaseProgram( programInfo );
  
			}
  
		}
  
		// Buffer rendering
  
		function renderObjectImmediate( object, program, material ) {
  
			object.render( function ( object ) {
  
				_this.renderBufferImmediate( object, program, material );
  
			} );
  
		}
  
		this.renderBufferImmediate = function ( object, program, material ) {
  
			state.initAttributes();
  
			var buffers = properties.get( object );
  
			if ( object.hasPositions && ! buffers.position ) buffers.position = _gl.createBuffer();
			if ( object.hasNormals && ! buffers.normal ) buffers.normal = _gl.createBuffer();
			if ( object.hasUvs && ! buffers.uv ) buffers.uv = _gl.createBuffer();
			if ( object.hasColors && ! buffers.color ) buffers.color = _gl.createBuffer();
  
			var programAttributes = program.getAttributes();
  
			if ( object.hasPositions ) {
  
				_gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.position );
				_gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW );
  
				state.enableAttribute( programAttributes.position );
				_gl.vertexAttribPointer( programAttributes.position, 3, _gl.FLOAT, false, 0, 0 );
  
			}
  
			if ( object.hasNormals ) {
  
				_gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.normal );
  
				if ( ! material.isMeshPhongMaterial &&
					! material.isMeshStandardMaterial &&
					! material.isMeshNormalMaterial &&
					material.flatShading === true ) {
  
					for ( var i = 0, l = object.count * 3; i < l; i += 9 ) {
  
						var array = object.normalArray;
  
						var nx = ( array[ i + 0 ] + array[ i + 3 ] + array[ i + 6 ] ) / 3;
						var ny = ( array[ i + 1 ] + array[ i + 4 ] + array[ i + 7 ] ) / 3;
						var nz = ( array[ i + 2 ] + array[ i + 5 ] + array[ i + 8 ] ) / 3;
  
						array[ i + 0 ] = nx;
						array[ i + 1 ] = ny;
						array[ i + 2 ] = nz;
  
						array[ i + 3 ] = nx;
						array[ i + 4 ] = ny;
						array[ i + 5 ] = nz;
  
						array[ i + 6 ] = nx;
						array[ i + 7 ] = ny;
						array[ i + 8 ] = nz;
  
					}
  
				}
  
				_gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW );
  
				state.enableAttribute( programAttributes.normal );
  
				_gl.vertexAttribPointer( programAttributes.normal, 3, _gl.FLOAT, false, 0, 0 );
  
			}
  
			if ( object.hasUvs && material.map ) {
  
				_gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.uv );
				_gl.bufferData( _gl.ARRAY_BUFFER, object.uvArray, _gl.DYNAMIC_DRAW );
  
				state.enableAttribute( programAttributes.uv );
  
				_gl.vertexAttribPointer( programAttributes.uv, 2, _gl.FLOAT, false, 0, 0 );
  
			}
  
			if ( object.hasColors && material.vertexColors !== NoColors ) {
  
				_gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.color );
				_gl.bufferData( _gl.ARRAY_BUFFER, object.colorArray, _gl.DYNAMIC_DRAW );
  
				state.enableAttribute( programAttributes.color );
  
				_gl.vertexAttribPointer( programAttributes.color, 3, _gl.FLOAT, false, 0, 0 );
  
			}
  
			state.disableUnusedAttributes();
  
			_gl.drawArrays( _gl.TRIANGLES, 0, object.count );
  
			object.count = 0;
  
		};
  
		this.renderBufferDirect = function ( camera, fog, geometry, material, object, group ) {
  
			var frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 );
  
			state.setMaterial( material, frontFaceCW );
  
			var program = setProgram( camera, fog, material, object );
			var geometryProgram = geometry.id + '_' + program.id + '_' + ( material.wireframe === true );
  
			var updateBuffers = false;
  
			if ( geometryProgram !== _currentGeometryProgram ) {
  
				_currentGeometryProgram = geometryProgram;
				updateBuffers = true;
  
			}
  
			if ( object.morphTargetInfluences ) {
  
				morphtargets.update( object, geometry, material, program );
  
				updateBuffers = true;
  
			}
  
			//
  
			var index = geometry.index;
			var position = geometry.attributes.position;
			var rangeFactor = 1;
  
			if ( material.wireframe === true ) {
  
				index = geometries.getWireframeAttribute( geometry );
				rangeFactor = 2;
  
			}
  
			var attribute;
			var renderer = bufferRenderer;
  
			if ( index !== null ) {
  
				attribute = attributes.get( index );
  
				renderer = indexedBufferRenderer;
				renderer.setIndex( attribute );
  
			}
  
			if ( updateBuffers ) {
  
				setupVertexAttributes( material, program, geometry );
  
				if ( index !== null ) {
  
					_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, attribute.buffer );
  
				}
  
			}
  
			//
  
			var dataCount = Infinity;
  
			if ( index !== null ) {
  
				dataCount = index.count;
  
			} else if ( position !== undefined ) {
  
				dataCount = position.count;
  
			}
  
			var rangeStart = geometry.drawRange.start * rangeFactor;
			var rangeCount = geometry.drawRange.count * rangeFactor;
  
			var groupStart = group !== null ? group.start * rangeFactor : 0;
			var groupCount = group !== null ? group.count * rangeFactor : Infinity;
  
			var drawStart = Math.max( rangeStart, groupStart );
			var drawEnd = Math.min( dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1;
  
			var drawCount = Math.max( 0, drawEnd - drawStart + 1 );
  
			if ( drawCount === 0 ) return;
  
			//
  
			if ( object.isMesh ) {
  
				if ( material.wireframe === true ) {
  
					state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() );
					renderer.setMode( _gl.LINES );
  
				} else {
  
					switch ( object.drawMode ) {
  
						case TrianglesDrawMode:
							renderer.setMode( _gl.TRIANGLES );
							break;
  
						case TriangleStripDrawMode:
							renderer.setMode( _gl.TRIANGLE_STRIP );
							break;
  
						case TriangleFanDrawMode:
							renderer.setMode( _gl.TRIANGLE_FAN );
							break;
  
					}
  
				}
  
  
			} else if ( object.isLine ) {
  
				var lineWidth = material.linewidth;
  
				if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material
  
				state.setLineWidth( lineWidth * getTargetPixelRatio() );
  
				if ( object.isLineSegments ) {
  
					renderer.setMode( _gl.LINES );
  
				} else if ( object.isLineLoop ) {
  
					renderer.setMode( _gl.LINE_LOOP );
  
				} else {
  
					renderer.setMode( _gl.LINE_STRIP );
  
				}
  
			} else if ( object.isPoints ) {
  
				renderer.setMode( _gl.POINTS );
  
			}
  
			if ( geometry && geometry.isInstancedBufferGeometry ) {
  
				if ( geometry.maxInstancedCount > 0 ) {
  
					renderer.renderInstances( geometry, drawStart, drawCount );
  
				}
  
			} else {
  
				renderer.render( drawStart, drawCount );
  
			}
  
		};
  
		function setupVertexAttributes( material, program, geometry ) {
  
			if ( geometry && geometry.isInstancedBufferGeometry ) {
  
				if ( extensions.get( 'ANGLE_instanced_arrays' ) === null ) {
  
					console.error( 'THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' );
					return;
  
				}
  
			}
  
			state.initAttributes();
  
			var geometryAttributes = geometry.attributes;
  
			var programAttributes = program.getAttributes();
  
			var materialDefaultAttributeValues = material.defaultAttributeValues;
  
			for ( var name in programAttributes ) {
  
				var programAttribute = programAttributes[ name ];
  
				if ( programAttribute >= 0 ) {
  
					var geometryAttribute = geometryAttributes[ name ];
  
					if ( geometryAttribute !== undefined ) {
  
						var normalized = geometryAttribute.normalized;
						var size = geometryAttribute.itemSize;
  
						var attribute = attributes.get( geometryAttribute );
  
						// TODO Attribute may not be available on context restore
  
						if ( attribute === undefined ) continue;
  
						var buffer = attribute.buffer;
						var type = attribute.type;
						var bytesPerElement = attribute.bytesPerElement;
  
						if ( geometryAttribute.isInterleavedBufferAttribute ) {
  
							var data = geometryAttribute.data;
							var stride = data.stride;
							var offset = geometryAttribute.offset;
  
							if ( data && data.isInstancedInterleavedBuffer ) {
  
								state.enableAttributeAndDivisor( programAttribute, data.meshPerAttribute );
  
								if ( geometry.maxInstancedCount === undefined ) {
  
									geometry.maxInstancedCount = data.meshPerAttribute * data.count;
  
								}
  
							} else {
  
								state.enableAttribute( programAttribute );
  
							}
  
							_gl.bindBuffer( _gl.ARRAY_BUFFER, buffer );
							_gl.vertexAttribPointer( programAttribute, size, type, normalized, stride * bytesPerElement, offset * bytesPerElement );
  
						} else {
  
							if ( geometryAttribute.isInstancedBufferAttribute ) {
  
								state.enableAttributeAndDivisor( programAttribute, geometryAttribute.meshPerAttribute );
  
								if ( geometry.maxInstancedCount === undefined ) {
  
									geometry.maxInstancedCount = geometryAttribute.meshPerAttribute * geometryAttribute.count;
  
								}
  
							} else {
  
								state.enableAttribute( programAttribute );
  
							}
  
							_gl.bindBuffer( _gl.ARRAY_BUFFER, buffer );
							_gl.vertexAttribPointer( programAttribute, size, type, normalized, 0, 0 );
  
						}
  
					} else if ( materialDefaultAttributeValues !== undefined ) {
  
						var value = materialDefaultAttributeValues[ name ];
  
						if ( value !== undefined ) {
  
							switch ( value.length ) {
  
								case 2:
									_gl.vertexAttrib2fv( programAttribute, value );
									break;
  
								case 3:
									_gl.vertexAttrib3fv( programAttribute, value );
									break;
  
								case 4:
									_gl.vertexAttrib4fv( programAttribute, value );
									break;
  
								default:
									_gl.vertexAttrib1fv( programAttribute, value );
  
							}
  
						}
  
					}
  
				}
  
			}
  
			state.disableUnusedAttributes();
  
		}
  
		// Compile
  
		this.compile = function ( scene, camera ) {
  
			currentRenderState = renderStates.get( scene, camera );
			currentRenderState.init();
  
			scene.traverse( function ( object ) {
  
				if ( object.isLight ) {
  
					currentRenderState.pushLight( object );
  
					if ( object.castShadow ) {
  
						currentRenderState.pushShadow( object );
  
					}
  
				}
  
			} );
  
			currentRenderState.setupLights( camera );
  
			scene.traverse( function ( object ) {
  
				if ( object.material ) {
  
					if ( Array.isArray( object.material ) ) {
  
						for ( var i = 0; i < object.material.length; i ++ ) {
  
							initMaterial( object.material[ i ], scene.fog, object );
  
						}
  
					} else {
  
						initMaterial( object.material, scene.fog, object );
  
					}
  
				}
  
			} );
  
		};
  
		// Animation Loop
  
		var onAnimationFrameCallback = null;
  
		function onAnimationFrame() {
  
			if ( vr.isPresenting() ) return;
			if ( onAnimationFrameCallback ) onAnimationFrameCallback();
  
		}
  
		var animation = new WebGLAnimation();
		animation.setAnimationLoop( onAnimationFrame );
		animation.setContext( window );
  
		this.setAnimationLoop = function ( callback ) {
  
			onAnimationFrameCallback = callback;
			vr.setAnimationLoop( callback );
  
			animation.start();
  
		};
  
		// Rendering
  
		this.render = function ( scene, camera, renderTarget, forceClear ) {
  
			if ( ! ( camera && camera.isCamera ) ) {
  
				console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' );
				return;
  
			}
  
			if ( _isContextLost ) return;
  
			// reset caching for this frame
  
			_currentGeometryProgram = '';
			_currentMaterialId = - 1;
			_currentCamera = null;
  
			// update scene graph
  
			if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
  
			// update camera matrices and frustum
  
			if ( camera.parent === null ) camera.updateMatrixWorld();
  
			if ( vr.enabled ) {
  
				camera = vr.getCamera( camera );
  
			}
  
			//
  
			currentRenderState = renderStates.get( scene, camera );
			currentRenderState.init();
  
			scene.onBeforeRender( _this, scene, camera, renderTarget );
  
			_projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
			_frustum.setFromMatrix( _projScreenMatrix );
  
			_localClippingEnabled = this.localClippingEnabled;
			_clippingEnabled = _clipping.init( this.clippingPlanes, _localClippingEnabled, camera );
  
			currentRenderList = renderLists.get( scene, camera );
			currentRenderList.init();
  
			projectObject( scene, camera, _this.sortObjects );
  
			if ( _this.sortObjects === true ) {
  
				currentRenderList.sort();
  
			}
  
			//
  
			if ( _clippingEnabled ) _clipping.beginShadows();
  
			var shadowsArray = currentRenderState.state.shadowsArray;
  
			shadowMap.render( shadowsArray, scene, camera );
  
			currentRenderState.setupLights( camera );
  
			if ( _clippingEnabled ) _clipping.endShadows();
  
			//
  
			if ( this.info.autoReset ) this.info.reset();
  
			if ( renderTarget === undefined ) {
  
				renderTarget = null;
  
			}
  
			this.setRenderTarget( renderTarget );
  
			//
  
			background.render( currentRenderList, scene, camera, forceClear );
  
			// render scene
  
			var opaqueObjects = currentRenderList.opaque;
			var transparentObjects = currentRenderList.transparent;
  
			if ( scene.overrideMaterial ) {
  
				var overrideMaterial = scene.overrideMaterial;
  
				if ( opaqueObjects.length ) renderObjects( opaqueObjects, scene, camera, overrideMaterial );
				if ( transparentObjects.length ) renderObjects( transparentObjects, scene, camera, overrideMaterial );
  
			} else {
  
				// opaque pass (front-to-back order)
  
				if ( opaqueObjects.length ) renderObjects( opaqueObjects, scene, camera );
  
				// transparent pass (back-to-front order)
  
				if ( transparentObjects.length ) renderObjects( transparentObjects, scene, camera );
  
			}
  
			// custom renderers
  
			var spritesArray = currentRenderState.state.spritesArray;
  
			spriteRenderer.render( spritesArray, scene, camera );
  
			// Generate mipmap if we're using any kind of mipmap filtering
  
			if ( renderTarget ) {
  
				textures.updateRenderTargetMipmap( renderTarget );
  
			}
  
			// Ensure depth buffer writing is enabled so it can be cleared on next render
  
			state.buffers.depth.setTest( true );
			state.buffers.depth.setMask( true );
			state.buffers.color.setMask( true );
  
			state.setPolygonOffset( false );
  
			scene.onAfterRender( _this, scene, camera );
  
			if ( vr.enabled ) {
  
				vr.submitFrame();
  
			}
  
			// _gl.finish();
  
			currentRenderList = null;
			currentRenderState = null;
  
		};
  
		/*
		// TODO Duplicated code (Frustum)
  
		var _sphere = new Sphere();
  
		function isObjectViewable( object ) {
  
			var geometry = object.geometry;
  
			if ( geometry.boundingSphere === null )
				geometry.computeBoundingSphere();
  
			_sphere.copy( geometry.boundingSphere ).
			applyMatrix4( object.matrixWorld );
  
			return isSphereViewable( _sphere );
  
		}
  
		function isSpriteViewable( sprite ) {
  
			_sphere.center.set( 0, 0, 0 );
			_sphere.radius = 0.7071067811865476;
			_sphere.applyMatrix4( sprite.matrixWorld );
  
			return isSphereViewable( _sphere );
  
		}
  
		function isSphereViewable( sphere ) {
  
			if ( ! _frustum.intersectsSphere( sphere ) ) return false;
  
			var numPlanes = _clipping.numPlanes;
  
			if ( numPlanes === 0 ) return true;
  
			var planes = _this.clippingPlanes,
  
				center = sphere.center,
				negRad = - sphere.radius,
				i = 0;
  
			do {
  
				// out when deeper than radius in the negative halfspace
				if ( planes[ i ].distanceToPoint( center ) < negRad ) return false;
  
			} while ( ++ i !== numPlanes );
  
			return true;
  
		}
		*/
  
		function projectObject( object, camera, sortObjects ) {
  
			if ( object.visible === false ) return;
  
			var visible = object.layers.test( camera.layers );
  
			if ( visible ) {
  
				if ( object.isLight ) {
  
					currentRenderState.pushLight( object );
  
					if ( object.castShadow ) {
  
						currentRenderState.pushShadow( object );
  
					}
  
				} else if ( object.isSprite ) {
  
					if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) {
  
						currentRenderState.pushSprite( object );
  
					}
  
				} else if ( object.isImmediateRenderObject ) {
  
					if ( sortObjects ) {
  
						_vector3.setFromMatrixPosition( object.matrixWorld )
							.applyMatrix4( _projScreenMatrix );
  
					}
  
					currentRenderList.push( object, null, object.material, _vector3.z, null );
  
				} else if ( object.isMesh || object.isLine || object.isPoints ) {
  
					if ( object.isSkinnedMesh ) {
  
						object.skeleton.update();
  
					}
  
					if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) {
  
						if ( sortObjects ) {
  
							_vector3.setFromMatrixPosition( object.matrixWorld )
								.applyMatrix4( _projScreenMatrix );
  
						}
  
						var geometry = objects.update( object );
						var material = object.material;
  
						if ( Array.isArray( material ) ) {
  
							var groups = geometry.groups;
  
							for ( var i = 0, l = groups.length; i < l; i ++ ) {
  
								var group = groups[ i ];
								var groupMaterial = material[ group.materialIndex ];
  
								if ( groupMaterial && groupMaterial.visible ) {
  
									currentRenderList.push( object, geometry, groupMaterial, _vector3.z, group );
  
								}
  
							}
  
						} else if ( material.visible ) {
  
							currentRenderList.push( object, geometry, material, _vector3.z, null );
  
						}
  
					}
  
				}
  
			}
  
			var children = object.children;
  
			for ( var i = 0, l = children.length; i < l; i ++ ) {
  
				projectObject( children[ i ], camera, sortObjects );
  
			}
  
		}
  
		function renderObjects( renderList, scene, camera, overrideMaterial ) {
  
			for ( var i = 0, l = renderList.length; i < l; i ++ ) {
  
				var renderItem = renderList[ i ];
  
				var object = renderItem.object;
				var geometry = renderItem.geometry;
				var material = overrideMaterial === undefined ? renderItem.material : overrideMaterial;
				var group = renderItem.group;
  
				if ( camera.isArrayCamera ) {
  
					_currentArrayCamera = camera;
  
					var cameras = camera.cameras;
  
					for ( var j = 0, jl = cameras.length; j < jl; j ++ ) {
  
						var camera2 = cameras[ j ];
  
						if ( object.layers.test( camera2.layers ) ) {
  
							if ( 'viewport' in camera2 ) { // XR
  
								state.viewport( _currentViewport.copy( camera2.viewport ) );
  
							} else {
  
								var bounds = camera2.bounds;
  
								var x = bounds.x * _width;
								var y = bounds.y * _height;
								var width = bounds.z * _width;
								var height = bounds.w * _height;
  
								state.viewport( _currentViewport.set( x, y, width, height ).multiplyScalar( _pixelRatio ) );
  
							}
  
							renderObject( object, scene, camera2, geometry, material, group );
  
						}
  
					}
  
				} else {
  
					_currentArrayCamera = null;
  
					renderObject( object, scene, camera, geometry, material, group );
  
				}
  
			}
  
		}
  
		function renderObject( object, scene, camera, geometry, material, group ) {
  
			object.onBeforeRender( _this, scene, camera, geometry, material, group );
			currentRenderState = renderStates.get( scene, _currentArrayCamera || camera );
  
			object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
			object.normalMatrix.getNormalMatrix( object.modelViewMatrix );
  
			if ( object.isImmediateRenderObject ) {
  
				var frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 );
  
				state.setMaterial( material, frontFaceCW );
  
				var program = setProgram( camera, scene.fog, material, object );
  
				_currentGeometryProgram = '';
  
				renderObjectImmediate( object, program, material );
  
			} else {
  
				_this.renderBufferDirect( camera, scene.fog, geometry, material, object, group );
  
			}
  
			object.onAfterRender( _this, scene, camera, geometry, material, group );
			currentRenderState = renderStates.get( scene, _currentArrayCamera || camera );
  
		}
  
		function initMaterial( material, fog, object ) {
  
			var materialProperties = properties.get( material );
  
			var lights = currentRenderState.state.lights;
			var shadowsArray = currentRenderState.state.shadowsArray;
  
			var parameters = programCache.getParameters(
				material, lights.state, shadowsArray, fog, _clipping.numPlanes, _clipping.numIntersection, object );
  
			var code = programCache.getProgramCode( material, parameters );
  
			var program = materialProperties.program;
			var programChange = true;
  
			if ( program === undefined ) {
  
				// new material
				material.addEventListener( 'dispose', onMaterialDispose );
  
			} else if ( program.code !== code ) {
  
				// changed glsl or parameters
				releaseMaterialProgramReference( material );
  
			} else if ( materialProperties.lightsHash !== lights.state.hash ) {
  
				properties.update( material, 'lightsHash', lights.state.hash );
				programChange = false;
  
			} else if ( parameters.shaderID !== undefined ) {
  
				// same glsl and uniform list
				return;
  
			} else {
  
				// only rebuild uniform list
				programChange = false;
  
			}
  
			if ( programChange ) {
  
				if ( parameters.shaderID ) {
  
					var shader = ShaderLib[ parameters.shaderID ];
  
					materialProperties.shader = {
						name: material.type,
						uniforms: UniformsUtils.clone( shader.uniforms ),
						vertexShader: shader.vertexShader,
						fragmentShader: shader.fragmentShader
					};
  
				} else {
  
					materialProperties.shader = {
						name: material.type,
						uniforms: material.uniforms,
						vertexShader: material.vertexShader,
						fragmentShader: material.fragmentShader
					};
  
				}
  
				material.onBeforeCompile( materialProperties.shader, _this );
  
				program = programCache.acquireProgram( material, materialProperties.shader, parameters, code );
  
				materialProperties.program = program;
				material.program = program;
  
			}
  
			var programAttributes = program.getAttributes();
  
			if ( material.morphTargets ) {
  
				material.numSupportedMorphTargets = 0;
  
				for ( var i = 0; i < _this.maxMorphTargets; i ++ ) {
  
					if ( programAttributes[ 'morphTarget' + i ] >= 0 ) {
  
						material.numSupportedMorphTargets ++;
  
					}
  
				}
  
			}
  
			if ( material.morphNormals ) {
  
				material.numSupportedMorphNormals = 0;
  
				for ( var i = 0; i < _this.maxMorphNormals; i ++ ) {
  
					if ( programAttributes[ 'morphNormal' + i ] >= 0 ) {
  
						material.numSupportedMorphNormals ++;
  
					}
  
				}
  
			}
  
			var uniforms = materialProperties.shader.uniforms;
  
			if ( ! material.isShaderMaterial &&
				! material.isRawShaderMaterial ||
				material.clipping === true ) {
  
				materialProperties.numClippingPlanes = _clipping.numPlanes;
				materialProperties.numIntersection = _clipping.numIntersection;
				uniforms.clippingPlanes = _clipping.uniform;
  
			}
  
			materialProperties.fog = fog;
  
			// store the light setup it was created for
  
			materialProperties.lightsHash = lights.state.hash;
  
			if ( material.lights ) {
  
				// wire up the material to this renderer's lighting state
  
				uniforms.ambientLightColor.value = lights.state.ambient;
				uniforms.directionalLights.value = lights.state.directional;
				uniforms.spotLights.value = lights.state.spot;
				uniforms.rectAreaLights.value = lights.state.rectArea;
				uniforms.pointLights.value = lights.state.point;
				uniforms.hemisphereLights.value = lights.state.hemi;
  
				uniforms.directionalShadowMap.value = lights.state.directionalShadowMap;
				uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix;
				uniforms.spotShadowMap.value = lights.state.spotShadowMap;
				uniforms.spotShadowMatrix.value = lights.state.spotShadowMatrix;
				uniforms.pointShadowMap.value = lights.state.pointShadowMap;
				uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix;
				// TODO (abelnation): add area lights shadow info to uniforms
  
			}
  
			var progUniforms = materialProperties.program.getUniforms(),
				uniformsList =
					WebGLUniforms.seqWithValue( progUniforms.seq, uniforms );
  
			materialProperties.uniformsList = uniformsList;
  
		}
  
		function setProgram( camera, fog, material, object ) {
  
			_usedTextureUnits = 0;
  
			var materialProperties = properties.get( material );
			var lights = currentRenderState.state.lights;
  
			if ( _clippingEnabled ) {
  
				if ( _localClippingEnabled || camera !== _currentCamera ) {
  
					var useCache =
						camera === _currentCamera &&
						material.id === _currentMaterialId;
  
					// we might want to call this function with some ClippingGroup
					// object instead of the material, once it becomes feasible
					// (#8465, #8379)
					_clipping.setState(
						material.clippingPlanes, material.clipIntersection, material.clipShadows,
						camera, materialProperties, useCache );
  
				}
  
			}
  
			if ( material.needsUpdate === false ) {
  
				if ( materialProperties.program === undefined ) {
  
					material.needsUpdate = true;
  
				} else if ( material.fog && materialProperties.fog !== fog ) {
  
					material.needsUpdate = true;
  
				} else if ( material.lights && materialProperties.lightsHash !== lights.state.hash ) {
  
					material.needsUpdate = true;
  
				} else if ( materialProperties.numClippingPlanes !== undefined &&
					( materialProperties.numClippingPlanes !== _clipping.numPlanes ||
					materialProperties.numIntersection !== _clipping.numIntersection ) ) {
  
					material.needsUpdate = true;
  
				}
  
			}
  
			if ( material.needsUpdate ) {
  
				initMaterial( material, fog, object );
				material.needsUpdate = false;
  
			}
  
			var refreshProgram = false;
			var refreshMaterial = false;
			var refreshLights = false;
  
			var program = materialProperties.program,
				p_uniforms = program.getUniforms(),
				m_uniforms = materialProperties.shader.uniforms;
  
			if ( state.useProgram( program.program ) ) {
  
				refreshProgram = true;
				refreshMaterial = true;
				refreshLights = true;
  
			}
  
			if ( material.id !== _currentMaterialId ) {
  
				_currentMaterialId = material.id;
  
				refreshMaterial = true;
  
			}
  
			if ( refreshProgram || camera !== _currentCamera ) {
  
				p_uniforms.setValue( _gl, 'projectionMatrix', camera.projectionMatrix );
  
				if ( capabilities.logarithmicDepthBuffer ) {
  
					p_uniforms.setValue( _gl, 'logDepthBufFC',
						2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) );
  
				}
  
				// Avoid unneeded uniform updates per ArrayCamera's sub-camera
  
				if ( _currentCamera !== ( _currentArrayCamera || camera ) ) {
  
					_currentCamera = ( _currentArrayCamera || camera );
  
					// lighting uniforms depend on the camera so enforce an update
					// now, in case this material supports lights - or later, when
					// the next material that does gets activated:
  
					refreshMaterial = true;		// set to true on material change
					refreshLights = true;		// remains set until update done
  
				}
  
				// load material specific uniforms
				// (shader material also gets them for the sake of genericity)
  
				if ( material.isShaderMaterial ||
					material.isMeshPhongMaterial ||
					material.isMeshStandardMaterial ||
					material.envMap ) {
  
					var uCamPos = p_uniforms.map.cameraPosition;
  
					if ( uCamPos !== undefined ) {
  
						uCamPos.setValue( _gl,
							_vector3.setFromMatrixPosition( camera.matrixWorld ) );
  
					}
  
				}
  
				if ( material.isMeshPhongMaterial ||
					material.isMeshLambertMaterial ||
					material.isMeshBasicMaterial ||
					material.isMeshStandardMaterial ||
					material.isShaderMaterial ||
					material.skinning ) {
  
					p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse );
  
				}
  
			}
  
			// skinning uniforms must be set even if material didn't change
			// auto-setting of texture unit for bone texture must go before other textures
			// not sure why, but otherwise weird things happen
  
			if ( material.skinning ) {
  
				p_uniforms.setOptional( _gl, object, 'bindMatrix' );
				p_uniforms.setOptional( _gl, object, 'bindMatrixInverse' );
  
				var skeleton = object.skeleton;
  
				if ( skeleton ) {
  
					var bones = skeleton.bones;
  
					if ( capabilities.floatVertexTextures ) {
  
						if ( skeleton.boneTexture === undefined ) {
  
							// layout (1 matrix = 4 pixels)
							//      RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4)
							//  with  8x8  pixel texture max   16 bones * 4 pixels =  (8 * 8)
							//       16x16 pixel texture max   64 bones * 4 pixels = (16 * 16)
							//       32x32 pixel texture max  256 bones * 4 pixels = (32 * 32)
							//       64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64)
  
  
							var size = Math.sqrt( bones.length * 4 ); // 4 pixels needed for 1 matrix
							size = _Math.ceilPowerOfTwo( size );
							size = Math.max( size, 4 );
  
							var boneMatrices = new Float32Array( size * size * 4 ); // 4 floats per RGBA pixel
							boneMatrices.set( skeleton.boneMatrices ); // copy current values
  
							var boneTexture = new DataTexture( boneMatrices, size, size, RGBAFormat, FloatType );
							boneTexture.needsUpdate = true;
  
							skeleton.boneMatrices = boneMatrices;
							skeleton.boneTexture = boneTexture;
							skeleton.boneTextureSize = size;
  
						}
  
						p_uniforms.setValue( _gl, 'boneTexture', skeleton.boneTexture );
						p_uniforms.setValue( _gl, 'boneTextureSize', skeleton.boneTextureSize );
  
					} else {
  
						p_uniforms.setOptional( _gl, skeleton, 'boneMatrices' );
  
					}
  
				}
  
			}
  
			if ( refreshMaterial ) {
  
				p_uniforms.setValue( _gl, 'toneMappingExposure', _this.toneMappingExposure );
				p_uniforms.setValue( _gl, 'toneMappingWhitePoint', _this.toneMappingWhitePoint );
  
				if ( material.lights ) {
  
					// the current material requires lighting info
  
					// note: all lighting uniforms are always set correctly
					// they simply reference the renderer's state for their
					// values
					//
					// use the current material's .needsUpdate flags to set
					// the GL state when required
  
					markUniformsLightsNeedsUpdate( m_uniforms, refreshLights );
  
				}
  
				// refresh uniforms common to several materials
  
				if ( fog && material.fog ) {
  
					refreshUniformsFog( m_uniforms, fog );
  
				}
  
				if ( material.isMeshBasicMaterial ) {
  
					refreshUniformsCommon( m_uniforms, material );
  
				} else if ( material.isMeshLambertMaterial ) {
  
					refreshUniformsCommon( m_uniforms, material );
					refreshUniformsLambert( m_uniforms, material );
  
				} else if ( material.isMeshPhongMaterial ) {
  
					refreshUniformsCommon( m_uniforms, material );
  
					if ( material.isMeshToonMaterial ) {
  
						refreshUniformsToon( m_uniforms, material );
  
					} else {
  
						refreshUniformsPhong( m_uniforms, material );
  
					}
  
				} else if ( material.isMeshStandardMaterial ) {
  
					refreshUniformsCommon( m_uniforms, material );
  
					if ( material.isMeshPhysicalMaterial ) {
  
						refreshUniformsPhysical( m_uniforms, material );
  
					} else {
  
						refreshUniformsStandard( m_uniforms, material );
  
					}
  
				} else if ( material.isMeshDepthMaterial ) {
  
					refreshUniformsCommon( m_uniforms, material );
					refreshUniformsDepth( m_uniforms, material );
  
				} else if ( material.isMeshDistanceMaterial ) {
  
					refreshUniformsCommon( m_uniforms, material );
					refreshUniformsDistance( m_uniforms, material );
  
				} else if ( material.isMeshNormalMaterial ) {
  
					refreshUniformsCommon( m_uniforms, material );
					refreshUniformsNormal( m_uniforms, material );
  
				} else if ( material.isLineBasicMaterial ) {
  
					refreshUniformsLine( m_uniforms, material );
  
					if ( material.isLineDashedMaterial ) {
  
						refreshUniformsDash( m_uniforms, material );
  
					}
  
				} else if ( material.isPointsMaterial ) {
  
					refreshUniformsPoints( m_uniforms, material );
  
				} else if ( material.isShadowMaterial ) {
  
					m_uniforms.color.value = material.color;
					m_uniforms.opacity.value = material.opacity;
  
				}
  
				// RectAreaLight Texture
				// TODO (mrdoob): Find a nicer implementation
  
				if ( m_uniforms.ltc_1 !== undefined ) m_uniforms.ltc_1.value = UniformsLib.LTC_1;
				if ( m_uniforms.ltc_2 !== undefined ) m_uniforms.ltc_2.value = UniformsLib.LTC_2;
  
				WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, _this );
  
			}
  
			if ( material.isShaderMaterial && material.uniformsNeedUpdate === true ) {
  
				WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, _this );
				material.uniformsNeedUpdate = false;
  
			}
  
			// common matrices
  
			p_uniforms.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix );
			p_uniforms.setValue( _gl, 'normalMatrix', object.normalMatrix );
			p_uniforms.setValue( _gl, 'modelMatrix', object.matrixWorld );
  
			return program;
  
		}
  
		// Uniforms (refresh uniforms objects)
  
		function refreshUniformsCommon( uniforms, material ) {
  
			uniforms.opacity.value = material.opacity;
  
			if ( material.color ) {
  
				uniforms.diffuse.value = material.color;
  
			}
  
			if ( material.emissive ) {
  
				uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity );
  
			}
  
			if ( material.map ) {
  
				uniforms.map.value = material.map;
  
			}
  
			if ( material.alphaMap ) {
  
				uniforms.alphaMap.value = material.alphaMap;
  
			}
  
			if ( material.specularMap ) {
  
				uniforms.specularMap.value = material.specularMap;
  
			}
  
			if ( material.envMap ) {
  
				uniforms.envMap.value = material.envMap;
  
				// don't flip CubeTexture envMaps, flip everything else:
				//  WebGLRenderTargetCube will be flipped for backwards compatibility
				//  WebGLRenderTargetCube.texture will be flipped because it's a Texture and NOT a CubeTexture
				// this check must be handled differently, or removed entirely, if WebGLRenderTargetCube uses a CubeTexture in the future
				uniforms.flipEnvMap.value = ( ! ( material.envMap && material.envMap.isCubeTexture ) ) ? 1 : - 1;
  
				uniforms.reflectivity.value = material.reflectivity;
				uniforms.refractionRatio.value = material.refractionRatio;
  
				uniforms.maxMipLevel.value = properties.get( material.envMap ).__maxMipLevel;
  
			}
  
			if ( material.lightMap ) {
  
				uniforms.lightMap.value = material.lightMap;
				uniforms.lightMapIntensity.value = material.lightMapIntensity;
  
			}
  
			if ( material.aoMap ) {
  
				uniforms.aoMap.value = material.aoMap;
				uniforms.aoMapIntensity.value = material.aoMapIntensity;
  
			}
  
			// uv repeat and offset setting priorities
			// 1. color map
			// 2. specular map
			// 3. normal map
			// 4. bump map
			// 5. alpha map
			// 6. emissive map
  
			var uvScaleMap;
  
			if ( material.map ) {
  
				uvScaleMap = material.map;
  
			} else if ( material.specularMap ) {
  
				uvScaleMap = material.specularMap;
  
			} else if ( material.displacementMap ) {
  
				uvScaleMap = material.displacementMap;
  
			} else if ( material.normalMap ) {
  
				uvScaleMap = material.normalMap;
  
			} else if ( material.bumpMap ) {
  
				uvScaleMap = material.bumpMap;
  
			} else if ( material.roughnessMap ) {
  
				uvScaleMap = material.roughnessMap;
  
			} else if ( material.metalnessMap ) {
  
				uvScaleMap = material.metalnessMap;
  
			} else if ( material.alphaMap ) {
  
				uvScaleMap = material.alphaMap;
  
			} else if ( material.emissiveMap ) {
  
				uvScaleMap = material.emissiveMap;
  
			}
  
			if ( uvScaleMap !== undefined ) {
  
				// backwards compatibility
				if ( uvScaleMap.isWebGLRenderTarget ) {
  
					uvScaleMap = uvScaleMap.texture;
  
				}
  
				if ( uvScaleMap.matrixAutoUpdate === true ) {
  
					uvScaleMap.updateMatrix();
  
				}
  
				uniforms.uvTransform.value.copy( uvScaleMap.matrix );
  
			}
  
		}
  
		function refreshUniformsLine( uniforms, material ) {
  
			uniforms.diffuse.value = material.color;
			uniforms.opacity.value = material.opacity;
  
		}
  
		function refreshUniformsDash( uniforms, material ) {
  
			uniforms.dashSize.value = material.dashSize;
			uniforms.totalSize.value = material.dashSize + material.gapSize;
			uniforms.scale.value = material.scale;
  
		}
  
		function refreshUniformsPoints( uniforms, material ) {
  
			uniforms.diffuse.value = material.color;
			uniforms.opacity.value = material.opacity;
			uniforms.size.value = material.size * _pixelRatio;
			uniforms.scale.value = _height * 0.5;
  
			uniforms.map.value = material.map;
  
			if ( material.map !== null ) {
  
				if ( material.map.matrixAutoUpdate === true ) {
  
					material.map.updateMatrix();
  
				}
  
				uniforms.uvTransform.value.copy( material.map.matrix );
  
			}
  
		}
  
		function refreshUniformsFog( uniforms, fog ) {
  
			uniforms.fogColor.value = fog.color;
  
			if ( fog.isFog ) {
  
				uniforms.fogNear.value = fog.near;
				uniforms.fogFar.value = fog.far;
  
			} else if ( fog.isFogExp2 ) {
  
				uniforms.fogDensity.value = fog.density;
  
			}
  
		}
  
		function refreshUniformsLambert( uniforms, material ) {
  
			if ( material.emissiveMap ) {
  
				uniforms.emissiveMap.value = material.emissiveMap;
  
			}
  
		}
  
		function refreshUniformsPhong( uniforms, material ) {
  
			uniforms.specular.value = material.specular;
			uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 )
  
			if ( material.emissiveMap ) {
  
				uniforms.emissiveMap.value = material.emissiveMap;
  
			}
  
			if ( material.bumpMap ) {
  
				uniforms.bumpMap.value = material.bumpMap;
				uniforms.bumpScale.value = material.bumpScale;
				if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1;
  
			}
  
			if ( material.normalMap ) {
  
				uniforms.normalMap.value = material.normalMap;
				uniforms.normalScale.value.copy( material.normalScale );
				if ( material.side === BackSide ) uniforms.normalScale.value.negate();
  
			}
  
			if ( material.displacementMap ) {
  
				uniforms.displacementMap.value = material.displacementMap;
				uniforms.displacementScale.value = material.displacementScale;
				uniforms.displacementBias.value = material.displacementBias;
  
			}
  
		}
  
		function refreshUniformsToon( uniforms, material ) {
  
			refreshUniformsPhong( uniforms, material );
  
			if ( material.gradientMap ) {
  
				uniforms.gradientMap.value = material.gradientMap;
  
			}
  
		}
  
		function refreshUniformsStandard( uniforms, material ) {
  
			uniforms.roughness.value = material.roughness;
			uniforms.metalness.value = material.metalness;
  
			if ( material.roughnessMap ) {
  
				uniforms.roughnessMap.value = material.roughnessMap;
  
			}
  
			if ( material.metalnessMap ) {
  
				uniforms.metalnessMap.value = material.metalnessMap;
  
			}
  
			if ( material.emissiveMap ) {
  
				uniforms.emissiveMap.value = material.emissiveMap;
  
			}
  
			if ( material.bumpMap ) {
  
				uniforms.bumpMap.value = material.bumpMap;
				uniforms.bumpScale.value = material.bumpScale;
				if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1;
  
			}
  
			if ( material.normalMap ) {
  
				uniforms.normalMap.value = material.normalMap;
				uniforms.normalScale.value.copy( material.normalScale );
				if ( material.side === BackSide ) uniforms.normalScale.value.negate();
  
			}
  
			if ( material.displacementMap ) {
  
				uniforms.displacementMap.value = material.displacementMap;
				uniforms.displacementScale.value = material.displacementScale;
				uniforms.displacementBias.value = material.displacementBias;
  
			}
  
			if ( material.envMap ) {
  
				//uniforms.envMap.value = material.envMap; // part of uniforms common
				uniforms.envMapIntensity.value = material.envMapIntensity;
  
			}
  
		}
  
		function refreshUniformsPhysical( uniforms, material ) {
  
			uniforms.clearCoat.value = material.clearCoat;
			uniforms.clearCoatRoughness.value = material.clearCoatRoughness;
  
			refreshUniformsStandard( uniforms, material );
  
		}
  
		function refreshUniformsDepth( uniforms, material ) {
  
			if ( material.displacementMap ) {
  
				uniforms.displacementMap.value = material.displacementMap;
				uniforms.displacementScale.value = material.displacementScale;
				uniforms.displacementBias.value = material.displacementBias;
  
			}
  
		}
  
		function refreshUniformsDistance( uniforms, material ) {
  
			if ( material.displacementMap ) {
  
				uniforms.displacementMap.value = material.displacementMap;
				uniforms.displacementScale.value = material.displacementScale;
				uniforms.displacementBias.value = material.displacementBias;
  
			}
  
			uniforms.referencePosition.value.copy( material.referencePosition );
			uniforms.nearDistance.value = material.nearDistance;
			uniforms.farDistance.value = material.farDistance;
  
		}
  
		function refreshUniformsNormal( uniforms, material ) {
  
			if ( material.bumpMap ) {
  
				uniforms.bumpMap.value = material.bumpMap;
				uniforms.bumpScale.value = material.bumpScale;
				if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1;
  
			}
  
			if ( material.normalMap ) {
  
				uniforms.normalMap.value = material.normalMap;
				uniforms.normalScale.value.copy( material.normalScale );
				if ( material.side === BackSide ) uniforms.normalScale.value.negate();
  
			}
  
			if ( material.displacementMap ) {
  
				uniforms.displacementMap.value = material.displacementMap;
				uniforms.displacementScale.value = material.displacementScale;
				uniforms.displacementBias.value = material.displacementBias;
  
			}
  
		}
  
		// If uniforms are marked as clean, they don't need to be loaded to the GPU.
  
		function markUniformsLightsNeedsUpdate( uniforms, value ) {
  
			uniforms.ambientLightColor.needsUpdate = value;
  
			uniforms.directionalLights.needsUpdate = value;
			uniforms.pointLights.needsUpdate = value;
			uniforms.spotLights.needsUpdate = value;
			uniforms.rectAreaLights.needsUpdate = value;
			uniforms.hemisphereLights.needsUpdate = value;
  
		}
  
		// Textures
  
		function allocTextureUnit() {
  
			var textureUnit = _usedTextureUnits;
  
			if ( textureUnit >= capabilities.maxTextures ) {
  
				console.warn( 'THREE.WebGLRenderer: Trying to use ' + textureUnit + ' texture units while this GPU supports only ' + capabilities.maxTextures );
  
			}
  
			_usedTextureUnits += 1;
  
			return textureUnit;
  
		}
  
		this.allocTextureUnit = allocTextureUnit;
  
		// this.setTexture2D = setTexture2D;
		this.setTexture2D = ( function () {
  
			var warned = false;
  
			// backwards compatibility: peel texture.texture
			return function setTexture2D( texture, slot ) {
  
				if ( texture && texture.isWebGLRenderTarget ) {
  
					if ( ! warned ) {
  
						console.warn( "THREE.WebGLRenderer.setTexture2D: don't use render targets as textures. Use their .texture property instead." );
						warned = true;
  
					}
  
					texture = texture.texture;
  
				}
  
				textures.setTexture2D( texture, slot );
  
			};
  
		}() );
  
		this.setTexture = ( function () {
  
			var warned = false;
  
			return function setTexture( texture, slot ) {
  
				if ( ! warned ) {
  
					console.warn( "THREE.WebGLRenderer: .setTexture is deprecated, use setTexture2D instead." );
					warned = true;
  
				}
  
				textures.setTexture2D( texture, slot );
  
			};
  
		}() );
  
		this.setTextureCube = ( function () {
  
			var warned = false;
  
			return function setTextureCube( texture, slot ) {
  
				// backwards compatibility: peel texture.texture
				if ( texture && texture.isWebGLRenderTargetCube ) {
  
					if ( ! warned ) {
  
						console.warn( "THREE.WebGLRenderer.setTextureCube: don't use cube render targets as textures. Use their .texture property instead." );
						warned = true;
  
					}
  
					texture = texture.texture;
  
				}
  
				// currently relying on the fact that WebGLRenderTargetCube.texture is a Texture and NOT a CubeTexture
				// TODO: unify these code paths
				if ( ( texture && texture.isCubeTexture ) ||
					( Array.isArray( texture.image ) && texture.image.length === 6 ) ) {
  
					// CompressedTexture can have Array in image :/
  
					// this function alone should take care of cube textures
					textures.setTextureCube( texture, slot );
  
				} else {
  
					// assumed: texture property of THREE.WebGLRenderTargetCube
  
					textures.setTextureCubeDynamic( texture, slot );
  
				}
  
			};
  
		}() );
  
		//
  
		this.setFramebuffer = function ( value ) {
  
			_framebuffer = value;
  
		};
  
		this.getRenderTarget = function () {
  
			return _currentRenderTarget;
  
		};
  
		this.setRenderTarget = function ( renderTarget ) {
  
			_currentRenderTarget = renderTarget;
  
			if ( renderTarget && properties.get( renderTarget ).__webglFramebuffer === undefined ) {
  
				textures.setupRenderTarget( renderTarget );
  
			}
  
			var framebuffer = _framebuffer;
			var isCube = false;
  
			if ( renderTarget ) {
  
				var __webglFramebuffer = properties.get( renderTarget ).__webglFramebuffer;
  
				if ( renderTarget.isWebGLRenderTargetCube ) {
  
					framebuffer = __webglFramebuffer[ renderTarget.activeCubeFace ];
					isCube = true;
  
				} else {
  
					framebuffer = __webglFramebuffer;
  
				}
  
				_currentViewport.copy( renderTarget.viewport );
				_currentScissor.copy( renderTarget.scissor );
				_currentScissorTest = renderTarget.scissorTest;
  
			} else {
  
				_currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio );
				_currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio );
				_currentScissorTest = _scissorTest;
  
			}
  
			if ( _currentFramebuffer !== framebuffer ) {
  
				_gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );
				_currentFramebuffer = framebuffer;
  
			}
  
			state.viewport( _currentViewport );
			state.scissor( _currentScissor );
			state.setScissorTest( _currentScissorTest );
  
			if ( isCube ) {
  
				var textureProperties = properties.get( renderTarget.texture );
				_gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + renderTarget.activeCubeFace, textureProperties.__webglTexture, renderTarget.activeMipMapLevel );
  
			}
  
		};
  
		this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer ) {
  
			if ( ! ( renderTarget && renderTarget.isWebGLRenderTarget ) ) {
  
				console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' );
				return;
  
			}
  
			var framebuffer = properties.get( renderTarget ).__webglFramebuffer;
  
			if ( framebuffer ) {
  
				var restore = false;
  
				if ( framebuffer !== _currentFramebuffer ) {
  
					_gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );
  
					restore = true;
  
				}
  
				try {
  
					var texture = renderTarget.texture;
					var textureFormat = texture.format;
					var textureType = texture.type;
  
					if ( textureFormat !== RGBAFormat && utils.convert( textureFormat ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_FORMAT ) ) {
  
						console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' );
						return;
  
					}
  
					if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_TYPE ) && // IE11, Edge and Chrome Mac < 52 (#9513)
						! ( textureType === FloatType && ( extensions.get( 'OES_texture_float' ) || extensions.get( 'WEBGL_color_buffer_float' ) ) ) && // Chrome Mac >= 52 and Firefox
						! ( textureType === HalfFloatType && extensions.get( 'EXT_color_buffer_half_float' ) ) ) {
  
						console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' );
						return;
  
					}
  
					if ( _gl.checkFramebufferStatus( _gl.FRAMEBUFFER ) === _gl.FRAMEBUFFER_COMPLETE ) {
  
						// the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604)
  
						if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) {
  
							_gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), buffer );
  
						}
  
					} else {
  
						console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.' );
  
					}
  
				} finally {
  
					if ( restore ) {
  
						_gl.bindFramebuffer( _gl.FRAMEBUFFER, _currentFramebuffer );
  
					}
  
				}
  
			}
  
		};
  
		this.copyFramebufferToTexture = function ( position, texture, level ) {
  
			var width = texture.image.width;
			var height = texture.image.height;
			var glFormat = utils.convert( texture.format );
  
			this.setTexture2D( texture, 0 );
  
			_gl.copyTexImage2D( _gl.TEXTURE_2D, level || 0, glFormat, position.x, position.y, width, height, 0 );
  
		};
  
		this.copyTextureToTexture = function ( position, srcTexture, dstTexture, level ) {
  
			var width = srcTexture.image.width;
			var height = srcTexture.image.height;
			var glFormat = utils.convert( dstTexture.format );
			var glType = utils.convert( dstTexture.type );
  
			this.setTexture2D( dstTexture, 0 );
  
			if ( srcTexture.isDataTexture ) {
  
				_gl.texSubImage2D( _gl.TEXTURE_2D, level || 0, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data );
  
			} else {
  
				_gl.texSubImage2D( _gl.TEXTURE_2D, level || 0, position.x, position.y, glFormat, glType, srcTexture.image );
  
			}
  
		};
  
	}
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 * @author alteredq / http://alteredqualia.com/
	 */
  
	function FogExp2( color, density ) {
  
		this.name = '';
  
		this.color = new Color( color );
		this.density = ( density !== undefined ) ? density : 0.00025;
  
	}
  
	FogExp2.prototype.isFogExp2 = true;
  
	FogExp2.prototype.clone = function () {
  
		return new FogExp2( this.color, this.density );
  
	};
  
	FogExp2.prototype.toJSON = function ( /* meta */ ) {
  
		return {
			type: 'FogExp2',
			color: this.color.getHex(),
			density: this.density
		};
  
	};
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 * @author alteredq / http://alteredqualia.com/
	 */
  
	function Fog( color, near, far ) {
  
		this.name = '';
  
		this.color = new Color( color );
  
		this.near = ( near !== undefined ) ? near : 1;
		this.far = ( far !== undefined ) ? far : 1000;
  
	}
  
	Fog.prototype.isFog = true;
  
	Fog.prototype.clone = function () {
  
		return new Fog( this.color, this.near, this.far );
  
	};
  
	Fog.prototype.toJSON = function ( /* meta */ ) {
  
		return {
			type: 'Fog',
			color: this.color.getHex(),
			near: this.near,
			far: this.far
		};
  
	};
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function Scene() {
  
		Object3D.call( this );
  
		this.type = 'Scene';
  
		this.background = null;
		this.fog = null;
		this.overrideMaterial = null;
  
		this.autoUpdate = true; // checked by the renderer
  
	}
  
	Scene.prototype = Object.assign( Object.create( Object3D.prototype ), {
  
		constructor: Scene,
  
		copy: function ( source, recursive ) {
  
			Object3D.prototype.copy.call( this, source, recursive );
  
			if ( source.background !== null ) this.background = source.background.clone();
			if ( source.fog !== null ) this.fog = source.fog.clone();
			if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone();
  
			this.autoUpdate = source.autoUpdate;
			this.matrixAutoUpdate = source.matrixAutoUpdate;
  
			return this;
  
		},
  
		toJSON: function ( meta ) {
  
			var data = Object3D.prototype.toJSON.call( this, meta );
  
			if ( this.background !== null ) data.object.background = this.background.toJSON( meta );
			if ( this.fog !== null ) data.object.fog = this.fog.toJSON();
  
			return data;
  
		}
  
	} );
  
	/**
	 * @author alteredq / http://alteredqualia.com/
	 *
	 * parameters = {
	 *  color: <hex>,
	 *  opacity: <float>,
	 *  map: new THREE.Texture( <Image> ),
	 *
	 *	uvOffset: new THREE.Vector2(),
	 *	uvScale: new THREE.Vector2()
	 * }
	 */
  
	function SpriteMaterial( parameters ) {
  
		Material.call( this );
  
		this.type = 'SpriteMaterial';
  
		this.color = new Color( 0xffffff );
		this.map = null;
  
		this.rotation = 0;
  
		this.fog = false;
		this.lights = false;
  
		this.setValues( parameters );
  
	}
  
	SpriteMaterial.prototype = Object.create( Material.prototype );
	SpriteMaterial.prototype.constructor = SpriteMaterial;
	SpriteMaterial.prototype.isSpriteMaterial = true;
  
	SpriteMaterial.prototype.copy = function ( source ) {
  
		Material.prototype.copy.call( this, source );
  
		this.color.copy( source.color );
		this.map = source.map;
  
		this.rotation = source.rotation;
  
		return this;
  
	};
  
	/**
	 * @author mikael emtinger / http://gomo.se/
	 * @author alteredq / http://alteredqualia.com/
	 */
  
	function Sprite( material ) {
  
		Object3D.call( this );
  
		this.type = 'Sprite';
  
		this.material = ( material !== undefined ) ? material : new SpriteMaterial();
  
		this.center = new Vector2( 0.5, 0.5 );
  
	}
  
	Sprite.prototype = Object.assign( Object.create( Object3D.prototype ), {
  
		constructor: Sprite,
  
		isSprite: true,
  
		raycast: ( function () {
  
			var intersectPoint = new Vector3();
			var worldPosition = new Vector3();
			var worldScale = new Vector3();
  
			return function raycast( raycaster, intersects ) {
  
				worldPosition.setFromMatrixPosition( this.matrixWorld );
				raycaster.ray.closestPointToPoint( worldPosition, intersectPoint );
  
				worldScale.setFromMatrixScale( this.matrixWorld );
				var guessSizeSq = worldScale.x * worldScale.y / 4;
  
				if ( worldPosition.distanceToSquared( intersectPoint ) > guessSizeSq ) return;
  
				var distance = raycaster.ray.origin.distanceTo( intersectPoint );
  
				if ( distance < raycaster.near || distance > raycaster.far ) return;
  
				intersects.push( {
  
					distance: distance,
					point: intersectPoint.clone(),
					face: null,
					object: this
  
				} );
  
			};
  
		}() ),
  
		clone: function () {
  
			return new this.constructor( this.material ).copy( this );
  
		},
  
		copy: function ( source ) {
  
			Object3D.prototype.copy.call( this, source );
  
			if ( source.center !== undefined ) this.center.copy( source.center );
  
			return this;
  
		}
  
  
	} );
  
	/**
	 * @author mikael emtinger / http://gomo.se/
	 * @author alteredq / http://alteredqualia.com/
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function LOD() {
  
		Object3D.call( this );
  
		this.type = 'LOD';
  
		Object.defineProperties( this, {
			levels: {
				enumerable: true,
				value: []
			}
		} );
  
	}
  
	LOD.prototype = Object.assign( Object.create( Object3D.prototype ), {
  
		constructor: LOD,
  
		copy: function ( source ) {
  
			Object3D.prototype.copy.call( this, source, false );
  
			var levels = source.levels;
  
			for ( var i = 0, l = levels.length; i < l; i ++ ) {
  
				var level = levels[ i ];
  
				this.addLevel( level.object.clone(), level.distance );
  
			}
  
			return this;
  
		},
  
		addLevel: function ( object, distance ) {
  
			if ( distance === undefined ) distance = 0;
  
			distance = Math.abs( distance );
  
			var levels = this.levels;
  
			for ( var l = 0; l < levels.length; l ++ ) {
  
				if ( distance < levels[ l ].distance ) {
  
					break;
  
				}
  
			}
  
			levels.splice( l, 0, { distance: distance, object: object } );
  
			this.add( object );
  
		},
  
		getObjectForDistance: function ( distance ) {
  
			var levels = this.levels;
  
			for ( var i = 1, l = levels.length; i < l; i ++ ) {
  
				if ( distance < levels[ i ].distance ) {
  
					break;
  
				}
  
			}
  
			return levels[ i - 1 ].object;
  
		},
  
		raycast: ( function () {
  
			var matrixPosition = new Vector3();
  
			return function raycast( raycaster, intersects ) {
  
				matrixPosition.setFromMatrixPosition( this.matrixWorld );
  
				var distance = raycaster.ray.origin.distanceTo( matrixPosition );
  
				this.getObjectForDistance( distance ).raycast( raycaster, intersects );
  
			};
  
		}() ),
  
		update: function () {
  
			var v1 = new Vector3();
			var v2 = new Vector3();
  
			return function update( camera ) {
  
				var levels = this.levels;
  
				if ( levels.length > 1 ) {
  
					v1.setFromMatrixPosition( camera.matrixWorld );
					v2.setFromMatrixPosition( this.matrixWorld );
  
					var distance = v1.distanceTo( v2 );
  
					levels[ 0 ].object.visible = true;
  
					for ( var i = 1, l = levels.length; i < l; i ++ ) {
  
						if ( distance >= levels[ i ].distance ) {
  
							levels[ i - 1 ].object.visible = false;
							levels[ i ].object.visible = true;
  
						} else {
  
							break;
  
						}
  
					}
  
					for ( ; i < l; i ++ ) {
  
						levels[ i ].object.visible = false;
  
					}
  
				}
  
			};
  
		}(),
  
		toJSON: function ( meta ) {
  
			var data = Object3D.prototype.toJSON.call( this, meta );
  
			data.object.levels = [];
  
			var levels = this.levels;
  
			for ( var i = 0, l = levels.length; i < l; i ++ ) {
  
				var level = levels[ i ];
  
				data.object.levels.push( {
					object: level.object.uuid,
					distance: level.distance
				} );
  
			}
  
			return data;
  
		}
  
	} );
  
	/**
	 * @author mikael emtinger / http://gomo.se/
	 * @author alteredq / http://alteredqualia.com/
	 * @author michael guerrero / http://realitymeltdown.com
	 * @author ikerr / http://verold.com
	 */
  
	function Skeleton( bones, boneInverses ) {
  
		// copy the bone array
  
		bones = bones || [];
  
		this.bones = bones.slice( 0 );
		this.boneMatrices = new Float32Array( this.bones.length * 16 );
  
		// use the supplied bone inverses or calculate the inverses
  
		if ( boneInverses === undefined ) {
  
			this.calculateInverses();
  
		} else {
  
			if ( this.bones.length === boneInverses.length ) {
  
				this.boneInverses = boneInverses.slice( 0 );
  
			} else {
  
				console.warn( 'THREE.Skeleton boneInverses is the wrong length.' );
  
				this.boneInverses = [];
  
				for ( var i = 0, il = this.bones.length; i < il; i ++ ) {
  
					this.boneInverses.push( new Matrix4() );
  
				}
  
			}
  
		}
  
	}
  
	Object.assign( Skeleton.prototype, {
  
		calculateInverses: function () {
  
			this.boneInverses = [];
  
			for ( var i = 0, il = this.bones.length; i < il; i ++ ) {
  
				var inverse = new Matrix4();
  
				if ( this.bones[ i ] ) {
  
					inverse.getInverse( this.bones[ i ].matrixWorld );
  
				}
  
				this.boneInverses.push( inverse );
  
			}
  
		},
  
		pose: function () {
  
			var bone, i, il;
  
			// recover the bind-time world matrices
  
			for ( i = 0, il = this.bones.length; i < il; i ++ ) {
  
				bone = this.bones[ i ];
  
				if ( bone ) {
  
					bone.matrixWorld.getInverse( this.boneInverses[ i ] );
  
				}
  
			}
  
			// compute the local matrices, positions, rotations and scales
  
			for ( i = 0, il = this.bones.length; i < il; i ++ ) {
  
				bone = this.bones[ i ];
  
				if ( bone ) {
  
					if ( bone.parent && bone.parent.isBone ) {
  
						bone.matrix.getInverse( bone.parent.matrixWorld );
						bone.matrix.multiply( bone.matrixWorld );
  
					} else {
  
						bone.matrix.copy( bone.matrixWorld );
  
					}
  
					bone.matrix.decompose( bone.position, bone.quaternion, bone.scale );
  
				}
  
			}
  
		},
  
		update: ( function () {
  
			var offsetMatrix = new Matrix4();
			var identityMatrix = new Matrix4();
  
			return function update() {
  
				var bones = this.bones;
				var boneInverses = this.boneInverses;
				var boneMatrices = this.boneMatrices;
				var boneTexture = this.boneTexture;
  
				// flatten bone matrices to array
  
				for ( var i = 0, il = bones.length; i < il; i ++ ) {
  
					// compute the offset between the current and the original transform
  
					var matrix = bones[ i ] ? bones[ i ].matrixWorld : identityMatrix;
  
					offsetMatrix.multiplyMatrices( matrix, boneInverses[ i ] );
					offsetMatrix.toArray( boneMatrices, i * 16 );
  
				}
  
				if ( boneTexture !== undefined ) {
  
					boneTexture.needsUpdate = true;
  
				}
  
			};
  
		} )(),
  
		clone: function () {
  
			return new Skeleton( this.bones, this.boneInverses );
  
		},
  
		getBoneByName: function ( name ) {
  
			for ( var i = 0, il = this.bones.length; i < il; i ++ ) {
  
				var bone = this.bones[ i ];
  
				if ( bone.name === name ) {
  
					return bone;
  
				}
  
			}
  
			return undefined;
  
		}
  
	} );
  
	/**
	 * @author mikael emtinger / http://gomo.se/
	 * @author alteredq / http://alteredqualia.com/
	 * @author ikerr / http://verold.com
	 */
  
	function Bone() {
  
		Object3D.call( this );
  
		this.type = 'Bone';
  
	}
  
	Bone.prototype = Object.assign( Object.create( Object3D.prototype ), {
  
		constructor: Bone,
  
		isBone: true
  
	} );
  
	/**
	 * @author mikael emtinger / http://gomo.se/
	 * @author alteredq / http://alteredqualia.com/
	 * @author ikerr / http://verold.com
	 */
  
	function SkinnedMesh( geometry, material ) {
  
		Mesh.call( this, geometry, material );
  
		this.type = 'SkinnedMesh';
  
		this.bindMode = 'attached';
		this.bindMatrix = new Matrix4();
		this.bindMatrixInverse = new Matrix4();
  
		var bones = this.initBones();
		var skeleton = new Skeleton( bones );
  
		this.bind( skeleton, this.matrixWorld );
  
		this.normalizeSkinWeights();
  
	}
  
	SkinnedMesh.prototype = Object.assign( Object.create( Mesh.prototype ), {
  
		constructor: SkinnedMesh,
  
		isSkinnedMesh: true,
  
		initBones: function () {
  
			var bones = [], bone, gbone;
			var i, il;
  
			if ( this.geometry && this.geometry.bones !== undefined ) {
  
				// first, create array of 'Bone' objects from geometry data
  
				for ( i = 0, il = this.geometry.bones.length; i < il; i ++ ) {
  
					gbone = this.geometry.bones[ i ];
  
					// create new 'Bone' object
  
					bone = new Bone();
					bones.push( bone );
  
					// apply values
  
					bone.name = gbone.name;
					bone.position.fromArray( gbone.pos );
					bone.quaternion.fromArray( gbone.rotq );
					if ( gbone.scl !== undefined ) bone.scale.fromArray( gbone.scl );
  
				}
  
				// second, create bone hierarchy
  
				for ( i = 0, il = this.geometry.bones.length; i < il; i ++ ) {
  
					gbone = this.geometry.bones[ i ];
  
					if ( ( gbone.parent !== - 1 ) && ( gbone.parent !== null ) && ( bones[ gbone.parent ] !== undefined ) ) {
  
						// subsequent bones in the hierarchy
  
						bones[ gbone.parent ].add( bones[ i ] );
  
					} else {
  
						// topmost bone, immediate child of the skinned mesh
  
						this.add( bones[ i ] );
  
					}
  
				}
  
			}
  
			// now the bones are part of the scene graph and children of the skinned mesh.
			// let's update the corresponding matrices
  
			this.updateMatrixWorld( true );
  
			return bones;
  
		},
  
		bind: function ( skeleton, bindMatrix ) {
  
			this.skeleton = skeleton;
  
			if ( bindMatrix === undefined ) {
  
				this.updateMatrixWorld( true );
  
				this.skeleton.calculateInverses();
  
				bindMatrix = this.matrixWorld;
  
			}
  
			this.bindMatrix.copy( bindMatrix );
			this.bindMatrixInverse.getInverse( bindMatrix );
  
		},
  
		pose: function () {
  
			this.skeleton.pose();
  
		},
  
		normalizeSkinWeights: function () {
  
			var scale, i;
  
			if ( this.geometry && this.geometry.isGeometry ) {
  
				for ( i = 0; i < this.geometry.skinWeights.length; i ++ ) {
  
					var sw = this.geometry.skinWeights[ i ];
  
					scale = 1.0 / sw.manhattanLength();
  
					if ( scale !== Infinity ) {
  
						sw.multiplyScalar( scale );
  
					} else {
  
						sw.set( 1, 0, 0, 0 ); // do something reasonable
  
					}
  
				}
  
			} else if ( this.geometry && this.geometry.isBufferGeometry ) {
  
				var vec = new Vector4();
  
				var skinWeight = this.geometry.attributes.skinWeight;
  
				for ( i = 0; i < skinWeight.count; i ++ ) {
  
					vec.x = skinWeight.getX( i );
					vec.y = skinWeight.getY( i );
					vec.z = skinWeight.getZ( i );
					vec.w = skinWeight.getW( i );
  
					scale = 1.0 / vec.manhattanLength();
  
					if ( scale !== Infinity ) {
  
						vec.multiplyScalar( scale );
  
					} else {
  
						vec.set( 1, 0, 0, 0 ); // do something reasonable
  
					}
  
					skinWeight.setXYZW( i, vec.x, vec.y, vec.z, vec.w );
  
				}
  
			}
  
		},
  
		updateMatrixWorld: function ( force ) {
  
			Mesh.prototype.updateMatrixWorld.call( this, force );
  
			if ( this.bindMode === 'attached' ) {
  
				this.bindMatrixInverse.getInverse( this.matrixWorld );
  
			} else if ( this.bindMode === 'detached' ) {
  
				this.bindMatrixInverse.getInverse( this.bindMatrix );
  
			} else {
  
				console.warn( 'THREE.SkinnedMesh: Unrecognized bindMode: ' + this.bindMode );
  
			}
  
		},
  
		clone: function () {
  
			return new this.constructor( this.geometry, this.material ).copy( this );
  
		}
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 * @author alteredq / http://alteredqualia.com/
	 *
	 * parameters = {
	 *  color: <hex>,
	 *  opacity: <float>,
	 *
	 *  linewidth: <float>,
	 *  linecap: "round",
	 *  linejoin: "round"
	 * }
	 */
  
	function LineBasicMaterial( parameters ) {
  
		Material.call( this );
  
		this.type = 'LineBasicMaterial';
  
		this.color = new Color( 0xffffff );
  
		this.linewidth = 1;
		this.linecap = 'round';
		this.linejoin = 'round';
  
		this.lights = false;
  
		this.setValues( parameters );
  
	}
  
	LineBasicMaterial.prototype = Object.create( Material.prototype );
	LineBasicMaterial.prototype.constructor = LineBasicMaterial;
  
	LineBasicMaterial.prototype.isLineBasicMaterial = true;
  
	LineBasicMaterial.prototype.copy = function ( source ) {
  
		Material.prototype.copy.call( this, source );
  
		this.color.copy( source.color );
  
		this.linewidth = source.linewidth;
		this.linecap = source.linecap;
		this.linejoin = source.linejoin;
  
		return this;
  
	};
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function Line( geometry, material, mode ) {
  
		if ( mode === 1 ) {
  
			console.warn( 'THREE.Line: parameter THREE.LinePieces no longer supported. Created THREE.LineSegments instead.' );
			return new LineSegments( geometry, material );
  
		}
  
		Object3D.call( this );
  
		this.type = 'Line';
  
		this.geometry = geometry !== undefined ? geometry : new BufferGeometry();
		this.material = material !== undefined ? material : new LineBasicMaterial( { color: Math.random() * 0xffffff } );
  
	}
  
	Line.prototype = Object.assign( Object.create( Object3D.prototype ), {
  
		constructor: Line,
  
		isLine: true,
  
		computeLineDistances: ( function () {
  
			var start = new Vector3();
			var end = new Vector3();
  
			return function computeLineDistances() {
  
				var geometry = this.geometry;
  
				if ( geometry.isBufferGeometry ) {
  
					// we assume non-indexed geometry
  
					if ( geometry.index === null ) {
  
						var positionAttribute = geometry.attributes.position;
						var lineDistances = [ 0 ];
  
						for ( var i = 1, l = positionAttribute.count; i < l; i ++ ) {
  
							start.fromBufferAttribute( positionAttribute, i - 1 );
							end.fromBufferAttribute( positionAttribute, i );
  
							lineDistances[ i ] = lineDistances[ i - 1 ];
							lineDistances[ i ] += start.distanceTo( end );
  
						}
  
						geometry.addAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) );
  
					} else {
  
						console.warn( 'THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' );
  
					}
  
				} else if ( geometry.isGeometry ) {
  
					var vertices = geometry.vertices;
					var lineDistances = geometry.lineDistances;
  
					lineDistances[ 0 ] = 0;
  
					for ( var i = 1, l = vertices.length; i < l; i ++ ) {
  
						lineDistances[ i ] = lineDistances[ i - 1 ];
						lineDistances[ i ] += vertices[ i - 1 ].distanceTo( vertices[ i ] );
  
					}
  
				}
  
				return this;
  
			};
  
		}() ),
  
		raycast: ( function () {
  
			var inverseMatrix = new Matrix4();
			var ray = new Ray();
			var sphere = new Sphere();
  
			return function raycast( raycaster, intersects ) {
  
				var precision = raycaster.linePrecision;
				var precisionSq = precision * precision;
  
				var geometry = this.geometry;
				var matrixWorld = this.matrixWorld;
  
				// Checking boundingSphere distance to ray
  
				if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();
  
				sphere.copy( geometry.boundingSphere );
				sphere.applyMatrix4( matrixWorld );
  
				if ( raycaster.ray.intersectsSphere( sphere ) === false ) return;
  
				//
  
				inverseMatrix.getInverse( matrixWorld );
				ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix );
  
				var vStart = new Vector3();
				var vEnd = new Vector3();
				var interSegment = new Vector3();
				var interRay = new Vector3();
				var step = ( this && this.isLineSegments ) ? 2 : 1;
  
				if ( geometry.isBufferGeometry ) {
  
					var index = geometry.index;
					var attributes = geometry.attributes;
					var positions = attributes.position.array;
  
					if ( index !== null ) {
  
						var indices = index.array;
  
						for ( var i = 0, l = indices.length - 1; i < l; i += step ) {
  
							var a = indices[ i ];
							var b = indices[ i + 1 ];
  
							vStart.fromArray( positions, a * 3 );
							vEnd.fromArray( positions, b * 3 );
  
							var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment );
  
							if ( distSq > precisionSq ) continue;
  
							interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation
  
							var distance = raycaster.ray.origin.distanceTo( interRay );
  
							if ( distance < raycaster.near || distance > raycaster.far ) continue;
  
							intersects.push( {
  
								distance: distance,
								// What do we want? intersection point on the ray or on the segment??
								// point: raycaster.ray.at( distance ),
								point: interSegment.clone().applyMatrix4( this.matrixWorld ),
								index: i,
								face: null,
								faceIndex: null,
								object: this
  
							} );
  
						}
  
					} else {
  
						for ( var i = 0, l = positions.length / 3 - 1; i < l; i += step ) {
  
							vStart.fromArray( positions, 3 * i );
							vEnd.fromArray( positions, 3 * i + 3 );
  
							var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment );
  
							if ( distSq > precisionSq ) continue;
  
							interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation
  
							var distance = raycaster.ray.origin.distanceTo( interRay );
  
							if ( distance < raycaster.near || distance > raycaster.far ) continue;
  
							intersects.push( {
  
								distance: distance,
								// What do we want? intersection point on the ray or on the segment??
								// point: raycaster.ray.at( distance ),
								point: interSegment.clone().applyMatrix4( this.matrixWorld ),
								index: i,
								face: null,
								faceIndex: null,
								object: this
  
							} );
  
						}
  
					}
  
				} else if ( geometry.isGeometry ) {
  
					var vertices = geometry.vertices;
					var nbVertices = vertices.length;
  
					for ( var i = 0; i < nbVertices - 1; i += step ) {
  
						var distSq = ray.distanceSqToSegment( vertices[ i ], vertices[ i + 1 ], interRay, interSegment );
  
						if ( distSq > precisionSq ) continue;
  
						interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation
  
						var distance = raycaster.ray.origin.distanceTo( interRay );
  
						if ( distance < raycaster.near || distance > raycaster.far ) continue;
  
						intersects.push( {
  
							distance: distance,
							// What do we want? intersection point on the ray or on the segment??
							// point: raycaster.ray.at( distance ),
							point: interSegment.clone().applyMatrix4( this.matrixWorld ),
							index: i,
							face: null,
							faceIndex: null,
							object: this
  
						} );
  
					}
  
				}
  
			};
  
		}() ),
  
		clone: function () {
  
			return new this.constructor( this.geometry, this.material ).copy( this );
  
		}
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function LineSegments( geometry, material ) {
  
		Line.call( this, geometry, material );
  
		this.type = 'LineSegments';
  
	}
  
	LineSegments.prototype = Object.assign( Object.create( Line.prototype ), {
  
		constructor: LineSegments,
  
		isLineSegments: true,
  
		computeLineDistances: ( function () {
  
			var start = new Vector3();
			var end = new Vector3();
  
			return function computeLineDistances() {
  
				var geometry = this.geometry;
  
				if ( geometry.isBufferGeometry ) {
  
					// we assume non-indexed geometry
  
					if ( geometry.index === null ) {
  
						var positionAttribute = geometry.attributes.position;
						var lineDistances = [];
  
						for ( var i = 0, l = positionAttribute.count; i < l; i += 2 ) {
  
							start.fromBufferAttribute( positionAttribute, i );
							end.fromBufferAttribute( positionAttribute, i + 1 );
  
							lineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ];
							lineDistances[ i + 1 ] = lineDistances[ i ] + start.distanceTo( end );
  
						}
  
						geometry.addAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) );
  
					} else {
  
						console.warn( 'THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' );
  
					}
  
				} else if ( geometry.isGeometry ) {
  
					var vertices = geometry.vertices;
					var lineDistances = geometry.lineDistances;
  
					for ( var i = 0, l = vertices.length; i < l; i += 2 ) {
  
						start.copy( vertices[ i ] );
						end.copy( vertices[ i + 1 ] );
  
						lineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ];
						lineDistances[ i + 1 ] = lineDistances[ i ] + start.distanceTo( end );
  
					}
  
				}
  
				return this;
  
			};
  
		}() )
  
	} );
  
	/**
	 * @author mgreter / http://github.com/mgreter
	 */
  
	function LineLoop( geometry, material ) {
  
		Line.call( this, geometry, material );
  
		this.type = 'LineLoop';
  
	}
  
	LineLoop.prototype = Object.assign( Object.create( Line.prototype ), {
  
		constructor: LineLoop,
  
		isLineLoop: true,
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 * @author alteredq / http://alteredqualia.com/
	 *
	 * parameters = {
	 *  color: <hex>,
	 *  opacity: <float>,
	 *  map: new THREE.Texture( <Image> ),
	 *
	 *  size: <float>,
	 *  sizeAttenuation: <bool>
	 *
	 *  morphTargets: <bool>
	 * }
	 */
  
	function PointsMaterial( parameters ) {
  
		Material.call( this );
  
		this.type = 'PointsMaterial';
  
		this.color = new Color( 0xffffff );
  
		this.map = null;
  
		this.size = 1;
		this.sizeAttenuation = true;
  
		this.morphTargets = false;
  
		this.lights = false;
  
		this.setValues( parameters );
  
	}
  
	PointsMaterial.prototype = Object.create( Material.prototype );
	PointsMaterial.prototype.constructor = PointsMaterial;
  
	PointsMaterial.prototype.isPointsMaterial = true;
  
	PointsMaterial.prototype.copy = function ( source ) {
  
		Material.prototype.copy.call( this, source );
  
		this.color.copy( source.color );
  
		this.map = source.map;
  
		this.size = source.size;
		this.sizeAttenuation = source.sizeAttenuation;
  
		this.morphTargets = source.morphTargets;
  
		return this;
  
	};
  
	/**
	 * @author alteredq / http://alteredqualia.com/
	 */
  
	function Points( geometry, material ) {
  
		Object3D.call( this );
  
		this.type = 'Points';
  
		this.geometry = geometry !== undefined ? geometry : new BufferGeometry();
		this.material = material !== undefined ? material : new PointsMaterial( { color: Math.random() * 0xffffff } );
  
	}
  
	Points.prototype = Object.assign( Object.create( Object3D.prototype ), {
  
		constructor: Points,
  
		isPoints: true,
  
		raycast: ( function () {
  
			var inverseMatrix = new Matrix4();
			var ray = new Ray();
			var sphere = new Sphere();
  
			return function raycast( raycaster, intersects ) {
  
				var object = this;
				var geometry = this.geometry;
				var matrixWorld = this.matrixWorld;
				var threshold = raycaster.params.Points.threshold;
  
				// Checking boundingSphere distance to ray
  
				if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();
  
				sphere.copy( geometry.boundingSphere );
				sphere.applyMatrix4( matrixWorld );
				sphere.radius += threshold;
  
				if ( raycaster.ray.intersectsSphere( sphere ) === false ) return;
  
				//
  
				inverseMatrix.getInverse( matrixWorld );
				ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix );
  
				var localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 );
				var localThresholdSq = localThreshold * localThreshold;
				var position = new Vector3();
				var intersectPoint = new Vector3();
  
				function testPoint( point, index ) {
  
					var rayPointDistanceSq = ray.distanceSqToPoint( point );
  
					if ( rayPointDistanceSq < localThresholdSq ) {
  
						ray.closestPointToPoint( point, intersectPoint );
						intersectPoint.applyMatrix4( matrixWorld );
  
						var distance = raycaster.ray.origin.distanceTo( intersectPoint );
  
						if ( distance < raycaster.near || distance > raycaster.far ) return;
  
						intersects.push( {
  
							distance: distance,
							distanceToRay: Math.sqrt( rayPointDistanceSq ),
							point: intersectPoint.clone(),
							index: index,
							face: null,
							object: object
  
						} );
  
					}
  
				}
  
				if ( geometry.isBufferGeometry ) {
  
					var index = geometry.index;
					var attributes = geometry.attributes;
					var positions = attributes.position.array;
  
					if ( index !== null ) {
  
						var indices = index.array;
  
						for ( var i = 0, il = indices.length; i < il; i ++ ) {
  
							var a = indices[ i ];
  
							position.fromArray( positions, a * 3 );
  
							testPoint( position, a );
  
						}
  
					} else {
  
						for ( var i = 0, l = positions.length / 3; i < l; i ++ ) {
  
							position.fromArray( positions, i * 3 );
  
							testPoint( position, i );
  
						}
  
					}
  
				} else {
  
					var vertices = geometry.vertices;
  
					for ( var i = 0, l = vertices.length; i < l; i ++ ) {
  
						testPoint( vertices[ i ], i );
  
					}
  
				}
  
			};
  
		}() ),
  
		clone: function () {
  
			return new this.constructor( this.geometry, this.material ).copy( this );
  
		}
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function Group() {
  
		Object3D.call( this );
  
		this.type = 'Group';
  
	}
  
	Group.prototype = Object.assign( Object.create( Object3D.prototype ), {
  
		constructor: Group,
  
		isGroup: true
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function VideoTexture( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {
  
		Texture.call( this, video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );
  
		this.generateMipmaps = false;
  
	}
  
	VideoTexture.prototype = Object.assign( Object.create( Texture.prototype ), {
  
		constructor: VideoTexture,
  
		isVideoTexture: true,
  
		update: function () {
  
			var video = this.image;
  
			if ( video.readyState >= video.HAVE_CURRENT_DATA ) {
  
				this.needsUpdate = true;
  
			}
  
		}
  
	} );
  
	/**
	 * @author alteredq / http://alteredqualia.com/
	 */
  
	function CompressedTexture( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) {
  
		Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding );
  
		this.image = { width: width, height: height };
		this.mipmaps = mipmaps;
  
		// no flipping for cube textures
		// (also flipping doesn't work for compressed textures )
  
		this.flipY = false;
  
		// can't generate mipmaps for compressed textures
		// mips must be embedded in DDS files
  
		this.generateMipmaps = false;
  
	}
  
	CompressedTexture.prototype = Object.create( Texture.prototype );
	CompressedTexture.prototype.constructor = CompressedTexture;
  
	CompressedTexture.prototype.isCompressedTexture = true;
  
	/**
	 * @author Matt DesLauriers / @mattdesl
	 * @author atix / arthursilber.de
	 */
  
	function DepthTexture( width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format ) {
  
		format = format !== undefined ? format : DepthFormat;
  
		if ( format !== DepthFormat && format !== DepthStencilFormat ) {
  
			throw new Error( 'DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat' );
  
		}
  
		if ( type === undefined && format === DepthFormat ) type = UnsignedShortType;
		if ( type === undefined && format === DepthStencilFormat ) type = UnsignedInt248Type;
  
		Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );
  
		this.image = { width: width, height: height };
  
		this.magFilter = magFilter !== undefined ? magFilter : NearestFilter;
		this.minFilter = minFilter !== undefined ? minFilter : NearestFilter;
  
		this.flipY = false;
		this.generateMipmaps	= false;
  
	}
  
	DepthTexture.prototype = Object.create( Texture.prototype );
	DepthTexture.prototype.constructor = DepthTexture;
	DepthTexture.prototype.isDepthTexture = true;
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 * @author Mugen87 / https://github.com/Mugen87
	 */
  
	function WireframeGeometry( geometry ) {
  
		BufferGeometry.call( this );
  
		this.type = 'WireframeGeometry';
  
		// buffer
  
		var vertices = [];
  
		// helper variables
  
		var i, j, l, o, ol;
		var edge = [ 0, 0 ], edges = {}, e, edge1, edge2;
		var key, keys = [ 'a', 'b', 'c' ];
		var vertex;
  
		// different logic for Geometry and BufferGeometry
  
		if ( geometry && geometry.isGeometry ) {
  
			// create a data structure that contains all edges without duplicates
  
			var faces = geometry.faces;
  
			for ( i = 0, l = faces.length; i < l; i ++ ) {
  
				var face = faces[ i ];
  
				for ( j = 0; j < 3; j ++ ) {
  
					edge1 = face[ keys[ j ] ];
					edge2 = face[ keys[ ( j + 1 ) % 3 ] ];
					edge[ 0 ] = Math.min( edge1, edge2 ); // sorting prevents duplicates
					edge[ 1 ] = Math.max( edge1, edge2 );
  
					key = edge[ 0 ] + ',' + edge[ 1 ];
  
					if ( edges[ key ] === undefined ) {
  
						edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ] };
  
					}
  
				}
  
			}
  
			// generate vertices
  
			for ( key in edges ) {
  
				e = edges[ key ];
  
				vertex = geometry.vertices[ e.index1 ];
				vertices.push( vertex.x, vertex.y, vertex.z );
  
				vertex = geometry.vertices[ e.index2 ];
				vertices.push( vertex.x, vertex.y, vertex.z );
  
			}
  
		} else if ( geometry && geometry.isBufferGeometry ) {
  
			var position, indices, groups;
			var group, start, count;
			var index1, index2;
  
			vertex = new Vector3();
  
			if ( geometry.index !== null ) {
  
				// indexed BufferGeometry
  
				position = geometry.attributes.position;
				indices = geometry.index;
				groups = geometry.groups;
  
				if ( groups.length === 0 ) {
  
					groups = [ { start: 0, count: indices.count, materialIndex: 0 } ];
  
				}
  
				// create a data structure that contains all eges without duplicates
  
				for ( o = 0, ol = groups.length; o < ol; ++ o ) {
  
					group = groups[ o ];
  
					start = group.start;
					count = group.count;
  
					for ( i = start, l = ( start + count ); i < l; i += 3 ) {
  
						for ( j = 0; j < 3; j ++ ) {
  
							edge1 = indices.getX( i + j );
							edge2 = indices.getX( i + ( j + 1 ) % 3 );
							edge[ 0 ] = Math.min( edge1, edge2 ); // sorting prevents duplicates
							edge[ 1 ] = Math.max( edge1, edge2 );
  
							key = edge[ 0 ] + ',' + edge[ 1 ];
  
							if ( edges[ key ] === undefined ) {
  
								edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ] };
  
							}
  
						}
  
					}
  
				}
  
				// generate vertices
  
				for ( key in edges ) {
  
					e = edges[ key ];
  
					vertex.fromBufferAttribute( position, e.index1 );
					vertices.push( vertex.x, vertex.y, vertex.z );
  
					vertex.fromBufferAttribute( position, e.index2 );
					vertices.push( vertex.x, vertex.y, vertex.z );
  
				}
  
			} else {
  
				// non-indexed BufferGeometry
  
				position = geometry.attributes.position;
  
				for ( i = 0, l = ( position.count / 3 ); i < l; i ++ ) {
  
					for ( j = 0; j < 3; j ++ ) {
  
						// three edges per triangle, an edge is represented as (index1, index2)
						// e.g. the first triangle has the following edges: (0,1),(1,2),(2,0)
  
						index1 = 3 * i + j;
						vertex.fromBufferAttribute( position, index1 );
						vertices.push( vertex.x, vertex.y, vertex.z );
  
						index2 = 3 * i + ( ( j + 1 ) % 3 );
						vertex.fromBufferAttribute( position, index2 );
						vertices.push( vertex.x, vertex.y, vertex.z );
  
					}
  
				}
  
			}
  
		}
  
		// build geometry
  
		this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
  
	}
  
	WireframeGeometry.prototype = Object.create( BufferGeometry.prototype );
	WireframeGeometry.prototype.constructor = WireframeGeometry;
  
	/**
	 * @author zz85 / https://github.com/zz85
	 * @author Mugen87 / https://github.com/Mugen87
	 *
	 * Parametric Surfaces Geometry
	 * based on the brilliant article by @prideout http://prideout.net/blog/?p=44
	 */
  
	// ParametricGeometry
  
	function ParametricGeometry( func, slices, stacks ) {
  
		Geometry.call( this );
  
		this.type = 'ParametricGeometry';
  
		this.parameters = {
			func: func,
			slices: slices,
			stacks: stacks
		};
  
		this.fromBufferGeometry( new ParametricBufferGeometry( func, slices, stacks ) );
		this.mergeVertices();
  
	}
  
	ParametricGeometry.prototype = Object.create( Geometry.prototype );
	ParametricGeometry.prototype.constructor = ParametricGeometry;
  
	// ParametricBufferGeometry
  
	function ParametricBufferGeometry( func, slices, stacks ) {
  
		BufferGeometry.call( this );
  
		this.type = 'ParametricBufferGeometry';
  
		this.parameters = {
			func: func,
			slices: slices,
			stacks: stacks
		};
  
		// buffers
  
		var indices = [];
		var vertices = [];
		var normals = [];
		var uvs = [];
  
		var EPS = 0.00001;
  
		var normal = new Vector3();
  
		var p0 = new Vector3(), p1 = new Vector3();
		var pu = new Vector3(), pv = new Vector3();
  
		var i, j;
  
		if ( func.length < 3 ) {
  
			console.error( 'THREE.ParametricGeometry: Function must now modify a Vector3 as third parameter.' );
  
		}
  
		// generate vertices, normals and uvs
  
		var sliceCount = slices + 1;
  
		for ( i = 0; i <= stacks; i ++ ) {
  
			var v = i / stacks;
  
			for ( j = 0; j <= slices; j ++ ) {
  
				var u = j / slices;
  
				// vertex
  
				func( u, v, p0 );
				vertices.push( p0.x, p0.y, p0.z );
  
				// normal
  
				// approximate tangent vectors via finite differences
  
				if ( u - EPS >= 0 ) {
  
					func( u - EPS, v, p1 );
					pu.subVectors( p0, p1 );
  
				} else {
  
					func( u + EPS, v, p1 );
					pu.subVectors( p1, p0 );
  
				}
  
				if ( v - EPS >= 0 ) {
  
					func( u, v - EPS, p1 );
					pv.subVectors( p0, p1 );
  
				} else {
  
					func( u, v + EPS, p1 );
					pv.subVectors( p1, p0 );
  
				}
  
				// cross product of tangent vectors returns surface normal
  
				normal.crossVectors( pu, pv ).normalize();
				normals.push( normal.x, normal.y, normal.z );
  
				// uv
  
				uvs.push( u, v );
  
			}
  
		}
  
		// generate indices
  
		for ( i = 0; i < stacks; i ++ ) {
  
			for ( j = 0; j < slices; j ++ ) {
  
				var a = i * sliceCount + j;
				var b = i * sliceCount + j + 1;
				var c = ( i + 1 ) * sliceCount + j + 1;
				var d = ( i + 1 ) * sliceCount + j;
  
				// faces one and two
  
				indices.push( a, b, d );
				indices.push( b, c, d );
  
			}
  
		}
  
		// build geometry
  
		this.setIndex( indices );
		this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
		this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
		this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
  
	}
  
	ParametricBufferGeometry.prototype = Object.create( BufferGeometry.prototype );
	ParametricBufferGeometry.prototype.constructor = ParametricBufferGeometry;
  
	/**
	 * @author clockworkgeek / https://github.com/clockworkgeek
	 * @author timothypratley / https://github.com/timothypratley
	 * @author WestLangley / http://github.com/WestLangley
	 * @author Mugen87 / https://github.com/Mugen87
	 */
  
	// PolyhedronGeometry
  
	function PolyhedronGeometry( vertices, indices, radius, detail ) {
  
		Geometry.call( this );
  
		this.type = 'PolyhedronGeometry';
  
		this.parameters = {
			vertices: vertices,
			indices: indices,
			radius: radius,
			detail: detail
		};
  
		this.fromBufferGeometry( new PolyhedronBufferGeometry( vertices, indices, radius, detail ) );
		this.mergeVertices();
  
	}
  
	PolyhedronGeometry.prototype = Object.create( Geometry.prototype );
	PolyhedronGeometry.prototype.constructor = PolyhedronGeometry;
  
	// PolyhedronBufferGeometry
  
	function PolyhedronBufferGeometry( vertices, indices, radius, detail ) {
  
		BufferGeometry.call( this );
  
		this.type = 'PolyhedronBufferGeometry';
  
		this.parameters = {
			vertices: vertices,
			indices: indices,
			radius: radius,
			detail: detail
		};
  
		radius = radius || 1;
		detail = detail || 0;
  
		// default buffer data
  
		var vertexBuffer = [];
		var uvBuffer = [];
  
		// the subdivision creates the vertex buffer data
  
		subdivide( detail );
  
		// all vertices should lie on a conceptual sphere with a given radius
  
		appplyRadius( radius );
  
		// finally, create the uv data
  
		generateUVs();
  
		// build non-indexed geometry
  
		this.addAttribute( 'position', new Float32BufferAttribute( vertexBuffer, 3 ) );
		this.addAttribute( 'normal', new Float32BufferAttribute( vertexBuffer.slice(), 3 ) );
		this.addAttribute( 'uv', new Float32BufferAttribute( uvBuffer, 2 ) );
  
		if ( detail === 0 ) {
  
			this.computeVertexNormals(); // flat normals
  
		} else {
  
			this.normalizeNormals(); // smooth normals
  
		}
  
		// helper functions
  
		function subdivide( detail ) {
  
			var a = new Vector3();
			var b = new Vector3();
			var c = new Vector3();
  
			// iterate over all faces and apply a subdivison with the given detail value
  
			for ( var i = 0; i < indices.length; i += 3 ) {
  
				// get the vertices of the face
  
				getVertexByIndex( indices[ i + 0 ], a );
				getVertexByIndex( indices[ i + 1 ], b );
				getVertexByIndex( indices[ i + 2 ], c );
  
				// perform subdivision
  
				subdivideFace( a, b, c, detail );
  
			}
  
		}
  
		function subdivideFace( a, b, c, detail ) {
  
			var cols = Math.pow( 2, detail );
  
			// we use this multidimensional array as a data structure for creating the subdivision
  
			var v = [];
  
			var i, j;
  
			// construct all of the vertices for this subdivision
  
			for ( i = 0; i <= cols; i ++ ) {
  
				v[ i ] = [];
  
				var aj = a.clone().lerp( c, i / cols );
				var bj = b.clone().lerp( c, i / cols );
  
				var rows = cols - i;
  
				for ( j = 0; j <= rows; j ++ ) {
  
					if ( j === 0 && i === cols ) {
  
						v[ i ][ j ] = aj;
  
					} else {
  
						v[ i ][ j ] = aj.clone().lerp( bj, j / rows );
  
					}
  
				}
  
			}
  
			// construct all of the faces
  
			for ( i = 0; i < cols; i ++ ) {
  
				for ( j = 0; j < 2 * ( cols - i ) - 1; j ++ ) {
  
					var k = Math.floor( j / 2 );
  
					if ( j % 2 === 0 ) {
  
						pushVertex( v[ i ][ k + 1 ] );
						pushVertex( v[ i + 1 ][ k ] );
						pushVertex( v[ i ][ k ] );
  
					} else {
  
						pushVertex( v[ i ][ k + 1 ] );
						pushVertex( v[ i + 1 ][ k + 1 ] );
						pushVertex( v[ i + 1 ][ k ] );
  
					}
  
				}
  
			}
  
		}
  
		function appplyRadius( radius ) {
  
			var vertex = new Vector3();
  
			// iterate over the entire buffer and apply the radius to each vertex
  
			for ( var i = 0; i < vertexBuffer.length; i += 3 ) {
  
				vertex.x = vertexBuffer[ i + 0 ];
				vertex.y = vertexBuffer[ i + 1 ];
				vertex.z = vertexBuffer[ i + 2 ];
  
				vertex.normalize().multiplyScalar( radius );
  
				vertexBuffer[ i + 0 ] = vertex.x;
				vertexBuffer[ i + 1 ] = vertex.y;
				vertexBuffer[ i + 2 ] = vertex.z;
  
			}
  
		}
  
		function generateUVs() {
  
			var vertex = new Vector3();
  
			for ( var i = 0; i < vertexBuffer.length; i += 3 ) {
  
				vertex.x = vertexBuffer[ i + 0 ];
				vertex.y = vertexBuffer[ i + 1 ];
				vertex.z = vertexBuffer[ i + 2 ];
  
				var u = azimuth( vertex ) / 2 / Math.PI + 0.5;
				var v = inclination( vertex ) / Math.PI + 0.5;
				uvBuffer.push( u, 1 - v );
  
			}
  
			correctUVs();
  
			correctSeam();
  
		}
  
		function correctSeam() {
  
			// handle case when face straddles the seam, see #3269
  
			for ( var i = 0; i < uvBuffer.length; i += 6 ) {
  
				// uv data of a single face
  
				var x0 = uvBuffer[ i + 0 ];
				var x1 = uvBuffer[ i + 2 ];
				var x2 = uvBuffer[ i + 4 ];
  
				var max = Math.max( x0, x1, x2 );
				var min = Math.min( x0, x1, x2 );
  
				// 0.9 is somewhat arbitrary
  
				if ( max > 0.9 && min < 0.1 ) {
  
					if ( x0 < 0.2 ) uvBuffer[ i + 0 ] += 1;
					if ( x1 < 0.2 ) uvBuffer[ i + 2 ] += 1;
					if ( x2 < 0.2 ) uvBuffer[ i + 4 ] += 1;
  
				}
  
			}
  
		}
  
		function pushVertex( vertex ) {
  
			vertexBuffer.push( vertex.x, vertex.y, vertex.z );
  
		}
  
		function getVertexByIndex( index, vertex ) {
  
			var stride = index * 3;
  
			vertex.x = vertices[ stride + 0 ];
			vertex.y = vertices[ stride + 1 ];
			vertex.z = vertices[ stride + 2 ];
  
		}
  
		function correctUVs() {
  
			var a = new Vector3();
			var b = new Vector3();
			var c = new Vector3();
  
			var centroid = new Vector3();
  
			var uvA = new Vector2();
			var uvB = new Vector2();
			var uvC = new Vector2();
  
			for ( var i = 0, j = 0; i < vertexBuffer.length; i += 9, j += 6 ) {
  
				a.set( vertexBuffer[ i + 0 ], vertexBuffer[ i + 1 ], vertexBuffer[ i + 2 ] );
				b.set( vertexBuffer[ i + 3 ], vertexBuffer[ i + 4 ], vertexBuffer[ i + 5 ] );
				c.set( vertexBuffer[ i + 6 ], vertexBuffer[ i + 7 ], vertexBuffer[ i + 8 ] );
  
				uvA.set( uvBuffer[ j + 0 ], uvBuffer[ j + 1 ] );
				uvB.set( uvBuffer[ j + 2 ], uvBuffer[ j + 3 ] );
				uvC.set( uvBuffer[ j + 4 ], uvBuffer[ j + 5 ] );
  
				centroid.copy( a ).add( b ).add( c ).divideScalar( 3 );
  
				var azi = azimuth( centroid );
  
				correctUV( uvA, j + 0, a, azi );
				correctUV( uvB, j + 2, b, azi );
				correctUV( uvC, j + 4, c, azi );
  
			}
  
		}
  
		function correctUV( uv, stride, vector, azimuth ) {
  
			if ( ( azimuth < 0 ) && ( uv.x === 1 ) ) {
  
				uvBuffer[ stride ] = uv.x - 1;
  
			}
  
			if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) {
  
				uvBuffer[ stride ] = azimuth / 2 / Math.PI + 0.5;
  
			}
  
		}
  
		// Angle around the Y axis, counter-clockwise when looking from above.
  
		function azimuth( vector ) {
  
			return Math.atan2( vector.z, - vector.x );
  
		}
  
  
		// Angle above the XZ plane.
  
		function inclination( vector ) {
  
			return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) );
  
		}
  
	}
  
	PolyhedronBufferGeometry.prototype = Object.create( BufferGeometry.prototype );
	PolyhedronBufferGeometry.prototype.constructor = PolyhedronBufferGeometry;
  
	/**
	 * @author timothypratley / https://github.com/timothypratley
	 * @author Mugen87 / https://github.com/Mugen87
	 */
  
	// TetrahedronGeometry
  
	function TetrahedronGeometry( radius, detail ) {
  
		Geometry.call( this );
  
		this.type = 'TetrahedronGeometry';
  
		this.parameters = {
			radius: radius,
			detail: detail
		};
  
		this.fromBufferGeometry( new TetrahedronBufferGeometry( radius, detail ) );
		this.mergeVertices();
  
	}
  
	TetrahedronGeometry.prototype = Object.create( Geometry.prototype );
	TetrahedronGeometry.prototype.constructor = TetrahedronGeometry;
  
	// TetrahedronBufferGeometry
  
	function TetrahedronBufferGeometry( radius, detail ) {
  
		var vertices = [
			1, 1, 1, 	- 1, - 1, 1, 	- 1, 1, - 1, 	1, - 1, - 1
		];
  
		var indices = [
			2, 1, 0, 	0, 3, 2,	1, 3, 0,	2, 3, 1
		];
  
		PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail );
  
		this.type = 'TetrahedronBufferGeometry';
  
		this.parameters = {
			radius: radius,
			detail: detail
		};
  
	}
  
	TetrahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype );
	TetrahedronBufferGeometry.prototype.constructor = TetrahedronBufferGeometry;
  
	/**
	 * @author timothypratley / https://github.com/timothypratley
	 * @author Mugen87 / https://github.com/Mugen87
	 */
  
	// OctahedronGeometry
  
	function OctahedronGeometry( radius, detail ) {
  
		Geometry.call( this );
  
		this.type = 'OctahedronGeometry';
  
		this.parameters = {
			radius: radius,
			detail: detail
		};
  
		this.fromBufferGeometry( new OctahedronBufferGeometry( radius, detail ) );
		this.mergeVertices();
  
	}
  
	OctahedronGeometry.prototype = Object.create( Geometry.prototype );
	OctahedronGeometry.prototype.constructor = OctahedronGeometry;
  
	// OctahedronBufferGeometry
  
	function OctahedronBufferGeometry( radius, detail ) {
  
		var vertices = [
			1, 0, 0, 	- 1, 0, 0,	0, 1, 0,
			0, - 1, 0, 	0, 0, 1,	0, 0, - 1
		];
  
		var indices = [
			0, 2, 4,	0, 4, 3,	0, 3, 5,
			0, 5, 2,	1, 2, 5,	1, 5, 3,
			1, 3, 4,	1, 4, 2
		];
  
		PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail );
  
		this.type = 'OctahedronBufferGeometry';
  
		this.parameters = {
			radius: radius,
			detail: detail
		};
  
	}
  
	OctahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype );
	OctahedronBufferGeometry.prototype.constructor = OctahedronBufferGeometry;
  
	/**
	 * @author timothypratley / https://github.com/timothypratley
	 * @author Mugen87 / https://github.com/Mugen87
	 */
  
	// IcosahedronGeometry
  
	function IcosahedronGeometry( radius, detail ) {
  
		Geometry.call( this );
  
		this.type = 'IcosahedronGeometry';
  
		this.parameters = {
			radius: radius,
			detail: detail
		};
  
		this.fromBufferGeometry( new IcosahedronBufferGeometry( radius, detail ) );
		this.mergeVertices();
  
	}
  
	IcosahedronGeometry.prototype = Object.create( Geometry.prototype );
	IcosahedronGeometry.prototype.constructor = IcosahedronGeometry;
  
	// IcosahedronBufferGeometry
  
	function IcosahedronBufferGeometry( radius, detail ) {
  
		var t = ( 1 + Math.sqrt( 5 ) ) / 2;
  
		var vertices = [
			- 1, t, 0, 	1, t, 0, 	- 1, - t, 0, 	1, - t, 0,
			 0, - 1, t, 	0, 1, t,	0, - 1, - t, 	0, 1, - t,
			 t, 0, - 1, 	t, 0, 1, 	- t, 0, - 1, 	- t, 0, 1
		];
  
		var indices = [
			 0, 11, 5, 	0, 5, 1, 	0, 1, 7, 	0, 7, 10, 	0, 10, 11,
			 1, 5, 9, 	5, 11, 4,	11, 10, 2,	10, 7, 6,	7, 1, 8,
			 3, 9, 4, 	3, 4, 2,	3, 2, 6,	3, 6, 8,	3, 8, 9,
			 4, 9, 5, 	2, 4, 11,	6, 2, 10,	8, 6, 7,	9, 8, 1
		];
  
		PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail );
  
		this.type = 'IcosahedronBufferGeometry';
  
		this.parameters = {
			radius: radius,
			detail: detail
		};
  
	}
  
	IcosahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype );
	IcosahedronBufferGeometry.prototype.constructor = IcosahedronBufferGeometry;
  
	/**
	 * @author Abe Pazos / https://hamoid.com
	 * @author Mugen87 / https://github.com/Mugen87
	 */
  
	// DodecahedronGeometry
  
	function DodecahedronGeometry( radius, detail ) {
  
		Geometry.call( this );
  
		this.type = 'DodecahedronGeometry';
  
		this.parameters = {
			radius: radius,
			detail: detail
		};
  
		this.fromBufferGeometry( new DodecahedronBufferGeometry( radius, detail ) );
		this.mergeVertices();
  
	}
  
	DodecahedronGeometry.prototype = Object.create( Geometry.prototype );
	DodecahedronGeometry.prototype.constructor = DodecahedronGeometry;
  
	// DodecahedronBufferGeometry
  
	function DodecahedronBufferGeometry( radius, detail ) {
  
		var t = ( 1 + Math.sqrt( 5 ) ) / 2;
		var r = 1 / t;
  
		var vertices = [
  
			// (±1, ±1, ±1)
			- 1, - 1, - 1,	- 1, - 1, 1,
			- 1, 1, - 1, - 1, 1, 1,
			1, - 1, - 1, 1, - 1, 1,
			1, 1, - 1, 1, 1, 1,
  
			// (0, ±1/φ, ±φ)
			 0, - r, - t, 0, - r, t,
			 0, r, - t, 0, r, t,
  
			// (±1/φ, ±φ, 0)
			- r, - t, 0, - r, t, 0,
			 r, - t, 0, r, t, 0,
  
			// (±φ, 0, ±1/φ)
			- t, 0, - r, t, 0, - r,
			- t, 0, r, t, 0, r
		];
  
		var indices = [
			3, 11, 7, 	3, 7, 15, 	3, 15, 13,
			7, 19, 17, 	7, 17, 6, 	7, 6, 15,
			17, 4, 8, 	17, 8, 10, 	17, 10, 6,
			8, 0, 16, 	8, 16, 2, 	8, 2, 10,
			0, 12, 1, 	0, 1, 18, 	0, 18, 16,
			6, 10, 2, 	6, 2, 13, 	6, 13, 15,
			2, 16, 18, 	2, 18, 3, 	2, 3, 13,
			18, 1, 9, 	18, 9, 11, 	18, 11, 3,
			4, 14, 12, 	4, 12, 0, 	4, 0, 8,
			11, 9, 5, 	11, 5, 19, 	11, 19, 7,
			19, 5, 14, 	19, 14, 4, 	19, 4, 17,
			1, 12, 14, 	1, 14, 5, 	1, 5, 9
		];
  
		PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail );
  
		this.type = 'DodecahedronBufferGeometry';
  
		this.parameters = {
			radius: radius,
			detail: detail
		};
  
	}
  
	DodecahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype );
	DodecahedronBufferGeometry.prototype.constructor = DodecahedronBufferGeometry;
  
	/**
	 * @author oosmoxiecode / https://github.com/oosmoxiecode
	 * @author WestLangley / https://github.com/WestLangley
	 * @author zz85 / https://github.com/zz85
	 * @author miningold / https://github.com/miningold
	 * @author jonobr1 / https://github.com/jonobr1
	 * @author Mugen87 / https://github.com/Mugen87
	 *
	 */
  
	// TubeGeometry
  
	function TubeGeometry( path, tubularSegments, radius, radialSegments, closed, taper ) {
  
		Geometry.call( this );
  
		this.type = 'TubeGeometry';
  
		this.parameters = {
			path: path,
			tubularSegments: tubularSegments,
			radius: radius,
			radialSegments: radialSegments,
			closed: closed
		};
  
		if ( taper !== undefined ) console.warn( 'THREE.TubeGeometry: taper has been removed.' );
  
		var bufferGeometry = new TubeBufferGeometry( path, tubularSegments, radius, radialSegments, closed );
  
		// expose internals
  
		this.tangents = bufferGeometry.tangents;
		this.normals = bufferGeometry.normals;
		this.binormals = bufferGeometry.binormals;
  
		// create geometry
  
		this.fromBufferGeometry( bufferGeometry );
		this.mergeVertices();
  
	}
  
	TubeGeometry.prototype = Object.create( Geometry.prototype );
	TubeGeometry.prototype.constructor = TubeGeometry;
  
	// TubeBufferGeometry
  
	function TubeBufferGeometry( path, tubularSegments, radius, radialSegments, closed ) {
  
		BufferGeometry.call( this );
  
		this.type = 'TubeBufferGeometry';
  
		this.parameters = {
			path: path,
			tubularSegments: tubularSegments,
			radius: radius,
			radialSegments: radialSegments,
			closed: closed
		};
  
		tubularSegments = tubularSegments || 64;
		radius = radius || 1;
		radialSegments = radialSegments || 8;
		closed = closed || false;
  
		var frames = path.computeFrenetFrames( tubularSegments, closed );
  
		// expose internals
  
		this.tangents = frames.tangents;
		this.normals = frames.normals;
		this.binormals = frames.binormals;
  
		// helper variables
  
		var vertex = new Vector3();
		var normal = new Vector3();
		var uv = new Vector2();
		var P = new Vector3();
  
		var i, j;
  
		// buffer
  
		var vertices = [];
		var normals = [];
		var uvs = [];
		var indices = [];
  
		// create buffer data
  
		generateBufferData();
  
		// build geometry
  
		this.setIndex( indices );
		this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
		this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
		this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
  
		// functions
  
		function generateBufferData() {
  
			for ( i = 0; i < tubularSegments; i ++ ) {
  
				generateSegment( i );
  
			}
  
			// if the geometry is not closed, generate the last row of vertices and normals
			// at the regular position on the given path
			//
			// if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ)
  
			generateSegment( ( closed === false ) ? tubularSegments : 0 );
  
			// uvs are generated in a separate function.
			// this makes it easy compute correct values for closed geometries
  
			generateUVs();
  
			// finally create faces
  
			generateIndices();
  
		}
  
		function generateSegment( i ) {
  
			// we use getPointAt to sample evenly distributed points from the given path
  
			P = path.getPointAt( i / tubularSegments, P );
  
			// retrieve corresponding normal and binormal
  
			var N = frames.normals[ i ];
			var B = frames.binormals[ i ];
  
			// generate normals and vertices for the current segment
  
			for ( j = 0; j <= radialSegments; j ++ ) {
  
				var v = j / radialSegments * Math.PI * 2;
  
				var sin = Math.sin( v );
				var cos = - Math.cos( v );
  
				// normal
  
				normal.x = ( cos * N.x + sin * B.x );
				normal.y = ( cos * N.y + sin * B.y );
				normal.z = ( cos * N.z + sin * B.z );
				normal.normalize();
  
				normals.push( normal.x, normal.y, normal.z );
  
				// vertex
  
				vertex.x = P.x + radius * normal.x;
				vertex.y = P.y + radius * normal.y;
				vertex.z = P.z + radius * normal.z;
  
				vertices.push( vertex.x, vertex.y, vertex.z );
  
			}
  
		}
  
		function generateIndices() {
  
			for ( j = 1; j <= tubularSegments; j ++ ) {
  
				for ( i = 1; i <= radialSegments; i ++ ) {
  
					var a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 );
					var b = ( radialSegments + 1 ) * j + ( i - 1 );
					var c = ( radialSegments + 1 ) * j + i;
					var d = ( radialSegments + 1 ) * ( j - 1 ) + i;
  
					// faces
  
					indices.push( a, b, d );
					indices.push( b, c, d );
  
				}
  
			}
  
		}
  
		function generateUVs() {
  
			for ( i = 0; i <= tubularSegments; i ++ ) {
  
				for ( j = 0; j <= radialSegments; j ++ ) {
  
					uv.x = i / tubularSegments;
					uv.y = j / radialSegments;
  
					uvs.push( uv.x, uv.y );
  
				}
  
			}
  
		}
  
	}
  
	TubeBufferGeometry.prototype = Object.create( BufferGeometry.prototype );
	TubeBufferGeometry.prototype.constructor = TubeBufferGeometry;
  
	/**
	 * @author oosmoxiecode
	 * @author Mugen87 / https://github.com/Mugen87
	 *
	 * based on http://www.blackpawn.com/texts/pqtorus/
	 */
  
	// TorusKnotGeometry
  
	function TorusKnotGeometry( radius, tube, tubularSegments, radialSegments, p, q, heightScale ) {
  
		Geometry.call( this );
  
		this.type = 'TorusKnotGeometry';
  
		this.parameters = {
			radius: radius,
			tube: tube,
			tubularSegments: tubularSegments,
			radialSegments: radialSegments,
			p: p,
			q: q
		};
  
		if ( heightScale !== undefined ) console.warn( 'THREE.TorusKnotGeometry: heightScale has been deprecated. Use .scale( x, y, z ) instead.' );
  
		this.fromBufferGeometry( new TorusKnotBufferGeometry( radius, tube, tubularSegments, radialSegments, p, q ) );
		this.mergeVertices();
  
	}
  
	TorusKnotGeometry.prototype = Object.create( Geometry.prototype );
	TorusKnotGeometry.prototype.constructor = TorusKnotGeometry;
  
	// TorusKnotBufferGeometry
  
	function TorusKnotBufferGeometry( radius, tube, tubularSegments, radialSegments, p, q ) {
  
		BufferGeometry.call( this );
  
		this.type = 'TorusKnotBufferGeometry';
  
		this.parameters = {
			radius: radius,
			tube: tube,
			tubularSegments: tubularSegments,
			radialSegments: radialSegments,
			p: p,
			q: q
		};
  
		radius = radius || 1;
		tube = tube || 0.4;
		tubularSegments = Math.floor( tubularSegments ) || 64;
		radialSegments = Math.floor( radialSegments ) || 8;
		p = p || 2;
		q = q || 3;
  
		// buffers
  
		var indices = [];
		var vertices = [];
		var normals = [];
		var uvs = [];
  
		// helper variables
  
		var i, j;
  
		var vertex = new Vector3();
		var normal = new Vector3();
  
		var P1 = new Vector3();
		var P2 = new Vector3();
  
		var B = new Vector3();
		var T = new Vector3();
		var N = new Vector3();
  
		// generate vertices, normals and uvs
  
		for ( i = 0; i <= tubularSegments; ++ i ) {
  
			// the radian "u" is used to calculate the position on the torus curve of the current tubular segement
  
			var u = i / tubularSegments * p * Math.PI * 2;
  
			// now we calculate two points. P1 is our current position on the curve, P2 is a little farther ahead.
			// these points are used to create a special "coordinate space", which is necessary to calculate the correct vertex positions
  
			calculatePositionOnCurve( u, p, q, radius, P1 );
			calculatePositionOnCurve( u + 0.01, p, q, radius, P2 );
  
			// calculate orthonormal basis
  
			T.subVectors( P2, P1 );
			N.addVectors( P2, P1 );
			B.crossVectors( T, N );
			N.crossVectors( B, T );
  
			// normalize B, N. T can be ignored, we don't use it
  
			B.normalize();
			N.normalize();
  
			for ( j = 0; j <= radialSegments; ++ j ) {
  
				// now calculate the vertices. they are nothing more than an extrusion of the torus curve.
				// because we extrude a shape in the xy-plane, there is no need to calculate a z-value.
  
				var v = j / radialSegments * Math.PI * 2;
				var cx = - tube * Math.cos( v );
				var cy = tube * Math.sin( v );
  
				// now calculate the final vertex position.
				// first we orient the extrusion with our basis vectos, then we add it to the current position on the curve
  
				vertex.x = P1.x + ( cx * N.x + cy * B.x );
				vertex.y = P1.y + ( cx * N.y + cy * B.y );
				vertex.z = P1.z + ( cx * N.z + cy * B.z );
  
				vertices.push( vertex.x, vertex.y, vertex.z );
  
				// normal (P1 is always the center/origin of the extrusion, thus we can use it to calculate the normal)
  
				normal.subVectors( vertex, P1 ).normalize();
  
				normals.push( normal.x, normal.y, normal.z );
  
				// uv
  
				uvs.push( i / tubularSegments );
				uvs.push( j / radialSegments );
  
			}
  
		}
  
		// generate indices
  
		for ( j = 1; j <= tubularSegments; j ++ ) {
  
			for ( i = 1; i <= radialSegments; i ++ ) {
  
				// indices
  
				var a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 );
				var b = ( radialSegments + 1 ) * j + ( i - 1 );
				var c = ( radialSegments + 1 ) * j + i;
				var d = ( radialSegments + 1 ) * ( j - 1 ) + i;
  
				// faces
  
				indices.push( a, b, d );
				indices.push( b, c, d );
  
			}
  
		}
  
		// build geometry
  
		this.setIndex( indices );
		this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
		this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
		this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
  
		// this function calculates the current position on the torus curve
  
		function calculatePositionOnCurve( u, p, q, radius, position ) {
  
			var cu = Math.cos( u );
			var su = Math.sin( u );
			var quOverP = q / p * u;
			var cs = Math.cos( quOverP );
  
			position.x = radius * ( 2 + cs ) * 0.5 * cu;
			position.y = radius * ( 2 + cs ) * su * 0.5;
			position.z = radius * Math.sin( quOverP ) * 0.5;
  
		}
  
	}
  
	TorusKnotBufferGeometry.prototype = Object.create( BufferGeometry.prototype );
	TorusKnotBufferGeometry.prototype.constructor = TorusKnotBufferGeometry;
  
	/**
	 * @author oosmoxiecode
	 * @author mrdoob / http://mrdoob.com/
	 * @author Mugen87 / https://github.com/Mugen87
	 */
  
	// TorusGeometry
  
	function TorusGeometry( radius, tube, radialSegments, tubularSegments, arc ) {
  
		Geometry.call( this );
  
		this.type = 'TorusGeometry';
  
		this.parameters = {
			radius: radius,
			tube: tube,
			radialSegments: radialSegments,
			tubularSegments: tubularSegments,
			arc: arc
		};
  
		this.fromBufferGeometry( new TorusBufferGeometry( radius, tube, radialSegments, tubularSegments, arc ) );
		this.mergeVertices();
  
	}
  
	TorusGeometry.prototype = Object.create( Geometry.prototype );
	TorusGeometry.prototype.constructor = TorusGeometry;
  
	// TorusBufferGeometry
  
	function TorusBufferGeometry( radius, tube, radialSegments, tubularSegments, arc ) {
  
		BufferGeometry.call( this );
  
		this.type = 'TorusBufferGeometry';
  
		this.parameters = {
			radius: radius,
			tube: tube,
			radialSegments: radialSegments,
			tubularSegments: tubularSegments,
			arc: arc
		};
  
		radius = radius || 1;
		tube = tube || 0.4;
		radialSegments = Math.floor( radialSegments ) || 8;
		tubularSegments = Math.floor( tubularSegments ) || 6;
		arc = arc || Math.PI * 2;
  
		// buffers
  
		var indices = [];
		var vertices = [];
		var normals = [];
		var uvs = [];
  
		// helper variables
  
		var center = new Vector3();
		var vertex = new Vector3();
		var normal = new Vector3();
  
		var j, i;
  
		// generate vertices, normals and uvs
  
		for ( j = 0; j <= radialSegments; j ++ ) {
  
			for ( i = 0; i <= tubularSegments; i ++ ) {
  
				var u = i / tubularSegments * arc;
				var v = j / radialSegments * Math.PI * 2;
  
				// vertex
  
				vertex.x = ( radius + tube * Math.cos( v ) ) * Math.cos( u );
				vertex.y = ( radius + tube * Math.cos( v ) ) * Math.sin( u );
				vertex.z = tube * Math.sin( v );
  
				vertices.push( vertex.x, vertex.y, vertex.z );
  
				// normal
  
				center.x = radius * Math.cos( u );
				center.y = radius * Math.sin( u );
				normal.subVectors( vertex, center ).normalize();
  
				normals.push( normal.x, normal.y, normal.z );
  
				// uv
  
				uvs.push( i / tubularSegments );
				uvs.push( j / radialSegments );
  
			}
  
		}
  
		// generate indices
  
		for ( j = 1; j <= radialSegments; j ++ ) {
  
			for ( i = 1; i <= tubularSegments; i ++ ) {
  
				// indices
  
				var a = ( tubularSegments + 1 ) * j + i - 1;
				var b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1;
				var c = ( tubularSegments + 1 ) * ( j - 1 ) + i;
				var d = ( tubularSegments + 1 ) * j + i;
  
				// faces
  
				indices.push( a, b, d );
				indices.push( b, c, d );
  
			}
  
		}
  
		// build geometry
  
		this.setIndex( indices );
		this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
		this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
		this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
  
	}
  
	TorusBufferGeometry.prototype = Object.create( BufferGeometry.prototype );
	TorusBufferGeometry.prototype.constructor = TorusBufferGeometry;
  
	/**
	 * @author Mugen87 / https://github.com/Mugen87
	 * Port from https://github.com/mapbox/earcut (v2.1.2)
	 */
  
	var Earcut = {
  
		triangulate: function ( data, holeIndices, dim ) {
  
			dim = dim || 2;
  
			var hasHoles = holeIndices && holeIndices.length,
				outerLen = hasHoles ? holeIndices[ 0 ] * dim : data.length,
				outerNode = linkedList( data, 0, outerLen, dim, true ),
				triangles = [];
  
			if ( ! outerNode ) return triangles;
  
			var minX, minY, maxX, maxY, x, y, invSize;
  
			if ( hasHoles ) outerNode = eliminateHoles( data, holeIndices, outerNode, dim );
  
			// if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox
  
			if ( data.length > 80 * dim ) {
  
				minX = maxX = data[ 0 ];
				minY = maxY = data[ 1 ];
  
				for ( var i = dim; i < outerLen; i += dim ) {
  
					x = data[ i ];
					y = data[ i + 1 ];
					if ( x < minX ) minX = x;
					if ( y < minY ) minY = y;
					if ( x > maxX ) maxX = x;
					if ( y > maxY ) maxY = y;
  
				}
  
				// minX, minY and invSize are later used to transform coords into integers for z-order calculation
  
				invSize = Math.max( maxX - minX, maxY - minY );
				invSize = invSize !== 0 ? 1 / invSize : 0;
  
			}
  
			earcutLinked( outerNode, triangles, dim, minX, minY, invSize );
  
			return triangles;
  
		}
  
	};
  
	// create a circular doubly linked list from polygon points in the specified winding order
  
	function linkedList( data, start, end, dim, clockwise ) {
  
		var i, last;
  
		if ( clockwise === ( signedArea( data, start, end, dim ) > 0 ) ) {
  
			for ( i = start; i < end; i += dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last );
  
		} else {
  
			for ( i = end - dim; i >= start; i -= dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last );
  
		}
  
		if ( last && equals( last, last.next ) ) {
  
			removeNode( last );
			last = last.next;
  
		}
  
		return last;
  
	}
  
	// eliminate colinear or duplicate points
  
	function filterPoints( start, end ) {
  
		if ( ! start ) return start;
		if ( ! end ) end = start;
  
		var p = start, again;
  
		do {
  
			again = false;
  
			if ( ! p.steiner && ( equals( p, p.next ) || area( p.prev, p, p.next ) === 0 ) ) {
  
				removeNode( p );
				p = end = p.prev;
				if ( p === p.next ) break;
				again = true;
  
			} else {
  
				p = p.next;
  
			}
  
		} while ( again || p !== end );
  
		return end;
  
	}
  
	// main ear slicing loop which triangulates a polygon (given as a linked list)
  
	function earcutLinked( ear, triangles, dim, minX, minY, invSize, pass ) {
  
		if ( ! ear ) return;
  
		// interlink polygon nodes in z-order
  
		if ( ! pass && invSize ) indexCurve( ear, minX, minY, invSize );
  
		var stop = ear, prev, next;
  
		// iterate through ears, slicing them one by one
  
		while ( ear.prev !== ear.next ) {
  
			prev = ear.prev;
			next = ear.next;
  
			if ( invSize ? isEarHashed( ear, minX, minY, invSize ) : isEar( ear ) ) {
  
				// cut off the triangle
				triangles.push( prev.i / dim );
				triangles.push( ear.i / dim );
				triangles.push( next.i / dim );
  
				removeNode( ear );
  
				// skipping the next vertice leads to less sliver triangles
				ear = next.next;
				stop = next.next;
  
				continue;
  
			}
  
			ear = next;
  
			// if we looped through the whole remaining polygon and can't find any more ears
  
			if ( ear === stop ) {
  
				// try filtering points and slicing again
  
				if ( ! pass ) {
  
					earcutLinked( filterPoints( ear ), triangles, dim, minX, minY, invSize, 1 );
  
					// if this didn't work, try curing all small self-intersections locally
  
				} else if ( pass === 1 ) {
  
					ear = cureLocalIntersections( ear, triangles, dim );
					earcutLinked( ear, triangles, dim, minX, minY, invSize, 2 );
  
				// as a last resort, try splitting the remaining polygon into two
  
				} else if ( pass === 2 ) {
  
					splitEarcut( ear, triangles, dim, minX, minY, invSize );
  
				}
  
				break;
  
			}
  
		}
  
	}
  
	// check whether a polygon node forms a valid ear with adjacent nodes
  
	function isEar( ear ) {
  
		var a = ear.prev,
			b = ear,
			c = ear.next;
  
		if ( area( a, b, c ) >= 0 ) return false; // reflex, can't be an ear
  
		// now make sure we don't have other points inside the potential ear
		var p = ear.next.next;
  
		while ( p !== ear.prev ) {
  
			if ( pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && area( p.prev, p, p.next ) >= 0 ) {
  
				return false;
  
			}
  
			p = p.next;
  
		}
  
		return true;
  
	}
  
	function isEarHashed( ear, minX, minY, invSize ) {
  
		var a = ear.prev,
			b = ear,
			c = ear.next;
  
		if ( area( a, b, c ) >= 0 ) return false; // reflex, can't be an ear
  
		// triangle bbox; min & max are calculated like this for speed
  
		var minTX = a.x < b.x ? ( a.x < c.x ? a.x : c.x ) : ( b.x < c.x ? b.x : c.x ),
			minTY = a.y < b.y ? ( a.y < c.y ? a.y : c.y ) : ( b.y < c.y ? b.y : c.y ),
			maxTX = a.x > b.x ? ( a.x > c.x ? a.x : c.x ) : ( b.x > c.x ? b.x : c.x ),
			maxTY = a.y > b.y ? ( a.y > c.y ? a.y : c.y ) : ( b.y > c.y ? b.y : c.y );
  
		// z-order range for the current triangle bbox;
  
		var minZ = zOrder( minTX, minTY, minX, minY, invSize ),
			maxZ = zOrder( maxTX, maxTY, minX, minY, invSize );
  
		// first look for points inside the triangle in increasing z-order
  
		var p = ear.nextZ;
  
		while ( p && p.z <= maxZ ) {
  
			if ( p !== ear.prev && p !== ear.next &&
					pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) &&
					area( p.prev, p, p.next ) >= 0 ) return false;
			p = p.nextZ;
  
		}
  
		// then look for points in decreasing z-order
  
		p = ear.prevZ;
  
		while ( p && p.z >= minZ ) {
  
			if ( p !== ear.prev && p !== ear.next &&
					pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) &&
					area( p.prev, p, p.next ) >= 0 ) return false;
  
			p = p.prevZ;
  
		}
  
		return true;
  
	}
  
	// go through all polygon nodes and cure small local self-intersections
  
	function cureLocalIntersections( start, triangles, dim ) {
  
		var p = start;
  
		do {
  
			var a = p.prev, b = p.next.next;
  
			if ( ! equals( a, b ) && intersects( a, p, p.next, b ) && locallyInside( a, b ) && locallyInside( b, a ) ) {
  
				triangles.push( a.i / dim );
				triangles.push( p.i / dim );
				triangles.push( b.i / dim );
  
				// remove two nodes involved
  
				removeNode( p );
				removeNode( p.next );
  
				p = start = b;
  
			}
  
			p = p.next;
  
		} while ( p !== start );
  
		return p;
  
	}
  
	// try splitting polygon into two and triangulate them independently
  
	function splitEarcut( start, triangles, dim, minX, minY, invSize ) {
  
		// look for a valid diagonal that divides the polygon into two
  
		var a = start;
  
		do {
  
			var b = a.next.next;
  
			while ( b !== a.prev ) {
  
				if ( a.i !== b.i && isValidDiagonal( a, b ) ) {
  
					// split the polygon in two by the diagonal
  
					var c = splitPolygon( a, b );
  
					// filter colinear points around the cuts
  
					a = filterPoints( a, a.next );
					c = filterPoints( c, c.next );
  
					// run earcut on each half
  
					earcutLinked( a, triangles, dim, minX, minY, invSize );
					earcutLinked( c, triangles, dim, minX, minY, invSize );
					return;
  
				}
  
				b = b.next;
  
			}
  
			a = a.next;
  
		} while ( a !== start );
  
	}
  
	// link every hole into the outer loop, producing a single-ring polygon without holes
  
	function eliminateHoles( data, holeIndices, outerNode, dim ) {
  
		var queue = [], i, len, start, end, list;
  
		for ( i = 0, len = holeIndices.length; i < len; i ++ ) {
  
			start = holeIndices[ i ] * dim;
			end = i < len - 1 ? holeIndices[ i + 1 ] * dim : data.length;
			list = linkedList( data, start, end, dim, false );
			if ( list === list.next ) list.steiner = true;
			queue.push( getLeftmost( list ) );
  
		}
  
		queue.sort( compareX );
  
		// process holes from left to right
  
		for ( i = 0; i < queue.length; i ++ ) {
  
			eliminateHole( queue[ i ], outerNode );
			outerNode = filterPoints( outerNode, outerNode.next );
  
		}
  
		return outerNode;
  
	}
  
	function compareX( a, b ) {
  
		return a.x - b.x;
  
	}
  
	// find a bridge between vertices that connects hole with an outer ring and and link it
  
	function eliminateHole( hole, outerNode ) {
  
		outerNode = findHoleBridge( hole, outerNode );
  
		if ( outerNode ) {
  
			var b = splitPolygon( outerNode, hole );
  
			filterPoints( b, b.next );
  
		}
  
	}
  
	// David Eberly's algorithm for finding a bridge between hole and outer polygon
  
	function findHoleBridge( hole, outerNode ) {
  
		var p = outerNode,
			hx = hole.x,
			hy = hole.y,
			qx = - Infinity,
			m;
  
		// find a segment intersected by a ray from the hole's leftmost point to the left;
		// segment's endpoint with lesser x will be potential connection point
  
		do {
  
			if ( hy <= p.y && hy >= p.next.y && p.next.y !== p.y ) {
  
				var x = p.x + ( hy - p.y ) * ( p.next.x - p.x ) / ( p.next.y - p.y );
  
				if ( x <= hx && x > qx ) {
  
					qx = x;
  
					if ( x === hx ) {
  
						if ( hy === p.y ) return p;
						if ( hy === p.next.y ) return p.next;
  
					}
  
					m = p.x < p.next.x ? p : p.next;
  
				}
  
			}
  
			p = p.next;
  
		} while ( p !== outerNode );
  
		if ( ! m ) return null;
  
		if ( hx === qx ) return m.prev; // hole touches outer segment; pick lower endpoint
  
		// look for points inside the triangle of hole point, segment intersection and endpoint;
		// if there are no points found, we have a valid connection;
		// otherwise choose the point of the minimum angle with the ray as connection point
  
		var stop = m,
			mx = m.x,
			my = m.y,
			tanMin = Infinity,
			tan;
  
		p = m.next;
  
		while ( p !== stop ) {
  
			if ( hx >= p.x && p.x >= mx && hx !== p.x &&
							pointInTriangle( hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y ) ) {
  
				tan = Math.abs( hy - p.y ) / ( hx - p.x ); // tangential
  
				if ( ( tan < tanMin || ( tan === tanMin && p.x > m.x ) ) && locallyInside( p, hole ) ) {
  
					m = p;
					tanMin = tan;
  
				}
  
			}
  
			p = p.next;
  
		}
  
		return m;
  
	}
  
	// interlink polygon nodes in z-order
  
	function indexCurve( start, minX, minY, invSize ) {
  
		var p = start;
  
		do {
  
			if ( p.z === null ) p.z = zOrder( p.x, p.y, minX, minY, invSize );
			p.prevZ = p.prev;
			p.nextZ = p.next;
			p = p.next;
  
		} while ( p !== start );
  
		p.prevZ.nextZ = null;
		p.prevZ = null;
  
		sortLinked( p );
  
	}
  
	// Simon Tatham's linked list merge sort algorithm
	// http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html
  
	function sortLinked( list ) {
  
		var i, p, q, e, tail, numMerges, pSize, qSize, inSize = 1;
  
		do {
  
			p = list;
			list = null;
			tail = null;
			numMerges = 0;
  
			while ( p ) {
  
				numMerges ++;
				q = p;
				pSize = 0;
  
				for ( i = 0; i < inSize; i ++ ) {
  
					pSize ++;
					q = q.nextZ;
					if ( ! q ) break;
  
				}
  
				qSize = inSize;
  
				while ( pSize > 0 || ( qSize > 0 && q ) ) {
  
					if ( pSize !== 0 && ( qSize === 0 || ! q || p.z <= q.z ) ) {
  
						e = p;
						p = p.nextZ;
						pSize --;
  
					} else {
  
						e = q;
						q = q.nextZ;
						qSize --;
  
					}
  
					if ( tail ) tail.nextZ = e;
					else list = e;
  
					e.prevZ = tail;
					tail = e;
  
				}
  
				p = q;
  
			}
  
			tail.nextZ = null;
			inSize *= 2;
  
		} while ( numMerges > 1 );
  
		return list;
  
	}
  
	// z-order of a point given coords and inverse of the longer side of data bbox
  
	function zOrder( x, y, minX, minY, invSize ) {
  
		// coords are transformed into non-negative 15-bit integer range
  
		x = 32767 * ( x - minX ) * invSize;
		y = 32767 * ( y - minY ) * invSize;
  
		x = ( x | ( x << 8 ) ) & 0x00FF00FF;
		x = ( x | ( x << 4 ) ) & 0x0F0F0F0F;
		x = ( x | ( x << 2 ) ) & 0x33333333;
		x = ( x | ( x << 1 ) ) & 0x55555555;
  
		y = ( y | ( y << 8 ) ) & 0x00FF00FF;
		y = ( y | ( y << 4 ) ) & 0x0F0F0F0F;
		y = ( y | ( y << 2 ) ) & 0x33333333;
		y = ( y | ( y << 1 ) ) & 0x55555555;
  
		return x | ( y << 1 );
  
	}
  
	// find the leftmost node of a polygon ring
  
	function getLeftmost( start ) {
  
		var p = start, leftmost = start;
  
		do {
  
			if ( p.x < leftmost.x ) leftmost = p;
			p = p.next;
  
		} while ( p !== start );
  
		return leftmost;
  
	}
  
	// check if a point lies within a convex triangle
  
	function pointInTriangle( ax, ay, bx, by, cx, cy, px, py ) {
  
		return ( cx - px ) * ( ay - py ) - ( ax - px ) * ( cy - py ) >= 0 &&
		 ( ax - px ) * ( by - py ) - ( bx - px ) * ( ay - py ) >= 0 &&
		 ( bx - px ) * ( cy - py ) - ( cx - px ) * ( by - py ) >= 0;
  
	}
  
	// check if a diagonal between two polygon nodes is valid (lies in polygon interior)
  
	function isValidDiagonal( a, b ) {
  
		return a.next.i !== b.i && a.prev.i !== b.i && ! intersectsPolygon( a, b ) &&
			locallyInside( a, b ) && locallyInside( b, a ) && middleInside( a, b );
  
	}
  
	// signed area of a triangle
  
	function area( p, q, r ) {
  
		return ( q.y - p.y ) * ( r.x - q.x ) - ( q.x - p.x ) * ( r.y - q.y );
  
	}
  
	// check if two points are equal
  
	function equals( p1, p2 ) {
  
		return p1.x === p2.x && p1.y === p2.y;
  
	}
  
	// check if two segments intersect
  
	function intersects( p1, q1, p2, q2 ) {
  
		if ( ( equals( p1, q1 ) && equals( p2, q2 ) ) ||
				( equals( p1, q2 ) && equals( p2, q1 ) ) ) return true;
  
		return area( p1, q1, p2 ) > 0 !== area( p1, q1, q2 ) > 0 &&
					 area( p2, q2, p1 ) > 0 !== area( p2, q2, q1 ) > 0;
  
	}
  
	// check if a polygon diagonal intersects any polygon segments
  
	function intersectsPolygon( a, b ) {
  
		var p = a;
  
		do {
  
			if ( p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i &&
							intersects( p, p.next, a, b ) ) {
  
				return true;
  
			}
  
			p = p.next;
  
		} while ( p !== a );
  
		return false;
  
	}
  
	// check if a polygon diagonal is locally inside the polygon
  
	function locallyInside( a, b ) {
  
		return area( a.prev, a, a.next ) < 0 ?
			area( a, b, a.next ) >= 0 && area( a, a.prev, b ) >= 0 :
			area( a, b, a.prev ) < 0 || area( a, a.next, b ) < 0;
  
	}
  
	// check if the middle point of a polygon diagonal is inside the polygon
  
	function middleInside( a, b ) {
  
		var p = a,
			inside = false,
			px = ( a.x + b.x ) / 2,
			py = ( a.y + b.y ) / 2;
  
		do {
  
			if ( ( ( p.y > py ) !== ( p.next.y > py ) ) && p.next.y !== p.y &&
							( px < ( p.next.x - p.x ) * ( py - p.y ) / ( p.next.y - p.y ) + p.x ) ) {
  
				inside = ! inside;
  
			}
  
			p = p.next;
  
		} while ( p !== a );
  
		return inside;
  
	}
  
	// link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two;
	// if one belongs to the outer ring and another to a hole, it merges it into a single ring
  
	function splitPolygon( a, b ) {
  
		var a2 = new Node( a.i, a.x, a.y ),
			b2 = new Node( b.i, b.x, b.y ),
			an = a.next,
			bp = b.prev;
  
		a.next = b;
		b.prev = a;
  
		a2.next = an;
		an.prev = a2;
  
		b2.next = a2;
		a2.prev = b2;
  
		bp.next = b2;
		b2.prev = bp;
  
		return b2;
  
	}
  
	// create a node and optionally link it with previous one (in a circular doubly linked list)
  
	function insertNode( i, x, y, last ) {
  
		var p = new Node( i, x, y );
  
		if ( ! last ) {
  
			p.prev = p;
			p.next = p;
  
		} else {
  
			p.next = last.next;
			p.prev = last;
			last.next.prev = p;
			last.next = p;
  
		}
  
		return p;
  
	}
  
	function removeNode( p ) {
  
		p.next.prev = p.prev;
		p.prev.next = p.next;
  
		if ( p.prevZ ) p.prevZ.nextZ = p.nextZ;
		if ( p.nextZ ) p.nextZ.prevZ = p.prevZ;
  
	}
  
	function Node( i, x, y ) {
  
		// vertice index in coordinates array
		this.i = i;
  
		// vertex coordinates
		this.x = x;
		this.y = y;
  
		// previous and next vertice nodes in a polygon ring
		this.prev = null;
		this.next = null;
  
		// z-order curve value
		this.z = null;
  
		// previous and next nodes in z-order
		this.prevZ = null;
		this.nextZ = null;
  
		// indicates whether this is a steiner point
		this.steiner = false;
  
	}
  
	function signedArea( data, start, end, dim ) {
  
		var sum = 0;
  
		for ( var i = start, j = end - dim; i < end; i += dim ) {
  
			sum += ( data[ j ] - data[ i ] ) * ( data[ i + 1 ] + data[ j + 1 ] );
			j = i;
  
		}
  
		return sum;
  
	}
  
	/**
	 * @author zz85 / http://www.lab4games.net/zz85/blog
	 */
  
	var ShapeUtils = {
  
		// calculate area of the contour polygon
  
		area: function ( contour ) {
  
			var n = contour.length;
			var a = 0.0;
  
			for ( var p = n - 1, q = 0; q < n; p = q ++ ) {
  
				a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y;
  
			}
  
			return a * 0.5;
  
		},
  
		isClockWise: function ( pts ) {
  
			return ShapeUtils.area( pts ) < 0;
  
		},
  
		triangulateShape: function ( contour, holes ) {
  
			var vertices = []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ]
			var holeIndices = []; // array of hole indices
			var faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ]
  
			removeDupEndPts( contour );
			addContour( vertices, contour );
  
			//
  
			var holeIndex = contour.length;
  
			holes.forEach( removeDupEndPts );
  
			for ( var i = 0; i < holes.length; i ++ ) {
  
				holeIndices.push( holeIndex );
				holeIndex += holes[ i ].length;
				addContour( vertices, holes[ i ] );
  
			}
  
			//
  
			var triangles = Earcut.triangulate( vertices, holeIndices );
  
			//
  
			for ( var i = 0; i < triangles.length; i += 3 ) {
  
				faces.push( triangles.slice( i, i + 3 ) );
  
			}
  
			return faces;
  
		}
  
	};
  
	function removeDupEndPts( points ) {
  
		var l = points.length;
  
		if ( l > 2 && points[ l - 1 ].equals( points[ 0 ] ) ) {
  
			points.pop();
  
		}
  
	}
  
	function addContour( vertices, contour ) {
  
		for ( var i = 0; i < contour.length; i ++ ) {
  
			vertices.push( contour[ i ].x );
			vertices.push( contour[ i ].y );
  
		}
  
	}
  
	/**
	 * @author zz85 / http://www.lab4games.net/zz85/blog
	 *
	 * Creates extruded geometry from a path shape.
	 *
	 * parameters = {
	 *
	 *  curveSegments: <int>, // number of points on the curves
	 *  steps: <int>, // number of points for z-side extrusions / used for subdividing segments of extrude spline too
	 *  depth: <float>, // Depth to extrude the shape
	 *
	 *  bevelEnabled: <bool>, // turn on bevel
	 *  bevelThickness: <float>, // how deep into the original shape bevel goes
	 *  bevelSize: <float>, // how far from shape outline is bevel
	 *  bevelSegments: <int>, // number of bevel layers
	 *
	 *  extrudePath: <THREE.Curve> // curve to extrude shape along
	 *
	 *  UVGenerator: <Object> // object that provides UV generator functions
	 *
	 * }
	 */
  
	// ExtrudeGeometry
  
	function ExtrudeGeometry( shapes, options ) {
  
		Geometry.call( this );
  
		this.type = 'ExtrudeGeometry';
  
		this.parameters = {
			shapes: shapes,
			options: options
		};
  
		this.fromBufferGeometry( new ExtrudeBufferGeometry( shapes, options ) );
		this.mergeVertices();
  
	}
  
	ExtrudeGeometry.prototype = Object.create( Geometry.prototype );
	ExtrudeGeometry.prototype.constructor = ExtrudeGeometry;
  
	ExtrudeGeometry.prototype.toJSON = function () {
  
		var data = Geometry.prototype.toJSON.call( this );
  
		var shapes = this.parameters.shapes;
		var options = this.parameters.options;
  
		return toJSON( shapes, options, data );
  
	};
  
	// ExtrudeBufferGeometry
  
	function ExtrudeBufferGeometry( shapes, options ) {
  
		BufferGeometry.call( this );
  
		this.type = 'ExtrudeBufferGeometry';
  
		this.parameters = {
			shapes: shapes,
			options: options
		};
  
		shapes = Array.isArray( shapes ) ? shapes : [ shapes ];
  
		var scope = this;
  
		var verticesArray = [];
		var uvArray = [];
  
		for ( var i = 0, l = shapes.length; i < l; i ++ ) {
  
			var shape = shapes[ i ];
			addShape( shape );
  
		}
  
		// build geometry
  
		this.addAttribute( 'position', new Float32BufferAttribute( verticesArray, 3 ) );
		this.addAttribute( 'uv', new Float32BufferAttribute( uvArray, 2 ) );
  
		this.computeVertexNormals();
  
		// functions
  
		function addShape( shape ) {
  
			var placeholder = [];
  
			// options
  
			var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12;
			var steps = options.steps !== undefined ? options.steps : 1;
			var depth = options.depth !== undefined ? options.depth : 100;
  
			var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true;
			var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6;
			var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2;
			var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3;
  
			var extrudePath = options.extrudePath;
  
			var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : WorldUVGenerator;
  
			// deprecated options
  
			if ( options.amount !== undefined ) {
  
				console.warn( 'THREE.ExtrudeBufferGeometry: amount has been renamed to depth.' );
				depth = options.amount;
  
			}
  
			//
  
			var extrudePts, extrudeByPath = false;
			var splineTube, binormal, normal, position2;
  
			if ( extrudePath ) {
  
				extrudePts = extrudePath.getSpacedPoints( steps );
  
				extrudeByPath = true;
				bevelEnabled = false; // bevels not supported for path extrusion
  
				// SETUP TNB variables
  
				// TODO1 - have a .isClosed in spline?
  
				splineTube = extrudePath.computeFrenetFrames( steps, false );
  
				// console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length);
  
				binormal = new Vector3();
				normal = new Vector3();
				position2 = new Vector3();
  
			}
  
			// Safeguards if bevels are not enabled
  
			if ( ! bevelEnabled ) {
  
				bevelSegments = 0;
				bevelThickness = 0;
				bevelSize = 0;
  
			}
  
			// Variables initialization
  
			var ahole, h, hl; // looping of holes
  
			var shapePoints = shape.extractPoints( curveSegments );
  
			var vertices = shapePoints.shape;
			var holes = shapePoints.holes;
  
			var reverse = ! ShapeUtils.isClockWise( vertices );
  
			if ( reverse ) {
  
				vertices = vertices.reverse();
  
				// Maybe we should also check if holes are in the opposite direction, just to be safe ...
  
				for ( h = 0, hl = holes.length; h < hl; h ++ ) {
  
					ahole = holes[ h ];
  
					if ( ShapeUtils.isClockWise( ahole ) ) {
  
						holes[ h ] = ahole.reverse();
  
					}
  
				}
  
			}
  
  
			var faces = ShapeUtils.triangulateShape( vertices, holes );
  
			/* Vertices */
  
			var contour = vertices; // vertices has all points but contour has only points of circumference
  
			for ( h = 0, hl = holes.length; h < hl; h ++ ) {
  
				ahole = holes[ h ];
  
				vertices = vertices.concat( ahole );
  
			}
  
  
			function scalePt2( pt, vec, size ) {
  
				if ( ! vec ) console.error( "THREE.ExtrudeGeometry: vec does not exist" );
  
				return vec.clone().multiplyScalar( size ).add( pt );
  
			}
  
			var b, bs, t, z,
				vert, vlen = vertices.length,
				face, flen = faces.length;
  
  
			// Find directions for point movement
  
  
			function getBevelVec( inPt, inPrev, inNext ) {
  
				// computes for inPt the corresponding point inPt' on a new contour
				//   shifted by 1 unit (length of normalized vector) to the left
				// if we walk along contour clockwise, this new contour is outside the old one
				//
				// inPt' is the intersection of the two lines parallel to the two
				//  adjacent edges of inPt at a distance of 1 unit on the left side.
  
				var v_trans_x, v_trans_y, shrink_by; // resulting translation vector for inPt
  
				// good reading for geometry algorithms (here: line-line intersection)
				// http://geomalgorithms.com/a05-_intersect-1.html
  
				var v_prev_x = inPt.x - inPrev.x,
					v_prev_y = inPt.y - inPrev.y;
				var v_next_x = inNext.x - inPt.x,
					v_next_y = inNext.y - inPt.y;
  
				var v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y );
  
				// check for collinear edges
				var collinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x );
  
				if ( Math.abs( collinear0 ) > Number.EPSILON ) {
  
					// not collinear
  
					// length of vectors for normalizing
  
					var v_prev_len = Math.sqrt( v_prev_lensq );
					var v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y );
  
					// shift adjacent points by unit vectors to the left
  
					var ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len );
					var ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len );
  
					var ptNextShift_x = ( inNext.x - v_next_y / v_next_len );
					var ptNextShift_y = ( inNext.y + v_next_x / v_next_len );
  
					// scaling factor for v_prev to intersection point
  
					var sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y -
							( ptNextShift_y - ptPrevShift_y ) * v_next_x ) /
						( v_prev_x * v_next_y - v_prev_y * v_next_x );
  
					// vector from inPt to intersection point
  
					v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x );
					v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y );
  
					// Don't normalize!, otherwise sharp corners become ugly
					//  but prevent crazy spikes
					var v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y );
					if ( v_trans_lensq <= 2 ) {
  
						return new Vector2( v_trans_x, v_trans_y );
  
					} else {
  
						shrink_by = Math.sqrt( v_trans_lensq / 2 );
  
					}
  
				} else {
  
					// handle special case of collinear edges
  
					var direction_eq = false; // assumes: opposite
					if ( v_prev_x > Number.EPSILON ) {
  
						if ( v_next_x > Number.EPSILON ) {
  
							direction_eq = true;
  
						}
  
					} else {
  
						if ( v_prev_x < - Number.EPSILON ) {
  
							if ( v_next_x < - Number.EPSILON ) {
  
								direction_eq = true;
  
							}
  
						} else {
  
							if ( Math.sign( v_prev_y ) === Math.sign( v_next_y ) ) {
  
								direction_eq = true;
  
							}
  
						}
  
					}
  
					if ( direction_eq ) {
  
						// console.log("Warning: lines are a straight sequence");
						v_trans_x = - v_prev_y;
						v_trans_y = v_prev_x;
						shrink_by = Math.sqrt( v_prev_lensq );
  
					} else {
  
						// console.log("Warning: lines are a straight spike");
						v_trans_x = v_prev_x;
						v_trans_y = v_prev_y;
						shrink_by = Math.sqrt( v_prev_lensq / 2 );
  
					}
  
				}
  
				return new Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by );
  
			}
  
  
			var contourMovements = [];
  
			for ( var i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {
  
				if ( j === il ) j = 0;
				if ( k === il ) k = 0;
  
				//  (j)---(i)---(k)
				// console.log('i,j,k', i, j , k)
  
				contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] );
  
			}
  
			var holesMovements = [],
				oneHoleMovements, verticesMovements = contourMovements.concat();
  
			for ( h = 0, hl = holes.length; h < hl; h ++ ) {
  
				ahole = holes[ h ];
  
				oneHoleMovements = [];
  
				for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {
  
					if ( j === il ) j = 0;
					if ( k === il ) k = 0;
  
					//  (j)---(i)---(k)
					oneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] );
  
				}
  
				holesMovements.push( oneHoleMovements );
				verticesMovements = verticesMovements.concat( oneHoleMovements );
  
			}
  
  
			// Loop bevelSegments, 1 for the front, 1 for the back
  
			for ( b = 0; b < bevelSegments; b ++ ) {
  
				//for ( b = bevelSegments; b > 0; b -- ) {
  
				t = b / bevelSegments;
				z = bevelThickness * Math.cos( t * Math.PI / 2 );
				bs = bevelSize * Math.sin( t * Math.PI / 2 );
  
				// contract shape
  
				for ( i = 0, il = contour.length; i < il; i ++ ) {
  
					vert = scalePt2( contour[ i ], contourMovements[ i ], bs );
  
					v( vert.x, vert.y, - z );
  
				}
  
				// expand holes
  
				for ( h = 0, hl = holes.length; h < hl; h ++ ) {
  
					ahole = holes[ h ];
					oneHoleMovements = holesMovements[ h ];
  
					for ( i = 0, il = ahole.length; i < il; i ++ ) {
  
						vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );
  
						v( vert.x, vert.y, - z );
  
					}
  
				}
  
			}
  
			bs = bevelSize;
  
			// Back facing vertices
  
			for ( i = 0; i < vlen; i ++ ) {
  
				vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];
  
				if ( ! extrudeByPath ) {
  
					v( vert.x, vert.y, 0 );
  
				} else {
  
					// v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x );
  
					normal.copy( splineTube.normals[ 0 ] ).multiplyScalar( vert.x );
					binormal.copy( splineTube.binormals[ 0 ] ).multiplyScalar( vert.y );
  
					position2.copy( extrudePts[ 0 ] ).add( normal ).add( binormal );
  
					v( position2.x, position2.y, position2.z );
  
				}
  
			}
  
			// Add stepped vertices...
			// Including front facing vertices
  
			var s;
  
			for ( s = 1; s <= steps; s ++ ) {
  
				for ( i = 0; i < vlen; i ++ ) {
  
					vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];
  
					if ( ! extrudeByPath ) {
  
						v( vert.x, vert.y, depth / steps * s );
  
					} else {
  
						// v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x );
  
						normal.copy( splineTube.normals[ s ] ).multiplyScalar( vert.x );
						binormal.copy( splineTube.binormals[ s ] ).multiplyScalar( vert.y );
  
						position2.copy( extrudePts[ s ] ).add( normal ).add( binormal );
  
						v( position2.x, position2.y, position2.z );
  
					}
  
				}
  
			}
  
  
			// Add bevel segments planes
  
			//for ( b = 1; b <= bevelSegments; b ++ ) {
			for ( b = bevelSegments - 1; b >= 0; b -- ) {
  
				t = b / bevelSegments;
				z = bevelThickness * Math.cos( t * Math.PI / 2 );
				bs = bevelSize * Math.sin( t * Math.PI / 2 );
  
				// contract shape
  
				for ( i = 0, il = contour.length; i < il; i ++ ) {
  
					vert = scalePt2( contour[ i ], contourMovements[ i ], bs );
					v( vert.x, vert.y, depth + z );
  
				}
  
				// expand holes
  
				for ( h = 0, hl = holes.length; h < hl; h ++ ) {
  
					ahole = holes[ h ];
					oneHoleMovements = holesMovements[ h ];
  
					for ( i = 0, il = ahole.length; i < il; i ++ ) {
  
						vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );
  
						if ( ! extrudeByPath ) {
  
							v( vert.x, vert.y, depth + z );
  
						} else {
  
							v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z );
  
						}
  
					}
  
				}
  
			}
  
			/* Faces */
  
			// Top and bottom faces
  
			buildLidFaces();
  
			// Sides faces
  
			buildSideFaces();
  
  
			/////  Internal functions
  
			function buildLidFaces() {
  
				var start = verticesArray.length / 3;
  
				if ( bevelEnabled ) {
  
					var layer = 0; // steps + 1
					var offset = vlen * layer;
  
					// Bottom faces
  
					for ( i = 0; i < flen; i ++ ) {
  
						face = faces[ i ];
						f3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset );
  
					}
  
					layer = steps + bevelSegments * 2;
					offset = vlen * layer;
  
					// Top faces
  
					for ( i = 0; i < flen; i ++ ) {
  
						face = faces[ i ];
						f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset );
  
					}
  
				} else {
  
					// Bottom faces
  
					for ( i = 0; i < flen; i ++ ) {
  
						face = faces[ i ];
						f3( face[ 2 ], face[ 1 ], face[ 0 ] );
  
					}
  
					// Top faces
  
					for ( i = 0; i < flen; i ++ ) {
  
						face = faces[ i ];
						f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps );
  
					}
  
				}
  
				scope.addGroup( start, verticesArray.length / 3 - start, 0 );
  
			}
  
			// Create faces for the z-sides of the shape
  
			function buildSideFaces() {
  
				var start = verticesArray.length / 3;
				var layeroffset = 0;
				sidewalls( contour, layeroffset );
				layeroffset += contour.length;
  
				for ( h = 0, hl = holes.length; h < hl; h ++ ) {
  
					ahole = holes[ h ];
					sidewalls( ahole, layeroffset );
  
					//, true
					layeroffset += ahole.length;
  
				}
  
  
				scope.addGroup( start, verticesArray.length / 3 - start, 1 );
  
  
			}
  
			function sidewalls( contour, layeroffset ) {
  
				var j, k;
				i = contour.length;
  
				while ( -- i >= 0 ) {
  
					j = i;
					k = i - 1;
					if ( k < 0 ) k = contour.length - 1;
  
					//console.log('b', i,j, i-1, k,vertices.length);
  
					var s = 0,
						sl = steps + bevelSegments * 2;
  
					for ( s = 0; s < sl; s ++ ) {
  
						var slen1 = vlen * s;
						var slen2 = vlen * ( s + 1 );
  
						var a = layeroffset + j + slen1,
							b = layeroffset + k + slen1,
							c = layeroffset + k + slen2,
							d = layeroffset + j + slen2;
  
						f4( a, b, c, d );
  
					}
  
				}
  
			}
  
			function v( x, y, z ) {
  
				placeholder.push( x );
				placeholder.push( y );
				placeholder.push( z );
  
			}
  
  
			function f3( a, b, c ) {
  
				addVertex( a );
				addVertex( b );
				addVertex( c );
  
				var nextIndex = verticesArray.length / 3;
				var uvs = uvgen.generateTopUV( scope, verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1 );
  
				addUV( uvs[ 0 ] );
				addUV( uvs[ 1 ] );
				addUV( uvs[ 2 ] );
  
			}
  
			function f4( a, b, c, d ) {
  
				addVertex( a );
				addVertex( b );
				addVertex( d );
  
				addVertex( b );
				addVertex( c );
				addVertex( d );
  
  
				var nextIndex = verticesArray.length / 3;
				var uvs = uvgen.generateSideWallUV( scope, verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1 );
  
				addUV( uvs[ 0 ] );
				addUV( uvs[ 1 ] );
				addUV( uvs[ 3 ] );
  
				addUV( uvs[ 1 ] );
				addUV( uvs[ 2 ] );
				addUV( uvs[ 3 ] );
  
			}
  
			function addVertex( index ) {
  
				verticesArray.push( placeholder[ index * 3 + 0 ] );
				verticesArray.push( placeholder[ index * 3 + 1 ] );
				verticesArray.push( placeholder[ index * 3 + 2 ] );
  
			}
  
  
			function addUV( vector2 ) {
  
				uvArray.push( vector2.x );
				uvArray.push( vector2.y );
  
			}
  
		}
  
	}
  
	ExtrudeBufferGeometry.prototype = Object.create( BufferGeometry.prototype );
	ExtrudeBufferGeometry.prototype.constructor = ExtrudeBufferGeometry;
  
	ExtrudeBufferGeometry.prototype.toJSON = function () {
  
		var data = BufferGeometry.prototype.toJSON.call( this );
  
		var shapes = this.parameters.shapes;
		var options = this.parameters.options;
  
		return toJSON( shapes, options, data );
  
	};
  
	//
  
	var WorldUVGenerator = {
  
		generateTopUV: function ( geometry, vertices, indexA, indexB, indexC ) {
  
			var a_x = vertices[ indexA * 3 ];
			var a_y = vertices[ indexA * 3 + 1 ];
			var b_x = vertices[ indexB * 3 ];
			var b_y = vertices[ indexB * 3 + 1 ];
			var c_x = vertices[ indexC * 3 ];
			var c_y = vertices[ indexC * 3 + 1 ];
  
			return [
				new Vector2( a_x, a_y ),
				new Vector2( b_x, b_y ),
				new Vector2( c_x, c_y )
			];
  
		},
  
		generateSideWallUV: function ( geometry, vertices, indexA, indexB, indexC, indexD ) {
  
			var a_x = vertices[ indexA * 3 ];
			var a_y = vertices[ indexA * 3 + 1 ];
			var a_z = vertices[ indexA * 3 + 2 ];
			var b_x = vertices[ indexB * 3 ];
			var b_y = vertices[ indexB * 3 + 1 ];
			var b_z = vertices[ indexB * 3 + 2 ];
			var c_x = vertices[ indexC * 3 ];
			var c_y = vertices[ indexC * 3 + 1 ];
			var c_z = vertices[ indexC * 3 + 2 ];
			var d_x = vertices[ indexD * 3 ];
			var d_y = vertices[ indexD * 3 + 1 ];
			var d_z = vertices[ indexD * 3 + 2 ];
  
			if ( Math.abs( a_y - b_y ) < 0.01 ) {
  
				return [
					new Vector2( a_x, 1 - a_z ),
					new Vector2( b_x, 1 - b_z ),
					new Vector2( c_x, 1 - c_z ),
					new Vector2( d_x, 1 - d_z )
				];
  
			} else {
  
				return [
					new Vector2( a_y, 1 - a_z ),
					new Vector2( b_y, 1 - b_z ),
					new Vector2( c_y, 1 - c_z ),
					new Vector2( d_y, 1 - d_z )
				];
  
			}
  
		}
	};
  
	function toJSON( shapes, options, data ) {
  
		//
  
		data.shapes = [];
  
		if ( Array.isArray( shapes ) ) {
  
			for ( var i = 0, l = shapes.length; i < l; i ++ ) {
  
				var shape = shapes[ i ];
  
				data.shapes.push( shape.uuid );
  
			}
  
		} else {
  
			data.shapes.push( shapes.uuid );
  
		}
  
		//
  
		if ( options.extrudePath !== undefined ) data.options.extrudePath = options.extrudePath.toJSON();
  
		return data;
  
	}
  
	/**
	 * @author zz85 / http://www.lab4games.net/zz85/blog
	 * @author alteredq / http://alteredqualia.com/
	 *
	 * Text = 3D Text
	 *
	 * parameters = {
	 *  font: <THREE.Font>, // font
	 *
	 *  size: <float>, // size of the text
	 *  height: <float>, // thickness to extrude text
	 *  curveSegments: <int>, // number of points on the curves
	 *
	 *  bevelEnabled: <bool>, // turn on bevel
	 *  bevelThickness: <float>, // how deep into text bevel goes
	 *  bevelSize: <float> // how far from text outline is bevel
	 * }
	 */
  
	// TextGeometry
  
	function TextGeometry( text, parameters ) {
  
		Geometry.call( this );
  
		this.type = 'TextGeometry';
  
		this.parameters = {
			text: text,
			parameters: parameters
		};
  
		this.fromBufferGeometry( new TextBufferGeometry( text, parameters ) );
		this.mergeVertices();
  
	}
  
	TextGeometry.prototype = Object.create( Geometry.prototype );
	TextGeometry.prototype.constructor = TextGeometry;
  
	// TextBufferGeometry
  
	function TextBufferGeometry( text, parameters ) {
  
		parameters = parameters || {};
  
		var font = parameters.font;
  
		if ( ! ( font && font.isFont ) ) {
  
			console.error( 'THREE.TextGeometry: font parameter is not an instance of THREE.Font.' );
			return new Geometry();
  
		}
  
		var shapes = font.generateShapes( text, parameters.size, parameters.curveSegments );
  
		// translate parameters to ExtrudeGeometry API
  
		parameters.depth = parameters.height !== undefined ? parameters.height : 50;
  
		// defaults
  
		if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10;
		if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8;
		if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false;
  
		ExtrudeBufferGeometry.call( this, shapes, parameters );
  
		this.type = 'TextBufferGeometry';
  
	}
  
	TextBufferGeometry.prototype = Object.create( ExtrudeBufferGeometry.prototype );
	TextBufferGeometry.prototype.constructor = TextBufferGeometry;
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 * @author benaadams / https://twitter.com/ben_a_adams
	 * @author Mugen87 / https://github.com/Mugen87
	 */
  
	// SphereGeometry
  
	function SphereGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) {
  
		Geometry.call( this );
  
		this.type = 'SphereGeometry';
  
		this.parameters = {
			radius: radius,
			widthSegments: widthSegments,
			heightSegments: heightSegments,
			phiStart: phiStart,
			phiLength: phiLength,
			thetaStart: thetaStart,
			thetaLength: thetaLength
		};
  
		this.fromBufferGeometry( new SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) );
		this.mergeVertices();
  
	}
  
	SphereGeometry.prototype = Object.create( Geometry.prototype );
	SphereGeometry.prototype.constructor = SphereGeometry;
  
	// SphereBufferGeometry
  
	function SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) {
  
		BufferGeometry.call( this );
  
		this.type = 'SphereBufferGeometry';
  
		this.parameters = {
			radius: radius,
			widthSegments: widthSegments,
			heightSegments: heightSegments,
			phiStart: phiStart,
			phiLength: phiLength,
			thetaStart: thetaStart,
			thetaLength: thetaLength
		};
  
		radius = radius || 1;
  
		widthSegments = Math.max( 3, Math.floor( widthSegments ) || 8 );
		heightSegments = Math.max( 2, Math.floor( heightSegments ) || 6 );
  
		phiStart = phiStart !== undefined ? phiStart : 0;
		phiLength = phiLength !== undefined ? phiLength : Math.PI * 2;
  
		thetaStart = thetaStart !== undefined ? thetaStart : 0;
		thetaLength = thetaLength !== undefined ? thetaLength : Math.PI;
  
		var thetaEnd = thetaStart + thetaLength;
  
		var ix, iy;
  
		var index = 0;
		var grid = [];
  
		var vertex = new Vector3();
		var normal = new Vector3();
  
		// buffers
  
		var indices = [];
		var vertices = [];
		var normals = [];
		var uvs = [];
  
		// generate vertices, normals and uvs
  
		for ( iy = 0; iy <= heightSegments; iy ++ ) {
  
			var verticesRow = [];
  
			var v = iy / heightSegments;
  
			for ( ix = 0; ix <= widthSegments; ix ++ ) {
  
				var u = ix / widthSegments;
  
				// vertex
  
				vertex.x = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength );
				vertex.y = radius * Math.cos( thetaStart + v * thetaLength );
				vertex.z = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength );
  
				vertices.push( vertex.x, vertex.y, vertex.z );
  
				// normal
  
				normal.set( vertex.x, vertex.y, vertex.z ).normalize();
				normals.push( normal.x, normal.y, normal.z );
  
				// uv
  
				uvs.push( u, 1 - v );
  
				verticesRow.push( index ++ );
  
			}
  
			grid.push( verticesRow );
  
		}
  
		// indices
  
		for ( iy = 0; iy < heightSegments; iy ++ ) {
  
			for ( ix = 0; ix < widthSegments; ix ++ ) {
  
				var a = grid[ iy ][ ix + 1 ];
				var b = grid[ iy ][ ix ];
				var c = grid[ iy + 1 ][ ix ];
				var d = grid[ iy + 1 ][ ix + 1 ];
  
				if ( iy !== 0 || thetaStart > 0 ) indices.push( a, b, d );
				if ( iy !== heightSegments - 1 || thetaEnd < Math.PI ) indices.push( b, c, d );
  
			}
  
		}
  
		// build geometry
  
		this.setIndex( indices );
		this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
		this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
		this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
  
	}
  
	SphereBufferGeometry.prototype = Object.create( BufferGeometry.prototype );
	SphereBufferGeometry.prototype.constructor = SphereBufferGeometry;
  
	/**
	 * @author Kaleb Murphy
	 * @author Mugen87 / https://github.com/Mugen87
	 */
  
	// RingGeometry
  
	function RingGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) {
  
		Geometry.call( this );
  
		this.type = 'RingGeometry';
  
		this.parameters = {
			innerRadius: innerRadius,
			outerRadius: outerRadius,
			thetaSegments: thetaSegments,
			phiSegments: phiSegments,
			thetaStart: thetaStart,
			thetaLength: thetaLength
		};
  
		this.fromBufferGeometry( new RingBufferGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) );
		this.mergeVertices();
  
	}
  
	RingGeometry.prototype = Object.create( Geometry.prototype );
	RingGeometry.prototype.constructor = RingGeometry;
  
	// RingBufferGeometry
  
	function RingBufferGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) {
  
		BufferGeometry.call( this );
  
		this.type = 'RingBufferGeometry';
  
		this.parameters = {
			innerRadius: innerRadius,
			outerRadius: outerRadius,
			thetaSegments: thetaSegments,
			phiSegments: phiSegments,
			thetaStart: thetaStart,
			thetaLength: thetaLength
		};
  
		innerRadius = innerRadius || 0.5;
		outerRadius = outerRadius || 1;
  
		thetaStart = thetaStart !== undefined ? thetaStart : 0;
		thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2;
  
		thetaSegments = thetaSegments !== undefined ? Math.max( 3, thetaSegments ) : 8;
		phiSegments = phiSegments !== undefined ? Math.max( 1, phiSegments ) : 1;
  
		// buffers
  
		var indices = [];
		var vertices = [];
		var normals = [];
		var uvs = [];
  
		// some helper variables
  
		var segment;
		var radius = innerRadius;
		var radiusStep = ( ( outerRadius - innerRadius ) / phiSegments );
		var vertex = new Vector3();
		var uv = new Vector2();
		var j, i;
  
		// generate vertices, normals and uvs
  
		for ( j = 0; j <= phiSegments; j ++ ) {
  
			for ( i = 0; i <= thetaSegments; i ++ ) {
  
				// values are generate from the inside of the ring to the outside
  
				segment = thetaStart + i / thetaSegments * thetaLength;
  
				// vertex
  
				vertex.x = radius * Math.cos( segment );
				vertex.y = radius * Math.sin( segment );
  
				vertices.push( vertex.x, vertex.y, vertex.z );
  
				// normal
  
				normals.push( 0, 0, 1 );
  
				// uv
  
				uv.x = ( vertex.x / outerRadius + 1 ) / 2;
				uv.y = ( vertex.y / outerRadius + 1 ) / 2;
  
				uvs.push( uv.x, uv.y );
  
			}
  
			// increase the radius for next row of vertices
  
			radius += radiusStep;
  
		}
  
		// indices
  
		for ( j = 0; j < phiSegments; j ++ ) {
  
			var thetaSegmentLevel = j * ( thetaSegments + 1 );
  
			for ( i = 0; i < thetaSegments; i ++ ) {
  
				segment = i + thetaSegmentLevel;
  
				var a = segment;
				var b = segment + thetaSegments + 1;
				var c = segment + thetaSegments + 2;
				var d = segment + 1;
  
				// faces
  
				indices.push( a, b, d );
				indices.push( b, c, d );
  
			}
  
		}
  
		// build geometry
  
		this.setIndex( indices );
		this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
		this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
		this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
  
	}
  
	RingBufferGeometry.prototype = Object.create( BufferGeometry.prototype );
	RingBufferGeometry.prototype.constructor = RingBufferGeometry;
  
	/**
	 * @author astrodud / http://astrodud.isgreat.org/
	 * @author zz85 / https://github.com/zz85
	 * @author bhouston / http://clara.io
	 * @author Mugen87 / https://github.com/Mugen87
	 */
  
	// LatheGeometry
  
	function LatheGeometry( points, segments, phiStart, phiLength ) {
  
		Geometry.call( this );
  
		this.type = 'LatheGeometry';
  
		this.parameters = {
			points: points,
			segments: segments,
			phiStart: phiStart,
			phiLength: phiLength
		};
  
		this.fromBufferGeometry( new LatheBufferGeometry( points, segments, phiStart, phiLength ) );
		this.mergeVertices();
  
	}
  
	LatheGeometry.prototype = Object.create( Geometry.prototype );
	LatheGeometry.prototype.constructor = LatheGeometry;
  
	// LatheBufferGeometry
  
	function LatheBufferGeometry( points, segments, phiStart, phiLength ) {
  
		BufferGeometry.call( this );
  
		this.type = 'LatheBufferGeometry';
  
		this.parameters = {
			points: points,
			segments: segments,
			phiStart: phiStart,
			phiLength: phiLength
		};
  
		segments = Math.floor( segments ) || 12;
		phiStart = phiStart || 0;
		phiLength = phiLength || Math.PI * 2;
  
		// clamp phiLength so it's in range of [ 0, 2PI ]
  
		phiLength = _Math.clamp( phiLength, 0, Math.PI * 2 );
  
  
		// buffers
  
		var indices = [];
		var vertices = [];
		var uvs = [];
  
		// helper variables
  
		var base;
		var inverseSegments = 1.0 / segments;
		var vertex = new Vector3();
		var uv = new Vector2();
		var i, j;
  
		// generate vertices and uvs
  
		for ( i = 0; i <= segments; i ++ ) {
  
			var phi = phiStart + i * inverseSegments * phiLength;
  
			var sin = Math.sin( phi );
			var cos = Math.cos( phi );
  
			for ( j = 0; j <= ( points.length - 1 ); j ++ ) {
  
				// vertex
  
				vertex.x = points[ j ].x * sin;
				vertex.y = points[ j ].y;
				vertex.z = points[ j ].x * cos;
  
				vertices.push( vertex.x, vertex.y, vertex.z );
  
				// uv
  
				uv.x = i / segments;
				uv.y = j / ( points.length - 1 );
  
				uvs.push( uv.x, uv.y );
  
  
			}
  
		}
  
		// indices
  
		for ( i = 0; i < segments; i ++ ) {
  
			for ( j = 0; j < ( points.length - 1 ); j ++ ) {
  
				base = j + i * points.length;
  
				var a = base;
				var b = base + points.length;
				var c = base + points.length + 1;
				var d = base + 1;
  
				// faces
  
				indices.push( a, b, d );
				indices.push( b, c, d );
  
			}
  
		}
  
		// build geometry
  
		this.setIndex( indices );
		this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
		this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
  
		// generate normals
  
		this.computeVertexNormals();
  
		// if the geometry is closed, we need to average the normals along the seam.
		// because the corresponding vertices are identical (but still have different UVs).
  
		if ( phiLength === Math.PI * 2 ) {
  
			var normals = this.attributes.normal.array;
			var n1 = new Vector3();
			var n2 = new Vector3();
			var n = new Vector3();
  
			// this is the buffer offset for the last line of vertices
  
			base = segments * points.length * 3;
  
			for ( i = 0, j = 0; i < points.length; i ++, j += 3 ) {
  
				// select the normal of the vertex in the first line
  
				n1.x = normals[ j + 0 ];
				n1.y = normals[ j + 1 ];
				n1.z = normals[ j + 2 ];
  
				// select the normal of the vertex in the last line
  
				n2.x = normals[ base + j + 0 ];
				n2.y = normals[ base + j + 1 ];
				n2.z = normals[ base + j + 2 ];
  
				// average normals
  
				n.addVectors( n1, n2 ).normalize();
  
				// assign the new values to both normals
  
				normals[ j + 0 ] = normals[ base + j + 0 ] = n.x;
				normals[ j + 1 ] = normals[ base + j + 1 ] = n.y;
				normals[ j + 2 ] = normals[ base + j + 2 ] = n.z;
  
			}
  
		}
  
	}
  
	LatheBufferGeometry.prototype = Object.create( BufferGeometry.prototype );
	LatheBufferGeometry.prototype.constructor = LatheBufferGeometry;
  
	/**
	 * @author jonobr1 / http://jonobr1.com
	 * @author Mugen87 / https://github.com/Mugen87
	 */
  
	// ShapeGeometry
  
	function ShapeGeometry( shapes, curveSegments ) {
  
		Geometry.call( this );
  
		this.type = 'ShapeGeometry';
  
		if ( typeof curveSegments === 'object' ) {
  
			console.warn( 'THREE.ShapeGeometry: Options parameter has been removed.' );
  
			curveSegments = curveSegments.curveSegments;
  
		}
  
		this.parameters = {
			shapes: shapes,
			curveSegments: curveSegments
		};
  
		this.fromBufferGeometry( new ShapeBufferGeometry( shapes, curveSegments ) );
		this.mergeVertices();
  
	}
  
	ShapeGeometry.prototype = Object.create( Geometry.prototype );
	ShapeGeometry.prototype.constructor = ShapeGeometry;
  
	ShapeGeometry.prototype.toJSON = function () {
  
		var data = Geometry.prototype.toJSON.call( this );
  
		var shapes = this.parameters.shapes;
  
		return toJSON$1( shapes, data );
  
	};
  
	// ShapeBufferGeometry
  
	function ShapeBufferGeometry( shapes, curveSegments ) {
  
		BufferGeometry.call( this );
  
		this.type = 'ShapeBufferGeometry';
  
		this.parameters = {
			shapes: shapes,
			curveSegments: curveSegments
		};
  
		curveSegments = curveSegments || 12;
  
		// buffers
  
		var indices = [];
		var vertices = [];
		var normals = [];
		var uvs = [];
  
		// helper variables
  
		var groupStart = 0;
		var groupCount = 0;
  
		// allow single and array values for "shapes" parameter
  
		if ( Array.isArray( shapes ) === false ) {
  
			addShape( shapes );
  
		} else {
  
			for ( var i = 0; i < shapes.length; i ++ ) {
  
				addShape( shapes[ i ] );
  
				this.addGroup( groupStart, groupCount, i ); // enables MultiMaterial support
  
				groupStart += groupCount;
				groupCount = 0;
  
			}
  
		}
  
		// build geometry
  
		this.setIndex( indices );
		this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
		this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
		this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
  
  
		// helper functions
  
		function addShape( shape ) {
  
			var i, l, shapeHole;
  
			var indexOffset = vertices.length / 3;
			var points = shape.extractPoints( curveSegments );
  
			var shapeVertices = points.shape;
			var shapeHoles = points.holes;
  
			// check direction of vertices
  
			if ( ShapeUtils.isClockWise( shapeVertices ) === false ) {
  
				shapeVertices = shapeVertices.reverse();
  
				// also check if holes are in the opposite direction
  
				for ( i = 0, l = shapeHoles.length; i < l; i ++ ) {
  
					shapeHole = shapeHoles[ i ];
  
					if ( ShapeUtils.isClockWise( shapeHole ) === true ) {
  
						shapeHoles[ i ] = shapeHole.reverse();
  
					}
  
				}
  
			}
  
			var faces = ShapeUtils.triangulateShape( shapeVertices, shapeHoles );
  
			// join vertices of inner and outer paths to a single array
  
			for ( i = 0, l = shapeHoles.length; i < l; i ++ ) {
  
				shapeHole = shapeHoles[ i ];
				shapeVertices = shapeVertices.concat( shapeHole );
  
			}
  
			// vertices, normals, uvs
  
			for ( i = 0, l = shapeVertices.length; i < l; i ++ ) {
  
				var vertex = shapeVertices[ i ];
  
				vertices.push( vertex.x, vertex.y, 0 );
				normals.push( 0, 0, 1 );
				uvs.push( vertex.x, vertex.y ); // world uvs
  
			}
  
			// incides
  
			for ( i = 0, l = faces.length; i < l; i ++ ) {
  
				var face = faces[ i ];
  
				var a = face[ 0 ] + indexOffset;
				var b = face[ 1 ] + indexOffset;
				var c = face[ 2 ] + indexOffset;
  
				indices.push( a, b, c );
				groupCount += 3;
  
			}
  
		}
  
	}
  
	ShapeBufferGeometry.prototype = Object.create( BufferGeometry.prototype );
	ShapeBufferGeometry.prototype.constructor = ShapeBufferGeometry;
  
	ShapeBufferGeometry.prototype.toJSON = function () {
  
		var data = BufferGeometry.prototype.toJSON.call( this );
  
		var shapes = this.parameters.shapes;
  
		return toJSON$1( shapes, data );
  
	};
  
	//
  
	function toJSON$1( shapes, data ) {
  
		data.shapes = [];
  
		if ( Array.isArray( shapes ) ) {
  
			for ( var i = 0, l = shapes.length; i < l; i ++ ) {
  
				var shape = shapes[ i ];
  
				data.shapes.push( shape.uuid );
  
			}
  
		} else {
  
			data.shapes.push( shapes.uuid );
  
		}
  
		return data;
  
	}
  
	/**
	 * @author WestLangley / http://github.com/WestLangley
	 * @author Mugen87 / https://github.com/Mugen87
	 */
  
	function EdgesGeometry( geometry, thresholdAngle ) {
  
		BufferGeometry.call( this );
  
		this.type = 'EdgesGeometry';
  
		this.parameters = {
			thresholdAngle: thresholdAngle
		};
  
		thresholdAngle = ( thresholdAngle !== undefined ) ? thresholdAngle : 1;
  
		// buffer
  
		var vertices = [];
  
		// helper variables
  
		var thresholdDot = Math.cos( _Math.DEG2RAD * thresholdAngle );
		var edge = [ 0, 0 ], edges = {}, edge1, edge2;
		var key, keys = [ 'a', 'b', 'c' ];
  
		// prepare source geometry
  
		var geometry2;
  
		if ( geometry.isBufferGeometry ) {
  
			geometry2 = new Geometry();
			geometry2.fromBufferGeometry( geometry );
  
		} else {
  
			geometry2 = geometry.clone();
  
		}
  
		geometry2.mergeVertices();
		geometry2.computeFaceNormals();
  
		var sourceVertices = geometry2.vertices;
		var faces = geometry2.faces;
  
		// now create a data structure where each entry represents an edge with its adjoining faces
  
		for ( var i = 0, l = faces.length; i < l; i ++ ) {
  
			var face = faces[ i ];
  
			for ( var j = 0; j < 3; j ++ ) {
  
				edge1 = face[ keys[ j ] ];
				edge2 = face[ keys[ ( j + 1 ) % 3 ] ];
				edge[ 0 ] = Math.min( edge1, edge2 );
				edge[ 1 ] = Math.max( edge1, edge2 );
  
				key = edge[ 0 ] + ',' + edge[ 1 ];
  
				if ( edges[ key ] === undefined ) {
  
					edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ], face1: i, face2: undefined };
  
				} else {
  
					edges[ key ].face2 = i;
  
				}
  
			}
  
		}
  
		// generate vertices
  
		for ( key in edges ) {
  
			var e = edges[ key ];
  
			// an edge is only rendered if the angle (in degrees) between the face normals of the adjoining faces exceeds this value. default = 1 degree.
  
			if ( e.face2 === undefined || faces[ e.face1 ].normal.dot( faces[ e.face2 ].normal ) <= thresholdDot ) {
  
				var vertex = sourceVertices[ e.index1 ];
				vertices.push( vertex.x, vertex.y, vertex.z );
  
				vertex = sourceVertices[ e.index2 ];
				vertices.push( vertex.x, vertex.y, vertex.z );
  
			}
  
		}
  
		// build geometry
  
		this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
  
	}
  
	EdgesGeometry.prototype = Object.create( BufferGeometry.prototype );
	EdgesGeometry.prototype.constructor = EdgesGeometry;
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 * @author Mugen87 / https://github.com/Mugen87
	 */
  
	// CylinderGeometry
  
	function CylinderGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) {
  
		Geometry.call( this );
  
		this.type = 'CylinderGeometry';
  
		this.parameters = {
			radiusTop: radiusTop,
			radiusBottom: radiusBottom,
			height: height,
			radialSegments: radialSegments,
			heightSegments: heightSegments,
			openEnded: openEnded,
			thetaStart: thetaStart,
			thetaLength: thetaLength
		};
  
		this.fromBufferGeometry( new CylinderBufferGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) );
		this.mergeVertices();
  
	}
  
	CylinderGeometry.prototype = Object.create( Geometry.prototype );
	CylinderGeometry.prototype.constructor = CylinderGeometry;
  
	// CylinderBufferGeometry
  
	function CylinderBufferGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) {
  
		BufferGeometry.call( this );
  
		this.type = 'CylinderBufferGeometry';
  
		this.parameters = {
			radiusTop: radiusTop,
			radiusBottom: radiusBottom,
			height: height,
			radialSegments: radialSegments,
			heightSegments: heightSegments,
			openEnded: openEnded,
			thetaStart: thetaStart,
			thetaLength: thetaLength
		};
  
		var scope = this;
  
		radiusTop = radiusTop !== undefined ? radiusTop : 1;
		radiusBottom = radiusBottom !== undefined ? radiusBottom : 1;
		height = height || 1;
  
		radialSegments = Math.floor( radialSegments ) || 8;
		heightSegments = Math.floor( heightSegments ) || 1;
  
		openEnded = openEnded !== undefined ? openEnded : false;
		thetaStart = thetaStart !== undefined ? thetaStart : 0.0;
		thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2;
  
		// buffers
  
		var indices = [];
		var vertices = [];
		var normals = [];
		var uvs = [];
  
		// helper variables
  
		var index = 0;
		var indexArray = [];
		var halfHeight = height / 2;
		var groupStart = 0;
  
		// generate geometry
  
		generateTorso();
  
		if ( openEnded === false ) {
  
			if ( radiusTop > 0 ) generateCap( true );
			if ( radiusBottom > 0 ) generateCap( false );
  
		}
  
		// build geometry
  
		this.setIndex( indices );
		this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
		this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
		this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
  
		function generateTorso() {
  
			var x, y;
			var normal = new Vector3();
			var vertex = new Vector3();
  
			var groupCount = 0;
  
			// this will be used to calculate the normal
			var slope = ( radiusBottom - radiusTop ) / height;
  
			// generate vertices, normals and uvs
  
			for ( y = 0; y <= heightSegments; y ++ ) {
  
				var indexRow = [];
  
				var v = y / heightSegments;
  
				// calculate the radius of the current row
  
				var radius = v * ( radiusBottom - radiusTop ) + radiusTop;
  
				for ( x = 0; x <= radialSegments; x ++ ) {
  
					var u = x / radialSegments;
  
					var theta = u * thetaLength + thetaStart;
  
					var sinTheta = Math.sin( theta );
					var cosTheta = Math.cos( theta );
  
					// vertex
  
					vertex.x = radius * sinTheta;
					vertex.y = - v * height + halfHeight;
					vertex.z = radius * cosTheta;
					vertices.push( vertex.x, vertex.y, vertex.z );
  
					// normal
  
					normal.set( sinTheta, slope, cosTheta ).normalize();
					normals.push( normal.x, normal.y, normal.z );
  
					// uv
  
					uvs.push( u, 1 - v );
  
					// save index of vertex in respective row
  
					indexRow.push( index ++ );
  
				}
  
				// now save vertices of the row in our index array
  
				indexArray.push( indexRow );
  
			}
  
			// generate indices
  
			for ( x = 0; x < radialSegments; x ++ ) {
  
				for ( y = 0; y < heightSegments; y ++ ) {
  
					// we use the index array to access the correct indices
  
					var a = indexArray[ y ][ x ];
					var b = indexArray[ y + 1 ][ x ];
					var c = indexArray[ y + 1 ][ x + 1 ];
					var d = indexArray[ y ][ x + 1 ];
  
					// faces
  
					indices.push( a, b, d );
					indices.push( b, c, d );
  
					// update group counter
  
					groupCount += 6;
  
				}
  
			}
  
			// add a group to the geometry. this will ensure multi material support
  
			scope.addGroup( groupStart, groupCount, 0 );
  
			// calculate new start value for groups
  
			groupStart += groupCount;
  
		}
  
		function generateCap( top ) {
  
			var x, centerIndexStart, centerIndexEnd;
  
			var uv = new Vector2();
			var vertex = new Vector3();
  
			var groupCount = 0;
  
			var radius = ( top === true ) ? radiusTop : radiusBottom;
			var sign = ( top === true ) ? 1 : - 1;
  
			// save the index of the first center vertex
			centerIndexStart = index;
  
			// first we generate the center vertex data of the cap.
			// because the geometry needs one set of uvs per face,
			// we must generate a center vertex per face/segment
  
			for ( x = 1; x <= radialSegments; x ++ ) {
  
				// vertex
  
				vertices.push( 0, halfHeight * sign, 0 );
  
				// normal
  
				normals.push( 0, sign, 0 );
  
				// uv
  
				uvs.push( 0.5, 0.5 );
  
				// increase index
  
				index ++;
  
			}
  
			// save the index of the last center vertex
  
			centerIndexEnd = index;
  
			// now we generate the surrounding vertices, normals and uvs
  
			for ( x = 0; x <= radialSegments; x ++ ) {
  
				var u = x / radialSegments;
				var theta = u * thetaLength + thetaStart;
  
				var cosTheta = Math.cos( theta );
				var sinTheta = Math.sin( theta );
  
				// vertex
  
				vertex.x = radius * sinTheta;
				vertex.y = halfHeight * sign;
				vertex.z = radius * cosTheta;
				vertices.push( vertex.x, vertex.y, vertex.z );
  
				// normal
  
				normals.push( 0, sign, 0 );
  
				// uv
  
				uv.x = ( cosTheta * 0.5 ) + 0.5;
				uv.y = ( sinTheta * 0.5 * sign ) + 0.5;
				uvs.push( uv.x, uv.y );
  
				// increase index
  
				index ++;
  
			}
  
			// generate indices
  
			for ( x = 0; x < radialSegments; x ++ ) {
  
				var c = centerIndexStart + x;
				var i = centerIndexEnd + x;
  
				if ( top === true ) {
  
					// face top
  
					indices.push( i, i + 1, c );
  
				} else {
  
					// face bottom
  
					indices.push( i + 1, i, c );
  
				}
  
				groupCount += 3;
  
			}
  
			// add a group to the geometry. this will ensure multi material support
  
			scope.addGroup( groupStart, groupCount, top === true ? 1 : 2 );
  
			// calculate new start value for groups
  
			groupStart += groupCount;
  
		}
  
	}
  
	CylinderBufferGeometry.prototype = Object.create( BufferGeometry.prototype );
	CylinderBufferGeometry.prototype.constructor = CylinderBufferGeometry;
  
	/**
	 * @author abelnation / http://github.com/abelnation
	 */
  
	// ConeGeometry
  
	function ConeGeometry( radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) {
  
		CylinderGeometry.call( this, 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength );
  
		this.type = 'ConeGeometry';
  
		this.parameters = {
			radius: radius,
			height: height,
			radialSegments: radialSegments,
			heightSegments: heightSegments,
			openEnded: openEnded,
			thetaStart: thetaStart,
			thetaLength: thetaLength
		};
  
	}
  
	ConeGeometry.prototype = Object.create( CylinderGeometry.prototype );
	ConeGeometry.prototype.constructor = ConeGeometry;
  
	// ConeBufferGeometry
  
	function ConeBufferGeometry( radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) {
  
		CylinderBufferGeometry.call( this, 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength );
  
		this.type = 'ConeBufferGeometry';
  
		this.parameters = {
			radius: radius,
			height: height,
			radialSegments: radialSegments,
			heightSegments: heightSegments,
			openEnded: openEnded,
			thetaStart: thetaStart,
			thetaLength: thetaLength
		};
  
	}
  
	ConeBufferGeometry.prototype = Object.create( CylinderBufferGeometry.prototype );
	ConeBufferGeometry.prototype.constructor = ConeBufferGeometry;
  
	/**
	 * @author benaadams / https://twitter.com/ben_a_adams
	 * @author Mugen87 / https://github.com/Mugen87
	 * @author hughes
	 */
  
	// CircleGeometry
  
	function CircleGeometry( radius, segments, thetaStart, thetaLength ) {
  
		Geometry.call( this );
  
		this.type = 'CircleGeometry';
  
		this.parameters = {
			radius: radius,
			segments: segments,
			thetaStart: thetaStart,
			thetaLength: thetaLength
		};
  
		this.fromBufferGeometry( new CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) );
		this.mergeVertices();
  
	}
  
	CircleGeometry.prototype = Object.create( Geometry.prototype );
	CircleGeometry.prototype.constructor = CircleGeometry;
  
	// CircleBufferGeometry
  
	function CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) {
  
		BufferGeometry.call( this );
  
		this.type = 'CircleBufferGeometry';
  
		this.parameters = {
			radius: radius,
			segments: segments,
			thetaStart: thetaStart,
			thetaLength: thetaLength
		};
  
		radius = radius || 1;
		segments = segments !== undefined ? Math.max( 3, segments ) : 8;
  
		thetaStart = thetaStart !== undefined ? thetaStart : 0;
		thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2;
  
		// buffers
  
		var indices = [];
		var vertices = [];
		var normals = [];
		var uvs = [];
  
		// helper variables
  
		var i, s;
		var vertex = new Vector3();
		var uv = new Vector2();
  
		// center point
  
		vertices.push( 0, 0, 0 );
		normals.push( 0, 0, 1 );
		uvs.push( 0.5, 0.5 );
  
		for ( s = 0, i = 3; s <= segments; s ++, i += 3 ) {
  
			var segment = thetaStart + s / segments * thetaLength;
  
			// vertex
  
			vertex.x = radius * Math.cos( segment );
			vertex.y = radius * Math.sin( segment );
  
			vertices.push( vertex.x, vertex.y, vertex.z );
  
			// normal
  
			normals.push( 0, 0, 1 );
  
			// uvs
  
			uv.x = ( vertices[ i ] / radius + 1 ) / 2;
			uv.y = ( vertices[ i + 1 ] / radius + 1 ) / 2;
  
			uvs.push( uv.x, uv.y );
  
		}
  
		// indices
  
		for ( i = 1; i <= segments; i ++ ) {
  
			indices.push( i, i + 1, 0 );
  
		}
  
		// build geometry
  
		this.setIndex( indices );
		this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
		this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
		this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
  
	}
  
	CircleBufferGeometry.prototype = Object.create( BufferGeometry.prototype );
	CircleBufferGeometry.prototype.constructor = CircleBufferGeometry;
  
  
  
	var Geometries = /*#__PURE__*/Object.freeze({
		WireframeGeometry: WireframeGeometry,
		ParametricGeometry: ParametricGeometry,
		ParametricBufferGeometry: ParametricBufferGeometry,
		TetrahedronGeometry: TetrahedronGeometry,
		TetrahedronBufferGeometry: TetrahedronBufferGeometry,
		OctahedronGeometry: OctahedronGeometry,
		OctahedronBufferGeometry: OctahedronBufferGeometry,
		IcosahedronGeometry: IcosahedronGeometry,
		IcosahedronBufferGeometry: IcosahedronBufferGeometry,
		DodecahedronGeometry: DodecahedronGeometry,
		DodecahedronBufferGeometry: DodecahedronBufferGeometry,
		PolyhedronGeometry: PolyhedronGeometry,
		PolyhedronBufferGeometry: PolyhedronBufferGeometry,
		TubeGeometry: TubeGeometry,
		TubeBufferGeometry: TubeBufferGeometry,
		TorusKnotGeometry: TorusKnotGeometry,
		TorusKnotBufferGeometry: TorusKnotBufferGeometry,
		TorusGeometry: TorusGeometry,
		TorusBufferGeometry: TorusBufferGeometry,
		TextGeometry: TextGeometry,
		TextBufferGeometry: TextBufferGeometry,
		SphereGeometry: SphereGeometry,
		SphereBufferGeometry: SphereBufferGeometry,
		RingGeometry: RingGeometry,
		RingBufferGeometry: RingBufferGeometry,
		PlaneGeometry: PlaneGeometry,
		PlaneBufferGeometry: PlaneBufferGeometry,
		LatheGeometry: LatheGeometry,
		LatheBufferGeometry: LatheBufferGeometry,
		ShapeGeometry: ShapeGeometry,
		ShapeBufferGeometry: ShapeBufferGeometry,
		ExtrudeGeometry: ExtrudeGeometry,
		ExtrudeBufferGeometry: ExtrudeBufferGeometry,
		EdgesGeometry: EdgesGeometry,
		ConeGeometry: ConeGeometry,
		ConeBufferGeometry: ConeBufferGeometry,
		CylinderGeometry: CylinderGeometry,
		CylinderBufferGeometry: CylinderBufferGeometry,
		CircleGeometry: CircleGeometry,
		CircleBufferGeometry: CircleBufferGeometry,
		BoxGeometry: BoxGeometry,
		BoxBufferGeometry: BoxBufferGeometry
	});
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 *
	 * parameters = {
	 *  color: <THREE.Color>
	 * }
	 */
  
	function ShadowMaterial( parameters ) {
  
		Material.call( this );
  
		this.type = 'ShadowMaterial';
  
		this.color = new Color( 0x000000 );
		this.transparent = true;
  
		this.setValues( parameters );
  
	}
  
	ShadowMaterial.prototype = Object.create( Material.prototype );
	ShadowMaterial.prototype.constructor = ShadowMaterial;
  
	ShadowMaterial.prototype.isShadowMaterial = true;
  
	ShadowMaterial.prototype.copy = function ( source ) {
  
		Material.prototype.copy.call( this, source );
  
		this.color.copy( source.color );
  
		return this;
  
	};
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function RawShaderMaterial( parameters ) {
  
		ShaderMaterial.call( this, parameters );
  
		this.type = 'RawShaderMaterial';
  
	}
  
	RawShaderMaterial.prototype = Object.create( ShaderMaterial.prototype );
	RawShaderMaterial.prototype.constructor = RawShaderMaterial;
  
	RawShaderMaterial.prototype.isRawShaderMaterial = true;
  
	/**
	 * @author WestLangley / http://github.com/WestLangley
	 *
	 * parameters = {
	 *  color: <hex>,
	 *  roughness: <float>,
	 *  metalness: <float>,
	 *  opacity: <float>,
	 *
	 *  map: new THREE.Texture( <Image> ),
	 *
	 *  lightMap: new THREE.Texture( <Image> ),
	 *  lightMapIntensity: <float>
	 *
	 *  aoMap: new THREE.Texture( <Image> ),
	 *  aoMapIntensity: <float>
	 *
	 *  emissive: <hex>,
	 *  emissiveIntensity: <float>
	 *  emissiveMap: new THREE.Texture( <Image> ),
	 *
	 *  bumpMap: new THREE.Texture( <Image> ),
	 *  bumpScale: <float>,
	 *
	 *  normalMap: new THREE.Texture( <Image> ),
	 *  normalScale: <Vector2>,
	 *
	 *  displacementMap: new THREE.Texture( <Image> ),
	 *  displacementScale: <float>,
	 *  displacementBias: <float>,
	 *
	 *  roughnessMap: new THREE.Texture( <Image> ),
	 *
	 *  metalnessMap: new THREE.Texture( <Image> ),
	 *
	 *  alphaMap: new THREE.Texture( <Image> ),
	 *
	 *  envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ),
	 *  envMapIntensity: <float>
	 *
	 *  refractionRatio: <float>,
	 *
	 *  wireframe: <boolean>,
	 *  wireframeLinewidth: <float>,
	 *
	 *  skinning: <bool>,
	 *  morphTargets: <bool>,
	 *  morphNormals: <bool>
	 * }
	 */
  
	function MeshStandardMaterial( parameters ) {
  
		Material.call( this );
  
		this.defines = { 'STANDARD': '' };
  
		this.type = 'MeshStandardMaterial';
  
		this.color = new Color( 0xffffff ); // diffuse
		this.roughness = 0.5;
		this.metalness = 0.5;
  
		this.map = null;
  
		this.lightMap = null;
		this.lightMapIntensity = 1.0;
  
		this.aoMap = null;
		this.aoMapIntensity = 1.0;
  
		this.emissive = new Color( 0x000000 );
		this.emissiveIntensity = 1.0;
		this.emissiveMap = null;
  
		this.bumpMap = null;
		this.bumpScale = 1;
  
		this.normalMap = null;
		this.normalScale = new Vector2( 1, 1 );
  
		this.displacementMap = null;
		this.displacementScale = 1;
		this.displacementBias = 0;
  
		this.roughnessMap = null;
  
		this.metalnessMap = null;
  
		this.alphaMap = null;
  
		this.envMap = null;
		this.envMapIntensity = 1.0;
  
		this.refractionRatio = 0.98;
  
		this.wireframe = false;
		this.wireframeLinewidth = 1;
		this.wireframeLinecap = 'round';
		this.wireframeLinejoin = 'round';
  
		this.skinning = false;
		this.morphTargets = false;
		this.morphNormals = false;
  
		this.setValues( parameters );
  
	}
  
	MeshStandardMaterial.prototype = Object.create( Material.prototype );
	MeshStandardMaterial.prototype.constructor = MeshStandardMaterial;
  
	MeshStandardMaterial.prototype.isMeshStandardMaterial = true;
  
	MeshStandardMaterial.prototype.copy = function ( source ) {
  
		Material.prototype.copy.call( this, source );
  
		this.defines = { 'STANDARD': '' };
  
		this.color.copy( source.color );
		this.roughness = source.roughness;
		this.metalness = source.metalness;
  
		this.map = source.map;
  
		this.lightMap = source.lightMap;
		this.lightMapIntensity = source.lightMapIntensity;
  
		this.aoMap = source.aoMap;
		this.aoMapIntensity = source.aoMapIntensity;
  
		this.emissive.copy( source.emissive );
		this.emissiveMap = source.emissiveMap;
		this.emissiveIntensity = source.emissiveIntensity;
  
		this.bumpMap = source.bumpMap;
		this.bumpScale = source.bumpScale;
  
		this.normalMap = source.normalMap;
		this.normalScale.copy( source.normalScale );
  
		this.displacementMap = source.displacementMap;
		this.displacementScale = source.displacementScale;
		this.displacementBias = source.displacementBias;
  
		this.roughnessMap = source.roughnessMap;
  
		this.metalnessMap = source.metalnessMap;
  
		this.alphaMap = source.alphaMap;
  
		this.envMap = source.envMap;
		this.envMapIntensity = source.envMapIntensity;
  
		this.refractionRatio = source.refractionRatio;
  
		this.wireframe = source.wireframe;
		this.wireframeLinewidth = source.wireframeLinewidth;
		this.wireframeLinecap = source.wireframeLinecap;
		this.wireframeLinejoin = source.wireframeLinejoin;
  
		this.skinning = source.skinning;
		this.morphTargets = source.morphTargets;
		this.morphNormals = source.morphNormals;
  
		return this;
  
	};
  
	/**
	 * @author WestLangley / http://github.com/WestLangley
	 *
	 * parameters = {
	 *  reflectivity: <float>
	 * }
	 */
  
	function MeshPhysicalMaterial( parameters ) {
  
		MeshStandardMaterial.call( this );
  
		this.defines = { 'PHYSICAL': '' };
  
		this.type = 'MeshPhysicalMaterial';
  
		this.reflectivity = 0.5; // maps to F0 = 0.04
  
		this.clearCoat = 0.0;
		this.clearCoatRoughness = 0.0;
  
		this.setValues( parameters );
  
	}
  
	MeshPhysicalMaterial.prototype = Object.create( MeshStandardMaterial.prototype );
	MeshPhysicalMaterial.prototype.constructor = MeshPhysicalMaterial;
  
	MeshPhysicalMaterial.prototype.isMeshPhysicalMaterial = true;
  
	MeshPhysicalMaterial.prototype.copy = function ( source ) {
  
		MeshStandardMaterial.prototype.copy.call( this, source );
  
		this.defines = { 'PHYSICAL': '' };
  
		this.reflectivity = source.reflectivity;
  
		this.clearCoat = source.clearCoat;
		this.clearCoatRoughness = source.clearCoatRoughness;
  
		return this;
  
	};
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 * @author alteredq / http://alteredqualia.com/
	 *
	 * parameters = {
	 *  color: <hex>,
	 *  specular: <hex>,
	 *  shininess: <float>,
	 *  opacity: <float>,
	 *
	 *  map: new THREE.Texture( <Image> ),
	 *
	 *  lightMap: new THREE.Texture( <Image> ),
	 *  lightMapIntensity: <float>
	 *
	 *  aoMap: new THREE.Texture( <Image> ),
	 *  aoMapIntensity: <float>
	 *
	 *  emissive: <hex>,
	 *  emissiveIntensity: <float>
	 *  emissiveMap: new THREE.Texture( <Image> ),
	 *
	 *  bumpMap: new THREE.Texture( <Image> ),
	 *  bumpScale: <float>,
	 *
	 *  normalMap: new THREE.Texture( <Image> ),
	 *  normalScale: <Vector2>,
	 *
	 *  displacementMap: new THREE.Texture( <Image> ),
	 *  displacementScale: <float>,
	 *  displacementBias: <float>,
	 *
	 *  specularMap: new THREE.Texture( <Image> ),
	 *
	 *  alphaMap: new THREE.Texture( <Image> ),
	 *
	 *  envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ),
	 *  combine: THREE.Multiply,
	 *  reflectivity: <float>,
	 *  refractionRatio: <float>,
	 *
	 *  wireframe: <boolean>,
	 *  wireframeLinewidth: <float>,
	 *
	 *  skinning: <bool>,
	 *  morphTargets: <bool>,
	 *  morphNormals: <bool>
	 * }
	 */
  
	function MeshPhongMaterial( parameters ) {
  
		Material.call( this );
  
		this.type = 'MeshPhongMaterial';
  
		this.color = new Color( 0xffffff ); // diffuse
		this.specular = new Color( 0x111111 );
		this.shininess = 30;
  
		this.map = null;
  
		this.lightMap = null;
		this.lightMapIntensity = 1.0;
  
		this.aoMap = null;
		this.aoMapIntensity = 1.0;
  
		this.emissive = new Color( 0x000000 );
		this.emissiveIntensity = 1.0;
		this.emissiveMap = null;
  
		this.bumpMap = null;
		this.bumpScale = 1;
  
		this.normalMap = null;
		this.normalScale = new Vector2( 1, 1 );
  
		this.displacementMap = null;
		this.displacementScale = 1;
		this.displacementBias = 0;
  
		this.specularMap = null;
  
		this.alphaMap = null;
  
		this.envMap = null;
		this.combine = MultiplyOperation;
		this.reflectivity = 1;
		this.refractionRatio = 0.98;
  
		this.wireframe = false;
		this.wireframeLinewidth = 1;
		this.wireframeLinecap = 'round';
		this.wireframeLinejoin = 'round';
  
		this.skinning = false;
		this.morphTargets = false;
		this.morphNormals = false;
  
		this.setValues( parameters );
  
	}
  
	MeshPhongMaterial.prototype = Object.create( Material.prototype );
	MeshPhongMaterial.prototype.constructor = MeshPhongMaterial;
  
	MeshPhongMaterial.prototype.isMeshPhongMaterial = true;
  
	MeshPhongMaterial.prototype.copy = function ( source ) {
  
		Material.prototype.copy.call( this, source );
  
		this.color.copy( source.color );
		this.specular.copy( source.specular );
		this.shininess = source.shininess;
  
		this.map = source.map;
  
		this.lightMap = source.lightMap;
		this.lightMapIntensity = source.lightMapIntensity;
  
		this.aoMap = source.aoMap;
		this.aoMapIntensity = source.aoMapIntensity;
  
		this.emissive.copy( source.emissive );
		this.emissiveMap = source.emissiveMap;
		this.emissiveIntensity = source.emissiveIntensity;
  
		this.bumpMap = source.bumpMap;
		this.bumpScale = source.bumpScale;
  
		this.normalMap = source.normalMap;
		this.normalScale.copy( source.normalScale );
  
		this.displacementMap = source.displacementMap;
		this.displacementScale = source.displacementScale;
		this.displacementBias = source.displacementBias;
  
		this.specularMap = source.specularMap;
  
		this.alphaMap = source.alphaMap;
  
		this.envMap = source.envMap;
		this.combine = source.combine;
		this.reflectivity = source.reflectivity;
		this.refractionRatio = source.refractionRatio;
  
		this.wireframe = source.wireframe;
		this.wireframeLinewidth = source.wireframeLinewidth;
		this.wireframeLinecap = source.wireframeLinecap;
		this.wireframeLinejoin = source.wireframeLinejoin;
  
		this.skinning = source.skinning;
		this.morphTargets = source.morphTargets;
		this.morphNormals = source.morphNormals;
  
		return this;
  
	};
  
	/**
	 * @author takahirox / http://github.com/takahirox
	 *
	 * parameters = {
	 *  gradientMap: new THREE.Texture( <Image> )
	 * }
	 */
  
	function MeshToonMaterial( parameters ) {
  
		MeshPhongMaterial.call( this );
  
		this.defines = { 'TOON': '' };
  
		this.type = 'MeshToonMaterial';
  
		this.gradientMap = null;
  
		this.setValues( parameters );
  
	}
  
	MeshToonMaterial.prototype = Object.create( MeshPhongMaterial.prototype );
	MeshToonMaterial.prototype.constructor = MeshToonMaterial;
  
	MeshToonMaterial.prototype.isMeshToonMaterial = true;
  
	MeshToonMaterial.prototype.copy = function ( source ) {
  
		MeshPhongMaterial.prototype.copy.call( this, source );
  
		this.gradientMap = source.gradientMap;
  
		return this;
  
	};
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 * @author WestLangley / http://github.com/WestLangley
	 *
	 * parameters = {
	 *  opacity: <float>,
	 *
	 *  bumpMap: new THREE.Texture( <Image> ),
	 *  bumpScale: <float>,
	 *
	 *  normalMap: new THREE.Texture( <Image> ),
	 *  normalScale: <Vector2>,
	 *
	 *  displacementMap: new THREE.Texture( <Image> ),
	 *  displacementScale: <float>,
	 *  displacementBias: <float>,
	 *
	 *  wireframe: <boolean>,
	 *  wireframeLinewidth: <float>
	 *
	 *  skinning: <bool>,
	 *  morphTargets: <bool>,
	 *  morphNormals: <bool>
	 * }
	 */
  
	function MeshNormalMaterial( parameters ) {
  
		Material.call( this );
  
		this.type = 'MeshNormalMaterial';
  
		this.bumpMap = null;
		this.bumpScale = 1;
  
		this.normalMap = null;
		this.normalScale = new Vector2( 1, 1 );
  
		this.displacementMap = null;
		this.displacementScale = 1;
		this.displacementBias = 0;
  
		this.wireframe = false;
		this.wireframeLinewidth = 1;
  
		this.fog = false;
		this.lights = false;
  
		this.skinning = false;
		this.morphTargets = false;
		this.morphNormals = false;
  
		this.setValues( parameters );
  
	}
  
	MeshNormalMaterial.prototype = Object.create( Material.prototype );
	MeshNormalMaterial.prototype.constructor = MeshNormalMaterial;
  
	MeshNormalMaterial.prototype.isMeshNormalMaterial = true;
  
	MeshNormalMaterial.prototype.copy = function ( source ) {
  
		Material.prototype.copy.call( this, source );
  
		this.bumpMap = source.bumpMap;
		this.bumpScale = source.bumpScale;
  
		this.normalMap = source.normalMap;
		this.normalScale.copy( source.normalScale );
  
		this.displacementMap = source.displacementMap;
		this.displacementScale = source.displacementScale;
		this.displacementBias = source.displacementBias;
  
		this.wireframe = source.wireframe;
		this.wireframeLinewidth = source.wireframeLinewidth;
  
		this.skinning = source.skinning;
		this.morphTargets = source.morphTargets;
		this.morphNormals = source.morphNormals;
  
		return this;
  
	};
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 * @author alteredq / http://alteredqualia.com/
	 *
	 * parameters = {
	 *  color: <hex>,
	 *  opacity: <float>,
	 *
	 *  map: new THREE.Texture( <Image> ),
	 *
	 *  lightMap: new THREE.Texture( <Image> ),
	 *  lightMapIntensity: <float>
	 *
	 *  aoMap: new THREE.Texture( <Image> ),
	 *  aoMapIntensity: <float>
	 *
	 *  emissive: <hex>,
	 *  emissiveIntensity: <float>
	 *  emissiveMap: new THREE.Texture( <Image> ),
	 *
	 *  specularMap: new THREE.Texture( <Image> ),
	 *
	 *  alphaMap: new THREE.Texture( <Image> ),
	 *
	 *  envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ),
	 *  combine: THREE.Multiply,
	 *  reflectivity: <float>,
	 *  refractionRatio: <float>,
	 *
	 *  wireframe: <boolean>,
	 *  wireframeLinewidth: <float>,
	 *
	 *  skinning: <bool>,
	 *  morphTargets: <bool>,
	 *  morphNormals: <bool>
	 * }
	 */
  
	function MeshLambertMaterial( parameters ) {
  
		Material.call( this );
  
		this.type = 'MeshLambertMaterial';
  
		this.color = new Color( 0xffffff ); // diffuse
  
		this.map = null;
  
		this.lightMap = null;
		this.lightMapIntensity = 1.0;
  
		this.aoMap = null;
		this.aoMapIntensity = 1.0;
  
		this.emissive = new Color( 0x000000 );
		this.emissiveIntensity = 1.0;
		this.emissiveMap = null;
  
		this.specularMap = null;
  
		this.alphaMap = null;
  
		this.envMap = null;
		this.combine = MultiplyOperation;
		this.reflectivity = 1;
		this.refractionRatio = 0.98;
  
		this.wireframe = false;
		this.wireframeLinewidth = 1;
		this.wireframeLinecap = 'round';
		this.wireframeLinejoin = 'round';
  
		this.skinning = false;
		this.morphTargets = false;
		this.morphNormals = false;
  
		this.setValues( parameters );
  
	}
  
	MeshLambertMaterial.prototype = Object.create( Material.prototype );
	MeshLambertMaterial.prototype.constructor = MeshLambertMaterial;
  
	MeshLambertMaterial.prototype.isMeshLambertMaterial = true;
  
	MeshLambertMaterial.prototype.copy = function ( source ) {
  
		Material.prototype.copy.call( this, source );
  
		this.color.copy( source.color );
  
		this.map = source.map;
  
		this.lightMap = source.lightMap;
		this.lightMapIntensity = source.lightMapIntensity;
  
		this.aoMap = source.aoMap;
		this.aoMapIntensity = source.aoMapIntensity;
  
		this.emissive.copy( source.emissive );
		this.emissiveMap = source.emissiveMap;
		this.emissiveIntensity = source.emissiveIntensity;
  
		this.specularMap = source.specularMap;
  
		this.alphaMap = source.alphaMap;
  
		this.envMap = source.envMap;
		this.combine = source.combine;
		this.reflectivity = source.reflectivity;
		this.refractionRatio = source.refractionRatio;
  
		this.wireframe = source.wireframe;
		this.wireframeLinewidth = source.wireframeLinewidth;
		this.wireframeLinecap = source.wireframeLinecap;
		this.wireframeLinejoin = source.wireframeLinejoin;
  
		this.skinning = source.skinning;
		this.morphTargets = source.morphTargets;
		this.morphNormals = source.morphNormals;
  
		return this;
  
	};
  
	/**
	 * @author alteredq / http://alteredqualia.com/
	 *
	 * parameters = {
	 *  color: <hex>,
	 *  opacity: <float>,
	 *
	 *  linewidth: <float>,
	 *
	 *  scale: <float>,
	 *  dashSize: <float>,
	 *  gapSize: <float>
	 * }
	 */
  
	function LineDashedMaterial( parameters ) {
  
		LineBasicMaterial.call( this );
  
		this.type = 'LineDashedMaterial';
  
		this.scale = 1;
		this.dashSize = 3;
		this.gapSize = 1;
  
		this.setValues( parameters );
  
	}
  
	LineDashedMaterial.prototype = Object.create( LineBasicMaterial.prototype );
	LineDashedMaterial.prototype.constructor = LineDashedMaterial;
  
	LineDashedMaterial.prototype.isLineDashedMaterial = true;
  
	LineDashedMaterial.prototype.copy = function ( source ) {
  
		LineBasicMaterial.prototype.copy.call( this, source );
  
		this.scale = source.scale;
		this.dashSize = source.dashSize;
		this.gapSize = source.gapSize;
  
		return this;
  
	};
  
  
  
	var Materials = /*#__PURE__*/Object.freeze({
		ShadowMaterial: ShadowMaterial,
		SpriteMaterial: SpriteMaterial,
		RawShaderMaterial: RawShaderMaterial,
		ShaderMaterial: ShaderMaterial,
		PointsMaterial: PointsMaterial,
		MeshPhysicalMaterial: MeshPhysicalMaterial,
		MeshStandardMaterial: MeshStandardMaterial,
		MeshPhongMaterial: MeshPhongMaterial,
		MeshToonMaterial: MeshToonMaterial,
		MeshNormalMaterial: MeshNormalMaterial,
		MeshLambertMaterial: MeshLambertMaterial,
		MeshDepthMaterial: MeshDepthMaterial,
		MeshDistanceMaterial: MeshDistanceMaterial,
		MeshBasicMaterial: MeshBasicMaterial,
		LineDashedMaterial: LineDashedMaterial,
		LineBasicMaterial: LineBasicMaterial,
		Material: Material
	});
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	var Cache = {
  
		enabled: false,
  
		files: {},
  
		add: function ( key, file ) {
  
			if ( this.enabled === false ) return;
  
			// console.log( 'THREE.Cache', 'Adding key:', key );
  
			this.files[ key ] = file;
  
		},
  
		get: function ( key ) {
  
			if ( this.enabled === false ) return;
  
			// console.log( 'THREE.Cache', 'Checking key:', key );
  
			return this.files[ key ];
  
		},
  
		remove: function ( key ) {
  
			delete this.files[ key ];
  
		},
  
		clear: function () {
  
			this.files = {};
  
		}
  
	};
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function LoadingManager( onLoad, onProgress, onError ) {
  
		var scope = this;
  
		var isLoading = false;
		var itemsLoaded = 0;
		var itemsTotal = 0;
		var urlModifier = undefined;
  
		this.onStart = undefined;
		this.onLoad = onLoad;
		this.onProgress = onProgress;
		this.onError = onError;
  
		this.itemStart = function ( url ) {
  
			itemsTotal ++;
  
			if ( isLoading === false ) {
  
				if ( scope.onStart !== undefined ) {
  
					scope.onStart( url, itemsLoaded, itemsTotal );
  
				}
  
			}
  
			isLoading = true;
  
		};
  
		this.itemEnd = function ( url ) {
  
			itemsLoaded ++;
  
			if ( scope.onProgress !== undefined ) {
  
				scope.onProgress( url, itemsLoaded, itemsTotal );
  
			}
  
			if ( itemsLoaded === itemsTotal ) {
  
				isLoading = false;
  
				if ( scope.onLoad !== undefined ) {
  
					scope.onLoad();
  
				}
  
			}
  
		};
  
		this.itemError = function ( url ) {
  
			if ( scope.onError !== undefined ) {
  
				scope.onError( url );
  
			}
  
		};
  
		this.resolveURL = function ( url ) {
  
			if ( urlModifier ) {
  
				return urlModifier( url );
  
			}
  
			return url;
  
		};
  
		this.setURLModifier = function ( transform ) {
  
			urlModifier = transform;
			return this;
  
		};
  
	}
  
	var DefaultLoadingManager = new LoadingManager();
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	var loading = {};
  
	function FileLoader( manager ) {
  
		this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;
  
	}
  
	Object.assign( FileLoader.prototype, {
  
		load: function ( url, onLoad, onProgress, onError ) {
  
			if ( url === undefined ) url = '';
  
			if ( this.path !== undefined ) url = this.path + url;
  
			url = this.manager.resolveURL( url );
  
			var scope = this;
  
			var cached = Cache.get( url );
  
			if ( cached !== undefined ) {
  
				scope.manager.itemStart( url );
  
				setTimeout( function () {
  
					if ( onLoad ) onLoad( cached );
  
					scope.manager.itemEnd( url );
  
				}, 0 );
  
				return cached;
  
			}
  
			// Check if request is duplicate
  
			if ( loading[ url ] !== undefined ) {
  
				loading[ url ].push( {
  
					onLoad: onLoad,
					onProgress: onProgress,
					onError: onError
  
				} );
  
				return;
  
			}
  
			// Check for data: URI
			var dataUriRegex = /^data:(.*?)(;base64)?,(.*)$/;
			var dataUriRegexResult = url.match( dataUriRegex );
  
			// Safari can not handle Data URIs through XMLHttpRequest so process manually
			if ( dataUriRegexResult ) {
  
				var mimeType = dataUriRegexResult[ 1 ];
				var isBase64 = !! dataUriRegexResult[ 2 ];
				var data = dataUriRegexResult[ 3 ];
  
				data = window.decodeURIComponent( data );
  
				if ( isBase64 ) data = window.atob( data );
  
				try {
  
					var response;
					var responseType = ( this.responseType || '' ).toLowerCase();
  
					switch ( responseType ) {
  
						case 'arraybuffer':
						case 'blob':
  
							var view = new Uint8Array( data.length );
  
							for ( var i = 0; i < data.length; i ++ ) {
  
								view[ i ] = data.charCodeAt( i );
  
							}
  
							if ( responseType === 'blob' ) {
  
								response = new Blob( [ view.buffer ], { type: mimeType } );
  
							} else {
  
								response = view.buffer;
  
							}
  
							break;
  
						case 'document':
  
							var parser = new DOMParser();
							response = parser.parseFromString( data, mimeType );
  
							break;
  
						case 'json':
  
							response = JSON.parse( data );
  
							break;
  
						default: // 'text' or other
  
							response = data;
  
							break;
  
					}
  
					// Wait for next browser tick like standard XMLHttpRequest event dispatching does
					window.setTimeout( function () {
  
						if ( onLoad ) onLoad( response );
  
						scope.manager.itemEnd( url );
  
					}, 0 );
  
				} catch ( error ) {
  
					// Wait for next browser tick like standard XMLHttpRequest event dispatching does
					window.setTimeout( function () {
  
						if ( onError ) onError( error );
  
						scope.manager.itemEnd( url );
						scope.manager.itemError( url );
  
					}, 0 );
  
				}
  
			} else {
  
				// Initialise array for duplicate requests
  
				loading[ url ] = [];
  
				loading[ url ].push( {
  
					onLoad: onLoad,
					onProgress: onProgress,
					onError: onError
  
				} );
  
				var request = new XMLHttpRequest();
  
				request.open( 'GET', url, true );
  
				request.addEventListener( 'load', function ( event ) {
  
					var response = this.response;
  
					Cache.add( url, response );
  
					var callbacks = loading[ url ];
  
					delete loading[ url ];
  
					if ( this.status === 200 || this.status === 0 ) {
  
						// Some browsers return HTTP Status 0 when using non-http protocol
						// e.g. 'file://' or 'data://'. Handle as success.
  
						if ( this.status === 0 ) console.warn( 'THREE.FileLoader: HTTP Status 0 received.' );
  
						for ( var i = 0, il = callbacks.length; i < il; i ++ ) {
  
							var callback = callbacks[ i ];
							if ( callback.onLoad ) callback.onLoad( response );
  
						}
  
						scope.manager.itemEnd( url );
  
					} else {
  
						for ( var i = 0, il = callbacks.length; i < il; i ++ ) {
  
							var callback = callbacks[ i ];
							if ( callback.onError ) callback.onError( event );
  
						}
  
						scope.manager.itemEnd( url );
						scope.manager.itemError( url );
  
					}
  
				}, false );
  
				request.addEventListener( 'progress', function ( event ) {
  
					var callbacks = loading[ url ];
  
					for ( var i = 0, il = callbacks.length; i < il; i ++ ) {
  
						var callback = callbacks[ i ];
						if ( callback.onProgress ) callback.onProgress( event );
  
					}
  
				}, false );
  
				request.addEventListener( 'error', function ( event ) {
  
					var callbacks = loading[ url ];
  
					delete loading[ url ];
  
					for ( var i = 0, il = callbacks.length; i < il; i ++ ) {
  
						var callback = callbacks[ i ];
						if ( callback.onError ) callback.onError( event );
  
					}
  
					scope.manager.itemEnd( url );
					scope.manager.itemError( url );
  
				}, false );
  
				if ( this.responseType !== undefined ) request.responseType = this.responseType;
				if ( this.withCredentials !== undefined ) request.withCredentials = this.withCredentials;
  
				if ( request.overrideMimeType ) request.overrideMimeType( this.mimeType !== undefined ? this.mimeType : 'text/plain' );
  
				for ( var header in this.requestHeader ) {
  
					request.setRequestHeader( header, this.requestHeader[ header ] );
  
				}
  
				request.send( null );
  
			}
  
			scope.manager.itemStart( url );
  
			return request;
  
		},
  
		setPath: function ( value ) {
  
			this.path = value;
			return this;
  
		},
  
		setResponseType: function ( value ) {
  
			this.responseType = value;
			return this;
  
		},
  
		setWithCredentials: function ( value ) {
  
			this.withCredentials = value;
			return this;
  
		},
  
		setMimeType: function ( value ) {
  
			this.mimeType = value;
			return this;
  
		},
  
		setRequestHeader: function ( value ) {
  
			this.requestHeader = value;
			return this;
  
		}
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 *
	 * Abstract Base class to block based textures loader (dds, pvr, ...)
	 */
  
	function CompressedTextureLoader( manager ) {
  
		this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;
  
		// override in sub classes
		this._parser = null;
  
	}
  
	Object.assign( CompressedTextureLoader.prototype, {
  
		load: function ( url, onLoad, onProgress, onError ) {
  
			var scope = this;
  
			var images = [];
  
			var texture = new CompressedTexture();
			texture.image = images;
  
			var loader = new FileLoader( this.manager );
			loader.setPath( this.path );
			loader.setResponseType( 'arraybuffer' );
  
			function loadTexture( i ) {
  
				loader.load( url[ i ], function ( buffer ) {
  
					var texDatas = scope._parser( buffer, true );
  
					images[ i ] = {
						width: texDatas.width,
						height: texDatas.height,
						format: texDatas.format,
						mipmaps: texDatas.mipmaps
					};
  
					loaded += 1;
  
					if ( loaded === 6 ) {
  
						if ( texDatas.mipmapCount === 1 )
							texture.minFilter = LinearFilter;
  
						texture.format = texDatas.format;
						texture.needsUpdate = true;
  
						if ( onLoad ) onLoad( texture );
  
					}
  
				}, onProgress, onError );
  
			}
  
			if ( Array.isArray( url ) ) {
  
				var loaded = 0;
  
				for ( var i = 0, il = url.length; i < il; ++ i ) {
  
					loadTexture( i );
  
				}
  
			} else {
  
				// compressed cubemap texture stored in a single DDS file
  
				loader.load( url, function ( buffer ) {
  
					var texDatas = scope._parser( buffer, true );
  
					if ( texDatas.isCubemap ) {
  
						var faces = texDatas.mipmaps.length / texDatas.mipmapCount;
  
						for ( var f = 0; f < faces; f ++ ) {
  
							images[ f ] = { mipmaps: [] };
  
							for ( var i = 0; i < texDatas.mipmapCount; i ++ ) {
  
								images[ f ].mipmaps.push( texDatas.mipmaps[ f * texDatas.mipmapCount + i ] );
								images[ f ].format = texDatas.format;
								images[ f ].width = texDatas.width;
								images[ f ].height = texDatas.height;
  
							}
  
						}
  
					} else {
  
						texture.image.width = texDatas.width;
						texture.image.height = texDatas.height;
						texture.mipmaps = texDatas.mipmaps;
  
					}
  
					if ( texDatas.mipmapCount === 1 ) {
  
						texture.minFilter = LinearFilter;
  
					}
  
					texture.format = texDatas.format;
					texture.needsUpdate = true;
  
					if ( onLoad ) onLoad( texture );
  
				}, onProgress, onError );
  
			}
  
			return texture;
  
		},
  
		setPath: function ( value ) {
  
			this.path = value;
			return this;
  
		}
  
	} );
  
	/**
	 * @author Nikos M. / https://github.com/foo123/
	 *
	 * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...)
	 */
  
	function DataTextureLoader( manager ) {
  
		this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;
  
		// override in sub classes
		this._parser = null;
  
	}
  
	Object.assign( DataTextureLoader.prototype, {
  
		load: function ( url, onLoad, onProgress, onError ) {
  
			var scope = this;
  
			var texture = new DataTexture();
  
			var loader = new FileLoader( this.manager );
			loader.setResponseType( 'arraybuffer' );
  
			loader.load( url, function ( buffer ) {
  
				var texData = scope._parser( buffer );
  
				if ( ! texData ) return;
  
				if ( undefined !== texData.image ) {
  
					texture.image = texData.image;
  
				} else if ( undefined !== texData.data ) {
  
					texture.image.width = texData.width;
					texture.image.height = texData.height;
					texture.image.data = texData.data;
  
				}
  
				texture.wrapS = undefined !== texData.wrapS ? texData.wrapS : ClampToEdgeWrapping;
				texture.wrapT = undefined !== texData.wrapT ? texData.wrapT : ClampToEdgeWrapping;
  
				texture.magFilter = undefined !== texData.magFilter ? texData.magFilter : LinearFilter;
				texture.minFilter = undefined !== texData.minFilter ? texData.minFilter : LinearMipMapLinearFilter;
  
				texture.anisotropy = undefined !== texData.anisotropy ? texData.anisotropy : 1;
  
				if ( undefined !== texData.format ) {
  
					texture.format = texData.format;
  
				}
				if ( undefined !== texData.type ) {
  
					texture.type = texData.type;
  
				}
  
				if ( undefined !== texData.mipmaps ) {
  
					texture.mipmaps = texData.mipmaps;
  
				}
  
				if ( 1 === texData.mipmapCount ) {
  
					texture.minFilter = LinearFilter;
  
				}
  
				texture.needsUpdate = true;
  
				if ( onLoad ) onLoad( texture, texData );
  
			}, onProgress, onError );
  
  
			return texture;
  
		}
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
  
	function ImageLoader( manager ) {
  
		this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;
  
	}
  
	Object.assign( ImageLoader.prototype, {
  
		crossOrigin: 'Anonymous',
  
		load: function ( url, onLoad, onProgress, onError ) {
  
			if ( url === undefined ) url = '';
  
			if ( this.path !== undefined ) url = this.path + url;
  
			url = this.manager.resolveURL( url );
  
			var scope = this;
  
			var cached = Cache.get( url );
  
			if ( cached !== undefined ) {
  
				scope.manager.itemStart( url );
  
				setTimeout( function () {
  
					if ( onLoad ) onLoad( cached );
  
					scope.manager.itemEnd( url );
  
				}, 0 );
  
				return cached;
  
			}
  
			var image = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'img' );
  
			function onImageLoad() {
  
				image.removeEventListener( 'load', onImageLoad, false );
				image.removeEventListener( 'error', onImageError, false );
  
				Cache.add( url, this );
  
				if ( onLoad ) onLoad( this );
  
				scope.manager.itemEnd( url );
  
			}
  
			function onImageError( event ) {
  
				image.removeEventListener( 'load', onImageLoad, false );
				image.removeEventListener( 'error', onImageError, false );
  
				if ( onError ) onError( event );
  
				scope.manager.itemEnd( url );
				scope.manager.itemError( url );
  
			}
  
			image.addEventListener( 'load', onImageLoad, false );
			image.addEventListener( 'error', onImageError, false );
  
			if ( url.substr( 0, 5 ) !== 'data:' ) {
  
				if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin;
  
			}
  
			scope.manager.itemStart( url );
  
			image.src = url;
  
			return image;
  
		},
  
		setCrossOrigin: function ( value ) {
  
			this.crossOrigin = value;
			return this;
  
		},
  
		setPath: function ( value ) {
  
			this.path = value;
			return this;
  
		}
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
  
	function CubeTextureLoader( manager ) {
  
		this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;
  
	}
  
	Object.assign( CubeTextureLoader.prototype, {
  
		crossOrigin: 'Anonymous',
  
		load: function ( urls, onLoad, onProgress, onError ) {
  
			var texture = new CubeTexture();
  
			var loader = new ImageLoader( this.manager );
			loader.setCrossOrigin( this.crossOrigin );
			loader.setPath( this.path );
  
			var loaded = 0;
  
			function loadTexture( i ) {
  
				loader.load( urls[ i ], function ( image ) {
  
					texture.images[ i ] = image;
  
					loaded ++;
  
					if ( loaded === 6 ) {
  
						texture.needsUpdate = true;
  
						if ( onLoad ) onLoad( texture );
  
					}
  
				}, undefined, onError );
  
			}
  
			for ( var i = 0; i < urls.length; ++ i ) {
  
				loadTexture( i );
  
			}
  
			return texture;
  
		},
  
		setCrossOrigin: function ( value ) {
  
			this.crossOrigin = value;
			return this;
  
		},
  
		setPath: function ( value ) {
  
			this.path = value;
			return this;
  
		}
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
  
	function TextureLoader( manager ) {
  
		this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;
  
	}
  
	Object.assign( TextureLoader.prototype, {
  
		crossOrigin: 'Anonymous',
  
		load: function ( url, onLoad, onProgress, onError ) {
  
			var texture = new Texture();
  
			var loader = new ImageLoader( this.manager );
			loader.setCrossOrigin( this.crossOrigin );
			loader.setPath( this.path );
  
			loader.load( url, function ( image ) {
  
				texture.image = image;
  
				// JPEGs can't have an alpha channel, so memory can be saved by storing them as RGB.
				var isJPEG = url.search( /\.(jpg|jpeg)$/ ) > 0 || url.search( /^data\:image\/jpeg/ ) === 0;
  
				texture.format = isJPEG ? RGBFormat : RGBAFormat;
				texture.needsUpdate = true;
  
				if ( onLoad !== undefined ) {
  
					onLoad( texture );
  
				}
  
			}, onProgress, onError );
  
			return texture;
  
		},
  
		setCrossOrigin: function ( value ) {
  
			this.crossOrigin = value;
			return this;
  
		},
  
		setPath: function ( value ) {
  
			this.path = value;
			return this;
  
		}
  
	} );
  
	/**
	 * @author zz85 / http://www.lab4games.net/zz85/blog
	 * Extensible curve object
	 *
	 * Some common of curve methods:
	 * .getPoint( t, optionalTarget ), .getTangent( t )
	 * .getPointAt( u, optionalTarget ), .getTangentAt( u )
	 * .getPoints(), .getSpacedPoints()
	 * .getLength()
	 * .updateArcLengths()
	 *
	 * This following curves inherit from THREE.Curve:
	 *
	 * -- 2D curves --
	 * THREE.ArcCurve
	 * THREE.CubicBezierCurve
	 * THREE.EllipseCurve
	 * THREE.LineCurve
	 * THREE.QuadraticBezierCurve
	 * THREE.SplineCurve
	 *
	 * -- 3D curves --
	 * THREE.CatmullRomCurve3
	 * THREE.CubicBezierCurve3
	 * THREE.LineCurve3
	 * THREE.QuadraticBezierCurve3
	 *
	 * A series of curves can be represented as a THREE.CurvePath.
	 *
	 **/
  
	/**************************************************************
	 *	Abstract Curve base class
	 **************************************************************/
  
	function Curve() {
  
		this.type = 'Curve';
  
		this.arcLengthDivisions = 200;
  
	}
  
	Object.assign( Curve.prototype, {
  
		// Virtual base class method to overwrite and implement in subclasses
		//	- t [0 .. 1]
  
		getPoint: function ( /* t, optionalTarget */ ) {
  
			console.warn( 'THREE.Curve: .getPoint() not implemented.' );
			return null;
  
		},
  
		// Get point at relative position in curve according to arc length
		// - u [0 .. 1]
  
		getPointAt: function ( u, optionalTarget ) {
  
			var t = this.getUtoTmapping( u );
			return this.getPoint( t, optionalTarget );
  
		},
  
		// Get sequence of points using getPoint( t )
  
		getPoints: function ( divisions ) {
  
			if ( divisions === undefined ) divisions = 5;
  
			var points = [];
  
			for ( var d = 0; d <= divisions; d ++ ) {
  
				points.push( this.getPoint( d / divisions ) );
  
			}
  
			return points;
  
		},
  
		// Get sequence of points using getPointAt( u )
  
		getSpacedPoints: function ( divisions ) {
  
			if ( divisions === undefined ) divisions = 5;
  
			var points = [];
  
			for ( var d = 0; d <= divisions; d ++ ) {
  
				points.push( this.getPointAt( d / divisions ) );
  
			}
  
			return points;
  
		},
  
		// Get total curve arc length
  
		getLength: function () {
  
			var lengths = this.getLengths();
			return lengths[ lengths.length - 1 ];
  
		},
  
		// Get list of cumulative segment lengths
  
		getLengths: function ( divisions ) {
  
			if ( divisions === undefined ) divisions = this.arcLengthDivisions;
  
			if ( this.cacheArcLengths &&
				( this.cacheArcLengths.length === divisions + 1 ) &&
				! this.needsUpdate ) {
  
				return this.cacheArcLengths;
  
			}
  
			this.needsUpdate = false;
  
			var cache = [];
			var current, last = this.getPoint( 0 );
			var p, sum = 0;
  
			cache.push( 0 );
  
			for ( p = 1; p <= divisions; p ++ ) {
  
				current = this.getPoint( p / divisions );
				sum += current.distanceTo( last );
				cache.push( sum );
				last = current;
  
			}
  
			this.cacheArcLengths = cache;
  
			return cache; // { sums: cache, sum: sum }; Sum is in the last element.
  
		},
  
		updateArcLengths: function () {
  
			this.needsUpdate = true;
			this.getLengths();
  
		},
  
		// Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant
  
		getUtoTmapping: function ( u, distance ) {
  
			var arcLengths = this.getLengths();
  
			var i = 0, il = arcLengths.length;
  
			var targetArcLength; // The targeted u distance value to get
  
			if ( distance ) {
  
				targetArcLength = distance;
  
			} else {
  
				targetArcLength = u * arcLengths[ il - 1 ];
  
			}
  
			// binary search for the index with largest value smaller than target u distance
  
			var low = 0, high = il - 1, comparison;
  
			while ( low <= high ) {
  
				i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats
  
				comparison = arcLengths[ i ] - targetArcLength;
  
				if ( comparison < 0 ) {
  
					low = i + 1;
  
				} else if ( comparison > 0 ) {
  
					high = i - 1;
  
				} else {
  
					high = i;
					break;
  
					// DONE
  
				}
  
			}
  
			i = high;
  
			if ( arcLengths[ i ] === targetArcLength ) {
  
				return i / ( il - 1 );
  
			}
  
			// we could get finer grain at lengths, or use simple interpolation between two points
  
			var lengthBefore = arcLengths[ i ];
			var lengthAfter = arcLengths[ i + 1 ];
  
			var segmentLength = lengthAfter - lengthBefore;
  
			// determine where we are between the 'before' and 'after' points
  
			var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength;
  
			// add that fractional amount to t
  
			var t = ( i + segmentFraction ) / ( il - 1 );
  
			return t;
  
		},
  
		// Returns a unit vector tangent at t
		// In case any sub curve does not implement its tangent derivation,
		// 2 points a small delta apart will be used to find its gradient
		// which seems to give a reasonable approximation
  
		getTangent: function ( t ) {
  
			var delta = 0.0001;
			var t1 = t - delta;
			var t2 = t + delta;
  
			// Capping in case of danger
  
			if ( t1 < 0 ) t1 = 0;
			if ( t2 > 1 ) t2 = 1;
  
			var pt1 = this.getPoint( t1 );
			var pt2 = this.getPoint( t2 );
  
			var vec = pt2.clone().sub( pt1 );
			return vec.normalize();
  
		},
  
		getTangentAt: function ( u ) {
  
			var t = this.getUtoTmapping( u );
			return this.getTangent( t );
  
		},
  
		computeFrenetFrames: function ( segments, closed ) {
  
			// see http://www.cs.indiana.edu/pub/techreports/TR425.pdf
  
			var normal = new Vector3();
  
			var tangents = [];
			var normals = [];
			var binormals = [];
  
			var vec = new Vector3();
			var mat = new Matrix4();
  
			var i, u, theta;
  
			// compute the tangent vectors for each segment on the curve
  
			for ( i = 0; i <= segments; i ++ ) {
  
				u = i / segments;
  
				tangents[ i ] = this.getTangentAt( u );
				tangents[ i ].normalize();
  
			}
  
			// select an initial normal vector perpendicular to the first tangent vector,
			// and in the direction of the minimum tangent xyz component
  
			normals[ 0 ] = new Vector3();
			binormals[ 0 ] = new Vector3();
			var min = Number.MAX_VALUE;
			var tx = Math.abs( tangents[ 0 ].x );
			var ty = Math.abs( tangents[ 0 ].y );
			var tz = Math.abs( tangents[ 0 ].z );
  
			if ( tx <= min ) {
  
				min = tx;
				normal.set( 1, 0, 0 );
  
			}
  
			if ( ty <= min ) {
  
				min = ty;
				normal.set( 0, 1, 0 );
  
			}
  
			if ( tz <= min ) {
  
				normal.set( 0, 0, 1 );
  
			}
  
			vec.crossVectors( tangents[ 0 ], normal ).normalize();
  
			normals[ 0 ].crossVectors( tangents[ 0 ], vec );
			binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] );
  
  
			// compute the slowly-varying normal and binormal vectors for each segment on the curve
  
			for ( i = 1; i <= segments; i ++ ) {
  
				normals[ i ] = normals[ i - 1 ].clone();
  
				binormals[ i ] = binormals[ i - 1 ].clone();
  
				vec.crossVectors( tangents[ i - 1 ], tangents[ i ] );
  
				if ( vec.length() > Number.EPSILON ) {
  
					vec.normalize();
  
					theta = Math.acos( _Math.clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors
  
					normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) );
  
				}
  
				binormals[ i ].crossVectors( tangents[ i ], normals[ i ] );
  
			}
  
			// if the curve is closed, postprocess the vectors so the first and last normal vectors are the same
  
			if ( closed === true ) {
  
				theta = Math.acos( _Math.clamp( normals[ 0 ].dot( normals[ segments ] ), - 1, 1 ) );
				theta /= segments;
  
				if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ segments ] ) ) > 0 ) {
  
					theta = - theta;
  
				}
  
				for ( i = 1; i <= segments; i ++ ) {
  
					// twist a little...
					normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) );
					binormals[ i ].crossVectors( tangents[ i ], normals[ i ] );
  
				}
  
			}
  
			return {
				tangents: tangents,
				normals: normals,
				binormals: binormals
			};
  
		},
  
		clone: function () {
  
			return new this.constructor().copy( this );
  
		},
  
		copy: function ( source ) {
  
			this.arcLengthDivisions = source.arcLengthDivisions;
  
			return this;
  
		},
  
		toJSON: function () {
  
			var data = {
				metadata: {
					version: 4.5,
					type: 'Curve',
					generator: 'Curve.toJSON'
				}
			};
  
			data.arcLengthDivisions = this.arcLengthDivisions;
			data.type = this.type;
  
			return data;
  
		},
  
		fromJSON: function ( json ) {
  
			this.arcLengthDivisions = json.arcLengthDivisions;
  
			return this;
  
		}
  
	} );
  
	function EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) {
  
		Curve.call( this );
  
		this.type = 'EllipseCurve';
  
		this.aX = aX || 0;
		this.aY = aY || 0;
  
		this.xRadius = xRadius || 1;
		this.yRadius = yRadius || 1;
  
		this.aStartAngle = aStartAngle || 0;
		this.aEndAngle = aEndAngle || 2 * Math.PI;
  
		this.aClockwise = aClockwise || false;
  
		this.aRotation = aRotation || 0;
  
	}
  
	EllipseCurve.prototype = Object.create( Curve.prototype );
	EllipseCurve.prototype.constructor = EllipseCurve;
  
	EllipseCurve.prototype.isEllipseCurve = true;
  
	EllipseCurve.prototype.getPoint = function ( t, optionalTarget ) {
  
		var point = optionalTarget || new Vector2();
  
		var twoPi = Math.PI * 2;
		var deltaAngle = this.aEndAngle - this.aStartAngle;
		var samePoints = Math.abs( deltaAngle ) < Number.EPSILON;
  
		// ensures that deltaAngle is 0 .. 2 PI
		while ( deltaAngle < 0 ) deltaAngle += twoPi;
		while ( deltaAngle > twoPi ) deltaAngle -= twoPi;
  
		if ( deltaAngle < Number.EPSILON ) {
  
			if ( samePoints ) {
  
				deltaAngle = 0;
  
			} else {
  
				deltaAngle = twoPi;
  
			}
  
		}
  
		if ( this.aClockwise === true && ! samePoints ) {
  
			if ( deltaAngle === twoPi ) {
  
				deltaAngle = - twoPi;
  
			} else {
  
				deltaAngle = deltaAngle - twoPi;
  
			}
  
		}
  
		var angle = this.aStartAngle + t * deltaAngle;
		var x = this.aX + this.xRadius * Math.cos( angle );
		var y = this.aY + this.yRadius * Math.sin( angle );
  
		if ( this.aRotation !== 0 ) {
  
			var cos = Math.cos( this.aRotation );
			var sin = Math.sin( this.aRotation );
  
			var tx = x - this.aX;
			var ty = y - this.aY;
  
			// Rotate the point about the center of the ellipse.
			x = tx * cos - ty * sin + this.aX;
			y = tx * sin + ty * cos + this.aY;
  
		}
  
		return point.set( x, y );
  
	};
  
	EllipseCurve.prototype.copy = function ( source ) {
  
		Curve.prototype.copy.call( this, source );
  
		this.aX = source.aX;
		this.aY = source.aY;
  
		this.xRadius = source.xRadius;
		this.yRadius = source.yRadius;
  
		this.aStartAngle = source.aStartAngle;
		this.aEndAngle = source.aEndAngle;
  
		this.aClockwise = source.aClockwise;
  
		this.aRotation = source.aRotation;
  
		return this;
  
	};
  
  
	EllipseCurve.prototype.toJSON = function () {
  
		var data = Curve.prototype.toJSON.call( this );
  
		data.aX = this.aX;
		data.aY = this.aY;
  
		data.xRadius = this.xRadius;
		data.yRadius = this.yRadius;
  
		data.aStartAngle = this.aStartAngle;
		data.aEndAngle = this.aEndAngle;
  
		data.aClockwise = this.aClockwise;
  
		data.aRotation = this.aRotation;
  
		return data;
  
	};
  
	EllipseCurve.prototype.fromJSON = function ( json ) {
  
		Curve.prototype.fromJSON.call( this, json );
  
		this.aX = json.aX;
		this.aY = json.aY;
  
		this.xRadius = json.xRadius;
		this.yRadius = json.yRadius;
  
		this.aStartAngle = json.aStartAngle;
		this.aEndAngle = json.aEndAngle;
  
		this.aClockwise = json.aClockwise;
  
		this.aRotation = json.aRotation;
  
		return this;
  
	};
  
	function ArcCurve( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {
  
		EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise );
  
		this.type = 'ArcCurve';
  
	}
  
	ArcCurve.prototype = Object.create( EllipseCurve.prototype );
	ArcCurve.prototype.constructor = ArcCurve;
  
	ArcCurve.prototype.isArcCurve = true;
  
	/**
	 * @author zz85 https://github.com/zz85
	 *
	 * Centripetal CatmullRom Curve - which is useful for avoiding
	 * cusps and self-intersections in non-uniform catmull rom curves.
	 * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf
	 *
	 * curve.type accepts centripetal(default), chordal and catmullrom
	 * curve.tension is used for catmullrom which defaults to 0.5
	 */
  
  
	/*
	Based on an optimized c++ solution in
	 - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/
	 - http://ideone.com/NoEbVM
  
	This CubicPoly class could be used for reusing some variables and calculations,
	but for three.js curve use, it could be possible inlined and flatten into a single function call
	which can be placed in CurveUtils.
	*/
  
	function CubicPoly() {
  
		var c0 = 0, c1 = 0, c2 = 0, c3 = 0;
  
		/*
		 * Compute coefficients for a cubic polynomial
		 *   p(s) = c0 + c1*s + c2*s^2 + c3*s^3
		 * such that
		 *   p(0) = x0, p(1) = x1
		 *  and
		 *   p'(0) = t0, p'(1) = t1.
		 */
		function init( x0, x1, t0, t1 ) {
  
			c0 = x0;
			c1 = t0;
			c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1;
			c3 = 2 * x0 - 2 * x1 + t0 + t1;
  
		}
  
		return {
  
			initCatmullRom: function ( x0, x1, x2, x3, tension ) {
  
				init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) );
  
			},
  
			initNonuniformCatmullRom: function ( x0, x1, x2, x3, dt0, dt1, dt2 ) {
  
				// compute tangents when parameterized in [t1,t2]
				var t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1;
				var t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2;
  
				// rescale tangents for parametrization in [0,1]
				t1 *= dt1;
				t2 *= dt1;
  
				init( x1, x2, t1, t2 );
  
			},
  
			calc: function ( t ) {
  
				var t2 = t * t;
				var t3 = t2 * t;
				return c0 + c1 * t + c2 * t2 + c3 * t3;
  
			}
  
		};
  
	}
  
	//
  
	var tmp = new Vector3();
	var px = new CubicPoly(), py = new CubicPoly(), pz = new CubicPoly();
  
	function CatmullRomCurve3( points, closed, curveType, tension ) {
  
		Curve.call( this );
  
		this.type = 'CatmullRomCurve3';
  
		this.points = points || [];
		this.closed = closed || false;
		this.curveType = curveType || 'centripetal';
		this.tension = tension || 0.5;
  
	}
  
	CatmullRomCurve3.prototype = Object.create( Curve.prototype );
	CatmullRomCurve3.prototype.constructor = CatmullRomCurve3;
  
	CatmullRomCurve3.prototype.isCatmullRomCurve3 = true;
  
	CatmullRomCurve3.prototype.getPoint = function ( t, optionalTarget ) {
  
		var point = optionalTarget || new Vector3();
  
		var points = this.points;
		var l = points.length;
  
		var p = ( l - ( this.closed ? 0 : 1 ) ) * t;
		var intPoint = Math.floor( p );
		var weight = p - intPoint;
  
		if ( this.closed ) {
  
			intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / l ) + 1 ) * l;
  
		} else if ( weight === 0 && intPoint === l - 1 ) {
  
			intPoint = l - 2;
			weight = 1;
  
		}
  
		var p0, p1, p2, p3; // 4 points
  
		if ( this.closed || intPoint > 0 ) {
  
			p0 = points[ ( intPoint - 1 ) % l ];
  
		} else {
  
			// extrapolate first point
			tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] );
			p0 = tmp;
  
		}
  
		p1 = points[ intPoint % l ];
		p2 = points[ ( intPoint + 1 ) % l ];
  
		if ( this.closed || intPoint + 2 < l ) {
  
			p3 = points[ ( intPoint + 2 ) % l ];
  
		} else {
  
			// extrapolate last point
			tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 1 ] );
			p3 = tmp;
  
		}
  
		if ( this.curveType === 'centripetal' || this.curveType === 'chordal' ) {
  
			// init Centripetal / Chordal Catmull-Rom
			var pow = this.curveType === 'chordal' ? 0.5 : 0.25;
			var dt0 = Math.pow( p0.distanceToSquared( p1 ), pow );
			var dt1 = Math.pow( p1.distanceToSquared( p2 ), pow );
			var dt2 = Math.pow( p2.distanceToSquared( p3 ), pow );
  
			// safety check for repeated points
			if ( dt1 < 1e-4 ) dt1 = 1.0;
			if ( dt0 < 1e-4 ) dt0 = dt1;
			if ( dt2 < 1e-4 ) dt2 = dt1;
  
			px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 );
			py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 );
			pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 );
  
		} else if ( this.curveType === 'catmullrom' ) {
  
			px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, this.tension );
			py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, this.tension );
			pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, this.tension );
  
		}
  
		point.set(
			px.calc( weight ),
			py.calc( weight ),
			pz.calc( weight )
		);
  
		return point;
  
	};
  
	CatmullRomCurve3.prototype.copy = function ( source ) {
  
		Curve.prototype.copy.call( this, source );
  
		this.points = [];
  
		for ( var i = 0, l = source.points.length; i < l; i ++ ) {
  
			var point = source.points[ i ];
  
			this.points.push( point.clone() );
  
		}
  
		this.closed = source.closed;
		this.curveType = source.curveType;
		this.tension = source.tension;
  
		return this;
  
	};
  
	CatmullRomCurve3.prototype.toJSON = function () {
  
		var data = Curve.prototype.toJSON.call( this );
  
		data.points = [];
  
		for ( var i = 0, l = this.points.length; i < l; i ++ ) {
  
			var point = this.points[ i ];
			data.points.push( point.toArray() );
  
		}
  
		data.closed = this.closed;
		data.curveType = this.curveType;
		data.tension = this.tension;
  
		return data;
  
	};
  
	CatmullRomCurve3.prototype.fromJSON = function ( json ) {
  
		Curve.prototype.fromJSON.call( this, json );
  
		this.points = [];
  
		for ( var i = 0, l = json.points.length; i < l; i ++ ) {
  
			var point = json.points[ i ];
			this.points.push( new Vector3().fromArray( point ) );
  
		}
  
		this.closed = json.closed;
		this.curveType = json.curveType;
		this.tension = json.tension;
  
		return this;
  
	};
  
	/**
	 * @author zz85 / http://www.lab4games.net/zz85/blog
	 *
	 * Bezier Curves formulas obtained from
	 * http://en.wikipedia.org/wiki/Bézier_curve
	 */
  
	function CatmullRom( t, p0, p1, p2, p3 ) {
  
		var v0 = ( p2 - p0 ) * 0.5;
		var v1 = ( p3 - p1 ) * 0.5;
		var t2 = t * t;
		var t3 = t * t2;
		return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1;
  
	}
  
	//
  
	function QuadraticBezierP0( t, p ) {
  
		var k = 1 - t;
		return k * k * p;
  
	}
  
	function QuadraticBezierP1( t, p ) {
  
		return 2 * ( 1 - t ) * t * p;
  
	}
  
	function QuadraticBezierP2( t, p ) {
  
		return t * t * p;
  
	}
  
	function QuadraticBezier( t, p0, p1, p2 ) {
  
		return QuadraticBezierP0( t, p0 ) + QuadraticBezierP1( t, p1 ) +
			QuadraticBezierP2( t, p2 );
  
	}
  
	//
  
	function CubicBezierP0( t, p ) {
  
		var k = 1 - t;
		return k * k * k * p;
  
	}
  
	function CubicBezierP1( t, p ) {
  
		var k = 1 - t;
		return 3 * k * k * t * p;
  
	}
  
	function CubicBezierP2( t, p ) {
  
		return 3 * ( 1 - t ) * t * t * p;
  
	}
  
	function CubicBezierP3( t, p ) {
  
		return t * t * t * p;
  
	}
  
	function CubicBezier( t, p0, p1, p2, p3 ) {
  
		return CubicBezierP0( t, p0 ) + CubicBezierP1( t, p1 ) + CubicBezierP2( t, p2 ) +
			CubicBezierP3( t, p3 );
  
	}
  
	function CubicBezierCurve( v0, v1, v2, v3 ) {
  
		Curve.call( this );
  
		this.type = 'CubicBezierCurve';
  
		this.v0 = v0 || new Vector2();
		this.v1 = v1 || new Vector2();
		this.v2 = v2 || new Vector2();
		this.v3 = v3 || new Vector2();
  
	}
  
	CubicBezierCurve.prototype = Object.create( Curve.prototype );
	CubicBezierCurve.prototype.constructor = CubicBezierCurve;
  
	CubicBezierCurve.prototype.isCubicBezierCurve = true;
  
	CubicBezierCurve.prototype.getPoint = function ( t, optionalTarget ) {
  
		var point = optionalTarget || new Vector2();
  
		var v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3;
  
		point.set(
			CubicBezier( t, v0.x, v1.x, v2.x, v3.x ),
			CubicBezier( t, v0.y, v1.y, v2.y, v3.y )
		);
  
		return point;
  
	};
  
	CubicBezierCurve.prototype.copy = function ( source ) {
  
		Curve.prototype.copy.call( this, source );
  
		this.v0.copy( source.v0 );
		this.v1.copy( source.v1 );
		this.v2.copy( source.v2 );
		this.v3.copy( source.v3 );
  
		return this;
  
	};
  
	CubicBezierCurve.prototype.toJSON = function () {
  
		var data = Curve.prototype.toJSON.call( this );
  
		data.v0 = this.v0.toArray();
		data.v1 = this.v1.toArray();
		data.v2 = this.v2.toArray();
		data.v3 = this.v3.toArray();
  
		return data;
  
	};
  
	CubicBezierCurve.prototype.fromJSON = function ( json ) {
  
		Curve.prototype.fromJSON.call( this, json );
  
		this.v0.fromArray( json.v0 );
		this.v1.fromArray( json.v1 );
		this.v2.fromArray( json.v2 );
		this.v3.fromArray( json.v3 );
  
		return this;
  
	};
  
	function CubicBezierCurve3( v0, v1, v2, v3 ) {
  
		Curve.call( this );
  
		this.type = 'CubicBezierCurve3';
  
		this.v0 = v0 || new Vector3();
		this.v1 = v1 || new Vector3();
		this.v2 = v2 || new Vector3();
		this.v3 = v3 || new Vector3();
  
	}
  
	CubicBezierCurve3.prototype = Object.create( Curve.prototype );
	CubicBezierCurve3.prototype.constructor = CubicBezierCurve3;
  
	CubicBezierCurve3.prototype.isCubicBezierCurve3 = true;
  
	CubicBezierCurve3.prototype.getPoint = function ( t, optionalTarget ) {
  
		var point = optionalTarget || new Vector3();
  
		var v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3;
  
		point.set(
			CubicBezier( t, v0.x, v1.x, v2.x, v3.x ),
			CubicBezier( t, v0.y, v1.y, v2.y, v3.y ),
			CubicBezier( t, v0.z, v1.z, v2.z, v3.z )
		);
  
		return point;
  
	};
  
	CubicBezierCurve3.prototype.copy = function ( source ) {
  
		Curve.prototype.copy.call( this, source );
  
		this.v0.copy( source.v0 );
		this.v1.copy( source.v1 );
		this.v2.copy( source.v2 );
		this.v3.copy( source.v3 );
  
		return this;
  
	};
  
	CubicBezierCurve3.prototype.toJSON = function () {
  
		var data = Curve.prototype.toJSON.call( this );
  
		data.v0 = this.v0.toArray();
		data.v1 = this.v1.toArray();
		data.v2 = this.v2.toArray();
		data.v3 = this.v3.toArray();
  
		return data;
  
	};
  
	CubicBezierCurve3.prototype.fromJSON = function ( json ) {
  
		Curve.prototype.fromJSON.call( this, json );
  
		this.v0.fromArray( json.v0 );
		this.v1.fromArray( json.v1 );
		this.v2.fromArray( json.v2 );
		this.v3.fromArray( json.v3 );
  
		return this;
  
	};
  
	function LineCurve( v1, v2 ) {
  
		Curve.call( this );
  
		this.type = 'LineCurve';
  
		this.v1 = v1 || new Vector2();
		this.v2 = v2 || new Vector2();
  
	}
  
	LineCurve.prototype = Object.create( Curve.prototype );
	LineCurve.prototype.constructor = LineCurve;
  
	LineCurve.prototype.isLineCurve = true;
  
	LineCurve.prototype.getPoint = function ( t, optionalTarget ) {
  
		var point = optionalTarget || new Vector2();
  
		if ( t === 1 ) {
  
			point.copy( this.v2 );
  
		} else {
  
			point.copy( this.v2 ).sub( this.v1 );
			point.multiplyScalar( t ).add( this.v1 );
  
		}
  
		return point;
  
	};
  
	// Line curve is linear, so we can overwrite default getPointAt
  
	LineCurve.prototype.getPointAt = function ( u, optionalTarget ) {
  
		return this.getPoint( u, optionalTarget );
  
	};
  
	LineCurve.prototype.getTangent = function ( /* t */ ) {
  
		var tangent = this.v2.clone().sub( this.v1 );
  
		return tangent.normalize();
  
	};
  
	LineCurve.prototype.copy = function ( source ) {
  
		Curve.prototype.copy.call( this, source );
  
		this.v1.copy( source.v1 );
		this.v2.copy( source.v2 );
  
		return this;
  
	};
  
	LineCurve.prototype.toJSON = function () {
  
		var data = Curve.prototype.toJSON.call( this );
  
		data.v1 = this.v1.toArray();
		data.v2 = this.v2.toArray();
  
		return data;
  
	};
  
	LineCurve.prototype.fromJSON = function ( json ) {
  
		Curve.prototype.fromJSON.call( this, json );
  
		this.v1.fromArray( json.v1 );
		this.v2.fromArray( json.v2 );
  
		return this;
  
	};
  
	function LineCurve3( v1, v2 ) {
  
		Curve.call( this );
  
		this.type = 'LineCurve3';
  
		this.v1 = v1 || new Vector3();
		this.v2 = v2 || new Vector3();
  
	}
  
	LineCurve3.prototype = Object.create( Curve.prototype );
	LineCurve3.prototype.constructor = LineCurve3;
  
	LineCurve3.prototype.isLineCurve3 = true;
  
	LineCurve3.prototype.getPoint = function ( t, optionalTarget ) {
  
		var point = optionalTarget || new Vector3();
  
		if ( t === 1 ) {
  
			point.copy( this.v2 );
  
		} else {
  
			point.copy( this.v2 ).sub( this.v1 );
			point.multiplyScalar( t ).add( this.v1 );
  
		}
  
		return point;
  
	};
  
	// Line curve is linear, so we can overwrite default getPointAt
  
	LineCurve3.prototype.getPointAt = function ( u, optionalTarget ) {
  
		return this.getPoint( u, optionalTarget );
  
	};
  
	LineCurve3.prototype.copy = function ( source ) {
  
		Curve.prototype.copy.call( this, source );
  
		this.v1.copy( source.v1 );
		this.v2.copy( source.v2 );
  
		return this;
  
	};
  
	LineCurve3.prototype.toJSON = function () {
  
		var data = Curve.prototype.toJSON.call( this );
  
		data.v1 = this.v1.toArray();
		data.v2 = this.v2.toArray();
  
		return data;
  
	};
  
	LineCurve3.prototype.fromJSON = function ( json ) {
  
		Curve.prototype.fromJSON.call( this, json );
  
		this.v1.fromArray( json.v1 );
		this.v2.fromArray( json.v2 );
  
		return this;
  
	};
  
	function QuadraticBezierCurve( v0, v1, v2 ) {
  
		Curve.call( this );
  
		this.type = 'QuadraticBezierCurve';
  
		this.v0 = v0 || new Vector2();
		this.v1 = v1 || new Vector2();
		this.v2 = v2 || new Vector2();
  
	}
  
	QuadraticBezierCurve.prototype = Object.create( Curve.prototype );
	QuadraticBezierCurve.prototype.constructor = QuadraticBezierCurve;
  
	QuadraticBezierCurve.prototype.isQuadraticBezierCurve = true;
  
	QuadraticBezierCurve.prototype.getPoint = function ( t, optionalTarget ) {
  
		var point = optionalTarget || new Vector2();
  
		var v0 = this.v0, v1 = this.v1, v2 = this.v2;
  
		point.set(
			QuadraticBezier( t, v0.x, v1.x, v2.x ),
			QuadraticBezier( t, v0.y, v1.y, v2.y )
		);
  
		return point;
  
	};
  
	QuadraticBezierCurve.prototype.copy = function ( source ) {
  
		Curve.prototype.copy.call( this, source );
  
		this.v0.copy( source.v0 );
		this.v1.copy( source.v1 );
		this.v2.copy( source.v2 );
  
		return this;
  
	};
  
	QuadraticBezierCurve.prototype.toJSON = function () {
  
		var data = Curve.prototype.toJSON.call( this );
  
		data.v0 = this.v0.toArray();
		data.v1 = this.v1.toArray();
		data.v2 = this.v2.toArray();
  
		return data;
  
	};
  
	QuadraticBezierCurve.prototype.fromJSON = function ( json ) {
  
		Curve.prototype.fromJSON.call( this, json );
  
		this.v0.fromArray( json.v0 );
		this.v1.fromArray( json.v1 );
		this.v2.fromArray( json.v2 );
  
		return this;
  
	};
  
	function QuadraticBezierCurve3( v0, v1, v2 ) {
  
		Curve.call( this );
  
		this.type = 'QuadraticBezierCurve3';
  
		this.v0 = v0 || new Vector3();
		this.v1 = v1 || new Vector3();
		this.v2 = v2 || new Vector3();
  
	}
  
	QuadraticBezierCurve3.prototype = Object.create( Curve.prototype );
	QuadraticBezierCurve3.prototype.constructor = QuadraticBezierCurve3;
  
	QuadraticBezierCurve3.prototype.isQuadraticBezierCurve3 = true;
  
	QuadraticBezierCurve3.prototype.getPoint = function ( t, optionalTarget ) {
  
		var point = optionalTarget || new Vector3();
  
		var v0 = this.v0, v1 = this.v1, v2 = this.v2;
  
		point.set(
			QuadraticBezier( t, v0.x, v1.x, v2.x ),
			QuadraticBezier( t, v0.y, v1.y, v2.y ),
			QuadraticBezier( t, v0.z, v1.z, v2.z )
		);
  
		return point;
  
	};
  
	QuadraticBezierCurve3.prototype.copy = function ( source ) {
  
		Curve.prototype.copy.call( this, source );
  
		this.v0.copy( source.v0 );
		this.v1.copy( source.v1 );
		this.v2.copy( source.v2 );
  
		return this;
  
	};
  
	QuadraticBezierCurve3.prototype.toJSON = function () {
  
		var data = Curve.prototype.toJSON.call( this );
  
		data.v0 = this.v0.toArray();
		data.v1 = this.v1.toArray();
		data.v2 = this.v2.toArray();
  
		return data;
  
	};
  
	QuadraticBezierCurve3.prototype.fromJSON = function ( json ) {
  
		Curve.prototype.fromJSON.call( this, json );
  
		this.v0.fromArray( json.v0 );
		this.v1.fromArray( json.v1 );
		this.v2.fromArray( json.v2 );
  
		return this;
  
	};
  
	function SplineCurve( points /* array of Vector2 */ ) {
  
		Curve.call( this );
  
		this.type = 'SplineCurve';
  
		this.points = points || [];
  
	}
  
	SplineCurve.prototype = Object.create( Curve.prototype );
	SplineCurve.prototype.constructor = SplineCurve;
  
	SplineCurve.prototype.isSplineCurve = true;
  
	SplineCurve.prototype.getPoint = function ( t, optionalTarget ) {
  
		var point = optionalTarget || new Vector2();
  
		var points = this.points;
		var p = ( points.length - 1 ) * t;
  
		var intPoint = Math.floor( p );
		var weight = p - intPoint;
  
		var p0 = points[ intPoint === 0 ? intPoint : intPoint - 1 ];
		var p1 = points[ intPoint ];
		var p2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ];
		var p3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ];
  
		point.set(
			CatmullRom( weight, p0.x, p1.x, p2.x, p3.x ),
			CatmullRom( weight, p0.y, p1.y, p2.y, p3.y )
		);
  
		return point;
  
	};
  
	SplineCurve.prototype.copy = function ( source ) {
  
		Curve.prototype.copy.call( this, source );
  
		this.points = [];
  
		for ( var i = 0, l = source.points.length; i < l; i ++ ) {
  
			var point = source.points[ i ];
  
			this.points.push( point.clone() );
  
		}
  
		return this;
  
	};
  
	SplineCurve.prototype.toJSON = function () {
  
		var data = Curve.prototype.toJSON.call( this );
  
		data.points = [];
  
		for ( var i = 0, l = this.points.length; i < l; i ++ ) {
  
			var point = this.points[ i ];
			data.points.push( point.toArray() );
  
		}
  
		return data;
  
	};
  
	SplineCurve.prototype.fromJSON = function ( json ) {
  
		Curve.prototype.fromJSON.call( this, json );
  
		this.points = [];
  
		for ( var i = 0, l = json.points.length; i < l; i ++ ) {
  
			var point = json.points[ i ];
			this.points.push( new Vector2().fromArray( point ) );
  
		}
  
		return this;
  
	};
  
  
  
	var Curves = /*#__PURE__*/Object.freeze({
		ArcCurve: ArcCurve,
		CatmullRomCurve3: CatmullRomCurve3,
		CubicBezierCurve: CubicBezierCurve,
		CubicBezierCurve3: CubicBezierCurve3,
		EllipseCurve: EllipseCurve,
		LineCurve: LineCurve,
		LineCurve3: LineCurve3,
		QuadraticBezierCurve: QuadraticBezierCurve,
		QuadraticBezierCurve3: QuadraticBezierCurve3,
		SplineCurve: SplineCurve
	});
  
	/**
	 * @author zz85 / http://www.lab4games.net/zz85/blog
	 *
	 **/
  
	/**************************************************************
	 *	Curved Path - a curve path is simply a array of connected
	 *  curves, but retains the api of a curve
	 **************************************************************/
  
	function CurvePath() {
  
		Curve.call( this );
  
		this.type = 'CurvePath';
  
		this.curves = [];
		this.autoClose = false; // Automatically closes the path
  
	}
  
	CurvePath.prototype = Object.assign( Object.create( Curve.prototype ), {
  
		constructor: CurvePath,
  
		add: function ( curve ) {
  
			this.curves.push( curve );
  
		},
  
		closePath: function () {
  
			// Add a line curve if start and end of lines are not connected
			var startPoint = this.curves[ 0 ].getPoint( 0 );
			var endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 );
  
			if ( ! startPoint.equals( endPoint ) ) {
  
				this.curves.push( new LineCurve( endPoint, startPoint ) );
  
			}
  
		},
  
		// To get accurate point with reference to
		// entire path distance at time t,
		// following has to be done:
  
		// 1. Length of each sub path have to be known
		// 2. Locate and identify type of curve
		// 3. Get t for the curve
		// 4. Return curve.getPointAt(t')
  
		getPoint: function ( t ) {
  
			var d = t * this.getLength();
			var curveLengths = this.getCurveLengths();
			var i = 0;
  
			// To think about boundaries points.
  
			while ( i < curveLengths.length ) {
  
				if ( curveLengths[ i ] >= d ) {
  
					var diff = curveLengths[ i ] - d;
					var curve = this.curves[ i ];
  
					var segmentLength = curve.getLength();
					var u = segmentLength === 0 ? 0 : 1 - diff / segmentLength;
  
					return curve.getPointAt( u );
  
				}
  
				i ++;
  
			}
  
			return null;
  
			// loop where sum != 0, sum > d , sum+1 <d
  
		},
  
		// We cannot use the default THREE.Curve getPoint() with getLength() because in
		// THREE.Curve, getLength() depends on getPoint() but in THREE.CurvePath
		// getPoint() depends on getLength
  
		getLength: function () {
  
			var lens = this.getCurveLengths();
			return lens[ lens.length - 1 ];
  
		},
  
		// cacheLengths must be recalculated.
		updateArcLengths: function () {
  
			this.needsUpdate = true;
			this.cacheLengths = null;
			this.getCurveLengths();
  
		},
  
		// Compute lengths and cache them
		// We cannot overwrite getLengths() because UtoT mapping uses it.
  
		getCurveLengths: function () {
  
			// We use cache values if curves and cache array are same length
  
			if ( this.cacheLengths && this.cacheLengths.length === this.curves.length ) {
  
				return this.cacheLengths;
  
			}
  
			// Get length of sub-curve
			// Push sums into cached array
  
			var lengths = [], sums = 0;
  
			for ( var i = 0, l = this.curves.length; i < l; i ++ ) {
  
				sums += this.curves[ i ].getLength();
				lengths.push( sums );
  
			}
  
			this.cacheLengths = lengths;
  
			return lengths;
  
		},
  
		getSpacedPoints: function ( divisions ) {
  
			if ( divisions === undefined ) divisions = 40;
  
			var points = [];
  
			for ( var i = 0; i <= divisions; i ++ ) {
  
				points.push( this.getPoint( i / divisions ) );
  
			}
  
			if ( this.autoClose ) {
  
				points.push( points[ 0 ] );
  
			}
  
			return points;
  
		},
  
		getPoints: function ( divisions ) {
  
			divisions = divisions || 12;
  
			var points = [], last;
  
			for ( var i = 0, curves = this.curves; i < curves.length; i ++ ) {
  
				var curve = curves[ i ];
				var resolution = ( curve && curve.isEllipseCurve ) ? divisions * 2
					: ( curve && curve.isLineCurve ) ? 1
						: ( curve && curve.isSplineCurve ) ? divisions * curve.points.length
							: divisions;
  
				var pts = curve.getPoints( resolution );
  
				for ( var j = 0; j < pts.length; j ++ ) {
  
					var point = pts[ j ];
  
					if ( last && last.equals( point ) ) continue; // ensures no consecutive points are duplicates
  
					points.push( point );
					last = point;
  
				}
  
			}
  
			if ( this.autoClose && points.length > 1 && ! points[ points.length - 1 ].equals( points[ 0 ] ) ) {
  
				points.push( points[ 0 ] );
  
			}
  
			return points;
  
		},
  
		copy: function ( source ) {
  
			Curve.prototype.copy.call( this, source );
  
			this.curves = [];
  
			for ( var i = 0, l = source.curves.length; i < l; i ++ ) {
  
				var curve = source.curves[ i ];
  
				this.curves.push( curve.clone() );
  
			}
  
			this.autoClose = source.autoClose;
  
			return this;
  
		},
  
		toJSON: function () {
  
			var data = Curve.prototype.toJSON.call( this );
  
			data.autoClose = this.autoClose;
			data.curves = [];
  
			for ( var i = 0, l = this.curves.length; i < l; i ++ ) {
  
				var curve = this.curves[ i ];
				data.curves.push( curve.toJSON() );
  
			}
  
			return data;
  
		},
  
		fromJSON: function ( json ) {
  
			Curve.prototype.fromJSON.call( this, json );
  
			this.autoClose = json.autoClose;
			this.curves = [];
  
			for ( var i = 0, l = json.curves.length; i < l; i ++ ) {
  
				var curve = json.curves[ i ];
				this.curves.push( new Curves[ curve.type ]().fromJSON( curve ) );
  
			}
  
			return this;
  
		}
  
	} );
  
	/**
	 * @author zz85 / http://www.lab4games.net/zz85/blog
	 * Creates free form 2d path using series of points, lines or curves.
	 **/
  
	function Path( points ) {
  
		CurvePath.call( this );
  
		this.type = 'Path';
  
		this.currentPoint = new Vector2();
  
		if ( points ) {
  
			this.setFromPoints( points );
  
		}
  
	}
  
	Path.prototype = Object.assign( Object.create( CurvePath.prototype ), {
  
		constructor: Path,
  
		setFromPoints: function ( points ) {
  
			this.moveTo( points[ 0 ].x, points[ 0 ].y );
  
			for ( var i = 1, l = points.length; i < l; i ++ ) {
  
				this.lineTo( points[ i ].x, points[ i ].y );
  
			}
  
		},
  
		moveTo: function ( x, y ) {
  
			this.currentPoint.set( x, y ); // TODO consider referencing vectors instead of copying?
  
		},
  
		lineTo: function ( x, y ) {
  
			var curve = new LineCurve( this.currentPoint.clone(), new Vector2( x, y ) );
			this.curves.push( curve );
  
			this.currentPoint.set( x, y );
  
		},
  
		quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) {
  
			var curve = new QuadraticBezierCurve(
				this.currentPoint.clone(),
				new Vector2( aCPx, aCPy ),
				new Vector2( aX, aY )
			);
  
			this.curves.push( curve );
  
			this.currentPoint.set( aX, aY );
  
		},
  
		bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) {
  
			var curve = new CubicBezierCurve(
				this.currentPoint.clone(),
				new Vector2( aCP1x, aCP1y ),
				new Vector2( aCP2x, aCP2y ),
				new Vector2( aX, aY )
			);
  
			this.curves.push( curve );
  
			this.currentPoint.set( aX, aY );
  
		},
  
		splineThru: function ( pts /*Array of Vector*/ ) {
  
			var npts = [ this.currentPoint.clone() ].concat( pts );
  
			var curve = new SplineCurve( npts );
			this.curves.push( curve );
  
			this.currentPoint.copy( pts[ pts.length - 1 ] );
  
		},
  
		arc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {
  
			var x0 = this.currentPoint.x;
			var y0 = this.currentPoint.y;
  
			this.absarc( aX + x0, aY + y0, aRadius,
				aStartAngle, aEndAngle, aClockwise );
  
		},
  
		absarc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {
  
			this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise );
  
		},
  
		ellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) {
  
			var x0 = this.currentPoint.x;
			var y0 = this.currentPoint.y;
  
			this.absellipse( aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation );
  
		},
  
		absellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) {
  
			var curve = new EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation );
  
			if ( this.curves.length > 0 ) {
  
				// if a previous curve is present, attempt to join
				var firstPoint = curve.getPoint( 0 );
  
				if ( ! firstPoint.equals( this.currentPoint ) ) {
  
					this.lineTo( firstPoint.x, firstPoint.y );
  
				}
  
			}
  
			this.curves.push( curve );
  
			var lastPoint = curve.getPoint( 1 );
			this.currentPoint.copy( lastPoint );
  
		},
  
		copy: function ( source ) {
  
			CurvePath.prototype.copy.call( this, source );
  
			this.currentPoint.copy( source.currentPoint );
  
			return this;
  
		},
  
		toJSON: function () {
  
			var data = CurvePath.prototype.toJSON.call( this );
  
			data.currentPoint = this.currentPoint.toArray();
  
			return data;
  
		},
  
		fromJSON: function ( json ) {
  
			CurvePath.prototype.fromJSON.call( this, json );
  
			this.currentPoint.fromArray( json.currentPoint );
  
			return this;
  
		}
  
	} );
  
	/**
	 * @author zz85 / http://www.lab4games.net/zz85/blog
	 * Defines a 2d shape plane using paths.
	 **/
  
	// STEP 1 Create a path.
	// STEP 2 Turn path into shape.
	// STEP 3 ExtrudeGeometry takes in Shape/Shapes
	// STEP 3a - Extract points from each shape, turn to vertices
	// STEP 3b - Triangulate each shape, add faces.
  
	function Shape( points ) {
  
		Path.call( this, points );
  
		this.uuid = _Math.generateUUID();
  
		this.type = 'Shape';
  
		this.holes = [];
  
	}
  
	Shape.prototype = Object.assign( Object.create( Path.prototype ), {
  
		constructor: Shape,
  
		getPointsHoles: function ( divisions ) {
  
			var holesPts = [];
  
			for ( var i = 0, l = this.holes.length; i < l; i ++ ) {
  
				holesPts[ i ] = this.holes[ i ].getPoints( divisions );
  
			}
  
			return holesPts;
  
		},
  
		// get points of shape and holes (keypoints based on segments parameter)
  
		extractPoints: function ( divisions ) {
  
			return {
  
				shape: this.getPoints( divisions ),
				holes: this.getPointsHoles( divisions )
  
			};
  
		},
  
		copy: function ( source ) {
  
			Path.prototype.copy.call( this, source );
  
			this.holes = [];
  
			for ( var i = 0, l = source.holes.length; i < l; i ++ ) {
  
				var hole = source.holes[ i ];
  
				this.holes.push( hole.clone() );
  
			}
  
			return this;
  
		},
  
		toJSON: function () {
  
			var data = Path.prototype.toJSON.call( this );
  
			data.uuid = this.uuid;
			data.holes = [];
  
			for ( var i = 0, l = this.holes.length; i < l; i ++ ) {
  
				var hole = this.holes[ i ];
				data.holes.push( hole.toJSON() );
  
			}
  
			return data;
  
		},
  
		fromJSON: function ( json ) {
  
			Path.prototype.fromJSON.call( this, json );
  
			this.uuid = json.uuid;
			this.holes = [];
  
			for ( var i = 0, l = json.holes.length; i < l; i ++ ) {
  
				var hole = json.holes[ i ];
				this.holes.push( new Path().fromJSON( hole ) );
  
			}
  
			return this;
  
		}
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 * @author alteredq / http://alteredqualia.com/
	 */
  
	function Light( color, intensity ) {
  
		Object3D.call( this );
  
		this.type = 'Light';
  
		this.color = new Color( color );
		this.intensity = intensity !== undefined ? intensity : 1;
  
		this.receiveShadow = undefined;
  
	}
  
	Light.prototype = Object.assign( Object.create( Object3D.prototype ), {
  
		constructor: Light,
  
		isLight: true,
  
		copy: function ( source ) {
  
			Object3D.prototype.copy.call( this, source );
  
			this.color.copy( source.color );
			this.intensity = source.intensity;
  
			return this;
  
		},
  
		toJSON: function ( meta ) {
  
			var data = Object3D.prototype.toJSON.call( this, meta );
  
			data.object.color = this.color.getHex();
			data.object.intensity = this.intensity;
  
			if ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex();
  
			if ( this.distance !== undefined ) data.object.distance = this.distance;
			if ( this.angle !== undefined ) data.object.angle = this.angle;
			if ( this.decay !== undefined ) data.object.decay = this.decay;
			if ( this.penumbra !== undefined ) data.object.penumbra = this.penumbra;
  
			if ( this.shadow !== undefined ) data.object.shadow = this.shadow.toJSON();
  
			return data;
  
		}
  
	} );
  
	/**
	 * @author alteredq / http://alteredqualia.com/
	 */
  
	function HemisphereLight( skyColor, groundColor, intensity ) {
  
		Light.call( this, skyColor, intensity );
  
		this.type = 'HemisphereLight';
  
		this.castShadow = undefined;
  
		this.position.copy( Object3D.DefaultUp );
		this.updateMatrix();
  
		this.groundColor = new Color( groundColor );
  
	}
  
	HemisphereLight.prototype = Object.assign( Object.create( Light.prototype ), {
  
		constructor: HemisphereLight,
  
		isHemisphereLight: true,
  
		copy: function ( source ) {
  
			Light.prototype.copy.call( this, source );
  
			this.groundColor.copy( source.groundColor );
  
			return this;
  
		}
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function LightShadow( camera ) {
  
		this.camera = camera;
  
		this.bias = 0;
		this.radius = 1;
  
		this.mapSize = new Vector2( 512, 512 );
  
		this.map = null;
		this.matrix = new Matrix4();
  
	}
  
	Object.assign( LightShadow.prototype, {
  
		copy: function ( source ) {
  
			this.camera = source.camera.clone();
  
			this.bias = source.bias;
			this.radius = source.radius;
  
			this.mapSize.copy( source.mapSize );
  
			return this;
  
		},
  
		clone: function () {
  
			return new this.constructor().copy( this );
  
		},
  
		toJSON: function () {
  
			var object = {};
  
			if ( this.bias !== 0 ) object.bias = this.bias;
			if ( this.radius !== 1 ) object.radius = this.radius;
			if ( this.mapSize.x !== 512 || this.mapSize.y !== 512 ) object.mapSize = this.mapSize.toArray();
  
			object.camera = this.camera.toJSON( false ).object;
			delete object.camera.matrix;
  
			return object;
  
		}
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function SpotLightShadow() {
  
		LightShadow.call( this, new PerspectiveCamera( 50, 1, 0.5, 500 ) );
  
	}
  
	SpotLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), {
  
		constructor: SpotLightShadow,
  
		isSpotLightShadow: true,
  
		update: function ( light ) {
  
			var camera = this.camera;
  
			var fov = _Math.RAD2DEG * 2 * light.angle;
			var aspect = this.mapSize.width / this.mapSize.height;
			var far = light.distance || camera.far;
  
			if ( fov !== camera.fov || aspect !== camera.aspect || far !== camera.far ) {
  
				camera.fov = fov;
				camera.aspect = aspect;
				camera.far = far;
				camera.updateProjectionMatrix();
  
			}
  
		}
  
	} );
  
	/**
	 * @author alteredq / http://alteredqualia.com/
	 */
  
	function SpotLight( color, intensity, distance, angle, penumbra, decay ) {
  
		Light.call( this, color, intensity );
  
		this.type = 'SpotLight';
  
		this.position.copy( Object3D.DefaultUp );
		this.updateMatrix();
  
		this.target = new Object3D();
  
		Object.defineProperty( this, 'power', {
			get: function () {
  
				// intensity = power per solid angle.
				// ref: equation (17) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf
				return this.intensity * Math.PI;
  
			},
			set: function ( power ) {
  
				// intensity = power per solid angle.
				// ref: equation (17) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf
				this.intensity = power / Math.PI;
  
			}
		} );
  
		this.distance = ( distance !== undefined ) ? distance : 0;
		this.angle = ( angle !== undefined ) ? angle : Math.PI / 3;
		this.penumbra = ( penumbra !== undefined ) ? penumbra : 0;
		this.decay = ( decay !== undefined ) ? decay : 1;	// for physically correct lights, should be 2.
  
		this.shadow = new SpotLightShadow();
  
	}
  
	SpotLight.prototype = Object.assign( Object.create( Light.prototype ), {
  
		constructor: SpotLight,
  
		isSpotLight: true,
  
		copy: function ( source ) {
  
			Light.prototype.copy.call( this, source );
  
			this.distance = source.distance;
			this.angle = source.angle;
			this.penumbra = source.penumbra;
			this.decay = source.decay;
  
			this.target = source.target.clone();
  
			this.shadow = source.shadow.clone();
  
			return this;
  
		}
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
  
	function PointLight( color, intensity, distance, decay ) {
  
		Light.call( this, color, intensity );
  
		this.type = 'PointLight';
  
		Object.defineProperty( this, 'power', {
			get: function () {
  
				// intensity = power per solid angle.
				// ref: equation (15) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf
				return this.intensity * 4 * Math.PI;
  
			},
			set: function ( power ) {
  
				// intensity = power per solid angle.
				// ref: equation (15) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf
				this.intensity = power / ( 4 * Math.PI );
  
			}
		} );
  
		this.distance = ( distance !== undefined ) ? distance : 0;
		this.decay = ( decay !== undefined ) ? decay : 1;	// for physically correct lights, should be 2.
  
		this.shadow = new LightShadow( new PerspectiveCamera( 90, 1, 0.5, 500 ) );
  
	}
  
	PointLight.prototype = Object.assign( Object.create( Light.prototype ), {
  
		constructor: PointLight,
  
		isPointLight: true,
  
		copy: function ( source ) {
  
			Light.prototype.copy.call( this, source );
  
			this.distance = source.distance;
			this.decay = source.decay;
  
			this.shadow = source.shadow.clone();
  
			return this;
  
		}
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function DirectionalLightShadow( ) {
  
		LightShadow.call( this, new OrthographicCamera( - 5, 5, 5, - 5, 0.5, 500 ) );
  
	}
  
	DirectionalLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), {
  
		constructor: DirectionalLightShadow
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 * @author alteredq / http://alteredqualia.com/
	 */
  
	function DirectionalLight( color, intensity ) {
  
		Light.call( this, color, intensity );
  
		this.type = 'DirectionalLight';
  
		this.position.copy( Object3D.DefaultUp );
		this.updateMatrix();
  
		this.target = new Object3D();
  
		this.shadow = new DirectionalLightShadow();
  
	}
  
	DirectionalLight.prototype = Object.assign( Object.create( Light.prototype ), {
  
		constructor: DirectionalLight,
  
		isDirectionalLight: true,
  
		copy: function ( source ) {
  
			Light.prototype.copy.call( this, source );
  
			this.target = source.target.clone();
  
			this.shadow = source.shadow.clone();
  
			return this;
  
		}
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function AmbientLight( color, intensity ) {
  
		Light.call( this, color, intensity );
  
		this.type = 'AmbientLight';
  
		this.castShadow = undefined;
  
	}
  
	AmbientLight.prototype = Object.assign( Object.create( Light.prototype ), {
  
		constructor: AmbientLight,
  
		isAmbientLight: true
  
	} );
  
	/**
	 * @author abelnation / http://github.com/abelnation
	 */
  
	function RectAreaLight( color, intensity, width, height ) {
  
		Light.call( this, color, intensity );
  
		this.type = 'RectAreaLight';
  
		this.width = ( width !== undefined ) ? width : 10;
		this.height = ( height !== undefined ) ? height : 10;
  
	}
  
	RectAreaLight.prototype = Object.assign( Object.create( Light.prototype ), {
  
		constructor: RectAreaLight,
  
		isRectAreaLight: true,
  
		copy: function ( source ) {
  
			Light.prototype.copy.call( this, source );
  
			this.width = source.width;
			this.height = source.height;
  
			return this;
  
		},
  
		toJSON: function ( meta ) {
  
			var data = Light.prototype.toJSON.call( this, meta );
  
			data.object.width = this.width;
			data.object.height = this.height;
  
			return data;
  
		}
  
	} );
  
	/**
	 *
	 * A Track that interpolates Strings
	 *
	 *
	 * @author Ben Houston / http://clara.io/
	 * @author David Sarno / http://lighthaus.us/
	 * @author tschw
	 */
  
	function StringKeyframeTrack( name, times, values, interpolation ) {
  
		KeyframeTrack.call( this, name, times, values, interpolation );
  
	}
  
	StringKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
  
		constructor: StringKeyframeTrack,
  
		ValueTypeName: 'string',
		ValueBufferType: Array,
  
		DefaultInterpolation: InterpolateDiscrete,
  
		InterpolantFactoryMethodLinear: undefined,
  
		InterpolantFactoryMethodSmooth: undefined
  
	} );
  
	/**
	 *
	 * A Track of Boolean keyframe values.
	 *
	 *
	 * @author Ben Houston / http://clara.io/
	 * @author David Sarno / http://lighthaus.us/
	 * @author tschw
	 */
  
	function BooleanKeyframeTrack( name, times, values ) {
  
		KeyframeTrack.call( this, name, times, values );
  
	}
  
	BooleanKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
  
		constructor: BooleanKeyframeTrack,
  
		ValueTypeName: 'bool',
		ValueBufferType: Array,
  
		DefaultInterpolation: InterpolateDiscrete,
  
		InterpolantFactoryMethodLinear: undefined,
		InterpolantFactoryMethodSmooth: undefined
  
		// Note: Actually this track could have a optimized / compressed
		// representation of a single value and a custom interpolant that
		// computes "firstValue ^ isOdd( index )".
  
	} );
  
	/**
	 * Abstract base class of interpolants over parametric samples.
	 *
	 * The parameter domain is one dimensional, typically the time or a path
	 * along a curve defined by the data.
	 *
	 * The sample values can have any dimensionality and derived classes may
	 * apply special interpretations to the data.
	 *
	 * This class provides the interval seek in a Template Method, deferring
	 * the actual interpolation to derived classes.
	 *
	 * Time complexity is O(1) for linear access crossing at most two points
	 * and O(log N) for random access, where N is the number of positions.
	 *
	 * References:
	 *
	 * 		http://www.oodesign.com/template-method-pattern.html
	 *
	 * @author tschw
	 */
  
	function Interpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {
  
		this.parameterPositions = parameterPositions;
		this._cachedIndex = 0;
  
		this.resultBuffer = resultBuffer !== undefined ?
			resultBuffer : new sampleValues.constructor( sampleSize );
		this.sampleValues = sampleValues;
		this.valueSize = sampleSize;
  
	}
  
	Object.assign( Interpolant.prototype, {
  
		evaluate: function ( t ) {
  
			var pp = this.parameterPositions,
				i1 = this._cachedIndex,
  
				t1 = pp[ i1 ],
				t0 = pp[ i1 - 1 ];
  
			validate_interval: {
  
				seek: {
  
					var right;
  
					linear_scan: {
  
						//- See http://jsperf.com/comparison-to-undefined/3
						//- slower code:
						//-
						//- 				if ( t >= t1 || t1 === undefined ) {
						forward_scan: if ( ! ( t < t1 ) ) {
  
							for ( var giveUpAt = i1 + 2; ; ) {
  
								if ( t1 === undefined ) {
  
									if ( t < t0 ) break forward_scan;
  
									// after end
  
									i1 = pp.length;
									this._cachedIndex = i1;
									return this.afterEnd_( i1 - 1, t, t0 );
  
								}
  
								if ( i1 === giveUpAt ) break; // this loop
  
								t0 = t1;
								t1 = pp[ ++ i1 ];
  
								if ( t < t1 ) {
  
									// we have arrived at the sought interval
									break seek;
  
								}
  
							}
  
							// prepare binary search on the right side of the index
							right = pp.length;
							break linear_scan;
  
						}
  
						//- slower code:
						//-					if ( t < t0 || t0 === undefined ) {
						if ( ! ( t >= t0 ) ) {
  
							// looping?
  
							var t1global = pp[ 1 ];
  
							if ( t < t1global ) {
  
								i1 = 2; // + 1, using the scan for the details
								t0 = t1global;
  
							}
  
							// linear reverse scan
  
							for ( var giveUpAt = i1 - 2; ; ) {
  
								if ( t0 === undefined ) {
  
									// before start
  
									this._cachedIndex = 0;
									return this.beforeStart_( 0, t, t1 );
  
								}
  
								if ( i1 === giveUpAt ) break; // this loop
  
								t1 = t0;
								t0 = pp[ -- i1 - 1 ];
  
								if ( t >= t0 ) {
  
									// we have arrived at the sought interval
									break seek;
  
								}
  
							}
  
							// prepare binary search on the left side of the index
							right = i1;
							i1 = 0;
							break linear_scan;
  
						}
  
						// the interval is valid
  
						break validate_interval;
  
					} // linear scan
  
					// binary search
  
					while ( i1 < right ) {
  
						var mid = ( i1 + right ) >>> 1;
  
						if ( t < pp[ mid ] ) {
  
							right = mid;
  
						} else {
  
							i1 = mid + 1;
  
						}
  
					}
  
					t1 = pp[ i1 ];
					t0 = pp[ i1 - 1 ];
  
					// check boundary cases, again
  
					if ( t0 === undefined ) {
  
						this._cachedIndex = 0;
						return this.beforeStart_( 0, t, t1 );
  
					}
  
					if ( t1 === undefined ) {
  
						i1 = pp.length;
						this._cachedIndex = i1;
						return this.afterEnd_( i1 - 1, t0, t );
  
					}
  
				} // seek
  
				this._cachedIndex = i1;
  
				this.intervalChanged_( i1, t0, t1 );
  
			} // validate_interval
  
			return this.interpolate_( i1, t0, t, t1 );
  
		},
  
		settings: null, // optional, subclass-specific settings structure
		// Note: The indirection allows central control of many interpolants.
  
		// --- Protected interface
  
		DefaultSettings_: {},
  
		getSettings_: function () {
  
			return this.settings || this.DefaultSettings_;
  
		},
  
		copySampleValue_: function ( index ) {
  
			// copies a sample value to the result buffer
  
			var result = this.resultBuffer,
				values = this.sampleValues,
				stride = this.valueSize,
				offset = index * stride;
  
			for ( var i = 0; i !== stride; ++ i ) {
  
				result[ i ] = values[ offset + i ];
  
			}
  
			return result;
  
		},
  
		// Template methods for derived classes:
  
		interpolate_: function ( /* i1, t0, t, t1 */ ) {
  
			throw new Error( 'call to abstract method' );
			// implementations shall return this.resultBuffer
  
		},
  
		intervalChanged_: function ( /* i1, t0, t1 */ ) {
  
			// empty
  
		}
  
	} );
  
	//!\ DECLARE ALIAS AFTER assign prototype !
	Object.assign( Interpolant.prototype, {
  
		//( 0, t, t0 ), returns this.resultBuffer
		beforeStart_: Interpolant.prototype.copySampleValue_,
  
		//( N-1, tN-1, t ), returns this.resultBuffer
		afterEnd_: Interpolant.prototype.copySampleValue_,
  
	} );
  
	/**
	 * Spherical linear unit quaternion interpolant.
	 *
	 * @author tschw
	 */
  
	function QuaternionLinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {
  
		Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );
  
	}
  
	QuaternionLinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), {
  
		constructor: QuaternionLinearInterpolant,
  
		interpolate_: function ( i1, t0, t, t1 ) {
  
			var result = this.resultBuffer,
				values = this.sampleValues,
				stride = this.valueSize,
  
				offset = i1 * stride,
  
				alpha = ( t - t0 ) / ( t1 - t0 );
  
			for ( var end = offset + stride; offset !== end; offset += 4 ) {
  
				Quaternion.slerpFlat( result, 0, values, offset - stride, values, offset, alpha );
  
			}
  
			return result;
  
		}
  
	} );
  
	/**
	 *
	 * A Track of quaternion keyframe values.
	 *
	 * @author Ben Houston / http://clara.io/
	 * @author David Sarno / http://lighthaus.us/
	 * @author tschw
	 */
  
	function QuaternionKeyframeTrack( name, times, values, interpolation ) {
  
		KeyframeTrack.call( this, name, times, values, interpolation );
  
	}
  
	QuaternionKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
  
		constructor: QuaternionKeyframeTrack,
  
		ValueTypeName: 'quaternion',
  
		// ValueBufferType is inherited
  
		DefaultInterpolation: InterpolateLinear,
  
		InterpolantFactoryMethodLinear: function ( result ) {
  
			return new QuaternionLinearInterpolant( this.times, this.values, this.getValueSize(), result );
  
		},
  
		InterpolantFactoryMethodSmooth: undefined // not yet implemented
  
	} );
  
	/**
	 *
	 * A Track of keyframe values that represent color.
	 *
	 *
	 * @author Ben Houston / http://clara.io/
	 * @author David Sarno / http://lighthaus.us/
	 * @author tschw
	 */
  
	function ColorKeyframeTrack( name, times, values, interpolation ) {
  
		KeyframeTrack.call( this, name, times, values, interpolation );
  
	}
  
	ColorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
  
		constructor: ColorKeyframeTrack,
  
		ValueTypeName: 'color'
  
		// ValueBufferType is inherited
  
		// DefaultInterpolation is inherited
  
		// Note: Very basic implementation and nothing special yet.
		// However, this is the place for color space parameterization.
  
	} );
  
	/**
	 *
	 * A Track of numeric keyframe values.
	 *
	 * @author Ben Houston / http://clara.io/
	 * @author David Sarno / http://lighthaus.us/
	 * @author tschw
	 */
  
	function NumberKeyframeTrack( name, times, values, interpolation ) {
  
		KeyframeTrack.call( this, name, times, values, interpolation );
  
	}
  
	NumberKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
  
		constructor: NumberKeyframeTrack,
  
		ValueTypeName: 'number'
  
		// ValueBufferType is inherited
  
		// DefaultInterpolation is inherited
  
	} );
  
	/**
	 * Fast and simple cubic spline interpolant.
	 *
	 * It was derived from a Hermitian construction setting the first derivative
	 * at each sample position to the linear slope between neighboring positions
	 * over their parameter interval.
	 *
	 * @author tschw
	 */
  
	function CubicInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {
  
		Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );
  
		this._weightPrev = - 0;
		this._offsetPrev = - 0;
		this._weightNext = - 0;
		this._offsetNext = - 0;
  
	}
  
	CubicInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), {
  
		constructor: CubicInterpolant,
  
		DefaultSettings_: {
  
			endingStart: ZeroCurvatureEnding,
			endingEnd: ZeroCurvatureEnding
  
		},
  
		intervalChanged_: function ( i1, t0, t1 ) {
  
			var pp = this.parameterPositions,
				iPrev = i1 - 2,
				iNext = i1 + 1,
  
				tPrev = pp[ iPrev ],
				tNext = pp[ iNext ];
  
			if ( tPrev === undefined ) {
  
				switch ( this.getSettings_().endingStart ) {
  
					case ZeroSlopeEnding:
  
						// f'(t0) = 0
						iPrev = i1;
						tPrev = 2 * t0 - t1;
  
						break;
  
					case WrapAroundEnding:
  
						// use the other end of the curve
						iPrev = pp.length - 2;
						tPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ];
  
						break;
  
					default: // ZeroCurvatureEnding
  
						// f''(t0) = 0 a.k.a. Natural Spline
						iPrev = i1;
						tPrev = t1;
  
				}
  
			}
  
			if ( tNext === undefined ) {
  
				switch ( this.getSettings_().endingEnd ) {
  
					case ZeroSlopeEnding:
  
						// f'(tN) = 0
						iNext = i1;
						tNext = 2 * t1 - t0;
  
						break;
  
					case WrapAroundEnding:
  
						// use the other end of the curve
						iNext = 1;
						tNext = t1 + pp[ 1 ] - pp[ 0 ];
  
						break;
  
					default: // ZeroCurvatureEnding
  
						// f''(tN) = 0, a.k.a. Natural Spline
						iNext = i1 - 1;
						tNext = t0;
  
				}
  
			}
  
			var halfDt = ( t1 - t0 ) * 0.5,
				stride = this.valueSize;
  
			this._weightPrev = halfDt / ( t0 - tPrev );
			this._weightNext = halfDt / ( tNext - t1 );
			this._offsetPrev = iPrev * stride;
			this._offsetNext = iNext * stride;
  
		},
  
		interpolate_: function ( i1, t0, t, t1 ) {
  
			var result = this.resultBuffer,
				values = this.sampleValues,
				stride = this.valueSize,
  
				o1 = i1 * stride,		o0 = o1 - stride,
				oP = this._offsetPrev, 	oN = this._offsetNext,
				wP = this._weightPrev,	wN = this._weightNext,
  
				p = ( t - t0 ) / ( t1 - t0 ),
				pp = p * p,
				ppp = pp * p;
  
			// evaluate polynomials
  
			var sP = - wP * ppp + 2 * wP * pp - wP * p;
			var s0 = ( 1 + wP ) * ppp + ( - 1.5 - 2 * wP ) * pp + ( - 0.5 + wP ) * p + 1;
			var s1 = ( - 1 - wN ) * ppp + ( 1.5 + wN ) * pp + 0.5 * p;
			var sN = wN * ppp - wN * pp;
  
			// combine data linearly
  
			for ( var i = 0; i !== stride; ++ i ) {
  
				result[ i ] =
						sP * values[ oP + i ] +
						s0 * values[ o0 + i ] +
						s1 * values[ o1 + i ] +
						sN * values[ oN + i ];
  
			}
  
			return result;
  
		}
  
	} );
  
	/**
	 * @author tschw
	 */
  
	function LinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {
  
		Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );
  
	}
  
	LinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), {
  
		constructor: LinearInterpolant,
  
		interpolate_: function ( i1, t0, t, t1 ) {
  
			var result = this.resultBuffer,
				values = this.sampleValues,
				stride = this.valueSize,
  
				offset1 = i1 * stride,
				offset0 = offset1 - stride,
  
				weight1 = ( t - t0 ) / ( t1 - t0 ),
				weight0 = 1 - weight1;
  
			for ( var i = 0; i !== stride; ++ i ) {
  
				result[ i ] =
						values[ offset0 + i ] * weight0 +
						values[ offset1 + i ] * weight1;
  
			}
  
			return result;
  
		}
  
	} );
  
	/**
	 *
	 * Interpolant that evaluates to the sample value at the position preceeding
	 * the parameter.
	 *
	 * @author tschw
	 */
  
	function DiscreteInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {
  
		Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );
  
	}
  
	DiscreteInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), {
  
		constructor: DiscreteInterpolant,
  
		interpolate_: function ( i1 /*, t0, t, t1 */ ) {
  
			return this.copySampleValue_( i1 - 1 );
  
		}
  
	} );
  
	/**
	 * @author tschw
	 * @author Ben Houston / http://clara.io/
	 * @author David Sarno / http://lighthaus.us/
	 */
  
	var AnimationUtils = {
  
		// same as Array.prototype.slice, but also works on typed arrays
		arraySlice: function ( array, from, to ) {
  
			if ( AnimationUtils.isTypedArray( array ) ) {
  
				// in ios9 array.subarray(from, undefined) will return empty array
				// but array.subarray(from) or array.subarray(from, len) is correct
				return new array.constructor( array.subarray( from, to !== undefined ? to : array.length ) );
  
			}
  
			return array.slice( from, to );
  
		},
  
		// converts an array to a specific type
		convertArray: function ( array, type, forceClone ) {
  
			if ( ! array || // let 'undefined' and 'null' pass
					! forceClone && array.constructor === type ) return array;
  
			if ( typeof type.BYTES_PER_ELEMENT === 'number' ) {
  
				return new type( array ); // create typed array
  
			}
  
			return Array.prototype.slice.call( array ); // create Array
  
		},
  
		isTypedArray: function ( object ) {
  
			return ArrayBuffer.isView( object ) &&
					! ( object instanceof DataView );
  
		},
  
		// returns an array by which times and values can be sorted
		getKeyframeOrder: function ( times ) {
  
			function compareTime( i, j ) {
  
				return times[ i ] - times[ j ];
  
			}
  
			var n = times.length;
			var result = new Array( n );
			for ( var i = 0; i !== n; ++ i ) result[ i ] = i;
  
			result.sort( compareTime );
  
			return result;
  
		},
  
		// uses the array previously returned by 'getKeyframeOrder' to sort data
		sortedArray: function ( values, stride, order ) {
  
			var nValues = values.length;
			var result = new values.constructor( nValues );
  
			for ( var i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) {
  
				var srcOffset = order[ i ] * stride;
  
				for ( var j = 0; j !== stride; ++ j ) {
  
					result[ dstOffset ++ ] = values[ srcOffset + j ];
  
				}
  
			}
  
			return result;
  
		},
  
		// function for parsing AOS keyframe formats
		flattenJSON: function ( jsonKeys, times, values, valuePropertyName ) {
  
			var i = 1, key = jsonKeys[ 0 ];
  
			while ( key !== undefined && key[ valuePropertyName ] === undefined ) {
  
				key = jsonKeys[ i ++ ];
  
			}
  
			if ( key === undefined ) return; // no data
  
			var value = key[ valuePropertyName ];
			if ( value === undefined ) return; // no data
  
			if ( Array.isArray( value ) ) {
  
				do {
  
					value = key[ valuePropertyName ];
  
					if ( value !== undefined ) {
  
						times.push( key.time );
						values.push.apply( values, value ); // push all elements
  
					}
  
					key = jsonKeys[ i ++ ];
  
				} while ( key !== undefined );
  
			} else if ( value.toArray !== undefined ) {
  
				// ...assume THREE.Math-ish
  
				do {
  
					value = key[ valuePropertyName ];
  
					if ( value !== undefined ) {
  
						times.push( key.time );
						value.toArray( values, values.length );
  
					}
  
					key = jsonKeys[ i ++ ];
  
				} while ( key !== undefined );
  
			} else {
  
				// otherwise push as-is
  
				do {
  
					value = key[ valuePropertyName ];
  
					if ( value !== undefined ) {
  
						times.push( key.time );
						values.push( value );
  
					}
  
					key = jsonKeys[ i ++ ];
  
				} while ( key !== undefined );
  
			}
  
		}
  
	};
  
	/**
	 *
	 * A timed sequence of keyframes for a specific property.
	 *
	 *
	 * @author Ben Houston / http://clara.io/
	 * @author David Sarno / http://lighthaus.us/
	 * @author tschw
	 */
  
	function KeyframeTrack( name, times, values, interpolation ) {
  
		if ( name === undefined ) throw new Error( 'THREE.KeyframeTrack: track name is undefined' );
		if ( times === undefined || times.length === 0 ) throw new Error( 'THREE.KeyframeTrack: no keyframes in track named ' + name );
  
		this.name = name;
  
		this.times = AnimationUtils.convertArray( times, this.TimeBufferType );
		this.values = AnimationUtils.convertArray( values, this.ValueBufferType );
  
		this.setInterpolation( interpolation || this.DefaultInterpolation );
  
		this.validate();
		this.optimize();
  
	}
  
	// Static methods:
  
	Object.assign( KeyframeTrack, {
  
		// Serialization (in static context, because of constructor invocation
		// and automatic invocation of .toJSON):
  
		parse: function ( json ) {
  
			if ( json.type === undefined ) {
  
				throw new Error( 'THREE.KeyframeTrack: track type undefined, can not parse' );
  
			}
  
			var trackType = KeyframeTrack._getTrackTypeForValueTypeName( json.type );
  
			if ( json.times === undefined ) {
  
				var times = [], values = [];
  
				AnimationUtils.flattenJSON( json.keys, times, values, 'value' );
  
				json.times = times;
				json.values = values;
  
			}
  
			// derived classes can define a static parse method
			if ( trackType.parse !== undefined ) {
  
				return trackType.parse( json );
  
			} else {
  
				// by default, we assume a constructor compatible with the base
				return new trackType( json.name, json.times, json.values, json.interpolation );
  
			}
  
		},
  
		toJSON: function ( track ) {
  
			var trackType = track.constructor;
  
			var json;
  
			// derived classes can define a static toJSON method
			if ( trackType.toJSON !== undefined ) {
  
				json = trackType.toJSON( track );
  
			} else {
  
				// by default, we assume the data can be serialized as-is
				json = {
  
					'name': track.name,
					'times': AnimationUtils.convertArray( track.times, Array ),
					'values': AnimationUtils.convertArray( track.values, Array )
  
				};
  
				var interpolation = track.getInterpolation();
  
				if ( interpolation !== track.DefaultInterpolation ) {
  
					json.interpolation = interpolation;
  
				}
  
			}
  
			json.type = track.ValueTypeName; // mandatory
  
			return json;
  
		},
  
		_getTrackTypeForValueTypeName: function ( typeName ) {
  
			switch ( typeName.toLowerCase() ) {
  
				case 'scalar':
				case 'double':
				case 'float':
				case 'number':
				case 'integer':
  
					return NumberKeyframeTrack;
  
				case 'vector':
				case 'vector2':
				case 'vector3':
				case 'vector4':
  
					return VectorKeyframeTrack;
  
				case 'color':
  
					return ColorKeyframeTrack;
  
				case 'quaternion':
  
					return QuaternionKeyframeTrack;
  
				case 'bool':
				case 'boolean':
  
					return BooleanKeyframeTrack;
  
				case 'string':
  
					return StringKeyframeTrack;
  
			}
  
			throw new Error( 'THREE.KeyframeTrack: Unsupported typeName: ' + typeName );
  
		}
  
	} );
  
	Object.assign( KeyframeTrack.prototype, {
  
		constructor: KeyframeTrack,
  
		TimeBufferType: Float32Array,
  
		ValueBufferType: Float32Array,
  
		DefaultInterpolation: InterpolateLinear,
  
		InterpolantFactoryMethodDiscrete: function ( result ) {
  
			return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result );
  
		},
  
		InterpolantFactoryMethodLinear: function ( result ) {
  
			return new LinearInterpolant( this.times, this.values, this.getValueSize(), result );
  
		},
  
		InterpolantFactoryMethodSmooth: function ( result ) {
  
			return new CubicInterpolant( this.times, this.values, this.getValueSize(), result );
  
		},
  
		setInterpolation: function ( interpolation ) {
  
			var factoryMethod;
  
			switch ( interpolation ) {
  
				case InterpolateDiscrete:
  
					factoryMethod = this.InterpolantFactoryMethodDiscrete;
  
					break;
  
				case InterpolateLinear:
  
					factoryMethod = this.InterpolantFactoryMethodLinear;
  
					break;
  
				case InterpolateSmooth:
  
					factoryMethod = this.InterpolantFactoryMethodSmooth;
  
					break;
  
			}
  
			if ( factoryMethod === undefined ) {
  
				var message = "unsupported interpolation for " +
					this.ValueTypeName + " keyframe track named " + this.name;
  
				if ( this.createInterpolant === undefined ) {
  
					// fall back to default, unless the default itself is messed up
					if ( interpolation !== this.DefaultInterpolation ) {
  
						this.setInterpolation( this.DefaultInterpolation );
  
					} else {
  
						throw new Error( message ); // fatal, in this case
  
					}
  
				}
  
				console.warn( 'THREE.KeyframeTrack:', message );
				return;
  
			}
  
			this.createInterpolant = factoryMethod;
  
		},
  
		getInterpolation: function () {
  
			switch ( this.createInterpolant ) {
  
				case this.InterpolantFactoryMethodDiscrete:
  
					return InterpolateDiscrete;
  
				case this.InterpolantFactoryMethodLinear:
  
					return InterpolateLinear;
  
				case this.InterpolantFactoryMethodSmooth:
  
					return InterpolateSmooth;
  
			}
  
		},
  
		getValueSize: function () {
  
			return this.values.length / this.times.length;
  
		},
  
		// move all keyframes either forwards or backwards in time
		shift: function ( timeOffset ) {
  
			if ( timeOffset !== 0.0 ) {
  
				var times = this.times;
  
				for ( var i = 0, n = times.length; i !== n; ++ i ) {
  
					times[ i ] += timeOffset;
  
				}
  
			}
  
			return this;
  
		},
  
		// scale all keyframe times by a factor (useful for frame <-> seconds conversions)
		scale: function ( timeScale ) {
  
			if ( timeScale !== 1.0 ) {
  
				var times = this.times;
  
				for ( var i = 0, n = times.length; i !== n; ++ i ) {
  
					times[ i ] *= timeScale;
  
				}
  
			}
  
			return this;
  
		},
  
		// removes keyframes before and after animation without changing any values within the range [startTime, endTime].
		// IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values
		trim: function ( startTime, endTime ) {
  
			var times = this.times,
				nKeys = times.length,
				from = 0,
				to = nKeys - 1;
  
			while ( from !== nKeys && times[ from ] < startTime ) {
  
				++ from;
  
			}
  
			while ( to !== - 1 && times[ to ] > endTime ) {
  
				-- to;
  
			}
  
			++ to; // inclusive -> exclusive bound
  
			if ( from !== 0 || to !== nKeys ) {
  
				// empty tracks are forbidden, so keep at least one keyframe
				if ( from >= to ) to = Math.max( to, 1 ), from = to - 1;
  
				var stride = this.getValueSize();
				this.times = AnimationUtils.arraySlice( times, from, to );
				this.values = AnimationUtils.arraySlice( this.values, from * stride, to * stride );
  
			}
  
			return this;
  
		},
  
		// ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable
		validate: function () {
  
			var valid = true;
  
			var valueSize = this.getValueSize();
			if ( valueSize - Math.floor( valueSize ) !== 0 ) {
  
				console.error( 'THREE.KeyframeTrack: Invalid value size in track.', this );
				valid = false;
  
			}
  
			var times = this.times,
				values = this.values,
  
				nKeys = times.length;
  
			if ( nKeys === 0 ) {
  
				console.error( 'THREE.KeyframeTrack: Track is empty.', this );
				valid = false;
  
			}
  
			var prevTime = null;
  
			for ( var i = 0; i !== nKeys; i ++ ) {
  
				var currTime = times[ i ];
  
				if ( typeof currTime === 'number' && isNaN( currTime ) ) {
  
					console.error( 'THREE.KeyframeTrack: Time is not a valid number.', this, i, currTime );
					valid = false;
					break;
  
				}
  
				if ( prevTime !== null && prevTime > currTime ) {
  
					console.error( 'THREE.KeyframeTrack: Out of order keys.', this, i, currTime, prevTime );
					valid = false;
					break;
  
				}
  
				prevTime = currTime;
  
			}
  
			if ( values !== undefined ) {
  
				if ( AnimationUtils.isTypedArray( values ) ) {
  
					for ( var i = 0, n = values.length; i !== n; ++ i ) {
  
						var value = values[ i ];
  
						if ( isNaN( value ) ) {
  
							console.error( 'THREE.KeyframeTrack: Value is not a valid number.', this, i, value );
							valid = false;
							break;
  
						}
  
					}
  
				}
  
			}
  
			return valid;
  
		},
  
		// removes equivalent sequential keys as common in morph target sequences
		// (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0)
		optimize: function () {
  
			var times = this.times,
				values = this.values,
				stride = this.getValueSize(),
  
				smoothInterpolation = this.getInterpolation() === InterpolateSmooth,
  
				writeIndex = 1,
				lastIndex = times.length - 1;
  
			for ( var i = 1; i < lastIndex; ++ i ) {
  
				var keep = false;
  
				var time = times[ i ];
				var timeNext = times[ i + 1 ];
  
				// remove adjacent keyframes scheduled at the same time
  
				if ( time !== timeNext && ( i !== 1 || time !== time[ 0 ] ) ) {
  
					if ( ! smoothInterpolation ) {
  
						// remove unnecessary keyframes same as their neighbors
  
						var offset = i * stride,
							offsetP = offset - stride,
							offsetN = offset + stride;
  
						for ( var j = 0; j !== stride; ++ j ) {
  
							var value = values[ offset + j ];
  
							if ( value !== values[ offsetP + j ] ||
								value !== values[ offsetN + j ] ) {
  
								keep = true;
								break;
  
							}
  
						}
  
					} else {
  
						keep = true;
  
					}
  
				}
  
				// in-place compaction
  
				if ( keep ) {
  
					if ( i !== writeIndex ) {
  
						times[ writeIndex ] = times[ i ];
  
						var readOffset = i * stride,
							writeOffset = writeIndex * stride;
  
						for ( var j = 0; j !== stride; ++ j ) {
  
							values[ writeOffset + j ] = values[ readOffset + j ];
  
						}
  
					}
  
					++ writeIndex;
  
				}
  
			}
  
			// flush last keyframe (compaction looks ahead)
  
			if ( lastIndex > 0 ) {
  
				times[ writeIndex ] = times[ lastIndex ];
  
				for ( var readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j ) {
  
					values[ writeOffset + j ] = values[ readOffset + j ];
  
				}
  
				++ writeIndex;
  
			}
  
			if ( writeIndex !== times.length ) {
  
				this.times = AnimationUtils.arraySlice( times, 0, writeIndex );
				this.values = AnimationUtils.arraySlice( values, 0, writeIndex * stride );
  
			}
  
			return this;
  
		}
  
	} );
  
	/**
	 *
	 * A Track of vectored keyframe values.
	 *
	 *
	 * @author Ben Houston / http://clara.io/
	 * @author David Sarno / http://lighthaus.us/
	 * @author tschw
	 */
  
	function VectorKeyframeTrack( name, times, values, interpolation ) {
  
		KeyframeTrack.call( this, name, times, values, interpolation );
  
	}
  
	VectorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
  
		constructor: VectorKeyframeTrack,
  
		ValueTypeName: 'vector'
  
		// ValueBufferType is inherited
  
		// DefaultInterpolation is inherited
  
	} );
  
	/**
	 *
	 * Reusable set of Tracks that represent an animation.
	 *
	 * @author Ben Houston / http://clara.io/
	 * @author David Sarno / http://lighthaus.us/
	 */
  
	function AnimationClip( name, duration, tracks ) {
  
		this.name = name;
		this.tracks = tracks;
		this.duration = ( duration !== undefined ) ? duration : - 1;
  
		this.uuid = _Math.generateUUID();
  
		// this means it should figure out its duration by scanning the tracks
		if ( this.duration < 0 ) {
  
			this.resetDuration();
  
		}
  
		this.optimize();
  
	}
  
	Object.assign( AnimationClip, {
  
		parse: function ( json ) {
  
			var tracks = [],
				jsonTracks = json.tracks,
				frameTime = 1.0 / ( json.fps || 1.0 );
  
			for ( var i = 0, n = jsonTracks.length; i !== n; ++ i ) {
  
				tracks.push( KeyframeTrack.parse( jsonTracks[ i ] ).scale( frameTime ) );
  
			}
  
			return new AnimationClip( json.name, json.duration, tracks );
  
		},
  
		toJSON: function ( clip ) {
  
			var tracks = [],
				clipTracks = clip.tracks;
  
			var json = {
  
				'name': clip.name,
				'duration': clip.duration,
				'tracks': tracks,
				'uuid': clip.uuid
  
			};
  
			for ( var i = 0, n = clipTracks.length; i !== n; ++ i ) {
  
				tracks.push( KeyframeTrack.toJSON( clipTracks[ i ] ) );
  
			}
  
			return json;
  
		},
  
		CreateFromMorphTargetSequence: function ( name, morphTargetSequence, fps, noLoop ) {
  
			var numMorphTargets = morphTargetSequence.length;
			var tracks = [];
  
			for ( var i = 0; i < numMorphTargets; i ++ ) {
  
				var times = [];
				var values = [];
  
				times.push(
					( i + numMorphTargets - 1 ) % numMorphTargets,
					i,
					( i + 1 ) % numMorphTargets );
  
				values.push( 0, 1, 0 );
  
				var order = AnimationUtils.getKeyframeOrder( times );
				times = AnimationUtils.sortedArray( times, 1, order );
				values = AnimationUtils.sortedArray( values, 1, order );
  
				// if there is a key at the first frame, duplicate it as the
				// last frame as well for perfect loop.
				if ( ! noLoop && times[ 0 ] === 0 ) {
  
					times.push( numMorphTargets );
					values.push( values[ 0 ] );
  
				}
  
				tracks.push(
					new NumberKeyframeTrack(
						'.morphTargetInfluences[' + morphTargetSequence[ i ].name + ']',
						times, values
					).scale( 1.0 / fps ) );
  
			}
  
			return new AnimationClip( name, - 1, tracks );
  
		},
  
		findByName: function ( objectOrClipArray, name ) {
  
			var clipArray = objectOrClipArray;
  
			if ( ! Array.isArray( objectOrClipArray ) ) {
  
				var o = objectOrClipArray;
				clipArray = o.geometry && o.geometry.animations || o.animations;
  
			}
  
			for ( var i = 0; i < clipArray.length; i ++ ) {
  
				if ( clipArray[ i ].name === name ) {
  
					return clipArray[ i ];
  
				}
  
			}
  
			return null;
  
		},
  
		CreateClipsFromMorphTargetSequences: function ( morphTargets, fps, noLoop ) {
  
			var animationToMorphTargets = {};
  
			// tested with https://regex101.com/ on trick sequences
			// such flamingo_flyA_003, flamingo_run1_003, crdeath0059
			var pattern = /^([\w-]*?)([\d]+)$/;
  
			// sort morph target names into animation groups based
			// patterns like Walk_001, Walk_002, Run_001, Run_002
			for ( var i = 0, il = morphTargets.length; i < il; i ++ ) {
  
				var morphTarget = morphTargets[ i ];
				var parts = morphTarget.name.match( pattern );
  
				if ( parts && parts.length > 1 ) {
  
					var name = parts[ 1 ];
  
					var animationMorphTargets = animationToMorphTargets[ name ];
					if ( ! animationMorphTargets ) {
  
						animationToMorphTargets[ name ] = animationMorphTargets = [];
  
					}
  
					animationMorphTargets.push( morphTarget );
  
				}
  
			}
  
			var clips = [];
  
			for ( var name in animationToMorphTargets ) {
  
				clips.push( AnimationClip.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps, noLoop ) );
  
			}
  
			return clips;
  
		},
  
		// parse the animation.hierarchy format
		parseAnimation: function ( animation, bones ) {
  
			if ( ! animation ) {
  
				console.error( 'THREE.AnimationClip: No animation in JSONLoader data.' );
				return null;
  
			}
  
			var addNonemptyTrack = function ( trackType, trackName, animationKeys, propertyName, destTracks ) {
  
				// only return track if there are actually keys.
				if ( animationKeys.length !== 0 ) {
  
					var times = [];
					var values = [];
  
					AnimationUtils.flattenJSON( animationKeys, times, values, propertyName );
  
					// empty keys are filtered out, so check again
					if ( times.length !== 0 ) {
  
						destTracks.push( new trackType( trackName, times, values ) );
  
					}
  
				}
  
			};
  
			var tracks = [];
  
			var clipName = animation.name || 'default';
			// automatic length determination in AnimationClip.
			var duration = animation.length || - 1;
			var fps = animation.fps || 30;
  
			var hierarchyTracks = animation.hierarchy || [];
  
			for ( var h = 0; h < hierarchyTracks.length; h ++ ) {
  
				var animationKeys = hierarchyTracks[ h ].keys;
  
				// skip empty tracks
				if ( ! animationKeys || animationKeys.length === 0 ) continue;
  
				// process morph targets
				if ( animationKeys[ 0 ].morphTargets ) {
  
					// figure out all morph targets used in this track
					var morphTargetNames = {};
  
					for ( var k = 0; k < animationKeys.length; k ++ ) {
  
						if ( animationKeys[ k ].morphTargets ) {
  
							for ( var m = 0; m < animationKeys[ k ].morphTargets.length; m ++ ) {
  
								morphTargetNames[ animationKeys[ k ].morphTargets[ m ] ] = - 1;
  
							}
  
						}
  
					}
  
					// create a track for each morph target with all zero
					// morphTargetInfluences except for the keys in which
					// the morphTarget is named.
					for ( var morphTargetName in morphTargetNames ) {
  
						var times = [];
						var values = [];
  
						for ( var m = 0; m !== animationKeys[ k ].morphTargets.length; ++ m ) {
  
							var animationKey = animationKeys[ k ];
  
							times.push( animationKey.time );
							values.push( ( animationKey.morphTarget === morphTargetName ) ? 1 : 0 );
  
						}
  
						tracks.push( new NumberKeyframeTrack( '.morphTargetInfluence[' + morphTargetName + ']', times, values ) );
  
					}
  
					duration = morphTargetNames.length * ( fps || 1.0 );
  
				} else {
  
					// ...assume skeletal animation
  
					var boneName = '.bones[' + bones[ h ].name + ']';
  
					addNonemptyTrack(
						VectorKeyframeTrack, boneName + '.position',
						animationKeys, 'pos', tracks );
  
					addNonemptyTrack(
						QuaternionKeyframeTrack, boneName + '.quaternion',
						animationKeys, 'rot', tracks );
  
					addNonemptyTrack(
						VectorKeyframeTrack, boneName + '.scale',
						animationKeys, 'scl', tracks );
  
				}
  
			}
  
			if ( tracks.length === 0 ) {
  
				return null;
  
			}
  
			var clip = new AnimationClip( clipName, duration, tracks );
  
			return clip;
  
		}
  
	} );
  
	Object.assign( AnimationClip.prototype, {
  
		resetDuration: function () {
  
			var tracks = this.tracks, duration = 0;
  
			for ( var i = 0, n = tracks.length; i !== n; ++ i ) {
  
				var track = this.tracks[ i ];
  
				duration = Math.max( duration, track.times[ track.times.length - 1 ] );
  
			}
  
			this.duration = duration;
  
		},
  
		trim: function () {
  
			for ( var i = 0; i < this.tracks.length; i ++ ) {
  
				this.tracks[ i ].trim( 0, this.duration );
  
			}
  
			return this;
  
		},
  
		optimize: function () {
  
			for ( var i = 0; i < this.tracks.length; i ++ ) {
  
				this.tracks[ i ].optimize();
  
			}
  
			return this;
  
		}
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function MaterialLoader( manager ) {
  
		this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;
		this.textures = {};
  
	}
  
	Object.assign( MaterialLoader.prototype, {
  
		load: function ( url, onLoad, onProgress, onError ) {
  
			var scope = this;
  
			var loader = new FileLoader( scope.manager );
			loader.load( url, function ( text ) {
  
				onLoad( scope.parse( JSON.parse( text ) ) );
  
			}, onProgress, onError );
  
		},
  
		setTextures: function ( value ) {
  
			this.textures = value;
  
		},
  
		parse: function ( json ) {
  
			var textures = this.textures;
  
			function getTexture( name ) {
  
				if ( textures[ name ] === undefined ) {
  
					console.warn( 'THREE.MaterialLoader: Undefined texture', name );
  
				}
  
				return textures[ name ];
  
			}
  
			var material = new Materials[ json.type ]();
  
			if ( json.uuid !== undefined ) material.uuid = json.uuid;
			if ( json.name !== undefined ) material.name = json.name;
			if ( json.color !== undefined ) material.color.setHex( json.color );
			if ( json.roughness !== undefined ) material.roughness = json.roughness;
			if ( json.metalness !== undefined ) material.metalness = json.metalness;
			if ( json.emissive !== undefined ) material.emissive.setHex( json.emissive );
			if ( json.specular !== undefined ) material.specular.setHex( json.specular );
			if ( json.shininess !== undefined ) material.shininess = json.shininess;
			if ( json.clearCoat !== undefined ) material.clearCoat = json.clearCoat;
			if ( json.clearCoatRoughness !== undefined ) material.clearCoatRoughness = json.clearCoatRoughness;
			if ( json.uniforms !== undefined ) material.uniforms = json.uniforms;
			if ( json.vertexShader !== undefined ) material.vertexShader = json.vertexShader;
			if ( json.fragmentShader !== undefined ) material.fragmentShader = json.fragmentShader;
			if ( json.vertexColors !== undefined ) material.vertexColors = json.vertexColors;
			if ( json.fog !== undefined ) material.fog = json.fog;
			if ( json.flatShading !== undefined ) material.flatShading = json.flatShading;
			if ( json.blending !== undefined ) material.blending = json.blending;
			if ( json.side !== undefined ) material.side = json.side;
			if ( json.opacity !== undefined ) material.opacity = json.opacity;
			if ( json.transparent !== undefined ) material.transparent = json.transparent;
			if ( json.alphaTest !== undefined ) material.alphaTest = json.alphaTest;
			if ( json.depthTest !== undefined ) material.depthTest = json.depthTest;
			if ( json.depthWrite !== undefined ) material.depthWrite = json.depthWrite;
			if ( json.colorWrite !== undefined ) material.colorWrite = json.colorWrite;
			if ( json.wireframe !== undefined ) material.wireframe = json.wireframe;
			if ( json.wireframeLinewidth !== undefined ) material.wireframeLinewidth = json.wireframeLinewidth;
			if ( json.wireframeLinecap !== undefined ) material.wireframeLinecap = json.wireframeLinecap;
			if ( json.wireframeLinejoin !== undefined ) material.wireframeLinejoin = json.wireframeLinejoin;
  
			if ( json.rotation !== undefined ) material.rotation = json.rotation;
  
			if ( json.linewidth !== 1 ) material.linewidth = json.linewidth;
			if ( json.dashSize !== undefined ) material.dashSize = json.dashSize;
			if ( json.gapSize !== undefined ) material.gapSize = json.gapSize;
			if ( json.scale !== undefined ) material.scale = json.scale;
  
			if ( json.polygonOffset !== undefined ) material.polygonOffset = json.polygonOffset;
			if ( json.polygonOffsetFactor !== undefined ) material.polygonOffsetFactor = json.polygonOffsetFactor;
			if ( json.polygonOffsetUnits !== undefined ) material.polygonOffsetUnits = json.polygonOffsetUnits;
  
			if ( json.skinning !== undefined ) material.skinning = json.skinning;
			if ( json.morphTargets !== undefined ) material.morphTargets = json.morphTargets;
			if ( json.dithering !== undefined ) material.dithering = json.dithering;
  
			if ( json.visible !== undefined ) material.visible = json.visible;
			if ( json.userData !== undefined ) material.userData = json.userData;
  
			// Deprecated
  
			if ( json.shading !== undefined ) material.flatShading = json.shading === 1; // THREE.FlatShading
  
			// for PointsMaterial
  
			if ( json.size !== undefined ) material.size = json.size;
			if ( json.sizeAttenuation !== undefined ) material.sizeAttenuation = json.sizeAttenuation;
  
			// maps
  
			if ( json.map !== undefined ) material.map = getTexture( json.map );
  
			if ( json.alphaMap !== undefined ) {
  
				material.alphaMap = getTexture( json.alphaMap );
				material.transparent = true;
  
			}
  
			if ( json.bumpMap !== undefined ) material.bumpMap = getTexture( json.bumpMap );
			if ( json.bumpScale !== undefined ) material.bumpScale = json.bumpScale;
  
			if ( json.normalMap !== undefined ) material.normalMap = getTexture( json.normalMap );
			if ( json.normalScale !== undefined ) {
  
				var normalScale = json.normalScale;
  
				if ( Array.isArray( normalScale ) === false ) {
  
					// Blender exporter used to export a scalar. See #7459
  
					normalScale = [ normalScale, normalScale ];
  
				}
  
				material.normalScale = new Vector2().fromArray( normalScale );
  
			}
  
			if ( json.displacementMap !== undefined ) material.displacementMap = getTexture( json.displacementMap );
			if ( json.displacementScale !== undefined ) material.displacementScale = json.displacementScale;
			if ( json.displacementBias !== undefined ) material.displacementBias = json.displacementBias;
  
			if ( json.roughnessMap !== undefined ) material.roughnessMap = getTexture( json.roughnessMap );
			if ( json.metalnessMap !== undefined ) material.metalnessMap = getTexture( json.metalnessMap );
  
			if ( json.emissiveMap !== undefined ) material.emissiveMap = getTexture( json.emissiveMap );
			if ( json.emissiveIntensity !== undefined ) material.emissiveIntensity = json.emissiveIntensity;
  
			if ( json.specularMap !== undefined ) material.specularMap = getTexture( json.specularMap );
  
			if ( json.envMap !== undefined ) material.envMap = getTexture( json.envMap );
  
			if ( json.reflectivity !== undefined ) material.reflectivity = json.reflectivity;
  
			if ( json.lightMap !== undefined ) material.lightMap = getTexture( json.lightMap );
			if ( json.lightMapIntensity !== undefined ) material.lightMapIntensity = json.lightMapIntensity;
  
			if ( json.aoMap !== undefined ) material.aoMap = getTexture( json.aoMap );
			if ( json.aoMapIntensity !== undefined ) material.aoMapIntensity = json.aoMapIntensity;
  
			if ( json.gradientMap !== undefined ) material.gradientMap = getTexture( json.gradientMap );
  
			return material;
  
		}
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function BufferGeometryLoader( manager ) {
  
		this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;
  
	}
  
	Object.assign( BufferGeometryLoader.prototype, {
  
		load: function ( url, onLoad, onProgress, onError ) {
  
			var scope = this;
  
			var loader = new FileLoader( scope.manager );
			loader.load( url, function ( text ) {
  
				onLoad( scope.parse( JSON.parse( text ) ) );
  
			}, onProgress, onError );
  
		},
  
		parse: function ( json ) {
  
			var geometry = new BufferGeometry();
  
			var index = json.data.index;
  
			if ( index !== undefined ) {
  
				var typedArray = new TYPED_ARRAYS[ index.type ]( index.array );
				geometry.setIndex( new BufferAttribute( typedArray, 1 ) );
  
			}
  
			var attributes = json.data.attributes;
  
			for ( var key in attributes ) {
  
				var attribute = attributes[ key ];
				var typedArray = new TYPED_ARRAYS[ attribute.type ]( attribute.array );
  
				geometry.addAttribute( key, new BufferAttribute( typedArray, attribute.itemSize, attribute.normalized ) );
  
			}
  
			var groups = json.data.groups || json.data.drawcalls || json.data.offsets;
  
			if ( groups !== undefined ) {
  
				for ( var i = 0, n = groups.length; i !== n; ++ i ) {
  
					var group = groups[ i ];
  
					geometry.addGroup( group.start, group.count, group.materialIndex );
  
				}
  
			}
  
			var boundingSphere = json.data.boundingSphere;
  
			if ( boundingSphere !== undefined ) {
  
				var center = new Vector3();
  
				if ( boundingSphere.center !== undefined ) {
  
					center.fromArray( boundingSphere.center );
  
				}
  
				geometry.boundingSphere = new Sphere( center, boundingSphere.radius );
  
			}
  
			return geometry;
  
		}
  
	} );
  
	var TYPED_ARRAYS = {
		Int8Array: Int8Array,
		Uint8Array: Uint8Array,
		// Workaround for IE11 pre KB2929437. See #11440
		Uint8ClampedArray: typeof Uint8ClampedArray !== 'undefined' ? Uint8ClampedArray : Uint8Array,
		Int16Array: Int16Array,
		Uint16Array: Uint16Array,
		Int32Array: Int32Array,
		Uint32Array: Uint32Array,
		Float32Array: Float32Array,
		Float64Array: Float64Array
	};
  
	/**
	 * @author alteredq / http://alteredqualia.com/
	 */
  
	function Loader() {}
  
	Loader.Handlers = {
  
		handlers: [],
  
		add: function ( regex, loader ) {
  
			this.handlers.push( regex, loader );
  
		},
  
		get: function ( file ) {
  
			var handlers = this.handlers;
  
			for ( var i = 0, l = handlers.length; i < l; i += 2 ) {
  
				var regex = handlers[ i ];
				var loader = handlers[ i + 1 ];
  
				if ( regex.test( file ) ) {
  
					return loader;
  
				}
  
			}
  
			return null;
  
		}
  
	};
  
	Object.assign( Loader.prototype, {
  
		crossOrigin: undefined,
  
		onLoadStart: function () {},
  
		onLoadProgress: function () {},
  
		onLoadComplete: function () {},
  
		initMaterials: function ( materials, texturePath, crossOrigin ) {
  
			var array = [];
  
			for ( var i = 0; i < materials.length; ++ i ) {
  
				array[ i ] = this.createMaterial( materials[ i ], texturePath, crossOrigin );
  
			}
  
			return array;
  
		},
  
		createMaterial: ( function () {
  
			var BlendingMode = {
				NoBlending: NoBlending,
				NormalBlending: NormalBlending,
				AdditiveBlending: AdditiveBlending,
				SubtractiveBlending: SubtractiveBlending,
				MultiplyBlending: MultiplyBlending,
				CustomBlending: CustomBlending
			};
  
			var color = new Color();
			var textureLoader = new TextureLoader();
			var materialLoader = new MaterialLoader();
  
			return function createMaterial( m, texturePath, crossOrigin ) {
  
				// convert from old material format
  
				var textures = {};
  
				function loadTexture( path, repeat, offset, wrap, anisotropy ) {
  
					var fullPath = texturePath + path;
					var loader = Loader.Handlers.get( fullPath );
  
					var texture;
  
					if ( loader !== null ) {
  
						texture = loader.load( fullPath );
  
					} else {
  
						textureLoader.setCrossOrigin( crossOrigin );
						texture = textureLoader.load( fullPath );
  
					}
  
					if ( repeat !== undefined ) {
  
						texture.repeat.fromArray( repeat );
  
						if ( repeat[ 0 ] !== 1 ) texture.wrapS = RepeatWrapping;
						if ( repeat[ 1 ] !== 1 ) texture.wrapT = RepeatWrapping;
  
					}
  
					if ( offset !== undefined ) {
  
						texture.offset.fromArray( offset );
  
					}
  
					if ( wrap !== undefined ) {
  
						if ( wrap[ 0 ] === 'repeat' ) texture.wrapS = RepeatWrapping;
						if ( wrap[ 0 ] === 'mirror' ) texture.wrapS = MirroredRepeatWrapping;
  
						if ( wrap[ 1 ] === 'repeat' ) texture.wrapT = RepeatWrapping;
						if ( wrap[ 1 ] === 'mirror' ) texture.wrapT = MirroredRepeatWrapping;
  
					}
  
					if ( anisotropy !== undefined ) {
  
						texture.anisotropy = anisotropy;
  
					}
  
					var uuid = _Math.generateUUID();
  
					textures[ uuid ] = texture;
  
					return uuid;
  
				}
  
				//
  
				var json = {
					uuid: _Math.generateUUID(),
					type: 'MeshLambertMaterial'
				};
  
				for ( var name in m ) {
  
					var value = m[ name ];
  
					switch ( name ) {
  
						case 'DbgColor':
						case 'DbgIndex':
						case 'opticalDensity':
						case 'illumination':
							break;
						case 'DbgName':
							json.name = value;
							break;
						case 'blending':
							json.blending = BlendingMode[ value ];
							break;
						case 'colorAmbient':
						case 'mapAmbient':
							console.warn( 'THREE.Loader.createMaterial:', name, 'is no longer supported.' );
							break;
						case 'colorDiffuse':
							json.color = color.fromArray( value ).getHex();
							break;
						case 'colorSpecular':
							json.specular = color.fromArray( value ).getHex();
							break;
						case 'colorEmissive':
							json.emissive = color.fromArray( value ).getHex();
							break;
						case 'specularCoef':
							json.shininess = value;
							break;
						case 'shading':
							if ( value.toLowerCase() === 'basic' ) json.type = 'MeshBasicMaterial';
							if ( value.toLowerCase() === 'phong' ) json.type = 'MeshPhongMaterial';
							if ( value.toLowerCase() === 'standard' ) json.type = 'MeshStandardMaterial';
							break;
						case 'mapDiffuse':
							json.map = loadTexture( value, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap, m.mapDiffuseAnisotropy );
							break;
						case 'mapDiffuseRepeat':
						case 'mapDiffuseOffset':
						case 'mapDiffuseWrap':
						case 'mapDiffuseAnisotropy':
							break;
						case 'mapEmissive':
							json.emissiveMap = loadTexture( value, m.mapEmissiveRepeat, m.mapEmissiveOffset, m.mapEmissiveWrap, m.mapEmissiveAnisotropy );
							break;
						case 'mapEmissiveRepeat':
						case 'mapEmissiveOffset':
						case 'mapEmissiveWrap':
						case 'mapEmissiveAnisotropy':
							break;
						case 'mapLight':
							json.lightMap = loadTexture( value, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap, m.mapLightAnisotropy );
							break;
						case 'mapLightRepeat':
						case 'mapLightOffset':
						case 'mapLightWrap':
						case 'mapLightAnisotropy':
							break;
						case 'mapAO':
							json.aoMap = loadTexture( value, m.mapAORepeat, m.mapAOOffset, m.mapAOWrap, m.mapAOAnisotropy );
							break;
						case 'mapAORepeat':
						case 'mapAOOffset':
						case 'mapAOWrap':
						case 'mapAOAnisotropy':
							break;
						case 'mapBump':
							json.bumpMap = loadTexture( value, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap, m.mapBumpAnisotropy );
							break;
						case 'mapBumpScale':
							json.bumpScale = value;
							break;
						case 'mapBumpRepeat':
						case 'mapBumpOffset':
						case 'mapBumpWrap':
						case 'mapBumpAnisotropy':
							break;
						case 'mapNormal':
							json.normalMap = loadTexture( value, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap, m.mapNormalAnisotropy );
							break;
						case 'mapNormalFactor':
							json.normalScale = value;
							break;
						case 'mapNormalRepeat':
						case 'mapNormalOffset':
						case 'mapNormalWrap':
						case 'mapNormalAnisotropy':
							break;
						case 'mapSpecular':
							json.specularMap = loadTexture( value, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap, m.mapSpecularAnisotropy );
							break;
						case 'mapSpecularRepeat':
						case 'mapSpecularOffset':
						case 'mapSpecularWrap':
						case 'mapSpecularAnisotropy':
							break;
						case 'mapMetalness':
							json.metalnessMap = loadTexture( value, m.mapMetalnessRepeat, m.mapMetalnessOffset, m.mapMetalnessWrap, m.mapMetalnessAnisotropy );
							break;
						case 'mapMetalnessRepeat':
						case 'mapMetalnessOffset':
						case 'mapMetalnessWrap':
						case 'mapMetalnessAnisotropy':
							break;
						case 'mapRoughness':
							json.roughnessMap = loadTexture( value, m.mapRoughnessRepeat, m.mapRoughnessOffset, m.mapRoughnessWrap, m.mapRoughnessAnisotropy );
							break;
						case 'mapRoughnessRepeat':
						case 'mapRoughnessOffset':
						case 'mapRoughnessWrap':
						case 'mapRoughnessAnisotropy':
							break;
						case 'mapAlpha':
							json.alphaMap = loadTexture( value, m.mapAlphaRepeat, m.mapAlphaOffset, m.mapAlphaWrap, m.mapAlphaAnisotropy );
							break;
						case 'mapAlphaRepeat':
						case 'mapAlphaOffset':
						case 'mapAlphaWrap':
						case 'mapAlphaAnisotropy':
							break;
						case 'flipSided':
							json.side = BackSide;
							break;
						case 'doubleSided':
							json.side = DoubleSide;
							break;
						case 'transparency':
							console.warn( 'THREE.Loader.createMaterial: transparency has been renamed to opacity' );
							json.opacity = value;
							break;
						case 'depthTest':
						case 'depthWrite':
						case 'colorWrite':
						case 'opacity':
						case 'reflectivity':
						case 'transparent':
						case 'visible':
						case 'wireframe':
							json[ name ] = value;
							break;
						case 'vertexColors':
							if ( value === true ) json.vertexColors = VertexColors;
							if ( value === 'face' ) json.vertexColors = FaceColors;
							break;
						default:
							console.error( 'THREE.Loader.createMaterial: Unsupported', name, value );
							break;
  
					}
  
				}
  
				if ( json.type === 'MeshBasicMaterial' ) delete json.emissive;
				if ( json.type !== 'MeshPhongMaterial' ) delete json.specular;
  
				if ( json.opacity < 1 ) json.transparent = true;
  
				materialLoader.setTextures( textures );
  
				return materialLoader.parse( json );
  
			};
  
		} )()
  
	} );
  
	/**
	 * @author Don McCurdy / https://www.donmccurdy.com
	 */
  
	var LoaderUtils = {
  
		decodeText: function ( array ) {
  
			if ( typeof TextDecoder !== 'undefined' ) {
  
				return new TextDecoder().decode( array );
  
			}
  
			// Avoid the String.fromCharCode.apply(null, array) shortcut, which
			// throws a "maximum call stack size exceeded" error for large arrays.
  
			var s = '';
  
			for ( var i = 0, il = array.length; i < il; i ++ ) {
  
				// Implicitly assumes little-endian.
				s += String.fromCharCode( array[ i ] );
  
			}
  
			// Merges multi-byte utf-8 characters.
			return decodeURIComponent( escape( s ) );
  
		},
  
		extractUrlBase: function ( url ) {
  
			var index = url.lastIndexOf( '/' );
  
			if ( index === - 1 ) return './';
  
			return url.substr( 0, index + 1 );
  
		}
  
	};
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 * @author alteredq / http://alteredqualia.com/
	 */
  
	function JSONLoader( manager ) {
  
		if ( typeof manager === 'boolean' ) {
  
			console.warn( 'THREE.JSONLoader: showStatus parameter has been removed from constructor.' );
			manager = undefined;
  
		}
  
		this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;
  
		this.withCredentials = false;
  
	}
  
	Object.assign( JSONLoader.prototype, {
  
		load: function ( url, onLoad, onProgress, onError ) {
  
			var scope = this;
  
			var texturePath = this.texturePath && ( typeof this.texturePath === 'string' ) ? this.texturePath : LoaderUtils.extractUrlBase( url );
  
			var loader = new FileLoader( this.manager );
			loader.setWithCredentials( this.withCredentials );
			loader.load( url, function ( text ) {
  
				var json = JSON.parse( text );
				var metadata = json.metadata;
  
				if ( metadata !== undefined ) {
  
					var type = metadata.type;
  
					if ( type !== undefined ) {
  
						if ( type.toLowerCase() === 'object' ) {
  
							console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.ObjectLoader instead.' );
							return;
  
						}
  
					}
  
				}
  
				var object = scope.parse( json, texturePath );
				onLoad( object.geometry, object.materials );
  
			}, onProgress, onError );
  
		},
  
		setTexturePath: function ( value ) {
  
			this.texturePath = value;
  
		},
  
		parse: ( function () {
  
			function parseModel( json, geometry ) {
  
				function isBitSet( value, position ) {
  
					return value & ( 1 << position );
  
				}
  
				var i, j, fi,
  
					offset, zLength,
  
					colorIndex, normalIndex, uvIndex, materialIndex,
  
					type,
					isQuad,
					hasMaterial,
					hasFaceVertexUv,
					hasFaceNormal, hasFaceVertexNormal,
					hasFaceColor, hasFaceVertexColor,
  
					vertex, face, faceA, faceB, hex, normal,
  
					uvLayer, uv, u, v,
  
					faces = json.faces,
					vertices = json.vertices,
					normals = json.normals,
					colors = json.colors,
  
					scale = json.scale,
  
					nUvLayers = 0;
  
  
				if ( json.uvs !== undefined ) {
  
					// disregard empty arrays
  
					for ( i = 0; i < json.uvs.length; i ++ ) {
  
						if ( json.uvs[ i ].length ) nUvLayers ++;
  
					}
  
					for ( i = 0; i < nUvLayers; i ++ ) {
  
						geometry.faceVertexUvs[ i ] = [];
  
					}
  
				}
  
				offset = 0;
				zLength = vertices.length;
  
				while ( offset < zLength ) {
  
					vertex = new Vector3();
  
					vertex.x = vertices[ offset ++ ] * scale;
					vertex.y = vertices[ offset ++ ] * scale;
					vertex.z = vertices[ offset ++ ] * scale;
  
					geometry.vertices.push( vertex );
  
				}
  
				offset = 0;
				zLength = faces.length;
  
				while ( offset < zLength ) {
  
					type = faces[ offset ++ ];
  
					isQuad = isBitSet( type, 0 );
					hasMaterial = isBitSet( type, 1 );
					hasFaceVertexUv = isBitSet( type, 3 );
					hasFaceNormal = isBitSet( type, 4 );
					hasFaceVertexNormal = isBitSet( type, 5 );
					hasFaceColor = isBitSet( type, 6 );
					hasFaceVertexColor = isBitSet( type, 7 );
  
					// console.log("type", type, "bits", isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor);
  
					if ( isQuad ) {
  
						faceA = new Face3();
						faceA.a = faces[ offset ];
						faceA.b = faces[ offset + 1 ];
						faceA.c = faces[ offset + 3 ];
  
						faceB = new Face3();
						faceB.a = faces[ offset + 1 ];
						faceB.b = faces[ offset + 2 ];
						faceB.c = faces[ offset + 3 ];
  
						offset += 4;
  
						if ( hasMaterial ) {
  
							materialIndex = faces[ offset ++ ];
							faceA.materialIndex = materialIndex;
							faceB.materialIndex = materialIndex;
  
						}
  
						// to get face <=> uv index correspondence
  
						fi = geometry.faces.length;
  
						if ( hasFaceVertexUv ) {
  
							for ( i = 0; i < nUvLayers; i ++ ) {
  
								uvLayer = json.uvs[ i ];
  
								geometry.faceVertexUvs[ i ][ fi ] = [];
								geometry.faceVertexUvs[ i ][ fi + 1 ] = [];
  
								for ( j = 0; j < 4; j ++ ) {
  
									uvIndex = faces[ offset ++ ];
  
									u = uvLayer[ uvIndex * 2 ];
									v = uvLayer[ uvIndex * 2 + 1 ];
  
									uv = new Vector2( u, v );
  
									if ( j !== 2 ) geometry.faceVertexUvs[ i ][ fi ].push( uv );
									if ( j !== 0 ) geometry.faceVertexUvs[ i ][ fi + 1 ].push( uv );
  
								}
  
							}
  
						}
  
						if ( hasFaceNormal ) {
  
							normalIndex = faces[ offset ++ ] * 3;
  
							faceA.normal.set(
								normals[ normalIndex ++ ],
								normals[ normalIndex ++ ],
								normals[ normalIndex ]
							);
  
							faceB.normal.copy( faceA.normal );
  
						}
  
						if ( hasFaceVertexNormal ) {
  
							for ( i = 0; i < 4; i ++ ) {
  
								normalIndex = faces[ offset ++ ] * 3;
  
								normal = new Vector3(
									normals[ normalIndex ++ ],
									normals[ normalIndex ++ ],
									normals[ normalIndex ]
								);
  
  
								if ( i !== 2 ) faceA.vertexNormals.push( normal );
								if ( i !== 0 ) faceB.vertexNormals.push( normal );
  
							}
  
						}
  
  
						if ( hasFaceColor ) {
  
							colorIndex = faces[ offset ++ ];
							hex = colors[ colorIndex ];
  
							faceA.color.setHex( hex );
							faceB.color.setHex( hex );
  
						}
  
  
						if ( hasFaceVertexColor ) {
  
							for ( i = 0; i < 4; i ++ ) {
  
								colorIndex = faces[ offset ++ ];
								hex = colors[ colorIndex ];
  
								if ( i !== 2 ) faceA.vertexColors.push( new Color( hex ) );
								if ( i !== 0 ) faceB.vertexColors.push( new Color( hex ) );
  
							}
  
						}
  
						geometry.faces.push( faceA );
						geometry.faces.push( faceB );
  
					} else {
  
						face = new Face3();
						face.a = faces[ offset ++ ];
						face.b = faces[ offset ++ ];
						face.c = faces[ offset ++ ];
  
						if ( hasMaterial ) {
  
							materialIndex = faces[ offset ++ ];
							face.materialIndex = materialIndex;
  
						}
  
						// to get face <=> uv index correspondence
  
						fi = geometry.faces.length;
  
						if ( hasFaceVertexUv ) {
  
							for ( i = 0; i < nUvLayers; i ++ ) {
  
								uvLayer = json.uvs[ i ];
  
								geometry.faceVertexUvs[ i ][ fi ] = [];
  
								for ( j = 0; j < 3; j ++ ) {
  
									uvIndex = faces[ offset ++ ];
  
									u = uvLayer[ uvIndex * 2 ];
									v = uvLayer[ uvIndex * 2 + 1 ];
  
									uv = new Vector2( u, v );
  
									geometry.faceVertexUvs[ i ][ fi ].push( uv );
  
								}
  
							}
  
						}
  
						if ( hasFaceNormal ) {
  
							normalIndex = faces[ offset ++ ] * 3;
  
							face.normal.set(
								normals[ normalIndex ++ ],
								normals[ normalIndex ++ ],
								normals[ normalIndex ]
							);
  
						}
  
						if ( hasFaceVertexNormal ) {
  
							for ( i = 0; i < 3; i ++ ) {
  
								normalIndex = faces[ offset ++ ] * 3;
  
								normal = new Vector3(
									normals[ normalIndex ++ ],
									normals[ normalIndex ++ ],
									normals[ normalIndex ]
								);
  
								face.vertexNormals.push( normal );
  
							}
  
						}
  
  
						if ( hasFaceColor ) {
  
							colorIndex = faces[ offset ++ ];
							face.color.setHex( colors[ colorIndex ] );
  
						}
  
  
						if ( hasFaceVertexColor ) {
  
							for ( i = 0; i < 3; i ++ ) {
  
								colorIndex = faces[ offset ++ ];
								face.vertexColors.push( new Color( colors[ colorIndex ] ) );
  
							}
  
						}
  
						geometry.faces.push( face );
  
					}
  
				}
  
			}
  
			function parseSkin( json, geometry ) {
  
				var influencesPerVertex = ( json.influencesPerVertex !== undefined ) ? json.influencesPerVertex : 2;
  
				if ( json.skinWeights ) {
  
					for ( var i = 0, l = json.skinWeights.length; i < l; i += influencesPerVertex ) {
  
						var x = json.skinWeights[ i ];
						var y = ( influencesPerVertex > 1 ) ? json.skinWeights[ i + 1 ] : 0;
						var z = ( influencesPerVertex > 2 ) ? json.skinWeights[ i + 2 ] : 0;
						var w = ( influencesPerVertex > 3 ) ? json.skinWeights[ i + 3 ] : 0;
  
						geometry.skinWeights.push( new Vector4( x, y, z, w ) );
  
					}
  
				}
  
				if ( json.skinIndices ) {
  
					for ( var i = 0, l = json.skinIndices.length; i < l; i += influencesPerVertex ) {
  
						var a = json.skinIndices[ i ];
						var b = ( influencesPerVertex > 1 ) ? json.skinIndices[ i + 1 ] : 0;
						var c = ( influencesPerVertex > 2 ) ? json.skinIndices[ i + 2 ] : 0;
						var d = ( influencesPerVertex > 3 ) ? json.skinIndices[ i + 3 ] : 0;
  
						geometry.skinIndices.push( new Vector4( a, b, c, d ) );
  
					}
  
				}
  
				geometry.bones = json.bones;
  
				if ( geometry.bones && geometry.bones.length > 0 && ( geometry.skinWeights.length !== geometry.skinIndices.length || geometry.skinIndices.length !== geometry.vertices.length ) ) {
  
					console.warn( 'When skinning, number of vertices (' + geometry.vertices.length + '), skinIndices (' +
						geometry.skinIndices.length + '), and skinWeights (' + geometry.skinWeights.length + ') should match.' );
  
				}
  
			}
  
			function parseMorphing( json, geometry ) {
  
				var scale = json.scale;
  
				if ( json.morphTargets !== undefined ) {
  
					for ( var i = 0, l = json.morphTargets.length; i < l; i ++ ) {
  
						geometry.morphTargets[ i ] = {};
						geometry.morphTargets[ i ].name = json.morphTargets[ i ].name;
						geometry.morphTargets[ i ].vertices = [];
  
						var dstVertices = geometry.morphTargets[ i ].vertices;
						var srcVertices = json.morphTargets[ i ].vertices;
  
						for ( var v = 0, vl = srcVertices.length; v < vl; v += 3 ) {
  
							var vertex = new Vector3();
							vertex.x = srcVertices[ v ] * scale;
							vertex.y = srcVertices[ v + 1 ] * scale;
							vertex.z = srcVertices[ v + 2 ] * scale;
  
							dstVertices.push( vertex );
  
						}
  
					}
  
				}
  
				if ( json.morphColors !== undefined && json.morphColors.length > 0 ) {
  
					console.warn( 'THREE.JSONLoader: "morphColors" no longer supported. Using them as face colors.' );
  
					var faces = geometry.faces;
					var morphColors = json.morphColors[ 0 ].colors;
  
					for ( var i = 0, l = faces.length; i < l; i ++ ) {
  
						faces[ i ].color.fromArray( morphColors, i * 3 );
  
					}
  
				}
  
			}
  
			function parseAnimations( json, geometry ) {
  
				var outputAnimations = [];
  
				// parse old style Bone/Hierarchy animations
				var animations = [];
  
				if ( json.animation !== undefined ) {
  
					animations.push( json.animation );
  
				}
  
				if ( json.animations !== undefined ) {
  
					if ( json.animations.length ) {
  
						animations = animations.concat( json.animations );
  
					} else {
  
						animations.push( json.animations );
  
					}
  
				}
  
				for ( var i = 0; i < animations.length; i ++ ) {
  
					var clip = AnimationClip.parseAnimation( animations[ i ], geometry.bones );
					if ( clip ) outputAnimations.push( clip );
  
				}
  
				// parse implicit morph animations
				if ( geometry.morphTargets ) {
  
					// TODO: Figure out what an appropraite FPS is for morph target animations -- defaulting to 10, but really it is completely arbitrary.
					var morphAnimationClips = AnimationClip.CreateClipsFromMorphTargetSequences( geometry.morphTargets, 10 );
					outputAnimations = outputAnimations.concat( morphAnimationClips );
  
				}
  
				if ( outputAnimations.length > 0 ) geometry.animations = outputAnimations;
  
			}
  
			return function parse( json, texturePath ) {
  
				if ( json.data !== undefined ) {
  
					// Geometry 4.0 spec
					json = json.data;
  
				}
  
				if ( json.scale !== undefined ) {
  
					json.scale = 1.0 / json.scale;
  
				} else {
  
					json.scale = 1.0;
  
				}
  
				var geometry = new Geometry();
  
				parseModel( json, geometry );
				parseSkin( json, geometry );
				parseMorphing( json, geometry );
				parseAnimations( json, geometry );
  
				geometry.computeFaceNormals();
				geometry.computeBoundingSphere();
  
				if ( json.materials === undefined || json.materials.length === 0 ) {
  
					return { geometry: geometry };
  
				} else {
  
					var materials = Loader.prototype.initMaterials( json.materials, texturePath, this.crossOrigin );
  
					return { geometry: geometry, materials: materials };
  
				}
  
			};
  
		} )()
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function ObjectLoader( manager ) {
  
		this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;
		this.texturePath = '';
  
	}
  
	Object.assign( ObjectLoader.prototype, {
  
		load: function ( url, onLoad, onProgress, onError ) {
  
			if ( this.texturePath === '' ) {
  
				this.texturePath = url.substring( 0, url.lastIndexOf( '/' ) + 1 );
  
			}
  
			var scope = this;
  
			var loader = new FileLoader( scope.manager );
			loader.load( url, function ( text ) {
  
				var json = null;
  
				try {
  
					json = JSON.parse( text );
  
				} catch ( error ) {
  
					if ( onError !== undefined ) onError( error );
  
					console.error( 'THREE:ObjectLoader: Can\'t parse ' + url + '.', error.message );
  
					return;
  
				}
  
				var metadata = json.metadata;
  
				if ( metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === 'geometry' ) {
  
					console.error( 'THREE.ObjectLoader: Can\'t load ' + url + '. Use THREE.JSONLoader instead.' );
					return;
  
				}
  
				scope.parse( json, onLoad );
  
			}, onProgress, onError );
  
		},
  
		setTexturePath: function ( value ) {
  
			this.texturePath = value;
			return this;
  
		},
  
		setCrossOrigin: function ( value ) {
  
			this.crossOrigin = value;
			return this;
  
		},
  
		parse: function ( json, onLoad ) {
  
			var shapes = this.parseShape( json.shapes );
			var geometries = this.parseGeometries( json.geometries, shapes );
  
			var images = this.parseImages( json.images, function () {
  
				if ( onLoad !== undefined ) onLoad( object );
  
			} );
  
			var textures = this.parseTextures( json.textures, images );
			var materials = this.parseMaterials( json.materials, textures );
  
			var object = this.parseObject( json.object, geometries, materials );
  
			if ( json.animations ) {
  
				object.animations = this.parseAnimations( json.animations );
  
			}
  
			if ( json.images === undefined || json.images.length === 0 ) {
  
				if ( onLoad !== undefined ) onLoad( object );
  
			}
  
			return object;
  
		},
  
		parseShape: function ( json ) {
  
			var shapes = {};
  
			if ( json !== undefined ) {
  
				for ( var i = 0, l = json.length; i < l; i ++ ) {
  
					var shape = new Shape().fromJSON( json[ i ] );
  
					shapes[ shape.uuid ] = shape;
  
				}
  
			}
  
			return shapes;
  
		},
  
		parseGeometries: function ( json, shapes ) {
  
			var geometries = {};
  
			if ( json !== undefined ) {
  
				var geometryLoader = new JSONLoader();
				var bufferGeometryLoader = new BufferGeometryLoader();
  
				for ( var i = 0, l = json.length; i < l; i ++ ) {
  
					var geometry;
					var data = json[ i ];
  
					switch ( data.type ) {
  
						case 'PlaneGeometry':
						case 'PlaneBufferGeometry':
  
							geometry = new Geometries[ data.type ](
								data.width,
								data.height,
								data.widthSegments,
								data.heightSegments
							);
  
							break;
  
						case 'BoxGeometry':
						case 'BoxBufferGeometry':
						case 'CubeGeometry': // backwards compatible
  
							geometry = new Geometries[ data.type ](
								data.width,
								data.height,
								data.depth,
								data.widthSegments,
								data.heightSegments,
								data.depthSegments
							);
  
							break;
  
						case 'CircleGeometry':
						case 'CircleBufferGeometry':
  
							geometry = new Geometries[ data.type ](
								data.radius,
								data.segments,
								data.thetaStart,
								data.thetaLength
							);
  
							break;
  
						case 'CylinderGeometry':
						case 'CylinderBufferGeometry':
  
							geometry = new Geometries[ data.type ](
								data.radiusTop,
								data.radiusBottom,
								data.height,
								data.radialSegments,
								data.heightSegments,
								data.openEnded,
								data.thetaStart,
								data.thetaLength
							);
  
							break;
  
						case 'ConeGeometry':
						case 'ConeBufferGeometry':
  
							geometry = new Geometries[ data.type ](
								data.radius,
								data.height,
								data.radialSegments,
								data.heightSegments,
								data.openEnded,
								data.thetaStart,
								data.thetaLength
							);
  
							break;
  
						case 'SphereGeometry':
						case 'SphereBufferGeometry':
  
							geometry = new Geometries[ data.type ](
								data.radius,
								data.widthSegments,
								data.heightSegments,
								data.phiStart,
								data.phiLength,
								data.thetaStart,
								data.thetaLength
							);
  
							break;
  
						case 'DodecahedronGeometry':
						case 'DodecahedronBufferGeometry':
						case 'IcosahedronGeometry':
						case 'IcosahedronBufferGeometry':
						case 'OctahedronGeometry':
						case 'OctahedronBufferGeometry':
						case 'TetrahedronGeometry':
						case 'TetrahedronBufferGeometry':
  
							geometry = new Geometries[ data.type ](
								data.radius,
								data.detail
							);
  
							break;
  
						case 'RingGeometry':
						case 'RingBufferGeometry':
  
							geometry = new Geometries[ data.type ](
								data.innerRadius,
								data.outerRadius,
								data.thetaSegments,
								data.phiSegments,
								data.thetaStart,
								data.thetaLength
							);
  
							break;
  
						case 'TorusGeometry':
						case 'TorusBufferGeometry':
  
							geometry = new Geometries[ data.type ](
								data.radius,
								data.tube,
								data.radialSegments,
								data.tubularSegments,
								data.arc
							);
  
							break;
  
						case 'TorusKnotGeometry':
						case 'TorusKnotBufferGeometry':
  
							geometry = new Geometries[ data.type ](
								data.radius,
								data.tube,
								data.tubularSegments,
								data.radialSegments,
								data.p,
								data.q
							);
  
							break;
  
						case 'LatheGeometry':
						case 'LatheBufferGeometry':
  
							geometry = new Geometries[ data.type ](
								data.points,
								data.segments,
								data.phiStart,
								data.phiLength
							);
  
							break;
  
						case 'PolyhedronGeometry':
						case 'PolyhedronBufferGeometry':
  
							geometry = new Geometries[ data.type ](
								data.vertices,
								data.indices,
								data.radius,
								data.details
							);
  
							break;
  
						case 'ShapeGeometry':
						case 'ShapeBufferGeometry':
  
							var geometryShapes = [];
  
							for ( var j = 0, jl = data.shapes.length; j < jl; j ++ ) {
  
								var shape = shapes[ data.shapes[ j ] ];
  
								geometryShapes.push( shape );
  
							}
  
							geometry = new Geometries[ data.type ](
								geometryShapes,
								data.curveSegments
							);
  
							break;
  
  
						case 'ExtrudeGeometry':
						case 'ExtrudeBufferGeometry':
  
							var geometryShapes = [];
  
							for ( var j = 0, jl = data.shapes.length; j < jl; j ++ ) {
  
								var shape = shapes[ data.shapes[ j ] ];
  
								geometryShapes.push( shape );
  
							}
  
							var extrudePath = data.options.extrudePath;
  
							if ( extrudePath !== undefined ) {
  
								data.options.extrudePath = new Curves[ extrudePath.type ]().fromJSON( extrudePath );
  
							}
  
							geometry = new Geometries[ data.type ](
								geometryShapes,
								data.options
							);
  
							break;
  
						case 'BufferGeometry':
  
							geometry = bufferGeometryLoader.parse( data );
  
							break;
  
						case 'Geometry':
  
							geometry = geometryLoader.parse( data, this.texturePath ).geometry;
  
							break;
  
						default:
  
							console.warn( 'THREE.ObjectLoader: Unsupported geometry type "' + data.type + '"' );
  
							continue;
  
					}
  
					geometry.uuid = data.uuid;
  
					if ( data.name !== undefined ) geometry.name = data.name;
					if ( geometry.isBufferGeometry === true && data.userData !== undefined ) geometry.userData = data.userData;
  
					geometries[ data.uuid ] = geometry;
  
				}
  
			}
  
			return geometries;
  
		},
  
		parseMaterials: function ( json, textures ) {
  
			var materials = {};
  
			if ( json !== undefined ) {
  
				var loader = new MaterialLoader();
				loader.setTextures( textures );
  
				for ( var i = 0, l = json.length; i < l; i ++ ) {
  
					var data = json[ i ];
  
					if ( data.type === 'MultiMaterial' ) {
  
						// Deprecated
  
						var array = [];
  
						for ( var j = 0; j < data.materials.length; j ++ ) {
  
							array.push( loader.parse( data.materials[ j ] ) );
  
						}
  
						materials[ data.uuid ] = array;
  
					} else {
  
						materials[ data.uuid ] = loader.parse( data );
  
					}
  
				}
  
			}
  
			return materials;
  
		},
  
		parseAnimations: function ( json ) {
  
			var animations = [];
  
			for ( var i = 0; i < json.length; i ++ ) {
  
				var data = json[ i ];
  
				var clip = AnimationClip.parse( data );
  
				if ( data.uuid !== undefined ) clip.uuid = data.uuid;
  
				animations.push( clip );
  
			}
  
			return animations;
  
		},
  
		parseImages: function ( json, onLoad ) {
  
			var scope = this;
			var images = {};
  
			function loadImage( url ) {
  
				scope.manager.itemStart( url );
  
				return loader.load( url, function () {
  
					scope.manager.itemEnd( url );
  
				}, undefined, function () {
  
					scope.manager.itemEnd( url );
					scope.manager.itemError( url );
  
				} );
  
			}
  
			if ( json !== undefined && json.length > 0 ) {
  
				var manager = new LoadingManager( onLoad );
  
				var loader = new ImageLoader( manager );
				loader.setCrossOrigin( this.crossOrigin );
  
				for ( var i = 0, l = json.length; i < l; i ++ ) {
  
					var image = json[ i ];
					var path = /^(\/\/)|([a-z]+:(\/\/)?)/i.test( image.url ) ? image.url : scope.texturePath + image.url;
  
					images[ image.uuid ] = loadImage( path );
  
				}
  
			}
  
			return images;
  
		},
  
		parseTextures: function ( json, images ) {
  
			function parseConstant( value, type ) {
  
				if ( typeof value === 'number' ) return value;
  
				console.warn( 'THREE.ObjectLoader.parseTexture: Constant should be in numeric form.', value );
  
				return type[ value ];
  
			}
  
			var textures = {};
  
			if ( json !== undefined ) {
  
				for ( var i = 0, l = json.length; i < l; i ++ ) {
  
					var data = json[ i ];
  
					if ( data.image === undefined ) {
  
						console.warn( 'THREE.ObjectLoader: No "image" specified for', data.uuid );
  
					}
  
					if ( images[ data.image ] === undefined ) {
  
						console.warn( 'THREE.ObjectLoader: Undefined image', data.image );
  
					}
  
					var texture = new Texture( images[ data.image ] );
					texture.needsUpdate = true;
  
					texture.uuid = data.uuid;
  
					if ( data.name !== undefined ) texture.name = data.name;
  
					if ( data.mapping !== undefined ) texture.mapping = parseConstant( data.mapping, TEXTURE_MAPPING );
  
					if ( data.offset !== undefined ) texture.offset.fromArray( data.offset );
					if ( data.repeat !== undefined ) texture.repeat.fromArray( data.repeat );
					if ( data.center !== undefined ) texture.center.fromArray( data.center );
					if ( data.rotation !== undefined ) texture.rotation = data.rotation;
  
					if ( data.wrap !== undefined ) {
  
						texture.wrapS = parseConstant( data.wrap[ 0 ], TEXTURE_WRAPPING );
						texture.wrapT = parseConstant( data.wrap[ 1 ], TEXTURE_WRAPPING );
  
					}
  
					if ( data.format !== undefined ) texture.format = data.format;
  
					if ( data.minFilter !== undefined ) texture.minFilter = parseConstant( data.minFilter, TEXTURE_FILTER );
					if ( data.magFilter !== undefined ) texture.magFilter = parseConstant( data.magFilter, TEXTURE_FILTER );
					if ( data.anisotropy !== undefined ) texture.anisotropy = data.anisotropy;
  
					if ( data.flipY !== undefined ) texture.flipY = data.flipY;
  
					textures[ data.uuid ] = texture;
  
				}
  
			}
  
			return textures;
  
		},
  
		parseObject: function ( data, geometries, materials ) {
  
			var object;
  
			function getGeometry( name ) {
  
				if ( geometries[ name ] === undefined ) {
  
					console.warn( 'THREE.ObjectLoader: Undefined geometry', name );
  
				}
  
				return geometries[ name ];
  
			}
  
			function getMaterial( name ) {
  
				if ( name === undefined ) return undefined;
  
				if ( Array.isArray( name ) ) {
  
					var array = [];
  
					for ( var i = 0, l = name.length; i < l; i ++ ) {
  
						var uuid = name[ i ];
  
						if ( materials[ uuid ] === undefined ) {
  
							console.warn( 'THREE.ObjectLoader: Undefined material', uuid );
  
						}
  
						array.push( materials[ uuid ] );
  
					}
  
					return array;
  
				}
  
				if ( materials[ name ] === undefined ) {
  
					console.warn( 'THREE.ObjectLoader: Undefined material', name );
  
				}
  
				return materials[ name ];
  
			}
  
			switch ( data.type ) {
  
				case 'Scene':
  
					object = new Scene();
  
					if ( data.background !== undefined ) {
  
						if ( Number.isInteger( data.background ) ) {
  
							object.background = new Color( data.background );
  
						}
  
					}
  
					if ( data.fog !== undefined ) {
  
						if ( data.fog.type === 'Fog' ) {
  
							object.fog = new Fog( data.fog.color, data.fog.near, data.fog.far );
  
						} else if ( data.fog.type === 'FogExp2' ) {
  
							object.fog = new FogExp2( data.fog.color, data.fog.density );
  
						}
  
					}
  
					break;
  
				case 'PerspectiveCamera':
  
					object = new PerspectiveCamera( data.fov, data.aspect, data.near, data.far );
  
					if ( data.focus !== undefined ) object.focus = data.focus;
					if ( data.zoom !== undefined ) object.zoom = data.zoom;
					if ( data.filmGauge !== undefined ) object.filmGauge = data.filmGauge;
					if ( data.filmOffset !== undefined ) object.filmOffset = data.filmOffset;
					if ( data.view !== undefined ) object.view = Object.assign( {}, data.view );
  
					break;
  
				case 'OrthographicCamera':
  
					object = new OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far );
  
					if ( data.zoom !== undefined ) object.zoom = data.zoom;
					if ( data.view !== undefined ) object.view = Object.assign( {}, data.view );
  
					break;
  
				case 'AmbientLight':
  
					object = new AmbientLight( data.color, data.intensity );
  
					break;
  
				case 'DirectionalLight':
  
					object = new DirectionalLight( data.color, data.intensity );
  
					break;
  
				case 'PointLight':
  
					object = new PointLight( data.color, data.intensity, data.distance, data.decay );
  
					break;
  
				case 'RectAreaLight':
  
					object = new RectAreaLight( data.color, data.intensity, data.width, data.height );
  
					break;
  
				case 'SpotLight':
  
					object = new SpotLight( data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay );
  
					break;
  
				case 'HemisphereLight':
  
					object = new HemisphereLight( data.color, data.groundColor, data.intensity );
  
					break;
  
				case 'SkinnedMesh':
  
					console.warn( 'THREE.ObjectLoader.parseObject() does not support SkinnedMesh yet.' );
  
				case 'Mesh':
  
					var geometry = getGeometry( data.geometry );
					var material = getMaterial( data.material );
  
					if ( geometry.bones && geometry.bones.length > 0 ) {
  
						object = new SkinnedMesh( geometry, material );
  
					} else {
  
						object = new Mesh( geometry, material );
  
					}
  
					break;
  
				case 'LOD':
  
					object = new LOD();
  
					break;
  
				case 'Line':
  
					object = new Line( getGeometry( data.geometry ), getMaterial( data.material ), data.mode );
  
					break;
  
				case 'LineLoop':
  
					object = new LineLoop( getGeometry( data.geometry ), getMaterial( data.material ) );
  
					break;
  
				case 'LineSegments':
  
					object = new LineSegments( getGeometry( data.geometry ), getMaterial( data.material ) );
  
					break;
  
				case 'PointCloud':
				case 'Points':
  
					object = new Points( getGeometry( data.geometry ), getMaterial( data.material ) );
  
					break;
  
				case 'Sprite':
  
					object = new Sprite( getMaterial( data.material ) );
  
					break;
  
				case 'Group':
  
					object = new Group();
  
					break;
  
				default:
  
					object = new Object3D();
  
			}
  
			object.uuid = data.uuid;
  
			if ( data.name !== undefined ) object.name = data.name;
  
			if ( data.matrix !== undefined ) {
  
				object.matrix.fromArray( data.matrix );
  
				if ( data.matrixAutoUpdate !== undefined ) object.matrixAutoUpdate = data.matrixAutoUpdate;
				if ( object.matrixAutoUpdate ) object.matrix.decompose( object.position, object.quaternion, object.scale );
  
			} else {
  
				if ( data.position !== undefined ) object.position.fromArray( data.position );
				if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation );
				if ( data.quaternion !== undefined ) object.quaternion.fromArray( data.quaternion );
				if ( data.scale !== undefined ) object.scale.fromArray( data.scale );
  
			}
  
			if ( data.castShadow !== undefined ) object.castShadow = data.castShadow;
			if ( data.receiveShadow !== undefined ) object.receiveShadow = data.receiveShadow;
  
			if ( data.shadow ) {
  
				if ( data.shadow.bias !== undefined ) object.shadow.bias = data.shadow.bias;
				if ( data.shadow.radius !== undefined ) object.shadow.radius = data.shadow.radius;
				if ( data.shadow.mapSize !== undefined ) object.shadow.mapSize.fromArray( data.shadow.mapSize );
				if ( data.shadow.camera !== undefined ) object.shadow.camera = this.parseObject( data.shadow.camera );
  
			}
  
			if ( data.visible !== undefined ) object.visible = data.visible;
			if ( data.frustumCulled !== undefined ) object.frustumCulled = data.frustumCulled;
			if ( data.renderOrder !== undefined ) object.renderOrder = data.renderOrder;
			if ( data.userData !== undefined ) object.userData = data.userData;
  
			if ( data.children !== undefined ) {
  
				var children = data.children;
  
				for ( var i = 0; i < children.length; i ++ ) {
  
					object.add( this.parseObject( children[ i ], geometries, materials ) );
  
				}
  
			}
  
			if ( data.type === 'LOD' ) {
  
				var levels = data.levels;
  
				for ( var l = 0; l < levels.length; l ++ ) {
  
					var level = levels[ l ];
					var child = object.getObjectByProperty( 'uuid', level.object );
  
					if ( child !== undefined ) {
  
						object.addLevel( child, level.distance );
  
					}
  
				}
  
			}
  
			return object;
  
		}
  
	} );
  
	var TEXTURE_MAPPING = {
		UVMapping: UVMapping,
		CubeReflectionMapping: CubeReflectionMapping,
		CubeRefractionMapping: CubeRefractionMapping,
		EquirectangularReflectionMapping: EquirectangularReflectionMapping,
		EquirectangularRefractionMapping: EquirectangularRefractionMapping,
		SphericalReflectionMapping: SphericalReflectionMapping,
		CubeUVReflectionMapping: CubeUVReflectionMapping,
		CubeUVRefractionMapping: CubeUVRefractionMapping
	};
  
	var TEXTURE_WRAPPING = {
		RepeatWrapping: RepeatWrapping,
		ClampToEdgeWrapping: ClampToEdgeWrapping,
		MirroredRepeatWrapping: MirroredRepeatWrapping
	};
  
	var TEXTURE_FILTER = {
		NearestFilter: NearestFilter,
		NearestMipMapNearestFilter: NearestMipMapNearestFilter,
		NearestMipMapLinearFilter: NearestMipMapLinearFilter,
		LinearFilter: LinearFilter,
		LinearMipMapNearestFilter: LinearMipMapNearestFilter,
		LinearMipMapLinearFilter: LinearMipMapLinearFilter
	};
  
	/**
	 * @author zz85 / http://www.lab4games.net/zz85/blog
	 * minimal class for proxing functions to Path. Replaces old "extractSubpaths()"
	 **/
  
	function ShapePath() {
  
		this.type = 'ShapePath';
  
		this.color = new Color();
  
		this.subPaths = [];
		this.currentPath = null;
  
	}
  
	Object.assign( ShapePath.prototype, {
  
		moveTo: function ( x, y ) {
  
			this.currentPath = new Path();
			this.subPaths.push( this.currentPath );
			this.currentPath.moveTo( x, y );
  
		},
  
		lineTo: function ( x, y ) {
  
			this.currentPath.lineTo( x, y );
  
		},
  
		quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) {
  
			this.currentPath.quadraticCurveTo( aCPx, aCPy, aX, aY );
  
		},
  
		bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) {
  
			this.currentPath.bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY );
  
		},
  
		splineThru: function ( pts ) {
  
			this.currentPath.splineThru( pts );
  
		},
  
		toShapes: function ( isCCW, noHoles ) {
  
			function toShapesNoHoles( inSubpaths ) {
  
				var shapes = [];
  
				for ( var i = 0, l = inSubpaths.length; i < l; i ++ ) {
  
					var tmpPath = inSubpaths[ i ];
  
					var tmpShape = new Shape();
					tmpShape.curves = tmpPath.curves;
  
					shapes.push( tmpShape );
  
				}
  
				return shapes;
  
			}
  
			function isPointInsidePolygon( inPt, inPolygon ) {
  
				var polyLen = inPolygon.length;
  
				// inPt on polygon contour => immediate success    or
				// toggling of inside/outside at every single! intersection point of an edge
				//  with the horizontal line through inPt, left of inPt
				//  not counting lowerY endpoints of edges and whole edges on that line
				var inside = false;
				for ( var p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) {
  
					var edgeLowPt = inPolygon[ p ];
					var edgeHighPt = inPolygon[ q ];
  
					var edgeDx = edgeHighPt.x - edgeLowPt.x;
					var edgeDy = edgeHighPt.y - edgeLowPt.y;
  
					if ( Math.abs( edgeDy ) > Number.EPSILON ) {
  
						// not parallel
						if ( edgeDy < 0 ) {
  
							edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx;
							edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy;
  
						}
						if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) 		continue;
  
						if ( inPt.y === edgeLowPt.y ) {
  
							if ( inPt.x === edgeLowPt.x )		return	true;		// inPt is on contour ?
							// continue;				// no intersection or edgeLowPt => doesn't count !!!
  
						} else {
  
							var perpEdge = edgeDy * ( inPt.x - edgeLowPt.x ) - edgeDx * ( inPt.y - edgeLowPt.y );
							if ( perpEdge === 0 )				return	true;		// inPt is on contour ?
							if ( perpEdge < 0 ) 				continue;
							inside = ! inside;		// true intersection left of inPt
  
						}
  
					} else {
  
						// parallel or collinear
						if ( inPt.y !== edgeLowPt.y ) 		continue;			// parallel
						// edge lies on the same horizontal line as inPt
						if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) ||
							 ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) )		return	true;	// inPt: Point on contour !
						// continue;
  
					}
  
				}
  
				return	inside;
  
			}
  
			var isClockWise = ShapeUtils.isClockWise;
  
			var subPaths = this.subPaths;
			if ( subPaths.length === 0 ) return [];
  
			if ( noHoles === true )	return	toShapesNoHoles( subPaths );
  
  
			var solid, tmpPath, tmpShape, shapes = [];
  
			if ( subPaths.length === 1 ) {
  
				tmpPath = subPaths[ 0 ];
				tmpShape = new Shape();
				tmpShape.curves = tmpPath.curves;
				shapes.push( tmpShape );
				return shapes;
  
			}
  
			var holesFirst = ! isClockWise( subPaths[ 0 ].getPoints() );
			holesFirst = isCCW ? ! holesFirst : holesFirst;
  
			// console.log("Holes first", holesFirst);
  
			var betterShapeHoles = [];
			var newShapes = [];
			var newShapeHoles = [];
			var mainIdx = 0;
			var tmpPoints;
  
			newShapes[ mainIdx ] = undefined;
			newShapeHoles[ mainIdx ] = [];
  
			for ( var i = 0, l = subPaths.length; i < l; i ++ ) {
  
				tmpPath = subPaths[ i ];
				tmpPoints = tmpPath.getPoints();
				solid = isClockWise( tmpPoints );
				solid = isCCW ? ! solid : solid;
  
				if ( solid ) {
  
					if ( ( ! holesFirst ) && ( newShapes[ mainIdx ] ) )	mainIdx ++;
  
					newShapes[ mainIdx ] = { s: new Shape(), p: tmpPoints };
					newShapes[ mainIdx ].s.curves = tmpPath.curves;
  
					if ( holesFirst )	mainIdx ++;
					newShapeHoles[ mainIdx ] = [];
  
					//console.log('cw', i);
  
				} else {
  
					newShapeHoles[ mainIdx ].push( { h: tmpPath, p: tmpPoints[ 0 ] } );
  
					//console.log('ccw', i);
  
				}
  
			}
  
			// only Holes? -> probably all Shapes with wrong orientation
			if ( ! newShapes[ 0 ] )	return	toShapesNoHoles( subPaths );
  
  
			if ( newShapes.length > 1 ) {
  
				var ambiguous = false;
				var toChange = [];
  
				for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) {
  
					betterShapeHoles[ sIdx ] = [];
  
				}
  
				for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) {
  
					var sho = newShapeHoles[ sIdx ];
  
					for ( var hIdx = 0; hIdx < sho.length; hIdx ++ ) {
  
						var ho = sho[ hIdx ];
						var hole_unassigned = true;
  
						for ( var s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) {
  
							if ( isPointInsidePolygon( ho.p, newShapes[ s2Idx ].p ) ) {
  
								if ( sIdx !== s2Idx )	toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } );
								if ( hole_unassigned ) {
  
									hole_unassigned = false;
									betterShapeHoles[ s2Idx ].push( ho );
  
								} else {
  
									ambiguous = true;
  
								}
  
							}
  
						}
						if ( hole_unassigned ) {
  
							betterShapeHoles[ sIdx ].push( ho );
  
						}
  
					}
  
				}
				// console.log("ambiguous: ", ambiguous);
				if ( toChange.length > 0 ) {
  
					// console.log("to change: ", toChange);
					if ( ! ambiguous )	newShapeHoles = betterShapeHoles;
  
				}
  
			}
  
			var tmpHoles;
  
			for ( var i = 0, il = newShapes.length; i < il; i ++ ) {
  
				tmpShape = newShapes[ i ].s;
				shapes.push( tmpShape );
				tmpHoles = newShapeHoles[ i ];
  
				for ( var j = 0, jl = tmpHoles.length; j < jl; j ++ ) {
  
					tmpShape.holes.push( tmpHoles[ j ].h );
  
				}
  
			}
  
			//console.log("shape", shapes);
  
			return shapes;
  
		}
  
	} );
  
	/**
	 * @author zz85 / http://www.lab4games.net/zz85/blog
	 * @author mrdoob / http://mrdoob.com/
	 */
  
  
	function Font( data ) {
  
		this.type = 'Font';
  
		this.data = data;
  
	}
  
	Object.assign( Font.prototype, {
  
		isFont: true,
  
		generateShapes: function ( text, size, divisions ) {
  
			if ( size === undefined ) size = 100;
			if ( divisions === undefined ) divisions = 4;
  
			var shapes = [];
			var paths = createPaths( text, size, divisions, this.data );
  
			for ( var p = 0, pl = paths.length; p < pl; p ++ ) {
  
				Array.prototype.push.apply( shapes, paths[ p ].toShapes() );
  
			}
  
			return shapes;
  
		}
  
	} );
  
	function createPaths( text, size, divisions, data ) {
  
		var chars = Array.from ? Array.from( text ) : String( text ).split( '' ); // see #13988
		var scale = size / data.resolution;
		var line_height = ( data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness ) * scale;
  
		var paths = [];
  
		var offsetX = 0, offsetY = 0;
  
		for ( var i = 0; i < chars.length; i ++ ) {
  
			var char = chars[ i ];
  
			if ( char === '\n' ) {
  
				offsetX = 0;
				offsetY -= line_height;
  
			} else {
  
				var ret = createPath( char, divisions, scale, offsetX, offsetY, data );
				offsetX += ret.offsetX;
				paths.push( ret.path );
  
			}
  
		}
  
		return paths;
  
	}
  
	function createPath( char, divisions, scale, offsetX, offsetY, data ) {
  
		var glyph = data.glyphs[ char ] || data.glyphs[ '?' ];
  
		if ( ! glyph ) return;
  
		var path = new ShapePath();
  
		var x, y, cpx, cpy, cpx1, cpy1, cpx2, cpy2;
  
		if ( glyph.o ) {
  
			var outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) );
  
			for ( var i = 0, l = outline.length; i < l; ) {
  
				var action = outline[ i ++ ];
  
				switch ( action ) {
  
					case 'm': // moveTo
  
						x = outline[ i ++ ] * scale + offsetX;
						y = outline[ i ++ ] * scale + offsetY;
  
						path.moveTo( x, y );
  
						break;
  
					case 'l': // lineTo
  
						x = outline[ i ++ ] * scale + offsetX;
						y = outline[ i ++ ] * scale + offsetY;
  
						path.lineTo( x, y );
  
						break;
  
					case 'q': // quadraticCurveTo
  
						cpx = outline[ i ++ ] * scale + offsetX;
						cpy = outline[ i ++ ] * scale + offsetY;
						cpx1 = outline[ i ++ ] * scale + offsetX;
						cpy1 = outline[ i ++ ] * scale + offsetY;
  
						path.quadraticCurveTo( cpx1, cpy1, cpx, cpy );
  
						break;
  
					case 'b': // bezierCurveTo
  
						cpx = outline[ i ++ ] * scale + offsetX;
						cpy = outline[ i ++ ] * scale + offsetY;
						cpx1 = outline[ i ++ ] * scale + offsetX;
						cpy1 = outline[ i ++ ] * scale + offsetY;
						cpx2 = outline[ i ++ ] * scale + offsetX;
						cpy2 = outline[ i ++ ] * scale + offsetY;
  
						path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy );
  
						break;
  
				}
  
			}
  
		}
  
		return { offsetX: glyph.ha * scale, path: path };
  
	}
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function FontLoader( manager ) {
  
		this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;
  
	}
  
	Object.assign( FontLoader.prototype, {
  
		load: function ( url, onLoad, onProgress, onError ) {
  
			var scope = this;
  
			var loader = new FileLoader( this.manager );
			loader.setPath( this.path );
			loader.load( url, function ( text ) {
  
				var json;
  
				try {
  
					json = JSON.parse( text );
  
				} catch ( e ) {
  
					console.warn( 'THREE.FontLoader: typeface.js support is being deprecated. Use typeface.json instead.' );
					json = JSON.parse( text.substring( 65, text.length - 2 ) );
  
				}
  
				var font = scope.parse( json );
  
				if ( onLoad ) onLoad( font );
  
			}, onProgress, onError );
  
		},
  
		parse: function ( json ) {
  
			return new Font( json );
  
		},
  
		setPath: function ( value ) {
  
			this.path = value;
			return this;
  
		}
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	var context;
  
	var AudioContext = {
  
		getContext: function () {
  
			if ( context === undefined ) {
  
				context = new ( window.AudioContext || window.webkitAudioContext )();
  
			}
  
			return context;
  
		},
  
		setContext: function ( value ) {
  
			context = value;
  
		}
  
	};
  
	/**
	 * @author Reece Aaron Lecrivain / http://reecenotes.com/
	 */
  
	function AudioLoader( manager ) {
  
		this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;
  
	}
  
	Object.assign( AudioLoader.prototype, {
  
		load: function ( url, onLoad, onProgress, onError ) {
  
			var loader = new FileLoader( this.manager );
			loader.setResponseType( 'arraybuffer' );
			loader.load( url, function ( buffer ) {
  
				var context = AudioContext.getContext();
  
				context.decodeAudioData( buffer, function ( audioBuffer ) {
  
					onLoad( audioBuffer );
  
				} );
  
			}, onProgress, onError );
  
		}
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function StereoCamera() {
  
		this.type = 'StereoCamera';
  
		this.aspect = 1;
  
		this.eyeSep = 0.064;
  
		this.cameraL = new PerspectiveCamera();
		this.cameraL.layers.enable( 1 );
		this.cameraL.matrixAutoUpdate = false;
  
		this.cameraR = new PerspectiveCamera();
		this.cameraR.layers.enable( 2 );
		this.cameraR.matrixAutoUpdate = false;
  
	}
  
	Object.assign( StereoCamera.prototype, {
  
		update: ( function () {
  
			var instance, focus, fov, aspect, near, far, zoom, eyeSep;
  
			var eyeRight = new Matrix4();
			var eyeLeft = new Matrix4();
  
			return function update( camera ) {
  
				var needsUpdate = instance !== this || focus !== camera.focus || fov !== camera.fov ||
													aspect !== camera.aspect * this.aspect || near !== camera.near ||
													far !== camera.far || zoom !== camera.zoom || eyeSep !== this.eyeSep;
  
				if ( needsUpdate ) {
  
					instance = this;
					focus = camera.focus;
					fov = camera.fov;
					aspect = camera.aspect * this.aspect;
					near = camera.near;
					far = camera.far;
					zoom = camera.zoom;
  
					// Off-axis stereoscopic effect based on
					// http://paulbourke.net/stereographics/stereorender/
  
					var projectionMatrix = camera.projectionMatrix.clone();
					eyeSep = this.eyeSep / 2;
					var eyeSepOnProjection = eyeSep * near / focus;
					var ymax = ( near * Math.tan( _Math.DEG2RAD * fov * 0.5 ) ) / zoom;
					var xmin, xmax;
  
					// translate xOffset
  
					eyeLeft.elements[ 12 ] = - eyeSep;
					eyeRight.elements[ 12 ] = eyeSep;
  
					// for left eye
  
					xmin = - ymax * aspect + eyeSepOnProjection;
					xmax = ymax * aspect + eyeSepOnProjection;
  
					projectionMatrix.elements[ 0 ] = 2 * near / ( xmax - xmin );
					projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin );
  
					this.cameraL.projectionMatrix.copy( projectionMatrix );
  
					// for right eye
  
					xmin = - ymax * aspect - eyeSepOnProjection;
					xmax = ymax * aspect - eyeSepOnProjection;
  
					projectionMatrix.elements[ 0 ] = 2 * near / ( xmax - xmin );
					projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin );
  
					this.cameraR.projectionMatrix.copy( projectionMatrix );
  
				}
  
				this.cameraL.matrixWorld.copy( camera.matrixWorld ).multiply( eyeLeft );
				this.cameraR.matrixWorld.copy( camera.matrixWorld ).multiply( eyeRight );
  
			};
  
		} )()
  
	} );
  
	/**
	 * Camera for rendering cube maps
	 *	- renders scene into axis-aligned cube
	 *
	 * @author alteredq / http://alteredqualia.com/
	 */
  
	function CubeCamera( near, far, cubeResolution ) {
  
		Object3D.call( this );
  
		this.type = 'CubeCamera';
  
		var fov = 90, aspect = 1;
  
		var cameraPX = new PerspectiveCamera( fov, aspect, near, far );
		cameraPX.up.set( 0, - 1, 0 );
		cameraPX.lookAt( new Vector3( 1, 0, 0 ) );
		this.add( cameraPX );
  
		var cameraNX = new PerspectiveCamera( fov, aspect, near, far );
		cameraNX.up.set( 0, - 1, 0 );
		cameraNX.lookAt( new Vector3( - 1, 0, 0 ) );
		this.add( cameraNX );
  
		var cameraPY = new PerspectiveCamera( fov, aspect, near, far );
		cameraPY.up.set( 0, 0, 1 );
		cameraPY.lookAt( new Vector3( 0, 1, 0 ) );
		this.add( cameraPY );
  
		var cameraNY = new PerspectiveCamera( fov, aspect, near, far );
		cameraNY.up.set( 0, 0, - 1 );
		cameraNY.lookAt( new Vector3( 0, - 1, 0 ) );
		this.add( cameraNY );
  
		var cameraPZ = new PerspectiveCamera( fov, aspect, near, far );
		cameraPZ.up.set( 0, - 1, 0 );
		cameraPZ.lookAt( new Vector3( 0, 0, 1 ) );
		this.add( cameraPZ );
  
		var cameraNZ = new PerspectiveCamera( fov, aspect, near, far );
		cameraNZ.up.set( 0, - 1, 0 );
		cameraNZ.lookAt( new Vector3( 0, 0, - 1 ) );
		this.add( cameraNZ );
  
		var options = { format: RGBFormat, magFilter: LinearFilter, minFilter: LinearFilter };
  
		this.renderTarget = new WebGLRenderTargetCube( cubeResolution, cubeResolution, options );
		this.renderTarget.texture.name = "CubeCamera";
  
		this.update = function ( renderer, scene ) {
  
			if ( this.parent === null ) this.updateMatrixWorld();
  
			var renderTarget = this.renderTarget;
			var generateMipmaps = renderTarget.texture.generateMipmaps;
  
			renderTarget.texture.generateMipmaps = false;
  
			renderTarget.activeCubeFace = 0;
			renderer.render( scene, cameraPX, renderTarget );
  
			renderTarget.activeCubeFace = 1;
			renderer.render( scene, cameraNX, renderTarget );
  
			renderTarget.activeCubeFace = 2;
			renderer.render( scene, cameraPY, renderTarget );
  
			renderTarget.activeCubeFace = 3;
			renderer.render( scene, cameraNY, renderTarget );
  
			renderTarget.activeCubeFace = 4;
			renderer.render( scene, cameraPZ, renderTarget );
  
			renderTarget.texture.generateMipmaps = generateMipmaps;
  
			renderTarget.activeCubeFace = 5;
			renderer.render( scene, cameraNZ, renderTarget );
  
			renderer.setRenderTarget( null );
  
		};
  
		this.clear = function ( renderer, color, depth, stencil ) {
  
			var renderTarget = this.renderTarget;
  
			for ( var i = 0; i < 6; i ++ ) {
  
				renderTarget.activeCubeFace = i;
				renderer.setRenderTarget( renderTarget );
  
				renderer.clear( color, depth, stencil );
  
			}
  
			renderer.setRenderTarget( null );
  
		};
  
	}
  
	CubeCamera.prototype = Object.create( Object3D.prototype );
	CubeCamera.prototype.constructor = CubeCamera;
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function AudioListener() {
  
		Object3D.call( this );
  
		this.type = 'AudioListener';
  
		this.context = AudioContext.getContext();
  
		this.gain = this.context.createGain();
		this.gain.connect( this.context.destination );
  
		this.filter = null;
  
	}
  
	AudioListener.prototype = Object.assign( Object.create( Object3D.prototype ), {
  
		constructor: AudioListener,
  
		getInput: function () {
  
			return this.gain;
  
		},
  
		removeFilter: function ( ) {
  
			if ( this.filter !== null ) {
  
				this.gain.disconnect( this.filter );
				this.filter.disconnect( this.context.destination );
				this.gain.connect( this.context.destination );
				this.filter = null;
  
			}
  
		},
  
		getFilter: function () {
  
			return this.filter;
  
		},
  
		setFilter: function ( value ) {
  
			if ( this.filter !== null ) {
  
				this.gain.disconnect( this.filter );
				this.filter.disconnect( this.context.destination );
  
			} else {
  
				this.gain.disconnect( this.context.destination );
  
			}
  
			this.filter = value;
			this.gain.connect( this.filter );
			this.filter.connect( this.context.destination );
  
		},
  
		getMasterVolume: function () {
  
			return this.gain.gain.value;
  
		},
  
		setMasterVolume: function ( value ) {
  
			this.gain.gain.setTargetAtTime( value, this.context.currentTime, 0.01 );
  
		},
  
		updateMatrixWorld: ( function () {
  
			var position = new Vector3();
			var quaternion = new Quaternion();
			var scale = new Vector3();
  
			var orientation = new Vector3();
  
			return function updateMatrixWorld( force ) {
  
				Object3D.prototype.updateMatrixWorld.call( this, force );
  
				var listener = this.context.listener;
				var up = this.up;
  
				this.matrixWorld.decompose( position, quaternion, scale );
  
				orientation.set( 0, 0, - 1 ).applyQuaternion( quaternion );
  
				if ( listener.positionX ) {
  
					listener.positionX.setValueAtTime( position.x, this.context.currentTime );
					listener.positionY.setValueAtTime( position.y, this.context.currentTime );
					listener.positionZ.setValueAtTime( position.z, this.context.currentTime );
					listener.forwardX.setValueAtTime( orientation.x, this.context.currentTime );
					listener.forwardY.setValueAtTime( orientation.y, this.context.currentTime );
					listener.forwardZ.setValueAtTime( orientation.z, this.context.currentTime );
					listener.upX.setValueAtTime( up.x, this.context.currentTime );
					listener.upY.setValueAtTime( up.y, this.context.currentTime );
					listener.upZ.setValueAtTime( up.z, this.context.currentTime );
  
				} else {
  
					listener.setPosition( position.x, position.y, position.z );
					listener.setOrientation( orientation.x, orientation.y, orientation.z, up.x, up.y, up.z );
  
				}
  
			};
  
		} )()
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 * @author Reece Aaron Lecrivain / http://reecenotes.com/
	 */
  
	function Audio( listener ) {
  
		Object3D.call( this );
  
		this.type = 'Audio';
  
		this.context = listener.context;
  
		this.gain = this.context.createGain();
		this.gain.connect( listener.getInput() );
  
		this.autoplay = false;
  
		this.buffer = null;
		this.loop = false;
		this.startTime = 0;
		this.offset = 0;
		this.playbackRate = 1;
		this.isPlaying = false;
		this.hasPlaybackControl = true;
		this.sourceType = 'empty';
  
		this.filters = [];
  
	}
  
	Audio.prototype = Object.assign( Object.create( Object3D.prototype ), {
  
		constructor: Audio,
  
		getOutput: function () {
  
			return this.gain;
  
		},
  
		setNodeSource: function ( audioNode ) {
  
			this.hasPlaybackControl = false;
			this.sourceType = 'audioNode';
			this.source = audioNode;
			this.connect();
  
			return this;
  
		},
  
		setMediaElementSource: function ( mediaElement ) {
  
			this.hasPlaybackControl = false;
			this.sourceType = 'mediaNode';
			this.source = this.context.createMediaElementSource( mediaElement );
			this.connect();
  
			return this;
  
		},
  
		setBuffer: function ( audioBuffer ) {
  
			this.buffer = audioBuffer;
			this.sourceType = 'buffer';
  
			if ( this.autoplay ) this.play();
  
			return this;
  
		},
  
		play: function () {
  
			if ( this.isPlaying === true ) {
  
				console.warn( 'THREE.Audio: Audio is already playing.' );
				return;
  
			}
  
			if ( this.hasPlaybackControl === false ) {
  
				console.warn( 'THREE.Audio: this Audio has no playback control.' );
				return;
  
			}
  
			var source = this.context.createBufferSource();
  
			source.buffer = this.buffer;
			source.loop = this.loop;
			source.onended = this.onEnded.bind( this );
			source.playbackRate.setValueAtTime( this.playbackRate, this.startTime );
			this.startTime = this.context.currentTime;
			source.start( this.startTime, this.offset );
  
			this.isPlaying = true;
  
			this.source = source;
  
			return this.connect();
  
		},
  
		pause: function () {
  
			if ( this.hasPlaybackControl === false ) {
  
				console.warn( 'THREE.Audio: this Audio has no playback control.' );
				return;
  
			}
  
			if ( this.isPlaying === true ) {
  
				this.source.stop();
				this.offset += ( this.context.currentTime - this.startTime ) * this.playbackRate;
				this.isPlaying = false;
  
			}
  
			return this;
  
		},
  
		stop: function () {
  
			if ( this.hasPlaybackControl === false ) {
  
				console.warn( 'THREE.Audio: this Audio has no playback control.' );
				return;
  
			}
  
			this.source.stop();
			this.offset = 0;
			this.isPlaying = false;
  
			return this;
  
		},
  
		connect: function () {
  
			if ( this.filters.length > 0 ) {
  
				this.source.connect( this.filters[ 0 ] );
  
				for ( var i = 1, l = this.filters.length; i < l; i ++ ) {
  
					this.filters[ i - 1 ].connect( this.filters[ i ] );
  
				}
  
				this.filters[ this.filters.length - 1 ].connect( this.getOutput() );
  
			} else {
  
				this.source.connect( this.getOutput() );
  
			}
  
			return this;
  
		},
  
		disconnect: function () {
  
			if ( this.filters.length > 0 ) {
  
				this.source.disconnect( this.filters[ 0 ] );
  
				for ( var i = 1, l = this.filters.length; i < l; i ++ ) {
  
					this.filters[ i - 1 ].disconnect( this.filters[ i ] );
  
				}
  
				this.filters[ this.filters.length - 1 ].disconnect( this.getOutput() );
  
			} else {
  
				this.source.disconnect( this.getOutput() );
  
			}
  
			return this;
  
		},
  
		getFilters: function () {
  
			return this.filters;
  
		},
  
		setFilters: function ( value ) {
  
			if ( ! value ) value = [];
  
			if ( this.isPlaying === true ) {
  
				this.disconnect();
				this.filters = value;
				this.connect();
  
			} else {
  
				this.filters = value;
  
			}
  
			return this;
  
		},
  
		getFilter: function () {
  
			return this.getFilters()[ 0 ];
  
		},
  
		setFilter: function ( filter ) {
  
			return this.setFilters( filter ? [ filter ] : [] );
  
		},
  
		setPlaybackRate: function ( value ) {
  
			if ( this.hasPlaybackControl === false ) {
  
				console.warn( 'THREE.Audio: this Audio has no playback control.' );
				return;
  
			}
  
			this.playbackRate = value;
  
			if ( this.isPlaying === true ) {
  
				this.source.playbackRate.setValueAtTime( this.playbackRate, this.context.currentTime );
  
			}
  
			return this;
  
		},
  
		getPlaybackRate: function () {
  
			return this.playbackRate;
  
		},
  
		onEnded: function () {
  
			this.isPlaying = false;
  
		},
  
		getLoop: function () {
  
			if ( this.hasPlaybackControl === false ) {
  
				console.warn( 'THREE.Audio: this Audio has no playback control.' );
				return false;
  
			}
  
			return this.loop;
  
		},
  
		setLoop: function ( value ) {
  
			if ( this.hasPlaybackControl === false ) {
  
				console.warn( 'THREE.Audio: this Audio has no playback control.' );
				return;
  
			}
  
			this.loop = value;
  
			if ( this.isPlaying === true ) {
  
				this.source.loop = this.loop;
  
			}
  
			return this;
  
		},
  
		getVolume: function () {
  
			return this.gain.gain.value;
  
		},
  
		setVolume: function ( value ) {
  
			this.gain.gain.setTargetAtTime( value, this.context.currentTime, 0.01 );
  
			return this;
  
		}
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function PositionalAudio( listener ) {
  
		Audio.call( this, listener );
  
		this.panner = this.context.createPanner();
		this.panner.connect( this.gain );
  
	}
  
	PositionalAudio.prototype = Object.assign( Object.create( Audio.prototype ), {
  
		constructor: PositionalAudio,
  
		getOutput: function () {
  
			return this.panner;
  
		},
  
		getRefDistance: function () {
  
			return this.panner.refDistance;
  
		},
  
		setRefDistance: function ( value ) {
  
			this.panner.refDistance = value;
  
		},
  
		getRolloffFactor: function () {
  
			return this.panner.rolloffFactor;
  
		},
  
		setRolloffFactor: function ( value ) {
  
			this.panner.rolloffFactor = value;
  
		},
  
		getDistanceModel: function () {
  
			return this.panner.distanceModel;
  
		},
  
		setDistanceModel: function ( value ) {
  
			this.panner.distanceModel = value;
  
		},
  
		getMaxDistance: function () {
  
			return this.panner.maxDistance;
  
		},
  
		setMaxDistance: function ( value ) {
  
			this.panner.maxDistance = value;
  
		},
  
		updateMatrixWorld: ( function () {
  
			var position = new Vector3();
  
			return function updateMatrixWorld( force ) {
  
				Object3D.prototype.updateMatrixWorld.call( this, force );
  
				position.setFromMatrixPosition( this.matrixWorld );
  
				this.panner.setPosition( position.x, position.y, position.z );
  
			};
  
		} )()
  
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function AudioAnalyser( audio, fftSize ) {
  
		this.analyser = audio.context.createAnalyser();
		this.analyser.fftSize = fftSize !== undefined ? fftSize : 2048;
  
		this.data = new Uint8Array( this.analyser.frequencyBinCount );
  
		audio.getOutput().connect( this.analyser );
  
	}
  
	Object.assign( AudioAnalyser.prototype, {
  
		getFrequencyData: function () {
  
			this.analyser.getByteFrequencyData( this.data );
  
			return this.data;
  
		},
  
		getAverageFrequency: function () {
  
			var value = 0, data = this.getFrequencyData();
  
			for ( var i = 0; i < data.length; i ++ ) {
  
				value += data[ i ];
  
			}
  
			return value / data.length;
  
		}
  
	} );
  
	/**
	 *
	 * Buffered scene graph property that allows weighted accumulation.
	 *
	 *
	 * @author Ben Houston / http://clara.io/
	 * @author David Sarno / http://lighthaus.us/
	 * @author tschw
	 */
  
	function PropertyMixer( binding, typeName, valueSize ) {
  
		this.binding = binding;
		this.valueSize = valueSize;
  
		var bufferType = Float64Array,
			mixFunction;
  
		switch ( typeName ) {
  
			case 'quaternion':
				mixFunction = this._slerp;
				break;
  
			case 'string':
			case 'bool':
				bufferType = Array;
				mixFunction = this._select;
				break;
  
			default:
				mixFunction = this._lerp;
  
		}
  
		this.buffer = new bufferType( valueSize * 4 );
		// layout: [ incoming | accu0 | accu1 | orig ]
		//
		// interpolators can use .buffer as their .result
		// the data then goes to 'incoming'
		//
		// 'accu0' and 'accu1' are used frame-interleaved for
		// the cumulative result and are compared to detect
		// changes
		//
		// 'orig' stores the original state of the property
  
		this._mixBufferRegion = mixFunction;
  
		this.cumulativeWeight = 0;
  
		this.useCount = 0;
		this.referenceCount = 0;
  
	}
  
	Object.assign( PropertyMixer.prototype, {
  
		// accumulate data in the 'incoming' region into 'accu<i>'
		accumulate: function ( accuIndex, weight ) {
  
			// note: happily accumulating nothing when weight = 0, the caller knows
			// the weight and shouldn't have made the call in the first place
  
			var buffer = this.buffer,
				stride = this.valueSize,
				offset = accuIndex * stride + stride,
  
				currentWeight = this.cumulativeWeight;
  
			if ( currentWeight === 0 ) {
  
				// accuN := incoming * weight
  
				for ( var i = 0; i !== stride; ++ i ) {
  
					buffer[ offset + i ] = buffer[ i ];
  
				}
  
				currentWeight = weight;
  
			} else {
  
				// accuN := accuN + incoming * weight
  
				currentWeight += weight;
				var mix = weight / currentWeight;
				this._mixBufferRegion( buffer, offset, 0, mix, stride );
  
			}
  
			this.cumulativeWeight = currentWeight;
  
		},
  
		// apply the state of 'accu<i>' to the binding when accus differ
		apply: function ( accuIndex ) {
  
			var stride = this.valueSize,
				buffer = this.buffer,
				offset = accuIndex * stride + stride,
  
				weight = this.cumulativeWeight,
  
				binding = this.binding;
  
			this.cumulativeWeight = 0;
  
			if ( weight < 1 ) {
  
				// accuN := accuN + original * ( 1 - cumulativeWeight )
  
				var originalValueOffset = stride * 3;
  
				this._mixBufferRegion(
					buffer, offset, originalValueOffset, 1 - weight, stride );
  
			}
  
			for ( var i = stride, e = stride + stride; i !== e; ++ i ) {
  
				if ( buffer[ i ] !== buffer[ i + stride ] ) {
  
					// value has changed -> update scene graph
  
					binding.setValue( buffer, offset );
					break;
  
				}
  
			}
  
		},
  
		// remember the state of the bound property and copy it to both accus
		saveOriginalState: function () {
  
			var binding = this.binding;
  
			var buffer = this.buffer,
				stride = this.valueSize,
  
				originalValueOffset = stride * 3;
  
			binding.getValue( buffer, originalValueOffset );
  
			// accu[0..1] := orig -- initially detect changes against the original
			for ( var i = stride, e = originalValueOffset; i !== e; ++ i ) {
  
				buffer[ i ] = buffer[ originalValueOffset + ( i % stride ) ];
  
			}
  
			this.cumulativeWeight = 0;
  
		},
  
		// apply the state previously taken via 'saveOriginalState' to the binding
		restoreOriginalState: function () {
  
			var originalValueOffset = this.valueSize * 3;
			this.binding.setValue( this.buffer, originalValueOffset );
  
		},
  
  
		// mix functions
  
		_select: function ( buffer, dstOffset, srcOffset, t, stride ) {
  
			if ( t >= 0.5 ) {
  
				for ( var i = 0; i !== stride; ++ i ) {
  
					buffer[ dstOffset + i ] = buffer[ srcOffset + i ];
  
				}
  
			}
  
		},
  
		_slerp: function ( buffer, dstOffset, srcOffset, t ) {
  
			Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t );
  
		},
  
		_lerp: function ( buffer, dstOffset, srcOffset, t, stride ) {
  
			var s = 1 - t;
  
			for ( var i = 0; i !== stride; ++ i ) {
  
				var j = dstOffset + i;
  
				buffer[ j ] = buffer[ j ] * s + buffer[ srcOffset + i ] * t;
  
			}
  
		}
  
	} );
  
	/**
	 *
	 * A reference to a real property in the scene graph.
	 *
	 *
	 * @author Ben Houston / http://clara.io/
	 * @author David Sarno / http://lighthaus.us/
	 * @author tschw
	 */
  
	// Characters [].:/ are reserved for track binding syntax.
	var RESERVED_CHARS_RE = '\\[\\]\\.:\\/';
  
	function Composite( targetGroup, path, optionalParsedPath ) {
  
		var parsedPath = optionalParsedPath || PropertyBinding.parseTrackName( path );
  
		this._targetGroup = targetGroup;
		this._bindings = targetGroup.subscribe_( path, parsedPath );
  
	}
  
	Object.assign( Composite.prototype, {
  
		getValue: function ( array, offset ) {
  
			this.bind(); // bind all binding
  
			var firstValidIndex = this._targetGroup.nCachedObjects_,
				binding = this._bindings[ firstValidIndex ];
  
			// and only call .getValue on the first
			if ( binding !== undefined ) binding.getValue( array, offset );
  
		},
  
		setValue: function ( array, offset ) {
  
			var bindings = this._bindings;
  
			for ( var i = this._targetGroup.nCachedObjects_,
					  n = bindings.length; i !== n; ++ i ) {
  
				bindings[ i ].setValue( array, offset );
  
			}
  
		},
  
		bind: function () {
  
			var bindings = this._bindings;
  
			for ( var i = this._targetGroup.nCachedObjects_,
					  n = bindings.length; i !== n; ++ i ) {
  
				bindings[ i ].bind();
  
			}
  
		},
  
		unbind: function () {
  
			var bindings = this._bindings;
  
			for ( var i = this._targetGroup.nCachedObjects_,
					  n = bindings.length; i !== n; ++ i ) {
  
				bindings[ i ].unbind();
  
			}
  
		}
  
	} );
  
  
	function PropertyBinding( rootNode, path, parsedPath ) {
  
		this.path = path;
		this.parsedPath = parsedPath || PropertyBinding.parseTrackName( path );
  
		this.node = PropertyBinding.findNode( rootNode, this.parsedPath.nodeName ) || rootNode;
  
		this.rootNode = rootNode;
  
	}
  
	Object.assign( PropertyBinding, {
  
		Composite: Composite,
  
		create: function ( root, path, parsedPath ) {
  
			if ( ! ( root && root.isAnimationObjectGroup ) ) {
  
				return new PropertyBinding( root, path, parsedPath );
  
			} else {
  
				return new PropertyBinding.Composite( root, path, parsedPath );
  
			}
  
		},
  
		/**
		 * Replaces spaces with underscores and removes unsupported characters from
		 * node names, to ensure compatibility with parseTrackName().
		 *
		 * @param  {string} name Node name to be sanitized.
		 * @return {string}
		 */
		sanitizeNodeName: ( function () {
  
			var reservedRe = new RegExp( '[' + RESERVED_CHARS_RE + ']', 'g' );
  
			return function sanitizeNodeName( name ) {
  
				return name.replace( /\s/g, '_' ).replace( reservedRe, '' );
  
			};
  
		}() ),
  
		parseTrackName: function () {
  
			// Attempts to allow node names from any language. ES5's `\w` regexp matches
			// only latin characters, and the unicode \p{L} is not yet supported. So
			// instead, we exclude reserved characters and match everything else.
			var wordChar = '[^' + RESERVED_CHARS_RE + ']';
			var wordCharOrDot = '[^' + RESERVED_CHARS_RE.replace( '\\.', '' ) + ']';
  
			// Parent directories, delimited by '/' or ':'. Currently unused, but must
			// be matched to parse the rest of the track name.
			var directoryRe = /((?:WC+[\/:])*)/.source.replace( 'WC', wordChar );
  
			// Target node. May contain word characters (a-zA-Z0-9_) and '.' or '-'.
			var nodeRe = /(WCOD+)?/.source.replace( 'WCOD', wordCharOrDot );
  
			// Object on target node, and accessor. May not contain reserved
			// characters. Accessor may contain any character except closing bracket.
			var objectRe = /(?:\.(WC+)(?:\[(.+)\])?)?/.source.replace( 'WC', wordChar );
  
			// Property and accessor. May not contain reserved characters. Accessor may
			// contain any non-bracket characters.
			var propertyRe = /\.(WC+)(?:\[(.+)\])?/.source.replace( 'WC', wordChar );
  
			var trackRe = new RegExp( ''
				+ '^'
				+ directoryRe
				+ nodeRe
				+ objectRe
				+ propertyRe
				+ '$'
			);
  
			var supportedObjectNames = [ 'material', 'materials', 'bones' ];
  
			return function parseTrackName( trackName ) {
  
				var matches = trackRe.exec( trackName );
  
				if ( ! matches ) {
  
					throw new Error( 'PropertyBinding: Cannot parse trackName: ' + trackName );
  
				}
  
				var results = {
					// directoryName: matches[ 1 ], // (tschw) currently unused
					nodeName: matches[ 2 ],
					objectName: matches[ 3 ],
					objectIndex: matches[ 4 ],
					propertyName: matches[ 5 ], // required
					propertyIndex: matches[ 6 ]
				};
  
				var lastDot = results.nodeName && results.nodeName.lastIndexOf( '.' );
  
				if ( lastDot !== undefined && lastDot !== - 1 ) {
  
					var objectName = results.nodeName.substring( lastDot + 1 );
  
					// Object names must be checked against a whitelist. Otherwise, there
					// is no way to parse 'foo.bar.baz': 'baz' must be a property, but
					// 'bar' could be the objectName, or part of a nodeName (which can
					// include '.' characters).
					if ( supportedObjectNames.indexOf( objectName ) !== - 1 ) {
  
						results.nodeName = results.nodeName.substring( 0, lastDot );
						results.objectName = objectName;
  
					}
  
				}
  
				if ( results.propertyName === null || results.propertyName.length === 0 ) {
  
					throw new Error( 'PropertyBinding: can not parse propertyName from trackName: ' + trackName );
  
				}
  
				return results;
  
			};
  
		}(),
  
		findNode: function ( root, nodeName ) {
  
			if ( ! nodeName || nodeName === "" || nodeName === "root" || nodeName === "." || nodeName === - 1 || nodeName === root.name || nodeName === root.uuid ) {
  
				return root;
  
			}
  
			// search into skeleton bones.
			if ( root.skeleton ) {
  
				var bone = root.skeleton.getBoneByName( nodeName );
  
				if ( bone !== undefined ) {
  
					return bone;
  
				}
  
			}
  
			// search into node subtree.
			if ( root.children ) {
  
				var searchNodeSubtree = function ( children ) {
  
					for ( var i = 0; i < children.length; i ++ ) {
  
						var childNode = children[ i ];
  
						if ( childNode.name === nodeName || childNode.uuid === nodeName ) {
  
							return childNode;
  
						}
  
						var result = searchNodeSubtree( childNode.children );
  
						if ( result ) return result;
  
					}
  
					return null;
  
				};
  
				var subTreeNode = searchNodeSubtree( root.children );
  
				if ( subTreeNode ) {
  
					return subTreeNode;
  
				}
  
			}
  
			return null;
  
		}
  
	} );
  
	Object.assign( PropertyBinding.prototype, { // prototype, continued
  
		// these are used to "bind" a nonexistent property
		_getValue_unavailable: function () {},
		_setValue_unavailable: function () {},
  
		BindingType: {
			Direct: 0,
			EntireArray: 1,
			ArrayElement: 2,
			HasFromToArray: 3
		},
  
		Versioning: {
			None: 0,
			NeedsUpdate: 1,
			MatrixWorldNeedsUpdate: 2
		},
  
		GetterByBindingType: [
  
			function getValue_direct( buffer, offset ) {
  
				buffer[ offset ] = this.node[ this.propertyName ];
  
			},
  
			function getValue_array( buffer, offset ) {
  
				var source = this.resolvedProperty;
  
				for ( var i = 0, n = source.length; i !== n; ++ i ) {
  
					buffer[ offset ++ ] = source[ i ];
  
				}
  
			},
  
			function getValue_arrayElement( buffer, offset ) {
  
				buffer[ offset ] = this.resolvedProperty[ this.propertyIndex ];
  
			},
  
			function getValue_toArray( buffer, offset ) {
  
				this.resolvedProperty.toArray( buffer, offset );
  
			}
  
		],
  
		SetterByBindingTypeAndVersioning: [
  
			[
				// Direct
  
				function setValue_direct( buffer, offset ) {
  
					this.targetObject[ this.propertyName ] = buffer[ offset ];
  
				},
  
				function setValue_direct_setNeedsUpdate( buffer, offset ) {
  
					this.targetObject[ this.propertyName ] = buffer[ offset ];
					this.targetObject.needsUpdate = true;
  
				},
  
				function setValue_direct_setMatrixWorldNeedsUpdate( buffer, offset ) {
  
					this.targetObject[ this.propertyName ] = buffer[ offset ];
					this.targetObject.matrixWorldNeedsUpdate = true;
  
				}
  
			], [
  
				// EntireArray
  
				function setValue_array( buffer, offset ) {
  
					var dest = this.resolvedProperty;
  
					for ( var i = 0, n = dest.length; i !== n; ++ i ) {
  
						dest[ i ] = buffer[ offset ++ ];
  
					}
  
				},
  
				function setValue_array_setNeedsUpdate( buffer, offset ) {
  
					var dest = this.resolvedProperty;
  
					for ( var i = 0, n = dest.length; i !== n; ++ i ) {
  
						dest[ i ] = buffer[ offset ++ ];
  
					}
  
					this.targetObject.needsUpdate = true;
  
				},
  
				function setValue_array_setMatrixWorldNeedsUpdate( buffer, offset ) {
  
					var dest = this.resolvedProperty;
  
					for ( var i = 0, n = dest.length; i !== n; ++ i ) {
  
						dest[ i ] = buffer[ offset ++ ];
  
					}
  
					this.targetObject.matrixWorldNeedsUpdate = true;
  
				}
  
			], [
  
				// ArrayElement
  
				function setValue_arrayElement( buffer, offset ) {
  
					this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ];
  
				},
  
				function setValue_arrayElement_setNeedsUpdate( buffer, offset ) {
  
					this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ];
					this.targetObject.needsUpdate = true;
  
				},
  
				function setValue_arrayElement_setMatrixWorldNeedsUpdate( buffer, offset ) {
  
					this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ];
					this.targetObject.matrixWorldNeedsUpdate = true;
  
				}
  
			], [
  
				// HasToFromArray
  
				function setValue_fromArray( buffer, offset ) {
  
					this.resolvedProperty.fromArray( buffer, offset );
  
				},
  
				function setValue_fromArray_setNeedsUpdate( buffer, offset ) {
  
					this.resolvedProperty.fromArray( buffer, offset );
					this.targetObject.needsUpdate = true;
  
				},
  
				function setValue_fromArray_setMatrixWorldNeedsUpdate( buffer, offset ) {
  
					this.resolvedProperty.fromArray( buffer, offset );
					this.targetObject.matrixWorldNeedsUpdate = true;
  
				}
  
			]
  
		],
  
		getValue: function getValue_unbound( targetArray, offset ) {
  
			this.bind();
			this.getValue( targetArray, offset );
  
			// Note: This class uses a State pattern on a per-method basis:
			// 'bind' sets 'this.getValue' / 'setValue' and shadows the
			// prototype version of these methods with one that represents
			// the bound state. When the property is not found, the methods
			// become no-ops.
  
		},
  
		setValue: function getValue_unbound( sourceArray, offset ) {
  
			this.bind();
			this.setValue( sourceArray, offset );
  
		},
  
		// create getter / setter pair for a property in the scene graph
		bind: function () {
  
			var targetObject = this.node,
				parsedPath = this.parsedPath,
  
				objectName = parsedPath.objectName,
				propertyName = parsedPath.propertyName,
				propertyIndex = parsedPath.propertyIndex;
  
			if ( ! targetObject ) {
  
				targetObject = PropertyBinding.findNode( this.rootNode, parsedPath.nodeName ) || this.rootNode;
  
				this.node = targetObject;
  
			}
  
			// set fail state so we can just 'return' on error
			this.getValue = this._getValue_unavailable;
			this.setValue = this._setValue_unavailable;
  
			// ensure there is a value node
			if ( ! targetObject ) {
  
				console.error( 'THREE.PropertyBinding: Trying to update node for track: ' + this.path + ' but it wasn\'t found.' );
				return;
  
			}
  
			if ( objectName ) {
  
				var objectIndex = parsedPath.objectIndex;
  
				// special cases were we need to reach deeper into the hierarchy to get the face materials....
				switch ( objectName ) {
  
					case 'materials':
  
						if ( ! targetObject.material ) {
  
							console.error( 'THREE.PropertyBinding: Can not bind to material as node does not have a material.', this );
							return;
  
						}
  
						if ( ! targetObject.material.materials ) {
  
							console.error( 'THREE.PropertyBinding: Can not bind to material.materials as node.material does not have a materials array.', this );
							return;
  
						}
  
						targetObject = targetObject.material.materials;
  
						break;
  
					case 'bones':
  
						if ( ! targetObject.skeleton ) {
  
							console.error( 'THREE.PropertyBinding: Can not bind to bones as node does not have a skeleton.', this );
							return;
  
						}
  
						// potential future optimization: skip this if propertyIndex is already an integer
						// and convert the integer string to a true integer.
  
						targetObject = targetObject.skeleton.bones;
  
						// support resolving morphTarget names into indices.
						for ( var i = 0; i < targetObject.length; i ++ ) {
  
							if ( targetObject[ i ].name === objectIndex ) {
  
								objectIndex = i;
								break;
  
							}
  
						}
  
						break;
  
					default:
  
						if ( targetObject[ objectName ] === undefined ) {
  
							console.error( 'THREE.PropertyBinding: Can not bind to objectName of node undefined.', this );
							return;
  
						}
  
						targetObject = targetObject[ objectName ];
  
				}
  
  
				if ( objectIndex !== undefined ) {
  
					if ( targetObject[ objectIndex ] === undefined ) {
  
						console.error( 'THREE.PropertyBinding: Trying to bind to objectIndex of objectName, but is undefined.', this, targetObject );
						return;
  
					}
  
					targetObject = targetObject[ objectIndex ];
  
				}
  
			}
  
			// resolve property
			var nodeProperty = targetObject[ propertyName ];
  
			if ( nodeProperty === undefined ) {
  
				var nodeName = parsedPath.nodeName;
  
				console.error( 'THREE.PropertyBinding: Trying to update property for track: ' + nodeName +
					'.' + propertyName + ' but it wasn\'t found.', targetObject );
				return;
  
			}
  
			// determine versioning scheme
			var versioning = this.Versioning.None;
  
			if ( targetObject.needsUpdate !== undefined ) { // material
  
				versioning = this.Versioning.NeedsUpdate;
				this.targetObject = targetObject;
  
			} else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform
  
				versioning = this.Versioning.MatrixWorldNeedsUpdate;
				this.targetObject = targetObject;
  
			}
  
			// determine how the property gets bound
			var bindingType = this.BindingType.Direct;
  
			if ( propertyIndex !== undefined ) {
  
				// access a sub element of the property array (only primitives are supported right now)
  
				if ( propertyName === "morphTargetInfluences" ) {
  
					// potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer.
  
					// support resolving morphTarget names into indices.
					if ( ! targetObject.geometry ) {
  
						console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.', this );
						return;
  
					}
  
					if ( targetObject.geometry.isBufferGeometry ) {
  
						if ( ! targetObject.geometry.morphAttributes ) {
  
							console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphAttributes.', this );
							return;
  
						}
  
						for ( var i = 0; i < this.node.geometry.morphAttributes.position.length; i ++ ) {
  
							if ( targetObject.geometry.morphAttributes.position[ i ].name === propertyIndex ) {
  
								propertyIndex = i;
								break;
  
							}
  
						}
  
  
					} else {
  
						if ( ! targetObject.geometry.morphTargets ) {
  
							console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphTargets.', this );
							return;
  
						}
  
						for ( var i = 0; i < this.node.geometry.morphTargets.length; i ++ ) {
  
							if ( targetObject.geometry.morphTargets[ i ].name === propertyIndex ) {
  
								propertyIndex = i;
								break;
  
							}
  
						}
  
					}
  
				}
  
				bindingType = this.BindingType.ArrayElement;
  
				this.resolvedProperty = nodeProperty;
				this.propertyIndex = propertyIndex;
  
			} else if ( nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined ) {
  
				// must use copy for Object3D.Euler/Quaternion
  
				bindingType = this.BindingType.HasFromToArray;
  
				this.resolvedProperty = nodeProperty;
  
			} else if ( Array.isArray( nodeProperty ) ) {
  
				bindingType = this.BindingType.EntireArray;
  
				this.resolvedProperty = nodeProperty;
  
			} else {
  
				this.propertyName = propertyName;
  
			}
  
			// select getter / setter
			this.getValue = this.GetterByBindingType[ bindingType ];
			this.setValue = this.SetterByBindingTypeAndVersioning[ bindingType ][ versioning ];
  
		},
  
		unbind: function () {
  
			this.node = null;
  
			// back to the prototype version of getValue / setValue
			// note: avoiding to mutate the shape of 'this' via 'delete'
			this.getValue = this._getValue_unbound;
			this.setValue = this._setValue_unbound;
  
		}
  
	} );
  
	//!\ DECLARE ALIAS AFTER assign prototype !
	Object.assign( PropertyBinding.prototype, {
  
		// initial state of these methods that calls 'bind'
		_getValue_unbound: PropertyBinding.prototype.getValue,
		_setValue_unbound: PropertyBinding.prototype.setValue,
  
	} );
  
	/**
	 *
	 * A group of objects that receives a shared animation state.
	 *
	 * Usage:
	 *
	 * 	-	Add objects you would otherwise pass as 'root' to the
	 * 		constructor or the .clipAction method of AnimationMixer.
	 *
	 * 	-	Instead pass this object as 'root'.
	 *
	 * 	-	You can also add and remove objects later when the mixer
	 * 		is running.
	 *
	 * Note:
	 *
	 *  	Objects of this class appear as one object to the mixer,
	 *  	so cache control of the individual objects must be done
	 *  	on the group.
	 *
	 * Limitation:
	 *
	 * 	- 	The animated properties must be compatible among the
	 * 		all objects in the group.
	 *
	 *  -	A single property can either be controlled through a
	 *  	target group or directly, but not both.
	 *
	 * @author tschw
	 */
  
	function AnimationObjectGroup() {
  
		this.uuid = _Math.generateUUID();
  
		// cached objects followed by the active ones
		this._objects = Array.prototype.slice.call( arguments );
  
		this.nCachedObjects_ = 0;			// threshold
		// note: read by PropertyBinding.Composite
  
		var indices = {};
		this._indicesByUUID = indices;		// for bookkeeping
  
		for ( var i = 0, n = arguments.length; i !== n; ++ i ) {
  
			indices[ arguments[ i ].uuid ] = i;
  
		}
  
		this._paths = [];					// inside: string
		this._parsedPaths = [];				// inside: { we don't care, here }
		this._bindings = []; 				// inside: Array< PropertyBinding >
		this._bindingsIndicesByPath = {}; 	// inside: indices in these arrays
  
		var scope = this;
  
		this.stats = {
  
			objects: {
				get total() {
  
					return scope._objects.length;
  
				},
				get inUse() {
  
					return this.total - scope.nCachedObjects_;
  
				}
			},
			get bindingsPerObject() {
  
				return scope._bindings.length;
  
			}
  
		};
  
	}
  
	Object.assign( AnimationObjectGroup.prototype, {
  
		isAnimationObjectGroup: true,
  
		add: function () {
  
			var objects = this._objects,
				nObjects = objects.length,
				nCachedObjects = this.nCachedObjects_,
				indicesByUUID = this._indicesByUUID,
				paths = this._paths,
				parsedPaths = this._parsedPaths,
				bindings = this._bindings,
				nBindings = bindings.length,
				knownObject = undefined;
  
			for ( var i = 0, n = arguments.length; i !== n; ++ i ) {
  
				var object = arguments[ i ],
					uuid = object.uuid,
					index = indicesByUUID[ uuid ];
  
				if ( index === undefined ) {
  
					// unknown object -> add it to the ACTIVE region
  
					index = nObjects ++;
					indicesByUUID[ uuid ] = index;
					objects.push( object );
  
					// accounting is done, now do the same for all bindings
  
					for ( var j = 0, m = nBindings; j !== m; ++ j ) {
  
						bindings[ j ].push( new PropertyBinding( object, paths[ j ], parsedPaths[ j ] ) );
  
					}
  
				} else if ( index < nCachedObjects ) {
  
					knownObject = objects[ index ];
  
					// move existing object to the ACTIVE region
  
					var firstActiveIndex = -- nCachedObjects,
						lastCachedObject = objects[ firstActiveIndex ];
  
					indicesByUUID[ lastCachedObject.uuid ] = index;
					objects[ index ] = lastCachedObject;
  
					indicesByUUID[ uuid ] = firstActiveIndex;
					objects[ firstActiveIndex ] = object;
  
					// accounting is done, now do the same for all bindings
  
					for ( var j = 0, m = nBindings; j !== m; ++ j ) {
  
						var bindingsForPath = bindings[ j ],
							lastCached = bindingsForPath[ firstActiveIndex ],
							binding = bindingsForPath[ index ];
  
						bindingsForPath[ index ] = lastCached;
  
						if ( binding === undefined ) {
  
							// since we do not bother to create new bindings
							// for objects that are cached, the binding may
							// or may not exist
  
							binding = new PropertyBinding( object, paths[ j ], parsedPaths[ j ] );
  
						}
  
						bindingsForPath[ firstActiveIndex ] = binding;
  
					}
  
				} else if ( objects[ index ] !== knownObject ) {
  
					console.error( 'THREE.AnimationObjectGroup: Different objects with the same UUID ' +
							'detected. Clean the caches or recreate your infrastructure when reloading scenes.' );
  
				} // else the object is already where we want it to be
  
			} // for arguments
  
			this.nCachedObjects_ = nCachedObjects;
  
		},
  
		remove: function () {
  
			var objects = this._objects,
				nCachedObjects = this.nCachedObjects_,
				indicesByUUID = this._indicesByUUID,
				bindings = this._bindings,
				nBindings = bindings.length;
  
			for ( var i = 0, n = arguments.length; i !== n; ++ i ) {
  
				var object = arguments[ i ],
					uuid = object.uuid,
					index = indicesByUUID[ uuid ];
  
				if ( index !== undefined && index >= nCachedObjects ) {
  
					// move existing object into the CACHED region
  
					var lastCachedIndex = nCachedObjects ++,
						firstActiveObject = objects[ lastCachedIndex ];
  
					indicesByUUID[ firstActiveObject.uuid ] = index;
					objects[ index ] = firstActiveObject;
  
					indicesByUUID[ uuid ] = lastCachedIndex;
					objects[ lastCachedIndex ] = object;
  
					// accounting is done, now do the same for all bindings
  
					for ( var j = 0, m = nBindings; j !== m; ++ j ) {
  
						var bindingsForPath = bindings[ j ],
							firstActive = bindingsForPath[ lastCachedIndex ],
							binding = bindingsForPath[ index ];
  
						bindingsForPath[ index ] = firstActive;
						bindingsForPath[ lastCachedIndex ] = binding;
  
					}
  
				}
  
			} // for arguments
  
			this.nCachedObjects_ = nCachedObjects;
  
		},
  
		// remove & forget
		uncache: function () {
  
			var objects = this._objects,
				nObjects = objects.length,
				nCachedObjects = this.nCachedObjects_,
				indicesByUUID = this._indicesByUUID,
				bindings = this._bindings,
				nBindings = bindings.length;
  
			for ( var i = 0, n = arguments.length; i !== n; ++ i ) {
  
				var object = arguments[ i ],
					uuid = object.uuid,
					index = indicesByUUID[ uuid ];
  
				if ( index !== undefined ) {
  
					delete indicesByUUID[ uuid ];
  
					if ( index < nCachedObjects ) {
  
						// object is cached, shrink the CACHED region
  
						var firstActiveIndex = -- nCachedObjects,
							lastCachedObject = objects[ firstActiveIndex ],
							lastIndex = -- nObjects,
							lastObject = objects[ lastIndex ];
  
						// last cached object takes this object's place
						indicesByUUID[ lastCachedObject.uuid ] = index;
						objects[ index ] = lastCachedObject;
  
						// last object goes to the activated slot and pop
						indicesByUUID[ lastObject.uuid ] = firstActiveIndex;
						objects[ firstActiveIndex ] = lastObject;
						objects.pop();
  
						// accounting is done, now do the same for all bindings
  
						for ( var j = 0, m = nBindings; j !== m; ++ j ) {
  
							var bindingsForPath = bindings[ j ],
								lastCached = bindingsForPath[ firstActiveIndex ],
								last = bindingsForPath[ lastIndex ];
  
							bindingsForPath[ index ] = lastCached;
							bindingsForPath[ firstActiveIndex ] = last;
							bindingsForPath.pop();
  
						}
  
					} else {
  
						// object is active, just swap with the last and pop
  
						var lastIndex = -- nObjects,
							lastObject = objects[ lastIndex ];
  
						indicesByUUID[ lastObject.uuid ] = index;
						objects[ index ] = lastObject;
						objects.pop();
  
						// accounting is done, now do the same for all bindings
  
						for ( var j = 0, m = nBindings; j !== m; ++ j ) {
  
							var bindingsForPath = bindings[ j ];
  
							bindingsForPath[ index ] = bindingsForPath[ lastIndex ];
							bindingsForPath.pop();
  
						}
  
					} // cached or active
  
				} // if object is known
  
			} // for arguments
  
			this.nCachedObjects_ = nCachedObjects;
  
		},
  
		// Internal interface used by befriended PropertyBinding.Composite:
  
		subscribe_: function ( path, parsedPath ) {
  
			// returns an array of bindings for the given path that is changed
			// according to the contained objects in the group
  
			var indicesByPath = this._bindingsIndicesByPath,
				index = indicesByPath[ path ],
				bindings = this._bindings;
  
			if ( index !== undefined ) return bindings[ index ];
  
			var paths = this._paths,
				parsedPaths = this._parsedPaths,
				objects = this._objects,
				nObjects = objects.length,
				nCachedObjects = this.nCachedObjects_,
				bindingsForPath = new Array( nObjects );
  
			index = bindings.length;
  
			indicesByPath[ path ] = index;
  
			paths.push( path );
			parsedPaths.push( parsedPath );
			bindings.push( bindingsForPath );
  
			for ( var i = nCachedObjects, n = objects.length; i !== n; ++ i ) {
  
				var object = objects[ i ];
				bindingsForPath[ i ] = new PropertyBinding( object, path, parsedPath );
  
			}
  
			return bindingsForPath;
  
		},
  
		unsubscribe_: function ( path ) {
  
			// tells the group to forget about a property path and no longer
			// update the array previously obtained with 'subscribe_'
  
			var indicesByPath = this._bindingsIndicesByPath,
				index = indicesByPath[ path ];
  
			if ( index !== undefined ) {
  
				var paths = this._paths,
					parsedPaths = this._parsedPaths,
					bindings = this._bindings,
					lastBindingsIndex = bindings.length - 1,
					lastBindings = bindings[ lastBindingsIndex ],
					lastBindingsPath = path[ lastBindingsIndex ];
  
				indicesByPath[ lastBindingsPath ] = index;
  
				bindings[ index ] = lastBindings;
				bindings.pop();
  
				parsedPaths[ index ] = parsedPaths[ lastBindingsIndex ];
				parsedPaths.pop();
  
				paths[ index ] = paths[ lastBindingsIndex ];
				paths.pop();
  
			}
  
		}
  
	} );
  
	/**
	 *
	 * Action provided by AnimationMixer for scheduling clip playback on specific
	 * objects.
	 *
	 * @author Ben Houston / http://clara.io/
	 * @author David Sarno / http://lighthaus.us/
	 * @author tschw
	 *
	 */
  
	function AnimationAction( mixer, clip, localRoot ) {
  
		this._mixer = mixer;
		this._clip = clip;
		this._localRoot = localRoot || null;
  
		var tracks = clip.tracks,
			nTracks = tracks.length,
			interpolants = new Array( nTracks );
  
		var interpolantSettings = {
			endingStart: ZeroCurvatureEnding,
			endingEnd: ZeroCurvatureEnding
		};
  
		for ( var i = 0; i !== nTracks; ++ i ) {
  
			var interpolant = tracks[ i ].createInterpolant( null );
			interpolants[ i ] = interpolant;
			interpolant.settings = interpolantSettings;
  
		}
  
		this._interpolantSettings = interpolantSettings;
  
		this._interpolants = interpolants;	// bound by the mixer
  
		// inside: PropertyMixer (managed by the mixer)
		this._propertyBindings = new Array( nTracks );
  
		this._cacheIndex = null;			// for the memory manager
		this._byClipCacheIndex = null;		// for the memory manager
  
		this._timeScaleInterpolant = null;
		this._weightInterpolant = null;
  
		this.loop = LoopRepeat;
		this._loopCount = - 1;
  
		// global mixer time when the action is to be started
		// it's set back to 'null' upon start of the action
		this._startTime = null;
  
		// scaled local time of the action
		// gets clamped or wrapped to 0..clip.duration according to loop
		this.time = 0;
  
		this.timeScale = 1;
		this._effectiveTimeScale = 1;
  
		this.weight = 1;
		this._effectiveWeight = 1;
  
		this.repetitions = Infinity; 		// no. of repetitions when looping
  
		this.paused = false;				// true -> zero effective time scale
		this.enabled = true;				// false -> zero effective weight
  
		this.clampWhenFinished 	= false;	// keep feeding the last frame?
  
		this.zeroSlopeAtStart 	= true;		// for smooth interpolation w/o separate
		this.zeroSlopeAtEnd		= true;		// clips for start, loop and end
  
	}
  
	Object.assign( AnimationAction.prototype, {
  
		// State & Scheduling
  
		play: function () {
  
			this._mixer._activateAction( this );
  
			return this;
  
		},
  
		stop: function () {
  
			this._mixer._deactivateAction( this );
  
			return this.reset();
  
		},
  
		reset: function () {
  
			this.paused = false;
			this.enabled = true;
  
			this.time = 0;			// restart clip
			this._loopCount = - 1;	// forget previous loops
			this._startTime = null;	// forget scheduling
  
			return this.stopFading().stopWarping();
  
		},
  
		isRunning: function () {
  
			return this.enabled && ! this.paused && this.timeScale !== 0 &&
					this._startTime === null && this._mixer._isActiveAction( this );
  
		},
  
		// return true when play has been called
		isScheduled: function () {
  
			return this._mixer._isActiveAction( this );
  
		},
  
		startAt: function ( time ) {
  
			this._startTime = time;
  
			return this;
  
		},
  
		setLoop: function ( mode, repetitions ) {
  
			this.loop = mode;
			this.repetitions = repetitions;
  
			return this;
  
		},
  
		// Weight
  
		// set the weight stopping any scheduled fading
		// although .enabled = false yields an effective weight of zero, this
		// method does *not* change .enabled, because it would be confusing
		setEffectiveWeight: function ( weight ) {
  
			this.weight = weight;
  
			// note: same logic as when updated at runtime
			this._effectiveWeight = this.enabled ? weight : 0;
  
			return this.stopFading();
  
		},
  
		// return the weight considering fading and .enabled
		getEffectiveWeight: function () {
  
			return this._effectiveWeight;
  
		},
  
		fadeIn: function ( duration ) {
  
			return this._scheduleFading( duration, 0, 1 );
  
		},
  
		fadeOut: function ( duration ) {
  
			return this._scheduleFading( duration, 1, 0 );
  
		},
  
		crossFadeFrom: function ( fadeOutAction, duration, warp ) {
  
			fadeOutAction.fadeOut( duration );
			this.fadeIn( duration );
  
			if ( warp ) {
  
				var fadeInDuration = this._clip.duration,
					fadeOutDuration = fadeOutAction._clip.duration,
  
					startEndRatio = fadeOutDuration / fadeInDuration,
					endStartRatio = fadeInDuration / fadeOutDuration;
  
				fadeOutAction.warp( 1.0, startEndRatio, duration );
				this.warp( endStartRatio, 1.0, duration );
  
			}
  
			return this;
  
		},
  
		crossFadeTo: function ( fadeInAction, duration, warp ) {
  
			return fadeInAction.crossFadeFrom( this, duration, warp );
  
		},
  
		stopFading: function () {
  
			var weightInterpolant = this._weightInterpolant;
  
			if ( weightInterpolant !== null ) {
  
				this._weightInterpolant = null;
				this._mixer._takeBackControlInterpolant( weightInterpolant );
  
			}
  
			return this;
  
		},
  
		// Time Scale Control
  
		// set the time scale stopping any scheduled warping
		// although .paused = true yields an effective time scale of zero, this
		// method does *not* change .paused, because it would be confusing
		setEffectiveTimeScale: function ( timeScale ) {
  
			this.timeScale = timeScale;
			this._effectiveTimeScale = this.paused ? 0 : timeScale;
  
			return this.stopWarping();
  
		},
  
		// return the time scale considering warping and .paused
		getEffectiveTimeScale: function () {
  
			return this._effectiveTimeScale;
  
		},
  
		setDuration: function ( duration ) {
  
			this.timeScale = this._clip.duration / duration;
  
			return this.stopWarping();
  
		},
  
		syncWith: function ( action ) {
  
			this.time = action.time;
			this.timeScale = action.timeScale;
  
			return this.stopWarping();
  
		},
  
		halt: function ( duration ) {
  
			return this.warp( this._effectiveTimeScale, 0, duration );
  
		},
  
		warp: function ( startTimeScale, endTimeScale, duration ) {
  
			var mixer = this._mixer, now = mixer.time,
				interpolant = this._timeScaleInterpolant,
  
				timeScale = this.timeScale;
  
			if ( interpolant === null ) {
  
				interpolant = mixer._lendControlInterpolant();
				this._timeScaleInterpolant = interpolant;
  
			}
  
			var times = interpolant.parameterPositions,
				values = interpolant.sampleValues;
  
			times[ 0 ] = now;
			times[ 1 ] = now + duration;
  
			values[ 0 ] = startTimeScale / timeScale;
			values[ 1 ] = endTimeScale / timeScale;
  
			return this;
  
		},
  
		stopWarping: function () {
  
			var timeScaleInterpolant = this._timeScaleInterpolant;
  
			if ( timeScaleInterpolant !== null ) {
  
				this._timeScaleInterpolant = null;
				this._mixer._takeBackControlInterpolant( timeScaleInterpolant );
  
			}
  
			return this;
  
		},
  
		// Object Accessors
  
		getMixer: function () {
  
			return this._mixer;
  
		},
  
		getClip: function () {
  
			return this._clip;
  
		},
  
		getRoot: function () {
  
			return this._localRoot || this._mixer._root;
  
		},
  
		// Interna
  
		_update: function ( time, deltaTime, timeDirection, accuIndex ) {
  
			// called by the mixer
  
			if ( ! this.enabled ) {
  
				// call ._updateWeight() to update ._effectiveWeight
  
				this._updateWeight( time );
				return;
  
			}
  
			var startTime = this._startTime;
  
			if ( startTime !== null ) {
  
				// check for scheduled start of action
  
				var timeRunning = ( time - startTime ) * timeDirection;
				if ( timeRunning < 0 || timeDirection === 0 ) {
  
					return; // yet to come / don't decide when delta = 0
  
				}
  
				// start
  
				this._startTime = null; // unschedule
				deltaTime = timeDirection * timeRunning;
  
			}
  
			// apply time scale and advance time
  
			deltaTime *= this._updateTimeScale( time );
			var clipTime = this._updateTime( deltaTime );
  
			// note: _updateTime may disable the action resulting in
			// an effective weight of 0
  
			var weight = this._updateWeight( time );
  
			if ( weight > 0 ) {
  
				var interpolants = this._interpolants;
				var propertyMixers = this._propertyBindings;
  
				for ( var j = 0, m = interpolants.length; j !== m; ++ j ) {
  
					interpolants[ j ].evaluate( clipTime );
					propertyMixers[ j ].accumulate( accuIndex, weight );
  
				}
  
			}
  
		},
  
		_updateWeight: function ( time ) {
  
			var weight = 0;
  
			if ( this.enabled ) {
  
				weight = this.weight;
				var interpolant = this._weightInterpolant;
  
				if ( interpolant !== null ) {
  
					var interpolantValue = interpolant.evaluate( time )[ 0 ];
  
					weight *= interpolantValue;
  
					if ( time > interpolant.parameterPositions[ 1 ] ) {
  
						this.stopFading();
  
						if ( interpolantValue === 0 ) {
  
							// faded out, disable
							this.enabled = false;
  
						}
  
					}
  
				}
  
			}
  
			this._effectiveWeight = weight;
			return weight;
  
		},
  
		_updateTimeScale: function ( time ) {
  
			var timeScale = 0;
  
			if ( ! this.paused ) {
  
				timeScale = this.timeScale;
  
				var interpolant = this._timeScaleInterpolant;
  
				if ( interpolant !== null ) {
  
					var interpolantValue = interpolant.evaluate( time )[ 0 ];
  
					timeScale *= interpolantValue;
  
					if ( time > interpolant.parameterPositions[ 1 ] ) {
  
						this.stopWarping();
  
						if ( timeScale === 0 ) {
  
							// motion has halted, pause
							this.paused = true;
  
						} else {
  
							// warp done - apply final time scale
							this.timeScale = timeScale;
  
						}
  
					}
  
				}
  
			}
  
			this._effectiveTimeScale = timeScale;
			return timeScale;
  
		},
  
		_updateTime: function ( deltaTime ) {
  
			var time = this.time + deltaTime;
  
			if ( deltaTime === 0 ) return time;
  
			var duration = this._clip.duration,
  
				loop = this.loop,
				loopCount = this._loopCount;
  
			if ( loop === LoopOnce ) {
  
				if ( loopCount === - 1 ) {
  
					// just started
  
					this._loopCount = 0;
					this._setEndings( true, true, false );
  
				}
  
				handle_stop: {
  
					if ( time >= duration ) {
  
						time = duration;
  
					} else if ( time < 0 ) {
  
						time = 0;
  
					} else break handle_stop;
  
					if ( this.clampWhenFinished ) this.paused = true;
					else this.enabled = false;
  
					this._mixer.dispatchEvent( {
						type: 'finished', action: this,
						direction: deltaTime < 0 ? - 1 : 1
					} );
  
				}
  
			} else { // repetitive Repeat or PingPong
  
				var pingPong = ( loop === LoopPingPong );
  
				if ( loopCount === - 1 ) {
  
					// just started
  
					if ( deltaTime >= 0 ) {
  
						loopCount = 0;
  
						this._setEndings( true, this.repetitions === 0, pingPong );
  
					} else {
  
						// when looping in reverse direction, the initial
						// transition through zero counts as a repetition,
						// so leave loopCount at -1
  
						this._setEndings( this.repetitions === 0, true, pingPong );
  
					}
  
				}
  
				if ( time >= duration || time < 0 ) {
  
					// wrap around
  
					var loopDelta = Math.floor( time / duration ); // signed
					time -= duration * loopDelta;
  
					loopCount += Math.abs( loopDelta );
  
					var pending = this.repetitions - loopCount;
  
					if ( pending <= 0 ) {
  
						// have to stop (switch state, clamp time, fire event)
  
						if ( this.clampWhenFinished ) this.paused = true;
						else this.enabled = false;
  
						time = deltaTime > 0 ? duration : 0;
  
						this._mixer.dispatchEvent( {
							type: 'finished', action: this,
							direction: deltaTime > 0 ? 1 : - 1
						} );
  
					} else {
  
						// keep running
  
						if ( pending === 1 ) {
  
							// entering the last round
  
							var atStart = deltaTime < 0;
							this._setEndings( atStart, ! atStart, pingPong );
  
						} else {
  
							this._setEndings( false, false, pingPong );
  
						}
  
						this._loopCount = loopCount;
  
						this._mixer.dispatchEvent( {
							type: 'loop', action: this, loopDelta: loopDelta
						} );
  
					}
  
				}
  
				if ( pingPong && ( loopCount & 1 ) === 1 ) {
  
					// invert time for the "pong round"
  
					this.time = time;
					return duration - time;
  
				}
  
			}
  
			this.time = time;
			return time;
  
		},
  
		_setEndings: function ( atStart, atEnd, pingPong ) {
  
			var settings = this._interpolantSettings;
  
			if ( pingPong ) {
  
				settings.endingStart 	= ZeroSlopeEnding;
				settings.endingEnd		= ZeroSlopeEnding;
  
			} else {
  
				// assuming for LoopOnce atStart == atEnd == true
  
				if ( atStart ) {
  
					settings.endingStart = this.zeroSlopeAtStart ? ZeroSlopeEnding : ZeroCurvatureEnding;
  
				} else {
  
					settings.endingStart = WrapAroundEnding;
  
				}
  
				if ( atEnd ) {
  
					settings.endingEnd = this.zeroSlopeAtEnd ? ZeroSlopeEnding : ZeroCurvatureEnding;
  
				} else {
  
					settings.endingEnd 	 = WrapAroundEnding;
  
				}
  
			}
  
		},
  
		_scheduleFading: function ( duration, weightNow, weightThen ) {
  
			var mixer = this._mixer, now = mixer.time,
				interpolant = this._weightInterpolant;
  
			if ( interpolant === null ) {
  
				interpolant = mixer._lendControlInterpolant();
				this._weightInterpolant = interpolant;
  
			}
  
			var times = interpolant.parameterPositions,
				values = interpolant.sampleValues;
  
			times[ 0 ] = now; 				values[ 0 ] = weightNow;
			times[ 1 ] = now + duration;	values[ 1 ] = weightThen;
  
			return this;
  
		}
  
	} );
  
	/**
	 *
	 * Player for AnimationClips.
	 *
	 *
	 * @author Ben Houston / http://clara.io/
	 * @author David Sarno / http://lighthaus.us/
	 * @author tschw
	 */
  
	function AnimationMixer( root ) {
  
		this._root = root;
		this._initMemoryManager();
		this._accuIndex = 0;
  
		this.time = 0;
  
		this.timeScale = 1.0;
  
	}
  
	AnimationMixer.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {
  
		constructor: AnimationMixer,
  
		_bindAction: function ( action, prototypeAction ) {
  
			var root = action._localRoot || this._root,
				tracks = action._clip.tracks,
				nTracks = tracks.length,
				bindings = action._propertyBindings,
				interpolants = action._interpolants,
				rootUuid = root.uuid,
				bindingsByRoot = this._bindingsByRootAndName,
				bindingsByName = bindingsByRoot[ rootUuid ];
  
			if ( bindingsByName === undefined ) {
  
				bindingsByName = {};
				bindingsByRoot[ rootUuid ] = bindingsByName;
  
			}
  
			for ( var i = 0; i !== nTracks; ++ i ) {
  
				var track = tracks[ i ],
					trackName = track.name,
					binding = bindingsByName[ trackName ];
  
				if ( binding !== undefined ) {
  
					bindings[ i ] = binding;
  
				} else {
  
					binding = bindings[ i ];
  
					if ( binding !== undefined ) {
  
						// existing binding, make sure the cache knows
  
						if ( binding._cacheIndex === null ) {
  
							++ binding.referenceCount;
							this._addInactiveBinding( binding, rootUuid, trackName );
  
						}
  
						continue;
  
					}
  
					var path = prototypeAction && prototypeAction.
						_propertyBindings[ i ].binding.parsedPath;
  
					binding = new PropertyMixer(
						PropertyBinding.create( root, trackName, path ),
						track.ValueTypeName, track.getValueSize() );
  
					++ binding.referenceCount;
					this._addInactiveBinding( binding, rootUuid, trackName );
  
					bindings[ i ] = binding;
  
				}
  
				interpolants[ i ].resultBuffer = binding.buffer;
  
			}
  
		},
  
		_activateAction: function ( action ) {
  
			if ( ! this._isActiveAction( action ) ) {
  
				if ( action._cacheIndex === null ) {
  
					// this action has been forgotten by the cache, but the user
					// appears to be still using it -> rebind
  
					var rootUuid = ( action._localRoot || this._root ).uuid,
						clipUuid = action._clip.uuid,
						actionsForClip = this._actionsByClip[ clipUuid ];
  
					this._bindAction( action,
						actionsForClip && actionsForClip.knownActions[ 0 ] );
  
					this._addInactiveAction( action, clipUuid, rootUuid );
  
				}
  
				var bindings = action._propertyBindings;
  
				// increment reference counts / sort out state
				for ( var i = 0, n = bindings.length; i !== n; ++ i ) {
  
					var binding = bindings[ i ];
  
					if ( binding.useCount ++ === 0 ) {
  
						this._lendBinding( binding );
						binding.saveOriginalState();
  
					}
  
				}
  
				this._lendAction( action );
  
			}
  
		},
  
		_deactivateAction: function ( action ) {
  
			if ( this._isActiveAction( action ) ) {
  
				var bindings = action._propertyBindings;
  
				// decrement reference counts / sort out state
				for ( var i = 0, n = bindings.length; i !== n; ++ i ) {
  
					var binding = bindings[ i ];
  
					if ( -- binding.useCount === 0 ) {
  
						binding.restoreOriginalState();
						this._takeBackBinding( binding );
  
					}
  
				}
  
				this._takeBackAction( action );
  
			}
  
		},
  
		// Memory manager
  
		_initMemoryManager: function () {
  
			this._actions = []; // 'nActiveActions' followed by inactive ones
			this._nActiveActions = 0;
  
			this._actionsByClip = {};
			// inside:
			// {
			// 		knownActions: Array< AnimationAction >	- used as prototypes
			// 		actionByRoot: AnimationAction			- lookup
			// }
  
  
			this._bindings = []; // 'nActiveBindings' followed by inactive ones
			this._nActiveBindings = 0;
  
			this._bindingsByRootAndName = {}; // inside: Map< name, PropertyMixer >
  
  
			this._controlInterpolants = []; // same game as above
			this._nActiveControlInterpolants = 0;
  
			var scope = this;
  
			this.stats = {
  
				actions: {
					get total() {
  
						return scope._actions.length;
  
					},
					get inUse() {
  
						return scope._nActiveActions;
  
					}
				},
				bindings: {
					get total() {
  
						return scope._bindings.length;
  
					},
					get inUse() {
  
						return scope._nActiveBindings;
  
					}
				},
				controlInterpolants: {
					get total() {
  
						return scope._controlInterpolants.length;
  
					},
					get inUse() {
  
						return scope._nActiveControlInterpolants;
  
					}
				}
  
			};
  
		},
  
		// Memory management for AnimationAction objects
  
		_isActiveAction: function ( action ) {
  
			var index = action._cacheIndex;
			return index !== null && index < this._nActiveActions;
  
		},
  
		_addInactiveAction: function ( action, clipUuid, rootUuid ) {
  
			var actions = this._actions,
				actionsByClip = this._actionsByClip,
				actionsForClip = actionsByClip[ clipUuid ];
  
			if ( actionsForClip === undefined ) {
  
				actionsForClip = {
  
					knownActions: [ action ],
					actionByRoot: {}
  
				};
  
				action._byClipCacheIndex = 0;
  
				actionsByClip[ clipUuid ] = actionsForClip;
  
			} else {
  
				var knownActions = actionsForClip.knownActions;
  
				action._byClipCacheIndex = knownActions.length;
				knownActions.push( action );
  
			}
  
			action._cacheIndex = actions.length;
			actions.push( action );
  
			actionsForClip.actionByRoot[ rootUuid ] = action;
  
		},
  
		_removeInactiveAction: function ( action ) {
  
			var actions = this._actions,
				lastInactiveAction = actions[ actions.length - 1 ],
				cacheIndex = action._cacheIndex;
  
			lastInactiveAction._cacheIndex = cacheIndex;
			actions[ cacheIndex ] = lastInactiveAction;
			actions.pop();
  
			action._cacheIndex = null;
  
  
			var clipUuid = action._clip.uuid,
				actionsByClip = this._actionsByClip,
				actionsForClip = actionsByClip[ clipUuid ],
				knownActionsForClip = actionsForClip.knownActions,
  
				lastKnownAction =
					knownActionsForClip[ knownActionsForClip.length - 1 ],
  
				byClipCacheIndex = action._byClipCacheIndex;
  
			lastKnownAction._byClipCacheIndex = byClipCacheIndex;
			knownActionsForClip[ byClipCacheIndex ] = lastKnownAction;
			knownActionsForClip.pop();
  
			action._byClipCacheIndex = null;
  
  
			var actionByRoot = actionsForClip.actionByRoot,
				rootUuid = ( action._localRoot || this._root ).uuid;
  
			delete actionByRoot[ rootUuid ];
  
			if ( knownActionsForClip.length === 0 ) {
  
				delete actionsByClip[ clipUuid ];
  
			}
  
			this._removeInactiveBindingsForAction( action );
  
		},
  
		_removeInactiveBindingsForAction: function ( action ) {
  
			var bindings = action._propertyBindings;
			for ( var i = 0, n = bindings.length; i !== n; ++ i ) {
  
				var binding = bindings[ i ];
  
				if ( -- binding.referenceCount === 0 ) {
  
					this._removeInactiveBinding( binding );
  
				}
  
			}
  
		},
  
		_lendAction: function ( action ) {
  
			// [ active actions |  inactive actions  ]
			// [  active actions >| inactive actions ]
			//                 s        a
			//                  <-swap->
			//                 a        s
  
			var actions = this._actions,
				prevIndex = action._cacheIndex,
  
				lastActiveIndex = this._nActiveActions ++,
  
				firstInactiveAction = actions[ lastActiveIndex ];
  
			action._cacheIndex = lastActiveIndex;
			actions[ lastActiveIndex ] = action;
  
			firstInactiveAction._cacheIndex = prevIndex;
			actions[ prevIndex ] = firstInactiveAction;
  
		},
  
		_takeBackAction: function ( action ) {
  
			// [  active actions  | inactive actions ]
			// [ active actions |< inactive actions  ]
			//        a        s
			//         <-swap->
			//        s        a
  
			var actions = this._actions,
				prevIndex = action._cacheIndex,
  
				firstInactiveIndex = -- this._nActiveActions,
  
				lastActiveAction = actions[ firstInactiveIndex ];
  
			action._cacheIndex = firstInactiveIndex;
			actions[ firstInactiveIndex ] = action;
  
			lastActiveAction._cacheIndex = prevIndex;
			actions[ prevIndex ] = lastActiveAction;
  
		},
  
		// Memory management for PropertyMixer objects
  
		_addInactiveBinding: function ( binding, rootUuid, trackName ) {
  
			var bindingsByRoot = this._bindingsByRootAndName,
				bindingByName = bindingsByRoot[ rootUuid ],
  
				bindings = this._bindings;
  
			if ( bindingByName === undefined ) {
  
				bindingByName = {};
				bindingsByRoot[ rootUuid ] = bindingByName;
  
			}
  
			bindingByName[ trackName ] = binding;
  
			binding._cacheIndex = bindings.length;
			bindings.push( binding );
  
		},
  
		_removeInactiveBinding: function ( binding ) {
  
			var bindings = this._bindings,
				propBinding = binding.binding,
				rootUuid = propBinding.rootNode.uuid,
				trackName = propBinding.path,
				bindingsByRoot = this._bindingsByRootAndName,
				bindingByName = bindingsByRoot[ rootUuid ],
  
				lastInactiveBinding = bindings[ bindings.length - 1 ],
				cacheIndex = binding._cacheIndex;
  
			lastInactiveBinding._cacheIndex = cacheIndex;
			bindings[ cacheIndex ] = lastInactiveBinding;
			bindings.pop();
  
			delete bindingByName[ trackName ];
  
			remove_empty_map: {
  
				for ( var _ in bindingByName ) break remove_empty_map; // eslint-disable-line no-unused-vars
  
				delete bindingsByRoot[ rootUuid ];
  
			}
  
		},
  
		_lendBinding: function ( binding ) {
  
			var bindings = this._bindings,
				prevIndex = binding._cacheIndex,
  
				lastActiveIndex = this._nActiveBindings ++,
  
				firstInactiveBinding = bindings[ lastActiveIndex ];
  
			binding._cacheIndex = lastActiveIndex;
			bindings[ lastActiveIndex ] = binding;
  
			firstInactiveBinding._cacheIndex = prevIndex;
			bindings[ prevIndex ] = firstInactiveBinding;
  
		},
  
		_takeBackBinding: function ( binding ) {
  
			var bindings = this._bindings,
				prevIndex = binding._cacheIndex,
  
				firstInactiveIndex = -- this._nActiveBindings,
  
				lastActiveBinding = bindings[ firstInactiveIndex ];
  
			binding._cacheIndex = firstInactiveIndex;
			bindings[ firstInactiveIndex ] = binding;
  
			lastActiveBinding._cacheIndex = prevIndex;
			bindings[ prevIndex ] = lastActiveBinding;
  
		},
  
  
		// Memory management of Interpolants for weight and time scale
  
		_lendControlInterpolant: function () {
  
			var interpolants = this._controlInterpolants,
				lastActiveIndex = this._nActiveControlInterpolants ++,
				interpolant = interpolants[ lastActiveIndex ];
  
			if ( interpolant === undefined ) {
  
				interpolant = new LinearInterpolant(
					new Float32Array( 2 ), new Float32Array( 2 ),
					1, this._controlInterpolantsResultBuffer );
  
				interpolant.__cacheIndex = lastActiveIndex;
				interpolants[ lastActiveIndex ] = interpolant;
  
			}
  
			return interpolant;
  
		},
  
		_takeBackControlInterpolant: function ( interpolant ) {
  
			var interpolants = this._controlInterpolants,
				prevIndex = interpolant.__cacheIndex,
  
				firstInactiveIndex = -- this._nActiveControlInterpolants,
  
				lastActiveInterpolant = interpolants[ firstInactiveIndex ];
  
			interpolant.__cacheIndex = firstInactiveIndex;
			interpolants[ firstInactiveIndex ] = interpolant;
  
			lastActiveInterpolant.__cacheIndex = prevIndex;
			interpolants[ prevIndex ] = lastActiveInterpolant;
  
		},
  
		_controlInterpolantsResultBuffer: new Float32Array( 1 ),
  
		// return an action for a clip optionally using a custom root target
		// object (this method allocates a lot of dynamic memory in case a
		// previously unknown clip/root combination is specified)
		clipAction: function ( clip, optionalRoot ) {
  
			var root = optionalRoot || this._root,
				rootUuid = root.uuid,
  
				clipObject = typeof clip === 'string' ?
					AnimationClip.findByName( root, clip ) : clip,
  
				clipUuid = clipObject !== null ? clipObject.uuid : clip,
  
				actionsForClip = this._actionsByClip[ clipUuid ],
				prototypeAction = null;
  
			if ( actionsForClip !== undefined ) {
  
				var existingAction =
						actionsForClip.actionByRoot[ rootUuid ];
  
				if ( existingAction !== undefined ) {
  
					return existingAction;
  
				}
  
				// we know the clip, so we don't have to parse all
				// the bindings again but can just copy
				prototypeAction = actionsForClip.knownActions[ 0 ];
  
				// also, take the clip from the prototype action
				if ( clipObject === null )
					clipObject = prototypeAction._clip;
  
			}
  
			// clip must be known when specified via string
			if ( clipObject === null ) return null;
  
			// allocate all resources required to run it
			var newAction = new AnimationAction( this, clipObject, optionalRoot );
  
			this._bindAction( newAction, prototypeAction );
  
			// and make the action known to the memory manager
			this._addInactiveAction( newAction, clipUuid, rootUuid );
  
			return newAction;
  
		},
  
		// get an existing action
		existingAction: function ( clip, optionalRoot ) {
  
			var root = optionalRoot || this._root,
				rootUuid = root.uuid,
  
				clipObject = typeof clip === 'string' ?
					AnimationClip.findByName( root, clip ) : clip,
  
				clipUuid = clipObject ? clipObject.uuid : clip,
  
				actionsForClip = this._actionsByClip[ clipUuid ];
  
			if ( actionsForClip !== undefined ) {
  
				return actionsForClip.actionByRoot[ rootUuid ] || null;
  
			}
  
			return null;
  
		},
  
		// deactivates all previously scheduled actions
		stopAllAction: function () {
  
			var actions = this._actions,
				nActions = this._nActiveActions,
				bindings = this._bindings,
				nBindings = this._nActiveBindings;
  
			this._nActiveActions = 0;
			this._nActiveBindings = 0;
  
			for ( var i = 0; i !== nActions; ++ i ) {
  
				actions[ i ].reset();
  
			}
  
			for ( var i = 0; i !== nBindings; ++ i ) {
  
				bindings[ i ].useCount = 0;
  
			}
  
			return this;
  
		},
  
		// advance the time and update apply the animation
		update: function ( deltaTime ) {
  
			deltaTime *= this.timeScale;
  
			var actions = this._actions,
				nActions = this._nActiveActions,
  
				time = this.time += deltaTime,
				timeDirection = Math.sign( deltaTime ),
  
				accuIndex = this._accuIndex ^= 1;
  
			// run active actions
  
			for ( var i = 0; i !== nActions; ++ i ) {
  
				var action = actions[ i ];
  
				action._update( time, deltaTime, timeDirection, accuIndex );
  
			}
  
			// update scene graph
  
			var bindings = this._bindings,
				nBindings = this._nActiveBindings;
  
			for ( var i = 0; i !== nBindings; ++ i ) {
  
				bindings[ i ].apply( accuIndex );
  
			}
  
			return this;
  
		},
  
		// return this mixer's root target object
		getRoot: function () {
  
			return this._root;
  
		},
  
		// free all resources specific to a particular clip
		uncacheClip: function ( clip ) {
  
			var actions = this._actions,
				clipUuid = clip.uuid,
				actionsByClip = this._actionsByClip,
				actionsForClip = actionsByClip[ clipUuid ];
  
			if ( actionsForClip !== undefined ) {
  
				// note: just calling _removeInactiveAction would mess up the
				// iteration state and also require updating the state we can
				// just throw away
  
				var actionsToRemove = actionsForClip.knownActions;
  
				for ( var i = 0, n = actionsToRemove.length; i !== n; ++ i ) {
  
					var action = actionsToRemove[ i ];
  
					this._deactivateAction( action );
  
					var cacheIndex = action._cacheIndex,
						lastInactiveAction = actions[ actions.length - 1 ];
  
					action._cacheIndex = null;
					action._byClipCacheIndex = null;
  
					lastInactiveAction._cacheIndex = cacheIndex;
					actions[ cacheIndex ] = lastInactiveAction;
					actions.pop();
  
					this._removeInactiveBindingsForAction( action );
  
				}
  
				delete actionsByClip[ clipUuid ];
  
			}
  
		},
  
		// free all resources specific to a particular root target object
		uncacheRoot: function ( root ) {
  
			var rootUuid = root.uuid,
				actionsByClip = this._actionsByClip;
  
			for ( var clipUuid in actionsByClip ) {
  
				var actionByRoot = actionsByClip[ clipUuid ].actionByRoot,
					action = actionByRoot[ rootUuid ];
  
				if ( action !== undefined ) {
  
					this._deactivateAction( action );
					this._removeInactiveAction( action );
  
				}
  
			}
  
			var bindingsByRoot = this._bindingsByRootAndName,
				bindingByName = bindingsByRoot[ rootUuid ];
  
			if ( bindingByName !== undefined ) {
  
				for ( var trackName in bindingByName ) {
  
					var binding = bindingByName[ trackName ];
					binding.restoreOriginalState();
					this._removeInactiveBinding( binding );
  
				}
  
			}
  
		},
  
		// remove a targeted clip from the cache
		uncacheAction: function ( clip, optionalRoot ) {
  
			var action = this.existingAction( clip, optionalRoot );
  
			if ( action !== null ) {
  
				this._deactivateAction( action );
				this._removeInactiveAction( action );
  
			}
  
		}
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function Uniform( value ) {
  
		if ( typeof value === 'string' ) {
  
			console.warn( 'THREE.Uniform: Type parameter is no longer needed.' );
			value = arguments[ 1 ];
  
		}
  
		this.value = value;
  
	}
  
	Uniform.prototype.clone = function () {
  
		return new Uniform( this.value.clone === undefined ? this.value : this.value.clone() );
  
	};
  
	/**
	 * @author benaadams / https://twitter.com/ben_a_adams
	 */
  
	function InstancedBufferGeometry() {
  
		BufferGeometry.call( this );
  
		this.type = 'InstancedBufferGeometry';
		this.maxInstancedCount = undefined;
  
	}
  
	InstancedBufferGeometry.prototype = Object.assign( Object.create( BufferGeometry.prototype ), {
  
		constructor: InstancedBufferGeometry,
  
		isInstancedBufferGeometry: true,
  
		copy: function ( source ) {
  
			BufferGeometry.prototype.copy.call( this, source );
  
			this.maxInstancedCount = source.maxInstancedCount;
  
			return this;
  
		},
  
		clone: function () {
  
			return new this.constructor().copy( this );
  
		}
  
	} );
  
	/**
	 * @author benaadams / https://twitter.com/ben_a_adams
	 */
  
	function InterleavedBufferAttribute( interleavedBuffer, itemSize, offset, normalized ) {
  
		this.data = interleavedBuffer;
		this.itemSize = itemSize;
		this.offset = offset;
  
		this.normalized = normalized === true;
  
	}
  
	Object.defineProperties( InterleavedBufferAttribute.prototype, {
  
		count: {
  
			get: function () {
  
				return this.data.count;
  
			}
  
		},
  
		array: {
  
			get: function () {
  
				return this.data.array;
  
			}
  
		}
  
	} );
  
	Object.assign( InterleavedBufferAttribute.prototype, {
  
		isInterleavedBufferAttribute: true,
  
		setX: function ( index, x ) {
  
			this.data.array[ index * this.data.stride + this.offset ] = x;
  
			return this;
  
		},
  
		setY: function ( index, y ) {
  
			this.data.array[ index * this.data.stride + this.offset + 1 ] = y;
  
			return this;
  
		},
  
		setZ: function ( index, z ) {
  
			this.data.array[ index * this.data.stride + this.offset + 2 ] = z;
  
			return this;
  
		},
  
		setW: function ( index, w ) {
  
			this.data.array[ index * this.data.stride + this.offset + 3 ] = w;
  
			return this;
  
		},
  
		getX: function ( index ) {
  
			return this.data.array[ index * this.data.stride + this.offset ];
  
		},
  
		getY: function ( index ) {
  
			return this.data.array[ index * this.data.stride + this.offset + 1 ];
  
		},
  
		getZ: function ( index ) {
  
			return this.data.array[ index * this.data.stride + this.offset + 2 ];
  
		},
  
		getW: function ( index ) {
  
			return this.data.array[ index * this.data.stride + this.offset + 3 ];
  
		},
  
		setXY: function ( index, x, y ) {
  
			index = index * this.data.stride + this.offset;
  
			this.data.array[ index + 0 ] = x;
			this.data.array[ index + 1 ] = y;
  
			return this;
  
		},
  
		setXYZ: function ( index, x, y, z ) {
  
			index = index * this.data.stride + this.offset;
  
			this.data.array[ index + 0 ] = x;
			this.data.array[ index + 1 ] = y;
			this.data.array[ index + 2 ] = z;
  
			return this;
  
		},
  
		setXYZW: function ( index, x, y, z, w ) {
  
			index = index * this.data.stride + this.offset;
  
			this.data.array[ index + 0 ] = x;
			this.data.array[ index + 1 ] = y;
			this.data.array[ index + 2 ] = z;
			this.data.array[ index + 3 ] = w;
  
			return this;
  
		}
  
	} );
  
	/**
	 * @author benaadams / https://twitter.com/ben_a_adams
	 */
  
	function InterleavedBuffer( array, stride ) {
  
		this.array = array;
		this.stride = stride;
		this.count = array !== undefined ? array.length / stride : 0;
  
		this.dynamic = false;
		this.updateRange = { offset: 0, count: - 1 };
  
		this.version = 0;
  
	}
  
	Object.defineProperty( InterleavedBuffer.prototype, 'needsUpdate', {
  
		set: function ( value ) {
  
			if ( value === true ) this.version ++;
  
		}
  
	} );
  
	Object.assign( InterleavedBuffer.prototype, {
  
		isInterleavedBuffer: true,
  
		onUploadCallback: function () {},
  
		setArray: function ( array ) {
  
			if ( Array.isArray( array ) ) {
  
				throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' );
  
			}
  
			this.count = array !== undefined ? array.length / this.stride : 0;
			this.array = array;
  
			return this;
  
		},
  
		setDynamic: function ( value ) {
  
			this.dynamic = value;
  
			return this;
  
		},
  
		copy: function ( source ) {
  
			this.array = new source.array.constructor( source.array );
			this.count = source.count;
			this.stride = source.stride;
			this.dynamic = source.dynamic;
  
			return this;
  
		},
  
		copyAt: function ( index1, attribute, index2 ) {
  
			index1 *= this.stride;
			index2 *= attribute.stride;
  
			for ( var i = 0, l = this.stride; i < l; i ++ ) {
  
				this.array[ index1 + i ] = attribute.array[ index2 + i ];
  
			}
  
			return this;
  
		},
  
		set: function ( value, offset ) {
  
			if ( offset === undefined ) offset = 0;
  
			this.array.set( value, offset );
  
			return this;
  
		},
  
		clone: function () {
  
			return new this.constructor().copy( this );
  
		},
  
		onUpload: function ( callback ) {
  
			this.onUploadCallback = callback;
  
			return this;
  
		}
  
	} );
  
	/**
	 * @author benaadams / https://twitter.com/ben_a_adams
	 */
  
	function InstancedInterleavedBuffer( array, stride, meshPerAttribute ) {
  
		InterleavedBuffer.call( this, array, stride );
  
		this.meshPerAttribute = meshPerAttribute || 1;
  
	}
  
	InstancedInterleavedBuffer.prototype = Object.assign( Object.create( InterleavedBuffer.prototype ), {
  
		constructor: InstancedInterleavedBuffer,
  
		isInstancedInterleavedBuffer: true,
  
		copy: function ( source ) {
  
			InterleavedBuffer.prototype.copy.call( this, source );
  
			this.meshPerAttribute = source.meshPerAttribute;
  
			return this;
  
		}
  
	} );
  
	/**
	 * @author benaadams / https://twitter.com/ben_a_adams
	 */
  
	function InstancedBufferAttribute( array, itemSize, meshPerAttribute ) {
  
		BufferAttribute.call( this, array, itemSize );
  
		this.meshPerAttribute = meshPerAttribute || 1;
  
	}
  
	InstancedBufferAttribute.prototype = Object.assign( Object.create( BufferAttribute.prototype ), {
  
		constructor: InstancedBufferAttribute,
  
		isInstancedBufferAttribute: true,
  
		copy: function ( source ) {
  
			BufferAttribute.prototype.copy.call( this, source );
  
			this.meshPerAttribute = source.meshPerAttribute;
  
			return this;
  
		}
  
	} );
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 * @author bhouston / http://clara.io/
	 * @author stephomi / http://stephaneginier.com/
	 */
  
	function Raycaster( origin, direction, near, far ) {
  
		this.ray = new Ray( origin, direction );
		// direction is assumed to be normalized (for accurate distance calculations)
  
		this.near = near || 0;
		this.far = far || Infinity;
  
		this.params = {
			Mesh: {},
			Line: {},
			LOD: {},
			Points: { threshold: 1 },
			Sprite: {}
		};
  
		Object.defineProperties( this.params, {
			PointCloud: {
				get: function () {
  
					console.warn( 'THREE.Raycaster: params.PointCloud has been renamed to params.Points.' );
					return this.Points;
  
				}
			}
		} );
  
	}
  
	function ascSort( a, b ) {
  
		return a.distance - b.distance;
  
	}
  
	function intersectObject( object, raycaster, intersects, recursive ) {
  
		if ( object.visible === false ) return;
  
		object.raycast( raycaster, intersects );
  
		if ( recursive === true ) {
  
			var children = object.children;
  
			for ( var i = 0, l = children.length; i < l; i ++ ) {
  
				intersectObject( children[ i ], raycaster, intersects, true );
  
			}
  
		}
  
	}
  
	Object.assign( Raycaster.prototype, {
  
		linePrecision: 1,
  
		set: function ( origin, direction ) {
  
			// direction is assumed to be normalized (for accurate distance calculations)
  
			this.ray.set( origin, direction );
  
		},
  
		setFromCamera: function ( coords, camera ) {
  
			if ( ( camera && camera.isPerspectiveCamera ) ) {
  
				this.ray.origin.setFromMatrixPosition( camera.matrixWorld );
				this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize();
  
			} else if ( ( camera && camera.isOrthographicCamera ) ) {
  
				this.ray.origin.set( coords.x, coords.y, ( camera.near + camera.far ) / ( camera.near - camera.far ) ).unproject( camera ); // set origin in plane of camera
				this.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld );
  
			} else {
  
				console.error( 'THREE.Raycaster: Unsupported camera type.' );
  
			}
  
		},
  
		intersectObject: function ( object, recursive, optionalTarget ) {
  
			var intersects = optionalTarget || [];
  
			intersectObject( object, this, intersects, recursive );
  
			intersects.sort( ascSort );
  
			return intersects;
  
		},
  
		intersectObjects: function ( objects, recursive, optionalTarget ) {
  
			var intersects = optionalTarget || [];
  
			if ( Array.isArray( objects ) === false ) {
  
				console.warn( 'THREE.Raycaster.intersectObjects: objects is not an Array.' );
				return intersects;
  
			}
  
			for ( var i = 0, l = objects.length; i < l; i ++ ) {
  
				intersectObject( objects[ i ], this, intersects, recursive );
  
			}
  
			intersects.sort( ascSort );
  
			return intersects;
  
		}
  
	} );
  
	/**
	 * @author alteredq / http://alteredqualia.com/
	 */
  
	function Clock( autoStart ) {
  
		this.autoStart = ( autoStart !== undefined ) ? autoStart : true;
  
		this.startTime = 0;
		this.oldTime = 0;
		this.elapsedTime = 0;
  
		this.running = false;
  
	}
  
	Object.assign( Clock.prototype, {
  
		start: function () {
  
			this.startTime = ( typeof performance === 'undefined' ? Date : performance ).now(); // see #10732
  
			this.oldTime = this.startTime;
			this.elapsedTime = 0;
			this.running = true;
  
		},
  
		stop: function () {
  
			this.getElapsedTime();
			this.running = false;
			this.autoStart = false;
  
		},
  
		getElapsedTime: function () {
  
			this.getDelta();
			return this.elapsedTime;
  
		},
  
		getDelta: function () {
  
			var diff = 0;
  
			if ( this.autoStart && ! this.running ) {
  
				this.start();
				return 0;
  
			}
  
			if ( this.running ) {
  
				var newTime = ( typeof performance === 'undefined' ? Date : performance ).now();
  
				diff = ( newTime - this.oldTime ) / 1000;
				this.oldTime = newTime;
  
				this.elapsedTime += diff;
  
			}
  
			return diff;
  
		}
  
	} );
  
	/**
	 * @author bhouston / http://clara.io
	 * @author WestLangley / http://github.com/WestLangley
	 *
	 * Ref: https://en.wikipedia.org/wiki/Spherical_coordinate_system
	 *
	 * The poles (phi) are at the positive and negative y axis.
	 * The equator starts at positive z.
	 */
  
	function Spherical( radius, phi, theta ) {
  
		this.radius = ( radius !== undefined ) ? radius : 1.0;
		this.phi = ( phi !== undefined ) ? phi : 0; // up / down towards top and bottom pole
		this.theta = ( theta !== undefined ) ? theta : 0; // around the equator of the sphere
  
		return this;
  
	}
  
	Object.assign( Spherical.prototype, {
  
		set: function ( radius, phi, theta ) {
  
			this.radius = radius;
			this.phi = phi;
			this.theta = theta;
  
			return this;
  
		},
  
		clone: function () {
  
			return new this.constructor().copy( this );
  
		},
  
		copy: function ( other ) {
  
			this.radius = other.radius;
			this.phi = other.phi;
			this.theta = other.theta;
  
			return this;
  
		},
  
		// restrict phi to be betwee EPS and PI-EPS
		makeSafe: function () {
  
			var EPS = 0.000001;
			this.phi = Math.max( EPS, Math.min( Math.PI - EPS, this.phi ) );
  
			return this;
  
		},
  
		setFromVector3: function ( vec3 ) {
  
			this.radius = vec3.length();
  
			if ( this.radius === 0 ) {
  
				this.theta = 0;
				this.phi = 0;
  
			} else {
  
				this.theta = Math.atan2( vec3.x, vec3.z ); // equator angle around y-up axis
				this.phi = Math.acos( _Math.clamp( vec3.y / this.radius, - 1, 1 ) ); // polar angle
  
			}
  
			return this;
  
		}
  
	} );
  
	/**
	 * @author Mugen87 / https://github.com/Mugen87
	 *
	 * Ref: https://en.wikipedia.org/wiki/Cylindrical_coordinate_system
	 *
	 */
  
	function Cylindrical( radius, theta, y ) {
  
		this.radius = ( radius !== undefined ) ? radius : 1.0; // distance from the origin to a point in the x-z plane
		this.theta = ( theta !== undefined ) ? theta : 0; // counterclockwise angle in the x-z plane measured in radians from the positive z-axis
		this.y = ( y !== undefined ) ? y : 0; // height above the x-z plane
  
		return this;
  
	}
  
	Object.assign( Cylindrical.prototype, {
  
		set: function ( radius, theta, y ) {
  
			this.radius = radius;
			this.theta = theta;
			this.y = y;
  
			return this;
  
		},
  
		clone: function () {
  
			return new this.constructor().copy( this );
  
		},
  
		copy: function ( other ) {
  
			this.radius = other.radius;
			this.theta = other.theta;
			this.y = other.y;
  
			return this;
  
		},
  
		setFromVector3: function ( vec3 ) {
  
			this.radius = Math.sqrt( vec3.x * vec3.x + vec3.z * vec3.z );
			this.theta = Math.atan2( vec3.x, vec3.z );
			this.y = vec3.y;
  
			return this;
  
		}
  
	} );
  
	/**
	 * @author bhouston / http://clara.io
	 */
  
	function Box2( min, max ) {
  
		this.min = ( min !== undefined ) ? min : new Vector2( + Infinity, + Infinity );
		this.max = ( max !== undefined ) ? max : new Vector2( - Infinity, - Infinity );
  
	}
  
	Object.assign( Box2.prototype, {
  
		set: function ( min, max ) {
  
			this.min.copy( min );
			this.max.copy( max );
  
			return this;
  
		},
  
		setFromPoints: function ( points ) {
  
			this.makeEmpty();
  
			for ( var i = 0, il = points.length; i < il; i ++ ) {
  
				this.expandByPoint( points[ i ] );
  
			}
  
			return this;
  
		},
  
		setFromCenterAndSize: function () {
  
			var v1 = new Vector2();
  
			return function setFromCenterAndSize( center, size ) {
  
				var halfSize = v1.copy( size ).multiplyScalar( 0.5 );
				this.min.copy( center ).sub( halfSize );
				this.max.copy( center ).add( halfSize );
  
				return this;
  
			};
  
		}(),
  
		clone: function () {
  
			return new this.constructor().copy( this );
  
		},
  
		copy: function ( box ) {
  
			this.min.copy( box.min );
			this.max.copy( box.max );
  
			return this;
  
		},
  
		makeEmpty: function () {
  
			this.min.x = this.min.y = + Infinity;
			this.max.x = this.max.y = - Infinity;
  
			return this;
  
		},
  
		isEmpty: function () {
  
			// this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes
  
			return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y );
  
		},
  
		getCenter: function ( target ) {
  
			if ( target === undefined ) {
  
				console.warn( 'THREE.Box2: .getCenter() target is now required' );
				target = new Vector2();
  
			}
  
			return this.isEmpty() ? target.set( 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 );
  
		},
  
		getSize: function ( target ) {
  
			if ( target === undefined ) {
  
				console.warn( 'THREE.Box2: .getSize() target is now required' );
				target = new Vector2();
  
			}
  
			return this.isEmpty() ? target.set( 0, 0 ) : target.subVectors( this.max, this.min );
  
		},
  
		expandByPoint: function ( point ) {
  
			this.min.min( point );
			this.max.max( point );
  
			return this;
  
		},
  
		expandByVector: function ( vector ) {
  
			this.min.sub( vector );
			this.max.add( vector );
  
			return this;
  
		},
  
		expandByScalar: function ( scalar ) {
  
			this.min.addScalar( - scalar );
			this.max.addScalar( scalar );
  
			return this;
  
		},
  
		containsPoint: function ( point ) {
  
			return point.x < this.min.x || point.x > this.max.x ||
				point.y < this.min.y || point.y > this.max.y ? false : true;
  
		},
  
		containsBox: function ( box ) {
  
			return this.min.x <= box.min.x && box.max.x <= this.max.x &&
				this.min.y <= box.min.y && box.max.y <= this.max.y;
  
		},
  
		getParameter: function ( point, target ) {
  
			// This can potentially have a divide by zero if the box
			// has a size dimension of 0.
  
			if ( target === undefined ) {
  
				console.warn( 'THREE.Box2: .getParameter() target is now required' );
				target = new Vector2();
  
			}
  
			return target.set(
				( point.x - this.min.x ) / ( this.max.x - this.min.x ),
				( point.y - this.min.y ) / ( this.max.y - this.min.y )
			);
  
		},
  
		intersectsBox: function ( box ) {
  
			// using 4 splitting planes to rule out intersections
  
			return box.max.x < this.min.x || box.min.x > this.max.x ||
				box.max.y < this.min.y || box.min.y > this.max.y ? false : true;
  
		},
  
		clampPoint: function ( point, target ) {
  
			if ( target === undefined ) {
  
				console.warn( 'THREE.Box2: .clampPoint() target is now required' );
				target = new Vector2();
  
			}
  
			return target.copy( point ).clamp( this.min, this.max );
  
		},
  
		distanceToPoint: function () {
  
			var v1 = new Vector2();
  
			return function distanceToPoint( point ) {
  
				var clampedPoint = v1.copy( point ).clamp( this.min, this.max );
				return clampedPoint.sub( point ).length();
  
			};
  
		}(),
  
		intersect: function ( box ) {
  
			this.min.max( box.min );
			this.max.min( box.max );
  
			return this;
  
		},
  
		union: function ( box ) {
  
			this.min.min( box.min );
			this.max.max( box.max );
  
			return this;
  
		},
  
		translate: function ( offset ) {
  
			this.min.add( offset );
			this.max.add( offset );
  
			return this;
  
		},
  
		equals: function ( box ) {
  
			return box.min.equals( this.min ) && box.max.equals( this.max );
  
		}
  
	} );
  
	/**
	 * @author alteredq / http://alteredqualia.com/
	 */
  
	function ImmediateRenderObject( material ) {
  
		Object3D.call( this );
  
		this.material = material;
		this.render = function ( /* renderCallback */ ) {};
  
	}
  
	ImmediateRenderObject.prototype = Object.create( Object3D.prototype );
	ImmediateRenderObject.prototype.constructor = ImmediateRenderObject;
  
	ImmediateRenderObject.prototype.isImmediateRenderObject = true;
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 * @author WestLangley / http://github.com/WestLangley
	 */
  
	function VertexNormalsHelper( object, size, hex, linewidth ) {
  
		this.object = object;
  
		this.size = ( size !== undefined ) ? size : 1;
  
		var color = ( hex !== undefined ) ? hex : 0xff0000;
  
		var width = ( linewidth !== undefined ) ? linewidth : 1;
  
		//
  
		var nNormals = 0;
  
		var objGeometry = this.object.geometry;
  
		if ( objGeometry && objGeometry.isGeometry ) {
  
			nNormals = objGeometry.faces.length * 3;
  
		} else if ( objGeometry && objGeometry.isBufferGeometry ) {
  
			nNormals = objGeometry.attributes.normal.count;
  
		}
  
		//
  
		var geometry = new BufferGeometry();
  
		var positions = new Float32BufferAttribute( nNormals * 2 * 3, 3 );
  
		geometry.addAttribute( 'position', positions );
  
		LineSegments.call( this, geometry, new LineBasicMaterial( { color: color, linewidth: width } ) );
  
		//
  
		this.matrixAutoUpdate = false;
  
		this.update();
  
	}
  
	VertexNormalsHelper.prototype = Object.create( LineSegments.prototype );
	VertexNormalsHelper.prototype.constructor = VertexNormalsHelper;
  
	VertexNormalsHelper.prototype.update = ( function () {
  
		var v1 = new Vector3();
		var v2 = new Vector3();
		var normalMatrix = new Matrix3();
  
		return function update() {
  
			var keys = [ 'a', 'b', 'c' ];
  
			this.object.updateMatrixWorld( true );
  
			normalMatrix.getNormalMatrix( this.object.matrixWorld );
  
			var matrixWorld = this.object.matrixWorld;
  
			var position = this.geometry.attributes.position;
  
			//
  
			var objGeometry = this.object.geometry;
  
			if ( objGeometry && objGeometry.isGeometry ) {
  
				var vertices = objGeometry.vertices;
  
				var faces = objGeometry.faces;
  
				var idx = 0;
  
				for ( var i = 0, l = faces.length; i < l; i ++ ) {
  
					var face = faces[ i ];
  
					for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) {
  
						var vertex = vertices[ face[ keys[ j ] ] ];
  
						var normal = face.vertexNormals[ j ];
  
						v1.copy( vertex ).applyMatrix4( matrixWorld );
  
						v2.copy( normal ).applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 );
  
						position.setXYZ( idx, v1.x, v1.y, v1.z );
  
						idx = idx + 1;
  
						position.setXYZ( idx, v2.x, v2.y, v2.z );
  
						idx = idx + 1;
  
					}
  
				}
  
			} else if ( objGeometry && objGeometry.isBufferGeometry ) {
  
				var objPos = objGeometry.attributes.position;
  
				var objNorm = objGeometry.attributes.normal;
  
				var idx = 0;
  
				// for simplicity, ignore index and drawcalls, and render every normal
  
				for ( var j = 0, jl = objPos.count; j < jl; j ++ ) {
  
					v1.set( objPos.getX( j ), objPos.getY( j ), objPos.getZ( j ) ).applyMatrix4( matrixWorld );
  
					v2.set( objNorm.getX( j ), objNorm.getY( j ), objNorm.getZ( j ) );
  
					v2.applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 );
  
					position.setXYZ( idx, v1.x, v1.y, v1.z );
  
					idx = idx + 1;
  
					position.setXYZ( idx, v2.x, v2.y, v2.z );
  
					idx = idx + 1;
  
				}
  
			}
  
			position.needsUpdate = true;
  
		};
  
	}() );
  
	/**
	 * @author alteredq / http://alteredqualia.com/
	 * @author mrdoob / http://mrdoob.com/
	 * @author WestLangley / http://github.com/WestLangley
	 */
  
	function SpotLightHelper( light, color ) {
  
		Object3D.call( this );
  
		this.light = light;
		this.light.updateMatrixWorld();
  
		this.matrix = light.matrixWorld;
		this.matrixAutoUpdate = false;
  
		this.color = color;
  
		var geometry = new BufferGeometry();
  
		var positions = [
			0, 0, 0, 	0, 0, 1,
			0, 0, 0, 	1, 0, 1,
			0, 0, 0,	- 1, 0, 1,
			0, 0, 0, 	0, 1, 1,
			0, 0, 0, 	0, - 1, 1
		];
  
		for ( var i = 0, j = 1, l = 32; i < l; i ++, j ++ ) {
  
			var p1 = ( i / l ) * Math.PI * 2;
			var p2 = ( j / l ) * Math.PI * 2;
  
			positions.push(
				Math.cos( p1 ), Math.sin( p1 ), 1,
				Math.cos( p2 ), Math.sin( p2 ), 1
			);
  
		}
  
		geometry.addAttribute( 'position', new Float32BufferAttribute( positions, 3 ) );
  
		var material = new LineBasicMaterial( { fog: false } );
  
		this.cone = new LineSegments( geometry, material );
		this.add( this.cone );
  
		this.update();
  
	}
  
	SpotLightHelper.prototype = Object.create( Object3D.prototype );
	SpotLightHelper.prototype.constructor = SpotLightHelper;
  
	SpotLightHelper.prototype.dispose = function () {
  
		this.cone.geometry.dispose();
		this.cone.material.dispose();
  
	};
  
	SpotLightHelper.prototype.update = function () {
  
		var vector = new Vector3();
		var vector2 = new Vector3();
  
		return function update() {
  
			this.light.updateMatrixWorld();
  
			var coneLength = this.light.distance ? this.light.distance : 1000;
			var coneWidth = coneLength * Math.tan( this.light.angle );
  
			this.cone.scale.set( coneWidth, coneWidth, coneLength );
  
			vector.setFromMatrixPosition( this.light.matrixWorld );
			vector2.setFromMatrixPosition( this.light.target.matrixWorld );
  
			this.cone.lookAt( vector2.sub( vector ) );
  
			if ( this.color !== undefined ) {
  
				this.cone.material.color.set( this.color );
  
			} else {
  
				this.cone.material.color.copy( this.light.color );
  
			}
  
		};
  
	}();
  
	/**
	 * @author Sean Griffin / http://twitter.com/sgrif
	 * @author Michael Guerrero / http://realitymeltdown.com
	 * @author mrdoob / http://mrdoob.com/
	 * @author ikerr / http://verold.com
	 * @author Mugen87 / https://github.com/Mugen87
	 */
  
	function getBoneList( object ) {
  
		var boneList = [];
  
		if ( object && object.isBone ) {
  
			boneList.push( object );
  
		}
  
		for ( var i = 0; i < object.children.length; i ++ ) {
  
			boneList.push.apply( boneList, getBoneList( object.children[ i ] ) );
  
		}
  
		return boneList;
  
	}
  
	function SkeletonHelper( object ) {
  
		var bones = getBoneList( object );
  
		var geometry = new BufferGeometry();
  
		var vertices = [];
		var colors = [];
  
		var color1 = new Color( 0, 0, 1 );
		var color2 = new Color( 0, 1, 0 );
  
		for ( var i = 0; i < bones.length; i ++ ) {
  
			var bone = bones[ i ];
  
			if ( bone.parent && bone.parent.isBone ) {
  
				vertices.push( 0, 0, 0 );
				vertices.push( 0, 0, 0 );
				colors.push( color1.r, color1.g, color1.b );
				colors.push( color2.r, color2.g, color2.b );
  
			}
  
		}
  
		geometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
		geometry.addAttribute( 'color', new Float32BufferAttribute( colors, 3 ) );
  
		var material = new LineBasicMaterial( { vertexColors: VertexColors, depthTest: false, depthWrite: false, transparent: true } );
  
		LineSegments.call( this, geometry, material );
  
		this.root = object;
		this.bones = bones;
  
		this.matrix = object.matrixWorld;
		this.matrixAutoUpdate = false;
  
	}
  
	SkeletonHelper.prototype = Object.create( LineSegments.prototype );
	SkeletonHelper.prototype.constructor = SkeletonHelper;
  
	SkeletonHelper.prototype.updateMatrixWorld = function () {
  
		var vector = new Vector3();
  
		var boneMatrix = new Matrix4();
		var matrixWorldInv = new Matrix4();
  
		return function updateMatrixWorld( force ) {
  
			var bones = this.bones;
  
			var geometry = this.geometry;
			var position = geometry.getAttribute( 'position' );
  
			matrixWorldInv.getInverse( this.root.matrixWorld );
  
			for ( var i = 0, j = 0; i < bones.length; i ++ ) {
  
				var bone = bones[ i ];
  
				if ( bone.parent && bone.parent.isBone ) {
  
					boneMatrix.multiplyMatrices( matrixWorldInv, bone.matrixWorld );
					vector.setFromMatrixPosition( boneMatrix );
					position.setXYZ( j, vector.x, vector.y, vector.z );
  
					boneMatrix.multiplyMatrices( matrixWorldInv, bone.parent.matrixWorld );
					vector.setFromMatrixPosition( boneMatrix );
					position.setXYZ( j + 1, vector.x, vector.y, vector.z );
  
					j += 2;
  
				}
  
			}
  
			geometry.getAttribute( 'position' ).needsUpdate = true;
  
			Object3D.prototype.updateMatrixWorld.call( this, force );
  
		};
  
	}();
  
	/**
	 * @author alteredq / http://alteredqualia.com/
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function PointLightHelper( light, sphereSize, color ) {
  
		this.light = light;
		this.light.updateMatrixWorld();
  
		this.color = color;
  
		var geometry = new SphereBufferGeometry( sphereSize, 4, 2 );
		var material = new MeshBasicMaterial( { wireframe: true, fog: false } );
  
		Mesh.call( this, geometry, material );
  
		this.matrix = this.light.matrixWorld;
		this.matrixAutoUpdate = false;
  
		this.update();
  
  
		/*
		var distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 );
		var distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } );
  
		this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial );
		this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial );
  
		var d = light.distance;
  
		if ( d === 0.0 ) {
  
			this.lightDistance.visible = false;
  
		} else {
  
			this.lightDistance.scale.set( d, d, d );
  
		}
  
		this.add( this.lightDistance );
		*/
  
	}
  
	PointLightHelper.prototype = Object.create( Mesh.prototype );
	PointLightHelper.prototype.constructor = PointLightHelper;
  
	PointLightHelper.prototype.dispose = function () {
  
		this.geometry.dispose();
		this.material.dispose();
  
	};
  
	PointLightHelper.prototype.update = function () {
  
		if ( this.color !== undefined ) {
  
			this.material.color.set( this.color );
  
		} else {
  
			this.material.color.copy( this.light.color );
  
		}
  
		/*
		var d = this.light.distance;
  
		if ( d === 0.0 ) {
  
			this.lightDistance.visible = false;
  
		} else {
  
			this.lightDistance.visible = true;
			this.lightDistance.scale.set( d, d, d );
  
		}
		*/
  
	};
  
	/**
	 * @author abelnation / http://github.com/abelnation
	 * @author Mugen87 / http://github.com/Mugen87
	 * @author WestLangley / http://github.com/WestLangley
	 */
  
	function RectAreaLightHelper( light, color ) {
  
		Object3D.call( this );
  
		this.light = light;
		this.light.updateMatrixWorld();
  
		this.matrix = light.matrixWorld;
		this.matrixAutoUpdate = false;
  
		this.color = color;
  
		var material = new LineBasicMaterial( { fog: false } );
  
		var geometry = new BufferGeometry();
  
		geometry.addAttribute( 'position', new BufferAttribute( new Float32Array( 5 * 3 ), 3 ) );
  
		this.line = new Line( geometry, material );
		this.add( this.line );
  
  
		this.update();
  
	}
  
	RectAreaLightHelper.prototype = Object.create( Object3D.prototype );
	RectAreaLightHelper.prototype.constructor = RectAreaLightHelper;
  
	RectAreaLightHelper.prototype.dispose = function () {
  
		this.children[ 0 ].geometry.dispose();
		this.children[ 0 ].material.dispose();
  
	};
  
	RectAreaLightHelper.prototype.update = function () {
  
		// calculate new dimensions of the helper
  
		var hx = this.light.width * 0.5;
		var hy = this.light.height * 0.5;
  
		var position = this.line.geometry.attributes.position;
		var array = position.array;
  
		// update vertices
  
		array[ 0 ] = hx; array[ 1 ] = - hy; array[ 2 ] = 0;
		array[ 3 ] = hx; array[ 4 ] = hy; array[ 5 ] = 0;
		array[ 6 ] = - hx; array[ 7 ] = hy; array[ 8 ] = 0;
		array[ 9 ] = - hx; array[ 10 ] = - hy; array[ 11 ] = 0;
		array[ 12 ] = hx; array[ 13 ] = - hy; array[ 14 ] = 0;
  
		position.needsUpdate = true;
  
		if ( this.color !== undefined ) {
  
			this.line.material.color.set( this.color );
  
		} else {
  
			this.line.material.color.copy( this.light.color );
  
		}
  
	};
  
	/**
	 * @author alteredq / http://alteredqualia.com/
	 * @author mrdoob / http://mrdoob.com/
	 * @author Mugen87 / https://github.com/Mugen87
	 */
  
	function HemisphereLightHelper( light, size, color ) {
  
		Object3D.call( this );
  
		this.light = light;
		this.light.updateMatrixWorld();
  
		this.matrix = light.matrixWorld;
		this.matrixAutoUpdate = false;
  
		this.color = color;
  
		var geometry = new OctahedronBufferGeometry( size );
		geometry.rotateY( Math.PI * 0.5 );
  
		this.material = new MeshBasicMaterial( { wireframe: true, fog: false } );
		if ( this.color === undefined ) this.material.vertexColors = VertexColors;
  
		var position = geometry.getAttribute( 'position' );
		var colors = new Float32Array( position.count * 3 );
  
		geometry.addAttribute( 'color', new BufferAttribute( colors, 3 ) );
  
		this.add( new Mesh( geometry, this.material ) );
  
		this.update();
  
	}
  
	HemisphereLightHelper.prototype = Object.create( Object3D.prototype );
	HemisphereLightHelper.prototype.constructor = HemisphereLightHelper;
  
	HemisphereLightHelper.prototype.dispose = function () {
  
		this.children[ 0 ].geometry.dispose();
		this.children[ 0 ].material.dispose();
  
	};
  
	HemisphereLightHelper.prototype.update = function () {
  
		var vector = new Vector3();
  
		var color1 = new Color();
		var color2 = new Color();
  
		return function update() {
  
			var mesh = this.children[ 0 ];
  
			if ( this.color !== undefined ) {
  
				this.material.color.set( this.color );
  
			} else {
  
				var colors = mesh.geometry.getAttribute( 'color' );
  
				color1.copy( this.light.color );
				color2.copy( this.light.groundColor );
  
				for ( var i = 0, l = colors.count; i < l; i ++ ) {
  
					var color = ( i < ( l / 2 ) ) ? color1 : color2;
  
					colors.setXYZ( i, color.r, color.g, color.b );
  
				}
  
				colors.needsUpdate = true;
  
			}
  
			mesh.lookAt( vector.setFromMatrixPosition( this.light.matrixWorld ).negate() );
  
		};
  
	}();
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function GridHelper( size, divisions, color1, color2 ) {
  
		size = size || 10;
		divisions = divisions || 10;
		color1 = new Color( color1 !== undefined ? color1 : 0x444444 );
		color2 = new Color( color2 !== undefined ? color2 : 0x888888 );
  
		var center = divisions / 2;
		var step = size / divisions;
		var halfSize = size / 2;
  
		var vertices = [], colors = [];
  
		for ( var i = 0, j = 0, k = - halfSize; i <= divisions; i ++, k += step ) {
  
			vertices.push( - halfSize, 0, k, halfSize, 0, k );
			vertices.push( k, 0, - halfSize, k, 0, halfSize );
  
			var color = i === center ? color1 : color2;
  
			color.toArray( colors, j ); j += 3;
			color.toArray( colors, j ); j += 3;
			color.toArray( colors, j ); j += 3;
			color.toArray( colors, j ); j += 3;
  
		}
  
		var geometry = new BufferGeometry();
		geometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
		geometry.addAttribute( 'color', new Float32BufferAttribute( colors, 3 ) );
  
		var material = new LineBasicMaterial( { vertexColors: VertexColors } );
  
		LineSegments.call( this, geometry, material );
  
	}
  
	GridHelper.prototype = Object.create( LineSegments.prototype );
	GridHelper.prototype.constructor = GridHelper;
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 * @author Mugen87 / http://github.com/Mugen87
	 * @author Hectate / http://www.github.com/Hectate
	 */
  
	function PolarGridHelper( radius, radials, circles, divisions, color1, color2 ) {
  
		radius = radius || 10;
		radials = radials || 16;
		circles = circles || 8;
		divisions = divisions || 64;
		color1 = new Color( color1 !== undefined ? color1 : 0x444444 );
		color2 = new Color( color2 !== undefined ? color2 : 0x888888 );
  
		var vertices = [];
		var colors = [];
  
		var x, z;
		var v, i, j, r, color;
  
		// create the radials
  
		for ( i = 0; i <= radials; i ++ ) {
  
			v = ( i / radials ) * ( Math.PI * 2 );
  
			x = Math.sin( v ) * radius;
			z = Math.cos( v ) * radius;
  
			vertices.push( 0, 0, 0 );
			vertices.push( x, 0, z );
  
			color = ( i & 1 ) ? color1 : color2;
  
			colors.push( color.r, color.g, color.b );
			colors.push( color.r, color.g, color.b );
  
		}
  
		// create the circles
  
		for ( i = 0; i <= circles; i ++ ) {
  
			color = ( i & 1 ) ? color1 : color2;
  
			r = radius - ( radius / circles * i );
  
			for ( j = 0; j < divisions; j ++ ) {
  
				// first vertex
  
				v = ( j / divisions ) * ( Math.PI * 2 );
  
				x = Math.sin( v ) * r;
				z = Math.cos( v ) * r;
  
				vertices.push( x, 0, z );
				colors.push( color.r, color.g, color.b );
  
				// second vertex
  
				v = ( ( j + 1 ) / divisions ) * ( Math.PI * 2 );
  
				x = Math.sin( v ) * r;
				z = Math.cos( v ) * r;
  
				vertices.push( x, 0, z );
				colors.push( color.r, color.g, color.b );
  
			}
  
		}
  
		var geometry = new BufferGeometry();
		geometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
		geometry.addAttribute( 'color', new Float32BufferAttribute( colors, 3 ) );
  
		var material = new LineBasicMaterial( { vertexColors: VertexColors } );
  
		LineSegments.call( this, geometry, material );
  
	}
  
	PolarGridHelper.prototype = Object.create( LineSegments.prototype );
	PolarGridHelper.prototype.constructor = PolarGridHelper;
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 * @author WestLangley / http://github.com/WestLangley
	 */
  
	function FaceNormalsHelper( object, size, hex, linewidth ) {
  
		// FaceNormalsHelper only supports THREE.Geometry
  
		this.object = object;
  
		this.size = ( size !== undefined ) ? size : 1;
  
		var color = ( hex !== undefined ) ? hex : 0xffff00;
  
		var width = ( linewidth !== undefined ) ? linewidth : 1;
  
		//
  
		var nNormals = 0;
  
		var objGeometry = this.object.geometry;
  
		if ( objGeometry && objGeometry.isGeometry ) {
  
			nNormals = objGeometry.faces.length;
  
		} else {
  
			console.warn( 'THREE.FaceNormalsHelper: only THREE.Geometry is supported. Use THREE.VertexNormalsHelper, instead.' );
  
		}
  
		//
  
		var geometry = new BufferGeometry();
  
		var positions = new Float32BufferAttribute( nNormals * 2 * 3, 3 );
  
		geometry.addAttribute( 'position', positions );
  
		LineSegments.call( this, geometry, new LineBasicMaterial( { color: color, linewidth: width } ) );
  
		//
  
		this.matrixAutoUpdate = false;
		this.update();
  
	}
  
	FaceNormalsHelper.prototype = Object.create( LineSegments.prototype );
	FaceNormalsHelper.prototype.constructor = FaceNormalsHelper;
  
	FaceNormalsHelper.prototype.update = ( function () {
  
		var v1 = new Vector3();
		var v2 = new Vector3();
		var normalMatrix = new Matrix3();
  
		return function update() {
  
			this.object.updateMatrixWorld( true );
  
			normalMatrix.getNormalMatrix( this.object.matrixWorld );
  
			var matrixWorld = this.object.matrixWorld;
  
			var position = this.geometry.attributes.position;
  
			//
  
			var objGeometry = this.object.geometry;
  
			var vertices = objGeometry.vertices;
  
			var faces = objGeometry.faces;
  
			var idx = 0;
  
			for ( var i = 0, l = faces.length; i < l; i ++ ) {
  
				var face = faces[ i ];
  
				var normal = face.normal;
  
				v1.copy( vertices[ face.a ] )
					.add( vertices[ face.b ] )
					.add( vertices[ face.c ] )
					.divideScalar( 3 )
					.applyMatrix4( matrixWorld );
  
				v2.copy( normal ).applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 );
  
				position.setXYZ( idx, v1.x, v1.y, v1.z );
  
				idx = idx + 1;
  
				position.setXYZ( idx, v2.x, v2.y, v2.z );
  
				idx = idx + 1;
  
			}
  
			position.needsUpdate = true;
  
		};
  
	}() );
  
	/**
	 * @author alteredq / http://alteredqualia.com/
	 * @author mrdoob / http://mrdoob.com/
	 * @author WestLangley / http://github.com/WestLangley
	 */
  
	function DirectionalLightHelper( light, size, color ) {
  
		Object3D.call( this );
  
		this.light = light;
		this.light.updateMatrixWorld();
  
		this.matrix = light.matrixWorld;
		this.matrixAutoUpdate = false;
  
		this.color = color;
  
		if ( size === undefined ) size = 1;
  
		var geometry = new BufferGeometry();
		geometry.addAttribute( 'position', new Float32BufferAttribute( [
			- size, size, 0,
			size, size, 0,
			size, - size, 0,
			- size, - size, 0,
			- size, size, 0
		], 3 ) );
  
		var material = new LineBasicMaterial( { fog: false } );
  
		this.lightPlane = new Line( geometry, material );
		this.add( this.lightPlane );
  
		geometry = new BufferGeometry();
		geometry.addAttribute( 'position', new Float32BufferAttribute( [ 0, 0, 0, 0, 0, 1 ], 3 ) );
  
		this.targetLine = new Line( geometry, material );
		this.add( this.targetLine );
  
		this.update();
  
	}
  
	DirectionalLightHelper.prototype = Object.create( Object3D.prototype );
	DirectionalLightHelper.prototype.constructor = DirectionalLightHelper;
  
	DirectionalLightHelper.prototype.dispose = function () {
  
		this.lightPlane.geometry.dispose();
		this.lightPlane.material.dispose();
		this.targetLine.geometry.dispose();
		this.targetLine.material.dispose();
  
	};
  
	DirectionalLightHelper.prototype.update = function () {
  
		var v1 = new Vector3();
		var v2 = new Vector3();
		var v3 = new Vector3();
  
		return function update() {
  
			v1.setFromMatrixPosition( this.light.matrixWorld );
			v2.setFromMatrixPosition( this.light.target.matrixWorld );
			v3.subVectors( v2, v1 );
  
			this.lightPlane.lookAt( v3 );
  
			if ( this.color !== undefined ) {
  
				this.lightPlane.material.color.set( this.color );
				this.targetLine.material.color.set( this.color );
  
			} else {
  
				this.lightPlane.material.color.copy( this.light.color );
				this.targetLine.material.color.copy( this.light.color );
  
			}
  
			this.targetLine.lookAt( v3 );
			this.targetLine.scale.z = v3.length();
  
		};
  
	}();
  
	/**
	 * @author alteredq / http://alteredqualia.com/
	 * @author Mugen87 / https://github.com/Mugen87
	 *
	 *	- shows frustum, line of sight and up of the camera
	 *	- suitable for fast updates
	 * 	- based on frustum visualization in lightgl.js shadowmap example
	 *		http://evanw.github.com/lightgl.js/tests/shadowmap.html
	 */
  
	function CameraHelper( camera ) {
  
		var geometry = new BufferGeometry();
		var material = new LineBasicMaterial( { color: 0xffffff, vertexColors: FaceColors } );
  
		var vertices = [];
		var colors = [];
  
		var pointMap = {};
  
		// colors
  
		var colorFrustum = new Color( 0xffaa00 );
		var colorCone = new Color( 0xff0000 );
		var colorUp = new Color( 0x00aaff );
		var colorTarget = new Color( 0xffffff );
		var colorCross = new Color( 0x333333 );
  
		// near
  
		addLine( 'n1', 'n2', colorFrustum );
		addLine( 'n2', 'n4', colorFrustum );
		addLine( 'n4', 'n3', colorFrustum );
		addLine( 'n3', 'n1', colorFrustum );
  
		// far
  
		addLine( 'f1', 'f2', colorFrustum );
		addLine( 'f2', 'f4', colorFrustum );
		addLine( 'f4', 'f3', colorFrustum );
		addLine( 'f3', 'f1', colorFrustum );
  
		// sides
  
		addLine( 'n1', 'f1', colorFrustum );
		addLine( 'n2', 'f2', colorFrustum );
		addLine( 'n3', 'f3', colorFrustum );
		addLine( 'n4', 'f4', colorFrustum );
  
		// cone
  
		addLine( 'p', 'n1', colorCone );
		addLine( 'p', 'n2', colorCone );
		addLine( 'p', 'n3', colorCone );
		addLine( 'p', 'n4', colorCone );
  
		// up
  
		addLine( 'u1', 'u2', colorUp );
		addLine( 'u2', 'u3', colorUp );
		addLine( 'u3', 'u1', colorUp );
  
		// target
  
		addLine( 'c', 't', colorTarget );
		addLine( 'p', 'c', colorCross );
  
		// cross
  
		addLine( 'cn1', 'cn2', colorCross );
		addLine( 'cn3', 'cn4', colorCross );
  
		addLine( 'cf1', 'cf2', colorCross );
		addLine( 'cf3', 'cf4', colorCross );
  
		function addLine( a, b, color ) {
  
			addPoint( a, color );
			addPoint( b, color );
  
		}
  
		function addPoint( id, color ) {
  
			vertices.push( 0, 0, 0 );
			colors.push( color.r, color.g, color.b );
  
			if ( pointMap[ id ] === undefined ) {
  
				pointMap[ id ] = [];
  
			}
  
			pointMap[ id ].push( ( vertices.length / 3 ) - 1 );
  
		}
  
		geometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
		geometry.addAttribute( 'color', new Float32BufferAttribute( colors, 3 ) );
  
		LineSegments.call( this, geometry, material );
  
		this.camera = camera;
		if ( this.camera.updateProjectionMatrix ) this.camera.updateProjectionMatrix();
  
		this.matrix = camera.matrixWorld;
		this.matrixAutoUpdate = false;
  
		this.pointMap = pointMap;
  
		this.update();
  
	}
  
	CameraHelper.prototype = Object.create( LineSegments.prototype );
	CameraHelper.prototype.constructor = CameraHelper;
  
	CameraHelper.prototype.update = function () {
  
		var geometry, pointMap;
  
		var vector = new Vector3();
		var camera = new Camera();
  
		function setPoint( point, x, y, z ) {
  
			vector.set( x, y, z ).unproject( camera );
  
			var points = pointMap[ point ];
  
			if ( points !== undefined ) {
  
				var position = geometry.getAttribute( 'position' );
  
				for ( var i = 0, l = points.length; i < l; i ++ ) {
  
					position.setXYZ( points[ i ], vector.x, vector.y, vector.z );
  
				}
  
			}
  
		}
  
		return function update() {
  
			geometry = this.geometry;
			pointMap = this.pointMap;
  
			var w = 1, h = 1;
  
			// we need just camera projection matrix
			// world matrix must be identity
  
			camera.projectionMatrix.copy( this.camera.projectionMatrix );
  
			// center / target
  
			setPoint( 'c', 0, 0, - 1 );
			setPoint( 't', 0, 0, 1 );
  
			// near
  
			setPoint( 'n1', - w, - h, - 1 );
			setPoint( 'n2', w, - h, - 1 );
			setPoint( 'n3', - w, h, - 1 );
			setPoint( 'n4', w, h, - 1 );
  
			// far
  
			setPoint( 'f1', - w, - h, 1 );
			setPoint( 'f2', w, - h, 1 );
			setPoint( 'f3', - w, h, 1 );
			setPoint( 'f4', w, h, 1 );
  
			// up
  
			setPoint( 'u1', w * 0.7, h * 1.1, - 1 );
			setPoint( 'u2', - w * 0.7, h * 1.1, - 1 );
			setPoint( 'u3', 0, h * 2, - 1 );
  
			// cross
  
			setPoint( 'cf1', - w, 0, 1 );
			setPoint( 'cf2', w, 0, 1 );
			setPoint( 'cf3', 0, - h, 1 );
			setPoint( 'cf4', 0, h, 1 );
  
			setPoint( 'cn1', - w, 0, - 1 );
			setPoint( 'cn2', w, 0, - 1 );
			setPoint( 'cn3', 0, - h, - 1 );
			setPoint( 'cn4', 0, h, - 1 );
  
			geometry.getAttribute( 'position' ).needsUpdate = true;
  
		};
  
	}();
  
	/**
	 * @author mrdoob / http://mrdoob.com/
	 * @author Mugen87 / http://github.com/Mugen87
	 */
  
	function BoxHelper( object, color ) {
  
		this.object = object;
  
		if ( color === undefined ) color = 0xffff00;
  
		var indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] );
		var positions = new Float32Array( 8 * 3 );
  
		var geometry = new BufferGeometry();
		geometry.setIndex( new BufferAttribute( indices, 1 ) );
		geometry.addAttribute( 'position', new BufferAttribute( positions, 3 ) );
  
		LineSegments.call( this, geometry, new LineBasicMaterial( { color: color } ) );
  
		this.matrixAutoUpdate = false;
  
		this.update();
  
	}
  
	BoxHelper.prototype = Object.create( LineSegments.prototype );
	BoxHelper.prototype.constructor = BoxHelper;
  
	BoxHelper.prototype.update = ( function () {
  
		var box = new Box3();
  
		return function update( object ) {
  
			if ( object !== undefined ) {
  
				console.warn( 'THREE.BoxHelper: .update() has no longer arguments.' );
  
			}
  
			if ( this.object !== undefined ) {
  
				box.setFromObject( this.object );
  
			}
  
			if ( box.isEmpty() ) return;
  
			var min = box.min;
			var max = box.max;
  
			/*
			  5____4
			1/___0/|
			| 6__|_7
			2/___3/
  
			0: max.x, max.y, max.z
			1: min.x, max.y, max.z
			2: min.x, min.y, max.z
			3: max.x, min.y, max.z
			4: max.x, max.y, min.z
			5: min.x, max.y, min.z
			6: min.x, min.y, min.z
			7: max.x, min.y, min.z
			*/
  
			var position = this.geometry.attributes.position;
			var array = position.array;
  
			array[ 0 ] = max.x; array[ 1 ] = max.y; array[ 2 ] = max.z;
			array[ 3 ] = min.x; array[ 4 ] = max.y; array[ 5 ] = max.z;
			array[ 6 ] = min.x; array[ 7 ] = min.y; array[ 8 ] = max.z;
			array[ 9 ] = max.x; array[ 10 ] = min.y; array[ 11 ] = max.z;
			array[ 12 ] = max.x; array[ 13 ] = max.y; array[ 14 ] = min.z;
			array[ 15 ] = min.x; array[ 16 ] = max.y; array[ 17 ] = min.z;
			array[ 18 ] = min.x; array[ 19 ] = min.y; array[ 20 ] = min.z;
			array[ 21 ] = max.x; array[ 22 ] = min.y; array[ 23 ] = min.z;
  
			position.needsUpdate = true;
  
			this.geometry.computeBoundingSphere();
  
		};
  
	} )();
  
	BoxHelper.prototype.setFromObject = function ( object ) {
  
		this.object = object;
		this.update();
  
		return this;
  
	};
  
	/**
	 * @author WestLangley / http://github.com/WestLangley
	 */
  
	function Box3Helper( box, hex ) {
  
		this.type = 'Box3Helper';
  
		this.box = box;
  
		var color = ( hex !== undefined ) ? hex : 0xffff00;
  
		var indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] );
  
		var positions = [ 1, 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, - 1, 1, 1, 1, - 1, - 1, 1, - 1, - 1, - 1, - 1, 1, - 1, - 1 ];
  
		var geometry = new BufferGeometry();
  
		geometry.setIndex( new BufferAttribute( indices, 1 ) );
  
		geometry.addAttribute( 'position', new Float32BufferAttribute( positions, 3 ) );
  
		LineSegments.call( this, geometry, new LineBasicMaterial( { color: color } ) );
  
		this.geometry.computeBoundingSphere();
  
	}
  
	Box3Helper.prototype = Object.create( LineSegments.prototype );
	Box3Helper.prototype.constructor = Box3Helper;
  
	Box3Helper.prototype.updateMatrixWorld = function ( force ) {
  
		var box = this.box;
  
		if ( box.isEmpty() ) return;
  
		box.getCenter( this.position );
  
		box.getSize( this.scale );
  
		this.scale.multiplyScalar( 0.5 );
  
		Object3D.prototype.updateMatrixWorld.call( this, force );
  
	};
  
	/**
	 * @author WestLangley / http://github.com/WestLangley
	 */
  
	function PlaneHelper( plane, size, hex ) {
  
		this.type = 'PlaneHelper';
  
		this.plane = plane;
  
		this.size = ( size === undefined ) ? 1 : size;
  
		var color = ( hex !== undefined ) ? hex : 0xffff00;
  
		var positions = [ 1, - 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, - 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0 ];
  
		var geometry = new BufferGeometry();
		geometry.addAttribute( 'position', new Float32BufferAttribute( positions, 3 ) );
		geometry.computeBoundingSphere();
  
		Line.call( this, geometry, new LineBasicMaterial( { color: color } ) );
  
		//
  
		var positions2 = [ 1, 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, 1, 1, - 1, - 1, 1, 1, - 1, 1 ];
  
		var geometry2 = new BufferGeometry();
		geometry2.addAttribute( 'position', new Float32BufferAttribute( positions2, 3 ) );
		geometry2.computeBoundingSphere();
  
		this.add( new Mesh( geometry2, new MeshBasicMaterial( { color: color, opacity: 0.2, transparent: true, depthWrite: false } ) ) );
  
	}
  
	PlaneHelper.prototype = Object.create( Line.prototype );
	PlaneHelper.prototype.constructor = PlaneHelper;
  
	PlaneHelper.prototype.updateMatrixWorld = function ( force ) {
  
		var scale = - this.plane.constant;
  
		if ( Math.abs( scale ) < 1e-8 ) scale = 1e-8; // sign does not matter
  
		this.scale.set( 0.5 * this.size, 0.5 * this.size, scale );
  
		this.children[ 0 ].material.side = ( scale < 0 ) ? BackSide : FrontSide; // renderer flips side when determinant < 0; flipping not wanted here
  
		this.lookAt( this.plane.normal );
  
		Object3D.prototype.updateMatrixWorld.call( this, force );
  
	};
  
	/**
	 * @author WestLangley / http://github.com/WestLangley
	 * @author zz85 / http://github.com/zz85
	 * @author bhouston / http://clara.io
	 *
	 * Creates an arrow for visualizing directions
	 *
	 * Parameters:
	 *  dir - Vector3
	 *  origin - Vector3
	 *  length - Number
	 *  color - color in hex value
	 *  headLength - Number
	 *  headWidth - Number
	 */
  
	var lineGeometry, coneGeometry;
  
	function ArrowHelper( dir, origin, length, color, headLength, headWidth ) {
  
		// dir is assumed to be normalized
  
		Object3D.call( this );
  
		if ( color === undefined ) color = 0xffff00;
		if ( length === undefined ) length = 1;
		if ( headLength === undefined ) headLength = 0.2 * length;
		if ( headWidth === undefined ) headWidth = 0.2 * headLength;
  
		if ( lineGeometry === undefined ) {
  
			lineGeometry = new BufferGeometry();
			lineGeometry.addAttribute( 'position', new Float32BufferAttribute( [ 0, 0, 0, 0, 1, 0 ], 3 ) );
  
			coneGeometry = new CylinderBufferGeometry( 0, 0.5, 1, 5, 1 );
			coneGeometry.translate( 0, - 0.5, 0 );
  
		}
  
		this.position.copy( origin );
  
		this.line = new Line( lineGeometry, new LineBasicMaterial( { color: color } ) );
		this.line.matrixAutoUpdate = false;
		this.add( this.line );
  
		this.cone = new Mesh( coneGeometry, new MeshBasicMaterial( { color: color } ) );
		this.cone.matrixAutoUpdate = false;
		this.add( this.cone );
  
		this.setDirection( dir );
		this.setLength( length, headLength, headWidth );
  
	}
  
	ArrowHelper.prototype = Object.create( Object3D.prototype );
	ArrowHelper.prototype.constructor = ArrowHelper;
  
	ArrowHelper.prototype.setDirection = ( function () {
  
		var axis = new Vector3();
		var radians;
  
		return function setDirection( dir ) {
  
			// dir is assumed to be normalized
  
			if ( dir.y > 0.99999 ) {
  
				this.quaternion.set( 0, 0, 0, 1 );
  
			} else if ( dir.y < - 0.99999 ) {
  
				this.quaternion.set( 1, 0, 0, 0 );
  
			} else {
  
				axis.set( dir.z, 0, - dir.x ).normalize();
  
				radians = Math.acos( dir.y );
  
				this.quaternion.setFromAxisAngle( axis, radians );
  
			}
  
		};
  
	}() );
  
	ArrowHelper.prototype.setLength = function ( length, headLength, headWidth ) {
  
		if ( headLength === undefined ) headLength = 0.2 * length;
		if ( headWidth === undefined ) headWidth = 0.2 * headLength;
  
		this.line.scale.set( 1, Math.max( 0, length - headLength ), 1 );
		this.line.updateMatrix();
  
		this.cone.scale.set( headWidth, headLength, headWidth );
		this.cone.position.y = length;
		this.cone.updateMatrix();
  
	};
  
	ArrowHelper.prototype.setColor = function ( color ) {
  
		this.line.material.color.copy( color );
		this.cone.material.color.copy( color );
  
	};
  
	/**
	 * @author sroucheray / http://sroucheray.org/
	 * @author mrdoob / http://mrdoob.com/
	 */
  
	function AxesHelper( size ) {
  
		size = size || 1;
  
		var vertices = [
			0, 0, 0,	size, 0, 0,
			0, 0, 0,	0, size, 0,
			0, 0, 0,	0, 0, size
		];
  
		var colors = [
			1, 0, 0,	1, 0.6, 0,
			0, 1, 0,	0.6, 1, 0,
			0, 0, 1,	0, 0.6, 1
		];
  
		var geometry = new BufferGeometry();
		geometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
		geometry.addAttribute( 'color', new Float32BufferAttribute( colors, 3 ) );
  
		var material = new LineBasicMaterial( { vertexColors: VertexColors } );
  
		LineSegments.call( this, geometry, material );
  
	}
  
	AxesHelper.prototype = Object.create( LineSegments.prototype );
	AxesHelper.prototype.constructor = AxesHelper;
  
	//
  
	Curve.create = function ( construct, getPoint ) {
  
		console.log( 'THREE.Curve.create() has been deprecated' );
  
		construct.prototype = Object.create( Curve.prototype );
		construct.prototype.constructor = construct;
		construct.prototype.getPoint = getPoint;
  
		return construct;
  
	};
  
	//
  
	Object.assign( CurvePath.prototype, {
  
		createPointsGeometry: function ( divisions ) {
  
			console.warn( 'THREE.CurvePath: .createPointsGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead.' );
  
			// generate geometry from path points (for Line or Points objects)
  
			var pts = this.getPoints( divisions );
			return this.createGeometry( pts );
  
		},
  
		createSpacedPointsGeometry: function ( divisions ) {
  
			console.warn( 'THREE.CurvePath: .createSpacedPointsGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead.' );
  
			// generate geometry from equidistant sampling along the path
  
			var pts = this.getSpacedPoints( divisions );
			return this.createGeometry( pts );
  
		},
  
		createGeometry: function ( points ) {
  
			console.warn( 'THREE.CurvePath: .createGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead.' );
  
			var geometry = new Geometry();
  
			for ( var i = 0, l = points.length; i < l; i ++ ) {
  
				var point = points[ i ];
				geometry.vertices.push( new Vector3( point.x, point.y, point.z || 0 ) );
  
			}
  
			return geometry;
  
		}
  
	} );
  
	//
  
	Object.assign( Path.prototype, {
  
		fromPoints: function ( points ) {
  
			console.warn( 'THREE.Path: .fromPoints() has been renamed to .setFromPoints().' );
			this.setFromPoints( points );
  
		}
  
	} );
  
	//
  
	function Spline( points ) {
  
		console.warn( 'THREE.Spline has been removed. Use THREE.CatmullRomCurve3 instead.' );
  
		CatmullRomCurve3.call( this, points );
		this.type = 'catmullrom';
  
	}
  
	Spline.prototype = Object.create( CatmullRomCurve3.prototype );
  
	Object.assign( Spline.prototype, {
  
		initFromArray: function ( /* a */ ) {
  
			console.error( 'THREE.Spline: .initFromArray() has been removed.' );
  
		},
		getControlPointsArray: function ( /* optionalTarget */ ) {
  
			console.error( 'THREE.Spline: .getControlPointsArray() has been removed.' );
  
		},
		reparametrizeByArcLength: function ( /* samplingCoef */ ) {
  
			console.error( 'THREE.Spline: .reparametrizeByArcLength() has been removed.' );
  
		}
  
	} );
  
	GridHelper.prototype.setColors = function () {
  
		console.error( 'THREE.GridHelper: setColors() has been deprecated, pass them in the constructor instead.' );
  
	};
  
	SkeletonHelper.prototype.update = function () {
  
		console.error( 'THREE.SkeletonHelper: update() no longer needs to be called.' );
  
	};
  
	//
  
	Object.assign( Loader.prototype, {
  
		extractUrlBase: function ( url ) {
  
			console.warn( 'THREE.Loader: .extractUrlBase() has been deprecated. Use THREE.LoaderUtils.extractUrlBase() instead.' );
			return LoaderUtils.extractUrlBase( url );
  
		}
  
	} );
  
	//
  
	Object.assign( Box2.prototype, {
  
		center: function ( optionalTarget ) {
  
			console.warn( 'THREE.Box2: .center() has been renamed to .getCenter().' );
			return this.getCenter( optionalTarget );
  
		},
		empty: function () {
  
			console.warn( 'THREE.Box2: .empty() has been renamed to .isEmpty().' );
			return this.isEmpty();
  
		},
		isIntersectionBox: function ( box ) {
  
			console.warn( 'THREE.Box2: .isIntersectionBox() has been renamed to .intersectsBox().' );
			return this.intersectsBox( box );
  
		},
		size: function ( optionalTarget ) {
  
			console.warn( 'THREE.Box2: .size() has been renamed to .getSize().' );
			return this.getSize( optionalTarget );
  
		}
	} );
  
	Object.assign( Box3.prototype, {
  
		center: function ( optionalTarget ) {
  
			console.warn( 'THREE.Box3: .center() has been renamed to .getCenter().' );
			return this.getCenter( optionalTarget );
  
		},
		empty: function () {
  
			console.warn( 'THREE.Box3: .empty() has been renamed to .isEmpty().' );
			return this.isEmpty();
  
		},
		isIntersectionBox: function ( box ) {
  
			console.warn( 'THREE.Box3: .isIntersectionBox() has been renamed to .intersectsBox().' );
			return this.intersectsBox( box );
  
		},
		isIntersectionSphere: function ( sphere ) {
  
			console.warn( 'THREE.Box3: .isIntersectionSphere() has been renamed to .intersectsSphere().' );
			return this.intersectsSphere( sphere );
  
		},
		size: function ( optionalTarget ) {
  
			console.warn( 'THREE.Box3: .size() has been renamed to .getSize().' );
			return this.getSize( optionalTarget );
  
		}
	} );
  
	Line3.prototype.center = function ( optionalTarget ) {
  
		console.warn( 'THREE.Line3: .center() has been renamed to .getCenter().' );
		return this.getCenter( optionalTarget );
  
	};
  
	Object.assign( _Math, {
  
		random16: function () {
  
			console.warn( 'THREE.Math: .random16() has been deprecated. Use Math.random() instead.' );
			return Math.random();
  
		},
  
		nearestPowerOfTwo: function ( value ) {
  
			console.warn( 'THREE.Math: .nearestPowerOfTwo() has been renamed to .floorPowerOfTwo().' );
			return _Math.floorPowerOfTwo( value );
  
		},
  
		nextPowerOfTwo: function ( value ) {
  
			console.warn( 'THREE.Math: .nextPowerOfTwo() has been renamed to .ceilPowerOfTwo().' );
			return _Math.ceilPowerOfTwo( value );
  
		}
  
	} );
  
	Object.assign( Matrix3.prototype, {
  
		flattenToArrayOffset: function ( array, offset ) {
  
			console.warn( "THREE.Matrix3: .flattenToArrayOffset() has been deprecated. Use .toArray() instead." );
			return this.toArray( array, offset );
  
		},
		multiplyVector3: function ( vector ) {
  
			console.warn( 'THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.' );
			return vector.applyMatrix3( this );
  
		},
		multiplyVector3Array: function ( /* a */ ) {
  
			console.error( 'THREE.Matrix3: .multiplyVector3Array() has been removed.' );
  
		},
		applyToBuffer: function ( buffer /*, offset, length */ ) {
  
			console.warn( 'THREE.Matrix3: .applyToBuffer() has been removed. Use matrix.applyToBufferAttribute( attribute ) instead.' );
			return this.applyToBufferAttribute( buffer );
  
		},
		applyToVector3Array: function ( /* array, offset, length */ ) {
  
			console.error( 'THREE.Matrix3: .applyToVector3Array() has been removed.' );
  
		}
  
	} );
  
	Object.assign( Matrix4.prototype, {
  
		extractPosition: function ( m ) {
  
			console.warn( 'THREE.Matrix4: .extractPosition() has been renamed to .copyPosition().' );
			return this.copyPosition( m );
  
		},
		flattenToArrayOffset: function ( array, offset ) {
  
			console.warn( "THREE.Matrix4: .flattenToArrayOffset() has been deprecated. Use .toArray() instead." );
			return this.toArray( array, offset );
  
		},
		getPosition: function () {
  
			var v1;
  
			return function getPosition() {
  
				if ( v1 === undefined ) v1 = new Vector3();
				console.warn( 'THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.' );
				return v1.setFromMatrixColumn( this, 3 );
  
			};
  
		}(),
		setRotationFromQuaternion: function ( q ) {
  
			console.warn( 'THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion().' );
			return this.makeRotationFromQuaternion( q );
  
		},
		multiplyToArray: function () {
  
			console.warn( 'THREE.Matrix4: .multiplyToArray() has been removed.' );
  
		},
		multiplyVector3: function ( vector ) {
  
			console.warn( 'THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) instead.' );
			return vector.applyMatrix4( this );
  
		},
		multiplyVector4: function ( vector ) {
  
			console.warn( 'THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.' );
			return vector.applyMatrix4( this );
  
		},
		multiplyVector3Array: function ( /* a */ ) {
  
			console.error( 'THREE.Matrix4: .multiplyVector3Array() has been removed.' );
  
		},
		rotateAxis: function ( v ) {
  
			console.warn( 'THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.' );
			v.transformDirection( this );
  
		},
		crossVector: function ( vector ) {
  
			console.warn( 'THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.' );
			return vector.applyMatrix4( this );
  
		},
		translate: function () {
  
			console.error( 'THREE.Matrix4: .translate() has been removed.' );
  
		},
		rotateX: function () {
  
			console.error( 'THREE.Matrix4: .rotateX() has been removed.' );
  
		},
		rotateY: function () {
  
			console.error( 'THREE.Matrix4: .rotateY() has been removed.' );
  
		},
		rotateZ: function () {
  
			console.error( 'THREE.Matrix4: .rotateZ() has been removed.' );
  
		},
		rotateByAxis: function () {
  
			console.error( 'THREE.Matrix4: .rotateByAxis() has been removed.' );
  
		},
		applyToBuffer: function ( buffer /*, offset, length */ ) {
  
			console.warn( 'THREE.Matrix4: .applyToBuffer() has been removed. Use matrix.applyToBufferAttribute( attribute ) instead.' );
			return this.applyToBufferAttribute( buffer );
  
		},
		applyToVector3Array: function ( /* array, offset, length */ ) {
  
			console.error( 'THREE.Matrix4: .applyToVector3Array() has been removed.' );
  
		},
		makeFrustum: function ( left, right, bottom, top, near, far ) {
  
			console.warn( 'THREE.Matrix4: .makeFrustum() has been removed. Use .makePerspective( left, right, top, bottom, near, far ) instead.' );
			return this.makePerspective( left, right, top, bottom, near, far );
  
		}
  
	} );
  
	Plane.prototype.isIntersectionLine = function ( line ) {
  
		console.warn( 'THREE.Plane: .isIntersectionLine() has been renamed to .intersectsLine().' );
		return this.intersectsLine( line );
  
	};
  
	Quaternion.prototype.multiplyVector3 = function ( vector ) {
  
		console.warn( 'THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' );
		return vector.applyQuaternion( this );
  
	};
  
	Object.assign( Ray.prototype, {
  
		isIntersectionBox: function ( box ) {
  
			console.warn( 'THREE.Ray: .isIntersectionBox() has been renamed to .intersectsBox().' );
			return this.intersectsBox( box );
  
		},
		isIntersectionPlane: function ( plane ) {
  
			console.warn( 'THREE.Ray: .isIntersectionPlane() has been renamed to .intersectsPlane().' );
			return this.intersectsPlane( plane );
  
		},
		isIntersectionSphere: function ( sphere ) {
  
			console.warn( 'THREE.Ray: .isIntersectionSphere() has been renamed to .intersectsSphere().' );
			return this.intersectsSphere( sphere );
  
		}
  
	} );
  
	Object.assign( Triangle.prototype, {
  
		area: function () {
  
			console.warn( 'THREE.Triangle: .area() has been renamed to .getArea().' );
			return this.getArea();
  
		},
		barycoordFromPoint: function ( point, target ) {
  
			console.warn( 'THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord().' );
			return this.getBarycoord( point, target );
  
		},
		midpoint: function ( target ) {
  
			console.warn( 'THREE.Triangle: .midpoint() has been renamed to .getMidpoint().' );
			return this.getMidpoint( target );
  
		},
		normal: function ( target ) {
  
			console.warn( 'THREE.Triangle: .normal() has been renamed to .getNormal().' );
			return this.getNormal( target );
  
		},
		plane: function ( target ) {
  
			console.warn( 'THREE.Triangle: .plane() has been renamed to .getPlane().' );
			return this.getPlane( target );
  
		}
  
	} );
  
	Object.assign( Triangle, {
  
		barycoordFromPoint: function ( point, a, b, c, target ) {
  
			console.warn( 'THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord().' );
			return Triangle.getBarycoord( point, a, b, c, target );
  
		},
		normal: function ( a, b, c, target ) {
  
			console.warn( 'THREE.Triangle: .normal() has been renamed to .getNormal().' );
			return Triangle.getNormal( a, b, c, target );
  
		}
  
	} );
  
	Object.assign( Shape.prototype, {
  
		extractAllPoints: function ( divisions ) {
  
			console.warn( 'THREE.Shape: .extractAllPoints() has been removed. Use .extractPoints() instead.' );
			return this.extractPoints( divisions );
  
		},
		extrude: function ( options ) {
  
			console.warn( 'THREE.Shape: .extrude() has been removed. Use ExtrudeGeometry() instead.' );
			return new ExtrudeGeometry( this, options );
  
		},
		makeGeometry: function ( options ) {
  
			console.warn( 'THREE.Shape: .makeGeometry() has been removed. Use ShapeGeometry() instead.' );
			return new ShapeGeometry( this, options );
  
		}
  
	} );
  
	Object.assign( Vector2.prototype, {
  
		fromAttribute: function ( attribute, index, offset ) {
  
			console.warn( 'THREE.Vector2: .fromAttribute() has been renamed to .fromBufferAttribute().' );
			return this.fromBufferAttribute( attribute, index, offset );
  
		},
		distanceToManhattan: function ( v ) {
  
			console.warn( 'THREE.Vector2: .distanceToManhattan() has been renamed to .manhattanDistanceTo().' );
			return this.manhattanDistanceTo( v );
  
		},
		lengthManhattan: function () {
  
			console.warn( 'THREE.Vector2: .lengthManhattan() has been renamed to .manhattanLength().' );
			return this.manhattanLength();
  
		}
  
	} );
  
	Object.assign( Vector3.prototype, {
  
		setEulerFromRotationMatrix: function () {
  
			console.error( 'THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.' );
  
		},
		setEulerFromQuaternion: function () {
  
			console.error( 'THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.' );
  
		},
		getPositionFromMatrix: function ( m ) {
  
			console.warn( 'THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().' );
			return this.setFromMatrixPosition( m );
  
		},
		getScaleFromMatrix: function ( m ) {
  
			console.warn( 'THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().' );
			return this.setFromMatrixScale( m );
  
		},
		getColumnFromMatrix: function ( index, matrix ) {
  
			console.warn( 'THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().' );
			return this.setFromMatrixColumn( matrix, index );
  
		},
		applyProjection: function ( m ) {
  
			console.warn( 'THREE.Vector3: .applyProjection() has been removed. Use .applyMatrix4( m ) instead.' );
			return this.applyMatrix4( m );
  
		},
		fromAttribute: function ( attribute, index, offset ) {
  
			console.warn( 'THREE.Vector3: .fromAttribute() has been renamed to .fromBufferAttribute().' );
			return this.fromBufferAttribute( attribute, index, offset );
  
		},
		distanceToManhattan: function ( v ) {
  
			console.warn( 'THREE.Vector3: .distanceToManhattan() has been renamed to .manhattanDistanceTo().' );
			return this.manhattanDistanceTo( v );
  
		},
		lengthManhattan: function () {
  
			console.warn( 'THREE.Vector3: .lengthManhattan() has been renamed to .manhattanLength().' );
			return this.manhattanLength();
  
		}
  
	} );
  
	Object.assign( Vector4.prototype, {
  
		fromAttribute: function ( attribute, index, offset ) {
  
			console.warn( 'THREE.Vector4: .fromAttribute() has been renamed to .fromBufferAttribute().' );
			return this.fromBufferAttribute( attribute, index, offset );
  
		},
		lengthManhattan: function () {
  
			console.warn( 'THREE.Vector4: .lengthManhattan() has been renamed to .manhattanLength().' );
			return this.manhattanLength();
  
		}
  
	} );
  
	//
  
	Object.assign( Geometry.prototype, {
  
		computeTangents: function () {
  
			console.error( 'THREE.Geometry: .computeTangents() has been removed.' );
  
		},
		computeLineDistances: function () {
  
			console.error( 'THREE.Geometry: .computeLineDistances() has been removed. Use THREE.Line.computeLineDistances() instead.' );
  
		}
  
	} );
  
	Object.assign( Object3D.prototype, {
  
		getChildByName: function ( name ) {
  
			console.warn( 'THREE.Object3D: .getChildByName() has been renamed to .getObjectByName().' );
			return this.getObjectByName( name );
  
		},
		renderDepth: function () {
  
			console.warn( 'THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead.' );
  
		},
		translate: function ( distance, axis ) {
  
			console.warn( 'THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead.' );
			return this.translateOnAxis( axis, distance );
  
		},
		getWorldRotation: function () {
  
			console.error( 'THREE.Object3D: .getWorldRotation() has been removed. Use THREE.Object3D.getWorldQuaternion( target ) instead.' );
  
		}
  
	} );
  
	Object.defineProperties( Object3D.prototype, {
  
		eulerOrder: {
			get: function () {
  
				console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' );
				return this.rotation.order;
  
			},
			set: function ( value ) {
  
				console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' );
				this.rotation.order = value;
  
			}
		},
		useQuaternion: {
			get: function () {
  
				console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' );
  
			},
			set: function () {
  
				console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' );
  
			}
		}
  
	} );
  
	Object.defineProperties( LOD.prototype, {
  
		objects: {
			get: function () {
  
				console.warn( 'THREE.LOD: .objects has been renamed to .levels.' );
				return this.levels;
  
			}
		}
  
	} );
  
	Object.defineProperty( Skeleton.prototype, 'useVertexTexture', {
  
		get: function () {
  
			console.warn( 'THREE.Skeleton: useVertexTexture has been removed.' );
  
		},
		set: function () {
  
			console.warn( 'THREE.Skeleton: useVertexTexture has been removed.' );
  
		}
  
	} );
  
	Object.defineProperty( Curve.prototype, '__arcLengthDivisions', {
  
		get: function () {
  
			console.warn( 'THREE.Curve: .__arcLengthDivisions is now .arcLengthDivisions.' );
			return this.arcLengthDivisions;
  
		},
		set: function ( value ) {
  
			console.warn( 'THREE.Curve: .__arcLengthDivisions is now .arcLengthDivisions.' );
			this.arcLengthDivisions = value;
  
		}
  
	} );
  
	//
  
	PerspectiveCamera.prototype.setLens = function ( focalLength, filmGauge ) {
  
		console.warn( "THREE.PerspectiveCamera.setLens is deprecated. " +
				"Use .setFocalLength and .filmGauge for a photographic setup." );
  
		if ( filmGauge !== undefined ) this.filmGauge = filmGauge;
		this.setFocalLength( focalLength );
  
	};
  
	//
  
	Object.defineProperties( Light.prototype, {
		onlyShadow: {
			set: function () {
  
				console.warn( 'THREE.Light: .onlyShadow has been removed.' );
  
			}
		},
		shadowCameraFov: {
			set: function ( value ) {
  
				console.warn( 'THREE.Light: .shadowCameraFov is now .shadow.camera.fov.' );
				this.shadow.camera.fov = value;
  
			}
		},
		shadowCameraLeft: {
			set: function ( value ) {
  
				console.warn( 'THREE.Light: .shadowCameraLeft is now .shadow.camera.left.' );
				this.shadow.camera.left = value;
  
			}
		},
		shadowCameraRight: {
			set: function ( value ) {
  
				console.warn( 'THREE.Light: .shadowCameraRight is now .shadow.camera.right.' );
				this.shadow.camera.right = value;
  
			}
		},
		shadowCameraTop: {
			set: function ( value ) {
  
				console.warn( 'THREE.Light: .shadowCameraTop is now .shadow.camera.top.' );
				this.shadow.camera.top = value;
  
			}
		},
		shadowCameraBottom: {
			set: function ( value ) {
  
				console.warn( 'THREE.Light: .shadowCameraBottom is now .shadow.camera.bottom.' );
				this.shadow.camera.bottom = value;
  
			}
		},
		shadowCameraNear: {
			set: function ( value ) {
  
				console.warn( 'THREE.Light: .shadowCameraNear is now .shadow.camera.near.' );
				this.shadow.camera.near = value;
  
			}
		},
		shadowCameraFar: {
			set: function ( value ) {
  
				console.warn( 'THREE.Light: .shadowCameraFar is now .shadow.camera.far.' );
				this.shadow.camera.far = value;
  
			}
		},
		shadowCameraVisible: {
			set: function () {
  
				console.warn( 'THREE.Light: .shadowCameraVisible has been removed. Use new THREE.CameraHelper( light.shadow.camera ) instead.' );
  
			}
		},
		shadowBias: {
			set: function ( value ) {
  
				console.warn( 'THREE.Light: .shadowBias is now .shadow.bias.' );
				this.shadow.bias = value;
  
			}
		},
		shadowDarkness: {
			set: function () {
  
				console.warn( 'THREE.Light: .shadowDarkness has been removed.' );
  
			}
		},
		shadowMapWidth: {
			set: function ( value ) {
  
				console.warn( 'THREE.Light: .shadowMapWidth is now .shadow.mapSize.width.' );
				this.shadow.mapSize.width = value;
  
			}
		},
		shadowMapHeight: {
			set: function ( value ) {
  
				console.warn( 'THREE.Light: .shadowMapHeight is now .shadow.mapSize.height.' );
				this.shadow.mapSize.height = value;
  
			}
		}
	} );
  
	//
  
	Object.defineProperties( BufferAttribute.prototype, {
  
		length: {
			get: function () {
  
				console.warn( 'THREE.BufferAttribute: .length has been deprecated. Use .count instead.' );
				return this.array.length;
  
			}
		},
		copyIndicesArray: function ( /* indices */ ) {
  
			console.error( 'THREE.BufferAttribute: .copyIndicesArray() has been removed.' );
  
		}
  
	} );
  
	Object.assign( BufferGeometry.prototype, {
  
		addIndex: function ( index ) {
  
			console.warn( 'THREE.BufferGeometry: .addIndex() has been renamed to .setIndex().' );
			this.setIndex( index );
  
		},
		addDrawCall: function ( start, count, indexOffset ) {
  
			if ( indexOffset !== undefined ) {
  
				console.warn( 'THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset.' );
  
			}
			console.warn( 'THREE.BufferGeometry: .addDrawCall() is now .addGroup().' );
			this.addGroup( start, count );
  
		},
		clearDrawCalls: function () {
  
			console.warn( 'THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups().' );
			this.clearGroups();
  
		},
		computeTangents: function () {
  
			console.warn( 'THREE.BufferGeometry: .computeTangents() has been removed.' );
  
		},
		computeOffsets: function () {
  
			console.warn( 'THREE.BufferGeometry: .computeOffsets() has been removed.' );
  
		}
  
	} );
  
	Object.defineProperties( BufferGeometry.prototype, {
  
		drawcalls: {
			get: function () {
  
				console.error( 'THREE.BufferGeometry: .drawcalls has been renamed to .groups.' );
				return this.groups;
  
			}
		},
		offsets: {
			get: function () {
  
				console.warn( 'THREE.BufferGeometry: .offsets has been renamed to .groups.' );
				return this.groups;
  
			}
		}
  
	} );
  
	//
  
	Object.assign( ExtrudeBufferGeometry.prototype, {
  
		getArrays: function () {
  
			console.error( 'THREE.ExtrudeBufferGeometry: .getArrays() has been removed.' );
  
		},
  
		addShapeList: function () {
  
			console.error( 'THREE.ExtrudeBufferGeometry: .addShapeList() has been removed.' );
  
		},
  
		addShape: function () {
  
			console.error( 'THREE.ExtrudeBufferGeometry: .addShape() has been removed.' );
  
		}
  
	} );
  
	//
  
	Object.defineProperties( Uniform.prototype, {
  
		dynamic: {
			set: function () {
  
				console.warn( 'THREE.Uniform: .dynamic has been removed. Use object.onBeforeRender() instead.' );
  
			}
		},
		onUpdate: {
			value: function () {
  
				console.warn( 'THREE.Uniform: .onUpdate() has been removed. Use object.onBeforeRender() instead.' );
				return this;
  
			}
		}
  
	} );
  
	//
  
	Object.defineProperties( Material.prototype, {
  
		wrapAround: {
			get: function () {
  
				console.warn( 'THREE.Material: .wrapAround has been removed.' );
  
			},
			set: function () {
  
				console.warn( 'THREE.Material: .wrapAround has been removed.' );
  
			}
		},
		wrapRGB: {
			get: function () {
  
				console.warn( 'THREE.Material: .wrapRGB has been removed.' );
				return new Color();
  
			}
		},
  
		shading: {
			get: function () {
  
				console.error( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' );
  
			},
			set: function ( value ) {
  
				console.warn( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' );
				this.flatShading = ( value === FlatShading );
  
			}
		}
  
	} );
  
	Object.defineProperties( MeshPhongMaterial.prototype, {
  
		metal: {
			get: function () {
  
				console.warn( 'THREE.MeshPhongMaterial: .metal has been removed. Use THREE.MeshStandardMaterial instead.' );
				return false;
  
			},
			set: function () {
  
				console.warn( 'THREE.MeshPhongMaterial: .metal has been removed. Use THREE.MeshStandardMaterial instead' );
  
			}
		}
  
	} );
  
	Object.defineProperties( ShaderMaterial.prototype, {
  
		derivatives: {
			get: function () {
  
				console.warn( 'THREE.ShaderMaterial: .derivatives has been moved to .extensions.derivatives.' );
				return this.extensions.derivatives;
  
			},
			set: function ( value ) {
  
				console.warn( 'THREE. ShaderMaterial: .derivatives has been moved to .extensions.derivatives.' );
				this.extensions.derivatives = value;
  
			}
		}
  
	} );
  
	//
  
	Object.assign( WebGLRenderer.prototype, {
  
		animate: function ( callback ) {
  
			console.warn( 'THREE.WebGLRenderer: .animate() is now .setAnimationLoop().' );
			this.setAnimationLoop( callback );
  
		},
  
		getCurrentRenderTarget: function () {
  
			console.warn( 'THREE.WebGLRenderer: .getCurrentRenderTarget() is now .getRenderTarget().' );
			return this.getRenderTarget();
  
		},
  
		getMaxAnisotropy: function () {
  
			console.warn( 'THREE.WebGLRenderer: .getMaxAnisotropy() is now .capabilities.getMaxAnisotropy().' );
			return this.capabilities.getMaxAnisotropy();
  
		},
  
		getPrecision: function () {
  
			console.warn( 'THREE.WebGLRenderer: .getPrecision() is now .capabilities.precision.' );
			return this.capabilities.precision;
  
		},
  
		resetGLState: function () {
  
			console.warn( 'THREE.WebGLRenderer: .resetGLState() is now .state.reset().' );
			return this.state.reset();
  
		},
  
		supportsFloatTextures: function () {
  
			console.warn( 'THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( \'OES_texture_float\' ).' );
			return this.extensions.get( 'OES_texture_float' );
  
		},
		supportsHalfFloatTextures: function () {
  
			console.warn( 'THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( \'OES_texture_half_float\' ).' );
			return this.extensions.get( 'OES_texture_half_float' );
  
		},
		supportsStandardDerivatives: function () {
  
			console.warn( 'THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( \'OES_standard_derivatives\' ).' );
			return this.extensions.get( 'OES_standard_derivatives' );
  
		},
		supportsCompressedTextureS3TC: function () {
  
			console.warn( 'THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( \'WEBGL_compressed_texture_s3tc\' ).' );
			return this.extensions.get( 'WEBGL_compressed_texture_s3tc' );
  
		},
		supportsCompressedTexturePVRTC: function () {
  
			console.warn( 'THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( \'WEBGL_compressed_texture_pvrtc\' ).' );
			return this.extensions.get( 'WEBGL_compressed_texture_pvrtc' );
  
		},
		supportsBlendMinMax: function () {
  
			console.warn( 'THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( \'EXT_blend_minmax\' ).' );
			return this.extensions.get( 'EXT_blend_minmax' );
  
		},
		supportsVertexTextures: function () {
  
			console.warn( 'THREE.WebGLRenderer: .supportsVertexTextures() is now .capabilities.vertexTextures.' );
			return this.capabilities.vertexTextures;
  
		},
		supportsInstancedArrays: function () {
  
			console.warn( 'THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( \'ANGLE_instanced_arrays\' ).' );
			return this.extensions.get( 'ANGLE_instanced_arrays' );
  
		},
		enableScissorTest: function ( boolean ) {
  
			console.warn( 'THREE.WebGLRenderer: .enableScissorTest() is now .setScissorTest().' );
			this.setScissorTest( boolean );
  
		},
		initMaterial: function () {
  
			console.warn( 'THREE.WebGLRenderer: .initMaterial() has been removed.' );
  
		},
		addPrePlugin: function () {
  
			console.warn( 'THREE.WebGLRenderer: .addPrePlugin() has been removed.' );
  
		},
		addPostPlugin: function () {
  
			console.warn( 'THREE.WebGLRenderer: .addPostPlugin() has been removed.' );
  
		},
		updateShadowMap: function () {
  
			console.warn( 'THREE.WebGLRenderer: .updateShadowMap() has been removed.' );
  
		},
		setFaceCulling: function () {
  
			console.warn( 'THREE.WebGLRenderer: .setFaceCulling() has been removed.' );
  
		}
  
	} );
  
	Object.defineProperties( WebGLRenderer.prototype, {
  
		shadowMapEnabled: {
			get: function () {
  
				return this.shadowMap.enabled;
  
			},
			set: function ( value ) {
  
				console.warn( 'THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled.' );
				this.shadowMap.enabled = value;
  
			}
		},
		shadowMapType: {
			get: function () {
  
				return this.shadowMap.type;
  
			},
			set: function ( value ) {
  
				console.warn( 'THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type.' );
				this.shadowMap.type = value;
  
			}
		},
		shadowMapCullFace: {
			get: function () {
  
				console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead.' );
				return undefined;
  
			},
			set: function ( /* value */ ) {
  
				console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead.' );
  
			}
		}
	} );
  
	Object.defineProperties( WebGLShadowMap.prototype, {
  
		cullFace: {
			get: function () {
  
				console.warn( 'THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead.' );
				return undefined;
  
			},
			set: function ( /* cullFace */ ) {
  
				console.warn( 'THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead.' );
  
			}
		},
		renderReverseSided: {
			get: function () {
  
				console.warn( 'THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead.' );
				return undefined;
  
			},
			set: function () {
  
				console.warn( 'THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead.' );
  
			}
		},
		renderSingleSided: {
			get: function () {
  
				console.warn( 'THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead.' );
				return undefined;
  
			},
			set: function () {
  
				console.warn( 'THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead.' );
  
			}
		}
  
	} );
  
	//
  
	Object.defineProperties( WebGLRenderTarget.prototype, {
  
		wrapS: {
			get: function () {
  
				console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' );
				return this.texture.wrapS;
  
			},
			set: function ( value ) {
  
				console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' );
				this.texture.wrapS = value;
  
			}
		},
		wrapT: {
			get: function () {
  
				console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' );
				return this.texture.wrapT;
  
			},
			set: function ( value ) {
  
				console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' );
				this.texture.wrapT = value;
  
			}
		},
		magFilter: {
			get: function () {
  
				console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' );
				return this.texture.magFilter;
  
			},
			set: function ( value ) {
  
				console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' );
				this.texture.magFilter = value;
  
			}
		},
		minFilter: {
			get: function () {
  
				console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' );
				return this.texture.minFilter;
  
			},
			set: function ( value ) {
  
				console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' );
				this.texture.minFilter = value;
  
			}
		},
		anisotropy: {
			get: function () {
  
				console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' );
				return this.texture.anisotropy;
  
			},
			set: function ( value ) {
  
				console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' );
				this.texture.anisotropy = value;
  
			}
		},
		offset: {
			get: function () {
  
				console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' );
				return this.texture.offset;
  
			},
			set: function ( value ) {
  
				console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' );
				this.texture.offset = value;
  
			}
		},
		repeat: {
			get: function () {
  
				console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' );
				return this.texture.repeat;
  
			},
			set: function ( value ) {
  
				console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' );
				this.texture.repeat = value;
  
			}
		},
		format: {
			get: function () {
  
				console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' );
				return this.texture.format;
  
			},
			set: function ( value ) {
  
				console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' );
				this.texture.format = value;
  
			}
		},
		type: {
			get: function () {
  
				console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' );
				return this.texture.type;
  
			},
			set: function ( value ) {
  
				console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' );
				this.texture.type = value;
  
			}
		},
		generateMipmaps: {
			get: function () {
  
				console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' );
				return this.texture.generateMipmaps;
  
			},
			set: function ( value ) {
  
				console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' );
				this.texture.generateMipmaps = value;
  
			}
		}
  
	} );
  
	//
  
	Object.defineProperties( WebVRManager.prototype, {
  
		standing: {
			set: function ( /* value */ ) {
  
				console.warn( 'THREE.WebVRManager: .standing has been removed.' );
  
			}
		}
  
	} );
  
	//
  
	Audio.prototype.load = function ( file ) {
  
		console.warn( 'THREE.Audio: .load has been deprecated. Use THREE.AudioLoader instead.' );
		var scope = this;
		var audioLoader = new AudioLoader();
		audioLoader.load( file, function ( buffer ) {
  
			scope.setBuffer( buffer );
  
		} );
		return this;
  
	};
  
	AudioAnalyser.prototype.getData = function () {
  
		console.warn( 'THREE.AudioAnalyser: .getData() is now .getFrequencyData().' );
		return this.getFrequencyData();
  
	};
  
	//
  
	CubeCamera.prototype.updateCubeMap = function ( renderer, scene ) {
  
		console.warn( 'THREE.CubeCamera: .updateCubeMap() is now .update().' );
		return this.update( renderer, scene );
  
	};
  
	/**
	 * @author dmarcos / https://github.com/dmarcos
	 * @author mrdoob / http://mrdoob.com
	 */
	var VRControls = function ( object, onError ) {
  
		var scope = this;
  
		var vrDisplay, vrDisplays;
  
		var standingMatrix = new Matrix4();
  
		var frameData = null;
  
		if ( 'VRFrameData' in window ) {
  
			frameData = new VRFrameData();
  
		}
  
		function gotVRDisplays( displays ) {
  
			vrDisplays = displays;
  
			if ( displays.length > 0 ) {
  
				vrDisplay = displays[ 0 ];
  
			} else {
  
				if ( onError ) onError( 'VR input not available.' );
  
			}
  
		}
  
		if ( navigator.getVRDisplays ) {
  
			navigator.getVRDisplays().then( gotVRDisplays ).catch( function () {
  
				console.warn( 'VRControls: Unable to get VR Displays' );
  
			} );
  
		}
  
		// the Rift SDK returns the position in meters
		// this scale factor allows the user to define how meters
		// are converted to scene units.
  
		this.scale = 1;
  
		// If true will use "standing space" coordinate system where y=0 is the
		// floor and x=0, z=0 is the center of the room.
		this.standing = false;
  
		// Distance from the users eyes to the floor in meters. Used when
		// standing=true but the VRDisplay doesn't provide stageParameters.
		this.userHeight = 1.6;
  
		this.getVRDisplay = function () {
  
			return vrDisplay;
  
		};
  
		this.setVRDisplay = function ( value ) {
  
			vrDisplay = value;
  
		};
  
		this.getVRDisplays = function () {
  
			console.warn( 'VRControls: getVRDisplays() is being deprecated.' );
			return vrDisplays;
  
		};
  
		this.getStandingMatrix = function () {
  
			return standingMatrix;
  
		};
  
		this.update = function () {
  
			if ( vrDisplay ) {
  
				var pose;
  
				if ( vrDisplay.getFrameData ) {
  
					vrDisplay.getFrameData( frameData );
					pose = frameData.pose;
  
				} else if ( vrDisplay.getPose ) {
  
					pose = vrDisplay.getPose();
  
				}
  
				if ( pose.orientation !== null ) {
  
					object.quaternion.fromArray( pose.orientation );
  
				}
  
				if ( pose.position !== null ) {
  
					object.position.fromArray( pose.position );
  
				} else {
  
					object.position.set( 0, 0, 0 );
  
				}
  
				if ( this.standing ) {
  
					if ( vrDisplay.stageParameters ) {
  
						object.updateMatrix();
  
						standingMatrix.fromArray( vrDisplay.stageParameters.sittingToStandingTransform );
						object.applyMatrix( standingMatrix );
  
					} else {
  
						object.position.setY( object.position.y + this.userHeight );
  
					}
  
				}
  
				object.position.multiplyScalar( scope.scale );
  
			}
  
		};
  
		this.dispose = function () {
  
			vrDisplay = null;
  
		};
  
	};
  
	/**
	 * @author dmarcos / https://github.com/dmarcos
	 * @author mrdoob / http://mrdoob.com
	 *
	 * WebVR Spec: http://mozvr.github.io/webvr-spec/webvr.html
	 *
	 * Firefox: http://mozvr.com/downloads/
	 * Chromium: https://webvr.info/get-chrome
	 */
	var VREffect = function ( renderer, onError ) {
  
		var vrDisplay, vrDisplays;
		var eyeTranslationL = new Vector3();
		var eyeTranslationR = new Vector3();
		var renderRectL, renderRectR;
		var headMatrix = new Matrix4();
		var eyeMatrixL = new Matrix4();
		var eyeMatrixR = new Matrix4();
  
		var frameData = null;
  
		if ( 'VRFrameData' in window ) {
  
			frameData = new window.VRFrameData();
  
		}
  
		function gotVRDisplays( displays ) {
  
			vrDisplays = displays;
  
			if ( displays.length > 0 ) {
  
				vrDisplay = displays[ 0 ];
  
			} else {
  
				if ( onError ) onError( 'HMD not available' );
  
			}
  
		}
  
		if ( navigator.getVRDisplays ) {
  
			navigator.getVRDisplays().then( gotVRDisplays ).catch( function () {
  
				console.warn( 'VREffect: Unable to get VR Displays' );
  
			} );
  
		}
  
		//
  
		this.isPresenting = false;
  
		var scope = this;
  
		var rendererSize = renderer.getSize();
		var rendererUpdateStyle = false;
		var rendererPixelRatio = renderer.getPixelRatio();
  
		this.getVRDisplay = function () {
  
			return vrDisplay;
  
		};
  
		this.setVRDisplay = function ( value ) {
  
			vrDisplay = value;
  
		};
  
		this.getVRDisplays = function () {
  
			console.warn( 'VREffect: getVRDisplays() is being deprecated.' );
			return vrDisplays;
  
		};
  
		this.setSize = function ( width, height, updateStyle ) {
  
			rendererSize = { width: width, height: height };
			rendererUpdateStyle = updateStyle;
  
			if ( scope.isPresenting ) {
  
				var eyeParamsL = vrDisplay.getEyeParameters( 'left' );
				renderer.setPixelRatio( 1 );
				renderer.setSize( eyeParamsL.renderWidth * 2, eyeParamsL.renderHeight, false );
  
			} else {
  
				renderer.setPixelRatio( rendererPixelRatio );
				renderer.setSize( width, height, updateStyle );
  
			}
  
		};
  
		// VR presentation
  
		var canvas = renderer.domElement;
		var defaultLeftBounds = [ 0.0, 0.0, 0.5, 1.0 ];
		var defaultRightBounds = [ 0.5, 0.0, 0.5, 1.0 ];
  
		function onVRDisplayPresentChange() {
  
			var wasPresenting = scope.isPresenting;
			scope.isPresenting = vrDisplay !== undefined && vrDisplay.isPresenting;
  
			if ( scope.isPresenting ) {
  
				var eyeParamsL = vrDisplay.getEyeParameters( 'left' );
				var eyeWidth = eyeParamsL.renderWidth;
				var eyeHeight = eyeParamsL.renderHeight;
  
				if ( ! wasPresenting ) {
  
					rendererPixelRatio = renderer.getPixelRatio();
					rendererSize = renderer.getSize();
  
					renderer.setPixelRatio( 1 );
					renderer.setSize( eyeWidth * 2, eyeHeight, false );
  
				}
  
			} else if ( wasPresenting ) {
  
				renderer.setPixelRatio( rendererPixelRatio );
				renderer.setSize( rendererSize.width, rendererSize.height, rendererUpdateStyle );
  
			}
  
		}
  
		window.addEventListener( 'vrdisplaypresentchange', onVRDisplayPresentChange, false );
  
		this.setFullScreen = function ( boolean ) {
  
			return new Promise( function ( resolve, reject ) {
  
				if ( vrDisplay === undefined ) {
  
					reject( new Error( 'No VR hardware found.' ) );
					return;
  
				}
  
				if ( scope.isPresenting === boolean ) {
  
					resolve();
					return;
  
				}
  
				if ( boolean ) {
  
					resolve( vrDisplay.requestPresent( [ { source: canvas } ] ) );
  
				} else {
  
					resolve( vrDisplay.exitPresent() );
  
				}
  
			} );
  
		};
  
		this.requestPresent = function () {
  
			return this.setFullScreen( true );
  
		};
  
		this.exitPresent = function () {
  
			return this.setFullScreen( false );
  
		};
  
		this.requestAnimationFrame = function ( f ) {
  
			if ( vrDisplay !== undefined ) {
  
				return vrDisplay.requestAnimationFrame( f );
  
			} else {
  
				return window.requestAnimationFrame( f );
  
			}
  
		};
  
		this.cancelAnimationFrame = function ( h ) {
  
			if ( vrDisplay !== undefined ) {
  
				vrDisplay.cancelAnimationFrame( h );
  
			} else {
  
				window.cancelAnimationFrame( h );
  
			}
  
		};
  
		this.submitFrame = function () {
  
			if ( vrDisplay !== undefined && scope.isPresenting ) {
  
				vrDisplay.submitFrame();
  
			}
  
		};
  
		this.autoSubmitFrame = true;
  
		// render
  
		var cameraL = new PerspectiveCamera();
		cameraL.layers.enable( 1 );
  
		var cameraR = new PerspectiveCamera();
		cameraR.layers.enable( 2 );
  
		this.render = function ( scene, camera, renderTarget, forceClear ) {
  
			if ( vrDisplay && scope.isPresenting ) {
  
				var autoUpdate = scene.autoUpdate;
  
				if ( autoUpdate ) {
  
					scene.updateMatrixWorld();
					scene.autoUpdate = false;
  
				}
  
				if ( Array.isArray( scene ) ) {
  
					console.warn( 'VREffect.render() no longer supports arrays. Use object.layers instead.' );
					scene = scene[ 0 ];
  
				}
  
				// When rendering we don't care what the recommended size is, only what the actual size
				// of the backbuffer is.
				var size = renderer.getSize();
				var layers = vrDisplay.getLayers();
				var leftBounds;
				var rightBounds;
  
				if ( layers.length ) {
  
					var layer = layers[ 0 ];
  
					leftBounds = layer.leftBounds !== null && layer.leftBounds.length === 4 ? layer.leftBounds : defaultLeftBounds;
					rightBounds = layer.rightBounds !== null && layer.rightBounds.length === 4 ? layer.rightBounds : defaultRightBounds;
  
				} else {
  
					leftBounds = defaultLeftBounds;
					rightBounds = defaultRightBounds;
  
				}
  
				renderRectL = {
					x: Math.round( size.width * leftBounds[ 0 ] ),
					y: Math.round( size.height * leftBounds[ 1 ] ),
					width: Math.round( size.width * leftBounds[ 2 ] ),
					height: Math.round( size.height * leftBounds[ 3 ] )
				};
				renderRectR = {
					x: Math.round( size.width * rightBounds[ 0 ] ),
					y: Math.round( size.height * rightBounds[ 1 ] ),
					width: Math.round( size.width * rightBounds[ 2 ] ),
					height: Math.round( size.height * rightBounds[ 3 ] )
				};
  
				if ( renderTarget ) {
  
					renderer.setRenderTarget( renderTarget );
					renderTarget.scissorTest = true;
  
				} else {
  
					renderer.setRenderTarget( null );
					renderer.setScissorTest( true );
  
				}
  
				if ( renderer.autoClear || forceClear ) renderer.clear();
  
				if ( camera.parent === null ) camera.updateMatrixWorld();
  
				camera.matrixWorld.decompose( cameraL.position, cameraL.quaternion, cameraL.scale );
  
				cameraR.position.copy( cameraL.position );
				cameraR.quaternion.copy( cameraL.quaternion );
				cameraR.scale.copy( cameraL.scale );
  
				if ( vrDisplay.getFrameData ) {
  
					vrDisplay.depthNear = camera.near;
					vrDisplay.depthFar = camera.far;
  
					vrDisplay.getFrameData( frameData );
  
					cameraL.projectionMatrix.elements = frameData.leftProjectionMatrix;
					cameraR.projectionMatrix.elements = frameData.rightProjectionMatrix;
  
					getEyeMatrices( frameData );
  
					cameraL.updateMatrix();
					cameraL.matrix.multiply( eyeMatrixL );
					cameraL.matrix.decompose( cameraL.position, cameraL.quaternion, cameraL.scale );
  
					cameraR.updateMatrix();
					cameraR.matrix.multiply( eyeMatrixR );
					cameraR.matrix.decompose( cameraR.position, cameraR.quaternion, cameraR.scale );
  
				} else {
  
					var eyeParamsL = vrDisplay.getEyeParameters( 'left' );
					var eyeParamsR = vrDisplay.getEyeParameters( 'right' );
  
					cameraL.projectionMatrix = fovToProjection( eyeParamsL.fieldOfView, true, camera.near, camera.far );
					cameraR.projectionMatrix = fovToProjection( eyeParamsR.fieldOfView, true, camera.near, camera.far );
  
					eyeTranslationL.fromArray( eyeParamsL.offset );
					eyeTranslationR.fromArray( eyeParamsR.offset );
  
					cameraL.translateOnAxis( eyeTranslationL, cameraL.scale.x );
					cameraR.translateOnAxis( eyeTranslationR, cameraR.scale.x );
  
				}
  
				// render left eye
				if ( renderTarget ) {
  
					renderTarget.viewport.set( renderRectL.x, renderRectL.y, renderRectL.width, renderRectL.height );
					renderTarget.scissor.set( renderRectL.x, renderRectL.y, renderRectL.width, renderRectL.height );
  
				} else {
  
					renderer.setViewport( renderRectL.x, renderRectL.y, renderRectL.width, renderRectL.height );
					renderer.setScissor( renderRectL.x, renderRectL.y, renderRectL.width, renderRectL.height );
  
				}
				renderer.render( scene, cameraL, renderTarget, forceClear );
  
				// render right eye
				if ( renderTarget ) {
  
					renderTarget.viewport.set( renderRectR.x, renderRectR.y, renderRectR.width, renderRectR.height );
					renderTarget.scissor.set( renderRectR.x, renderRectR.y, renderRectR.width, renderRectR.height );
  
				} else {
  
					renderer.setViewport( renderRectR.x, renderRectR.y, renderRectR.width, renderRectR.height );
					renderer.setScissor( renderRectR.x, renderRectR.y, renderRectR.width, renderRectR.height );
  
				}
				renderer.render( scene, cameraR, renderTarget, forceClear );
  
				if ( renderTarget ) {
  
					renderTarget.viewport.set( 0, 0, size.width, size.height );
					renderTarget.scissor.set( 0, 0, size.width, size.height );
					renderTarget.scissorTest = false;
					renderer.setRenderTarget( null );
  
				} else {
  
					renderer.setViewport( 0, 0, size.width, size.height );
					renderer.setScissorTest( false );
  
				}
  
				if ( autoUpdate ) {
  
					scene.autoUpdate = true;
  
				}
  
				if ( scope.autoSubmitFrame ) {
  
					scope.submitFrame();
  
				}
  
				return;
  
			}
  
			// Regular render mode if not HMD
  
			renderer.render( scene, camera, renderTarget, forceClear );
  
		};
  
		this.dispose = function () {
  
			window.removeEventListener( 'vrdisplaypresentchange', onVRDisplayPresentChange, false );
  
		};
  
		//
  
		var poseOrientation = new Quaternion();
		var posePosition = new Vector3();
  
		// Compute model matrices of the eyes with respect to the head.
		function getEyeMatrices( frameData ) {
  
			// Compute the matrix for the position of the head based on the pose
			if ( frameData.pose.orientation ) {
  
				poseOrientation.fromArray( frameData.pose.orientation );
				headMatrix.makeRotationFromQuaternion( poseOrientation );
  
			}	else {
  
				headMatrix.identity();
  
			}
  
			if ( frameData.pose.position ) {
  
				posePosition.fromArray( frameData.pose.position );
				headMatrix.setPosition( posePosition );
  
			}
  
			// The view matrix transforms vertices from sitting space to eye space. As such, the view matrix can be thought of as a product of two matrices:
			// headToEyeMatrix * sittingToHeadMatrix
  
			// The headMatrix that we've calculated above is the model matrix of the head in sitting space, which is the inverse of sittingToHeadMatrix.
			// So when we multiply the view matrix with headMatrix, we're left with headToEyeMatrix:
			// viewMatrix * headMatrix = headToEyeMatrix * sittingToHeadMatrix * headMatrix = headToEyeMatrix
  
			eyeMatrixL.fromArray( frameData.leftViewMatrix );
			eyeMatrixL.multiply( headMatrix );
			eyeMatrixR.fromArray( frameData.rightViewMatrix );
			eyeMatrixR.multiply( headMatrix );
  
			// The eye's model matrix in head space is the inverse of headToEyeMatrix we calculated above.
  
			eyeMatrixL.getInverse( eyeMatrixL );
			eyeMatrixR.getInverse( eyeMatrixR );
  
		}
  
		function fovToNDCScaleOffset( fov ) {
  
			var pxscale = 2.0 / ( fov.leftTan + fov.rightTan );
			var pxoffset = ( fov.leftTan - fov.rightTan ) * pxscale * 0.5;
			var pyscale = 2.0 / ( fov.upTan + fov.downTan );
			var pyoffset = ( fov.upTan - fov.downTan ) * pyscale * 0.5;
			return { scale: [ pxscale, pyscale ], offset: [ pxoffset, pyoffset ] };
  
		}
  
		function fovPortToProjection( fov, rightHanded, zNear, zFar ) {
  
			rightHanded = rightHanded === undefined ? true : rightHanded;
			zNear = zNear === undefined ? 0.01 : zNear;
			zFar = zFar === undefined ? 10000.0 : zFar;
  
			var handednessScale = rightHanded ? - 1.0 : 1.0;
  
			// start with an identity matrix
			var mobj = new Matrix4();
			var m = mobj.elements;
  
			// and with scale/offset info for normalized device coords
			var scaleAndOffset = fovToNDCScaleOffset( fov );
  
			// X result, map clip edges to [-w,+w]
			m[ 0 * 4 + 0 ] = scaleAndOffset.scale[ 0 ];
			m[ 0 * 4 + 1 ] = 0.0;
			m[ 0 * 4 + 2 ] = scaleAndOffset.offset[ 0 ] * handednessScale;
			m[ 0 * 4 + 3 ] = 0.0;
  
			// Y result, map clip edges to [-w,+w]
			// Y offset is negated because this proj matrix transforms from world coords with Y=up,
			// but the NDC scaling has Y=down (thanks D3D?)
			m[ 1 * 4 + 0 ] = 0.0;
			m[ 1 * 4 + 1 ] = scaleAndOffset.scale[ 1 ];
			m[ 1 * 4 + 2 ] = - scaleAndOffset.offset[ 1 ] * handednessScale;
			m[ 1 * 4 + 3 ] = 0.0;
  
			// Z result (up to the app)
			m[ 2 * 4 + 0 ] = 0.0;
			m[ 2 * 4 + 1 ] = 0.0;
			m[ 2 * 4 + 2 ] = zFar / ( zNear - zFar ) * - handednessScale;
			m[ 2 * 4 + 3 ] = ( zFar * zNear ) / ( zNear - zFar );
  
			// W result (= Z in)
			m[ 3 * 4 + 0 ] = 0.0;
			m[ 3 * 4 + 1 ] = 0.0;
			m[ 3 * 4 + 2 ] = handednessScale;
			m[ 3 * 4 + 3 ] = 0.0;
  
			mobj.transpose();
			return mobj;
  
		}
  
		function fovToProjection( fov, rightHanded, zNear, zFar ) {
  
			var DEG2RAD = Math.PI / 180.0;
  
			var fovPort = {
				upTan: Math.tan( fov.upDegrees * DEG2RAD ),
				downTan: Math.tan( fov.downDegrees * DEG2RAD ),
				leftTan: Math.tan( fov.leftDegrees * DEG2RAD ),
				rightTan: Math.tan( fov.rightDegrees * DEG2RAD )
			};
  
			return fovPortToProjection( fovPort, rightHanded, zNear, zFar );
  
		}
  
	};
  
	/**
	 * @author qiao / https://github.com/qiao
	 * @author mrdoob / http://mrdoob.com
	 * @author alteredq / http://alteredqualia.com/
	 * @author WestLangley / http://github.com/WestLangley
	 * @author erich666 / http://erichaines.com
	 */
	var OrbitControls = function ( object, domElement ) {
  
		this.object = object;
  
		this.domElement = ( domElement !== undefined ) ? domElement : document;
  
		// Set to false to disable this control
		this.enabled = true;
  
		// "target" sets the location of focus, where the object orbits around
		this.target = new Vector3();
  
		// How far you can dolly in and out ( PerspectiveCamera only )
		this.minDistance = 0;
		this.maxDistance = Infinity;
  
		// How far you can zoom in and out ( OrthographicCamera only )
		this.minZoom = 0;
		this.maxZoom = Infinity;
  
		// How far you can orbit vertically, upper and lower limits.
		// Range is 0 to Math.PI radians.
		this.minPolarAngle = 0; // radians
		this.maxPolarAngle = Math.PI; // radians
  
		// How far you can orbit horizontally, upper and lower limits.
		// If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ].
		this.minAzimuthAngle = - Infinity; // radians
		this.maxAzimuthAngle = Infinity; // radians
  
		// Set to true to enable damping (inertia)
		// If damping is enabled, you must call controls.update() in your animation loop
		this.enableDamping = false;
		this.dampingFactor = 0.25;
  
		// This option actually enables dollying in and out; left as "zoom" for backwards compatibility.
		// Set to false to disable zooming
		this.enableZoom = true;
		this.zoomSpeed = 1.0;
  
		// Set to false to disable rotating
		this.enableRotate = true;
		this.rotateSpeed = 1.0;
  
		// Set to false to disable panning
		this.enablePan = true;
		this.panSpeed = 1.0;
		this.screenSpacePanning = false; // if true, pan in screen-space
		this.keyPanSpeed = 7.0;	// pixels moved per arrow key push
  
		// Set to true to automatically rotate around the target
		// If auto-rotate is enabled, you must call controls.update() in your animation loop
		this.autoRotate = false;
		this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
  
		// Set to false to disable use of the keys
		this.enableKeys = true;
  
		// The four arrow keys
		this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
  
		// Mouse buttons
		this.mouseButtons = { ORBIT: MOUSE.LEFT, ZOOM: MOUSE.MIDDLE, PAN: MOUSE.RIGHT };
  
		// for reset
		this.target0 = this.target.clone();
		this.position0 = this.object.position.clone();
		this.zoom0 = this.object.zoom;
  
		//
		// public methods
		//
  
		this.getPolarAngle = function () {
  
			return spherical.phi;
  
		};
  
		this.getAzimuthalAngle = function () {
  
			return spherical.theta;
  
		};
  
		this.saveState = function () {
  
			scope.target0.copy( scope.target );
			scope.position0.copy( scope.object.position );
			scope.zoom0 = scope.object.zoom;
  
		};
  
		this.reset = function () {
  
			scope.target.copy( scope.target0 );
			scope.object.position.copy( scope.position0 );
			scope.object.zoom = scope.zoom0;
  
			scope.object.updateProjectionMatrix();
			scope.dispatchEvent( changeEvent );
  
			scope.update();
  
			state = STATE.NONE;
  
		};
  
		// this method is exposed, but perhaps it would be better if we can make it private...
		this.update = function () {
  
			var offset = new Vector3();
  
			// so camera.up is the orbit axis
			var quat = new Quaternion().setFromUnitVectors( object.up, new Vector3( 0, 1, 0 ) );
			var quatInverse = quat.clone().inverse();
  
			var lastPosition = new Vector3();
			var lastQuaternion = new Quaternion();
  
			return function update() {
  
				var position = scope.object.position;
  
				offset.copy( position ).sub( scope.target );
  
				// rotate offset to "y-axis-is-up" space
				offset.applyQuaternion( quat );
  
				// angle from z-axis around y-axis
				spherical.setFromVector3( offset );
  
				if ( scope.autoRotate && state === STATE.NONE ) {
  
					scope.rotateLeft( getAutoRotationAngle() );
  
				}
  
				spherical.theta += sphericalDelta.theta;
				spherical.phi += sphericalDelta.phi;
  
				// restrict theta to be between desired limits
				spherical.theta = Math.max( scope.minAzimuthAngle, Math.min( scope.maxAzimuthAngle, spherical.theta ) );
  
				// restrict phi to be between desired limits
				spherical.phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, spherical.phi ) );
  
				spherical.makeSafe();
  
  
				spherical.radius *= scale;
  
				// restrict radius to be between desired limits
				spherical.radius = Math.max( scope.minDistance, Math.min( scope.maxDistance, spherical.radius ) );
  
				// move target to panned location
				scope.target.add( panOffset );
  
				offset.setFromSpherical( spherical );
  
				// rotate offset back to "camera-up-vector-is-up" space
				offset.applyQuaternion( quatInverse );
  
				position.copy( scope.target ).add( offset );
  
				scope.object.lookAt( scope.target );
  
				if ( scope.enableDamping === true ) {
  
					sphericalDelta.theta *= ( 1 - scope.dampingFactor );
					sphericalDelta.phi *= ( 1 - scope.dampingFactor );
  
					panOffset.multiplyScalar( 1 - scope.dampingFactor );
  
				} else {
  
					sphericalDelta.set( 0, 0, 0 );
  
					panOffset.set( 0, 0, 0 );
  
				}
  
				scale = 1;
  
				// update condition is:
				// min(camera displacement, camera rotation in radians)^2 > EPS
				// using small-angle approximation cos(x/2) = 1 - x^2 / 8
  
				if ( zoomChanged ||
					lastPosition.distanceToSquared( scope.object.position ) > EPS ||
					8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) {
  
					scope.dispatchEvent( changeEvent );
  
					lastPosition.copy( scope.object.position );
					lastQuaternion.copy( scope.object.quaternion );
					zoomChanged = false;
  
					return true;
  
				}
  
				return false;
  
			};
  
		}();
  
		this.dispose = function () {
  
			scope.domElement.removeEventListener( 'contextmenu', onContextMenu, false );
			scope.domElement.removeEventListener( 'mousedown', onMouseDown, false );
			scope.domElement.removeEventListener( 'wheel', onMouseWheel, false );
  
			scope.domElement.removeEventListener( 'touchstart', onTouchStart, false );
			scope.domElement.removeEventListener( 'touchend', onTouchEnd, false );
			scope.domElement.removeEventListener( 'touchmove', onTouchMove, false );
  
			document.removeEventListener( 'mousemove', onMouseMove, false );
			document.removeEventListener( 'mouseup', onMouseUp, false );
  
			window.removeEventListener( 'keydown', onKeyDown, false );
  
			//scope.dispatchEvent( { type: 'dispose' } ); // should this be added here?
  
		};
  
		//
		// internals
		//
  
		var scope = this;
  
		var changeEvent = { type: 'change' };
		var startEvent = { type: 'start' };
		var endEvent = { type: 'end' };
  
		var STATE = { NONE: - 1, ROTATE: 0, DOLLY: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_DOLLY_PAN: 4 };
  
		var state = STATE.NONE;
  
		var EPS = 0.000001;
  
		// current position in spherical coordinates
		var spherical = new Spherical();
		var sphericalDelta = new Spherical();
  
		var scale = 1;
		var panOffset = new Vector3();
		var zoomChanged = false;
  
		var rotateStart = new Vector2();
		var rotateEnd = new Vector2();
		var rotateDelta = new Vector2();
  
		var panStart = new Vector2();
		var panEnd = new Vector2();
		var panDelta = new Vector2();
  
		var dollyStart = new Vector2();
		var dollyEnd = new Vector2();
		var dollyDelta = new Vector2();
  
		function getAutoRotationAngle() {
  
			return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
  
		}
  
		function getZoomScale() {
  
			return Math.pow( 0.95, scope.zoomSpeed );
  
		}
  
		scope.rotateLeft = function( angle ) {
  
			sphericalDelta.theta -= angle;
  
		};
  
		scope.rotateUp = function( angle ) {
  
			sphericalDelta.phi -= angle;
  
		};
  
		var panLeft = function () {
  
			var v = new Vector3();
  
			return function panLeft( distance, objectMatrix ) {
  
				v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix
				v.multiplyScalar( - distance );
  
				panOffset.add( v );
  
			};
  
		}();
  
		var panUp = function () {
  
			var v = new Vector3();
  
			return function panUp( distance, objectMatrix ) {
  
				if ( scope.screenSpacePanning === true ) {
  
					v.setFromMatrixColumn( objectMatrix, 1 );
  
				} else {
  
					v.setFromMatrixColumn( objectMatrix, 0 );
					v.crossVectors( scope.object.up, v );
  
				}
  
				v.multiplyScalar( distance );
  
				panOffset.add( v );
  
			};
  
		}();
  
		// deltaX and deltaY are in pixels; right and down are positive
		var pan = function () {
  
			var offset = new Vector3();
  
			return function pan( deltaX, deltaY ) {
  
				var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
  
				if ( scope.object.isPerspectiveCamera ) {
  
					// perspective
					var position = scope.object.position;
					offset.copy( position ).sub( scope.target );
					var targetDistance = offset.length();
  
					// half of the fov is center to top of screen
					targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 );
  
					// we use only clientHeight here so aspect ratio does not distort speed
					panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix );
					panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix );
  
				} else if ( scope.object.isOrthographicCamera ) {
  
					// orthographic
					panLeft( deltaX * ( scope.object.right - scope.object.left ) / scope.object.zoom / element.clientWidth, scope.object.matrix );
					panUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom / element.clientHeight, scope.object.matrix );
  
				} else {
  
					// camera neither orthographic nor perspective
					console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );
					scope.enablePan = false;
  
				}
  
			};
  
		}();
  
		function dollyIn( dollyScale ) {
  
			if ( scope.object.isPerspectiveCamera ) {
  
				scale /= dollyScale;
  
			} else if ( scope.object.isOrthographicCamera ) {
  
				scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) );
				scope.object.updateProjectionMatrix();
				zoomChanged = true;
  
			} else {
  
				console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
				scope.enableZoom = false;
  
			}
  
		}
  
		function dollyOut( dollyScale ) {
  
			if ( scope.object.isPerspectiveCamera ) {
  
				scale *= dollyScale;
  
			} else if ( scope.object.isOrthographicCamera ) {
  
				scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) );
				scope.object.updateProjectionMatrix();
				zoomChanged = true;
  
			} else {
  
				console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
				scope.enableZoom = false;
  
			}
  
		}
  
		//
		// event callbacks - update the object state
		//
  
		function handleMouseDownRotate( event ) {
  
			//console.log( 'handleMouseDownRotate' );
  
			rotateStart.set( event.clientX, event.clientY );
  
		}
  
		function handleMouseDownDolly( event ) {
  
			//console.log( 'handleMouseDownDolly' );
  
			dollyStart.set( event.clientX, event.clientY );
  
		}
  
		function handleMouseDownPan( event ) {
  
			//console.log( 'handleMouseDownPan' );
  
			panStart.set( event.clientX, event.clientY );
  
		}
  
		function handleMouseMoveRotate( event ) {
  
			//console.log( 'handleMouseMoveRotate' );
  
			rotateEnd.set( event.clientX, event.clientY );
  
			rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed );
  
			var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
  
			scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height
  
			scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight );
  
			rotateStart.copy( rotateEnd );
  
			scope.update();
  
		}
  
		function handleMouseMoveDolly( event ) {
  
			//console.log( 'handleMouseMoveDolly' );
  
			dollyEnd.set( event.clientX, event.clientY );
  
			dollyDelta.subVectors( dollyEnd, dollyStart );
  
			if ( dollyDelta.y > 0 ) {
  
				dollyIn( getZoomScale() );
  
			} else if ( dollyDelta.y < 0 ) {
  
				dollyOut( getZoomScale() );
  
			}
  
			dollyStart.copy( dollyEnd );
  
			scope.update();
  
		}
  
		function handleMouseMovePan( event ) {
  
			//console.log( 'handleMouseMovePan' );
  
			panEnd.set( event.clientX, event.clientY );
  
			panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed );
  
			pan( panDelta.x, panDelta.y );
  
			panStart.copy( panEnd );
  
			scope.update();
  
		}
  
		function handleMouseWheel( event ) {
  
			// console.log( 'handleMouseWheel' );
  
			if ( event.deltaY < 0 ) {
  
				dollyOut( getZoomScale() );
  
			} else if ( event.deltaY > 0 ) {
  
				dollyIn( getZoomScale() );
  
			}
  
			scope.update();
  
		}
  
		function handleKeyDown( event ) {
  
			//console.log( 'handleKeyDown' );
  
			switch ( event.keyCode ) {
  
				case scope.keys.UP:
					pan( 0, scope.keyPanSpeed );
					scope.update();
					break;
  
				case scope.keys.BOTTOM:
					pan( 0, - scope.keyPanSpeed );
					scope.update();
					break;
  
				case scope.keys.LEFT:
					pan( scope.keyPanSpeed, 0 );
					scope.update();
					break;
  
				case scope.keys.RIGHT:
					pan( - scope.keyPanSpeed, 0 );
					scope.update();
					break;
  
			}
  
		}
  
		function handleTouchStartRotate( event ) {
  
			//console.log( 'handleTouchStartRotate' );
  
			rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
  
		}
  
		function handleTouchStartDollyPan( event ) {
  
			//console.log( 'handleTouchStartDollyPan' );
  
			if ( scope.enableZoom ) {
  
				var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
				var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
  
				var distance = Math.sqrt( dx * dx + dy * dy );
  
				dollyStart.set( 0, distance );
  
			}
  
			if ( scope.enablePan ) {
  
				var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
				var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
  
				panStart.set( x, y );
  
			}
  
		}
  
		function handleTouchMoveRotate( event ) {
  
			//console.log( 'handleTouchMoveRotate' );
  
			rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
  
			rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed );
  
			var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
  
			scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height
  
			scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight );
  
			rotateStart.copy( rotateEnd );
  
			scope.update();
  
		}
  
		function handleTouchMoveDollyPan( event ) {
  
			//console.log( 'handleTouchMoveDollyPan' );
  
			if ( scope.enableZoom ) {
  
				var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
				var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
  
				var distance = Math.sqrt( dx * dx + dy * dy );
  
				dollyEnd.set( 0, distance );
  
				dollyDelta.set( 0, Math.pow( dollyEnd.y / dollyStart.y, scope.zoomSpeed ) );
  
				dollyIn( dollyDelta.y );
  
				dollyStart.copy( dollyEnd );
  
			}
  
			if ( scope.enablePan ) {
  
				var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
				var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
  
				panEnd.set( x, y );
  
				panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed );
  
				pan( panDelta.x, panDelta.y );
  
				panStart.copy( panEnd );
  
			}
  
			scope.update();
  
		}
  
		//
		// event handlers - FSM: listen for events and reset state
		//
  
		function onMouseDown( event ) {
  
			if ( scope.enabled === false ) return;
  
			event.preventDefault();
  
			switch ( event.button ) {
  
				case scope.mouseButtons.ORBIT:
  
					if ( scope.enableRotate === false ) return;
  
					handleMouseDownRotate( event );
  
					state = STATE.ROTATE;
  
					break;
  
				case scope.mouseButtons.ZOOM:
  
					if ( scope.enableZoom === false ) return;
  
					handleMouseDownDolly( event );
  
					state = STATE.DOLLY;
  
					break;
  
				case scope.mouseButtons.PAN:
  
					if ( scope.enablePan === false ) return;
  
					handleMouseDownPan( event );
  
					state = STATE.PAN;
  
					break;
  
			}
  
			if ( state !== STATE.NONE ) {
  
				document.addEventListener( 'mousemove', onMouseMove, false );
				document.addEventListener( 'mouseup', onMouseUp, false );
  
				scope.dispatchEvent( startEvent );
  
			}
  
		}
  
		function onMouseMove( event ) {
  
			if ( scope.enabled === false ) return;
  
			event.preventDefault();
  
			switch ( state ) {
  
				case STATE.ROTATE:
  
					if ( scope.enableRotate === false ) return;
  
					handleMouseMoveRotate( event );
  
					break;
  
				case STATE.DOLLY:
  
					if ( scope.enableZoom === false ) return;
  
					handleMouseMoveDolly( event );
  
					break;
  
				case STATE.PAN:
  
					if ( scope.enablePan === false ) return;
  
					handleMouseMovePan( event );
  
					break;
  
			}
  
		}
  
		function onMouseUp( event ) {
  
			if ( scope.enabled === false ) return;
  
			document.removeEventListener( 'mousemove', onMouseMove, false );
			document.removeEventListener( 'mouseup', onMouseUp, false );
  
			scope.dispatchEvent( endEvent );
  
			state = STATE.NONE;
  
		}
  
		function onMouseWheel( event ) {
  
			if ( scope.enabled === false || scope.enableZoom === false || ( state !== STATE.NONE && state !== STATE.ROTATE ) ) return;
  
			event.preventDefault();
			event.stopPropagation();
  
			scope.dispatchEvent( startEvent );
  
			handleMouseWheel( event );
  
			scope.dispatchEvent( endEvent );
  
		}
  
		function onKeyDown( event ) {
  
			if ( scope.enabled === false || scope.enableKeys === false || scope.enablePan === false ) return;
  
			handleKeyDown( event );
  
		}
  
		function onTouchStart( event ) {
  
			if ( scope.enabled === false ) return;
  
			event.preventDefault();
  
			switch ( event.touches.length ) {
  
				case 1:	// one-fingered touch: rotate
  
					if ( scope.enableRotate === false ) return;
  
					handleTouchStartRotate( event );
  
					state = STATE.TOUCH_ROTATE;
  
					break;
  
				case 2:	// two-fingered touch: dolly-pan
  
					if ( scope.enableZoom === false && scope.enablePan === false ) return;
  
					handleTouchStartDollyPan( event );
  
					state = STATE.TOUCH_DOLLY_PAN;
  
					break;
  
				default:
  
					state = STATE.NONE;
  
			}
  
			if ( state !== STATE.NONE ) {
  
				scope.dispatchEvent( startEvent );
  
			}
  
		}
  
		function onTouchMove( event ) {
  
			if ( scope.enabled === false ) return;
  
			event.preventDefault();
			event.stopPropagation();
  
			switch ( event.touches.length ) {
  
				case 1: // one-fingered touch: rotate
  
					if ( scope.enableRotate === false ) return;
					if ( state !== STATE.TOUCH_ROTATE ) return; // is this needed?
  
					handleTouchMoveRotate( event );
  
					break;
  
				case 2: // two-fingered touch: dolly-pan
  
					if ( scope.enableZoom === false && scope.enablePan === false ) return;
					if ( state !== STATE.TOUCH_DOLLY_PAN ) return; // is this needed?
  
					handleTouchMoveDollyPan( event );
  
					break;
  
				default:
  
					state = STATE.NONE;
  
			}
  
		}
  
		function onTouchEnd( event ) {
  
			if ( scope.enabled === false ) return;
  
			scope.dispatchEvent( endEvent );
  
			state = STATE.NONE;
  
		}
  
		function onContextMenu( event ) {
  
			if ( scope.enabled === false ) return;
  
			event.preventDefault();
  
		}
  
		//
  
		//scope.domElement.addEventListener\( 'contextmenu', onContextMenu, false );
  
		scope.domElement.addEventListener( 'mousedown', onMouseDown, false );
		scope.domElement.addEventListener( 'wheel', onMouseWheel, false );
  
		scope.domElement.addEventListener( 'touchstart', onTouchStart, false );
		scope.domElement.addEventListener( 'touchend', onTouchEnd, false );
		scope.domElement.addEventListener( 'touchmove', onTouchMove, false );
  
		window.addEventListener( 'keydown', onKeyDown, false );
  
		// force an update at start
  
		this.update();
  
	};
  
	OrbitControls.prototype = Object.create( EventDispatcher.prototype );
	OrbitControls.prototype.constructor = OrbitControls;
  
	Object.defineProperties( OrbitControls.prototype, {
  
		center: {
  
			get: function () {
  
				console.warn( 'OrbitControls: .center has been renamed to .target' );
				return this.target;
  
			}
  
		},
  
		// backward compatibility
  
		noZoom: {
  
			get: function () {
  
				console.warn( 'OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' );
				return ! this.enableZoom;
  
			},
  
			set: function ( value ) {
  
				console.warn( 'OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' );
				this.enableZoom = ! value;
  
			}
  
		},
  
		noRotate: {
  
			get: function () {
  
				console.warn( 'OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' );
				return ! this.enableRotate;
  
			},
  
			set: function ( value ) {
  
				console.warn( 'OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' );
				this.enableRotate = ! value;
  
			}
  
		},
  
		noPan: {
  
			get: function () {
  
				console.warn( 'OrbitControls: .noPan has been deprecated. Use .enablePan instead.' );
				return ! this.enablePan;
  
			},
  
			set: function ( value ) {
  
				console.warn( 'OrbitControls: .noPan has been deprecated. Use .enablePan instead.' );
				this.enablePan = ! value;
  
			}
  
		},
  
		noKeys: {
  
			get: function () {
  
				console.warn( 'OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' );
				return ! this.enableKeys;
  
			},
  
			set: function ( value ) {
  
				console.warn( 'OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' );
				this.enableKeys = ! value;
  
			}
  
		},
  
		staticMoving: {
  
			get: function () {
  
				console.warn( 'OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' );
				return ! this.enableDamping;
  
			},
  
			set: function ( value ) {
  
				console.warn( 'OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' );
				this.enableDamping = ! value;
  
			}
  
		},
  
		dynamicDampingFactor: {
  
			get: function () {
  
				console.warn( 'OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' );
				return this.dampingFactor;
  
			},
  
			set: function ( value ) {
  
				console.warn( 'OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' );
				this.dampingFactor = value;
  
			}
  
		}
  
	} );
  
	/**
	 * @author richt / http://richt.me
	 * @author WestLangley / http://github.com/WestLangley
	 *
	 * W3C Device Orientation control (http://w3c.github.io/deviceorientation/spec-source-orientation.html)
	 */
	var DeviceOrientationControls = function ( object ) {
  
		var scope = this;
  
		this.object = object;
		this.object.rotation.reorder( 'YXZ' );
  
		this.enabled = true;
  
		this.deviceOrientation = {};
		this.screenOrientation = 0;
  
		this.alphaOffset = 0; // radians
  
		var onDeviceOrientationChangeEvent = function ( event ) {
  
			scope.deviceOrientation = event;
  
		};
  
		var onScreenOrientationChangeEvent = function () {
  
			scope.screenOrientation = window.orientation || 0;
  
		};
  
		// The angles alpha, beta and gamma form a set of intrinsic Tait-Bryan angles of type Z-X'-Y''
  
		var setObjectQuaternion = function () {
  
			var zee = new Vector3( 0, 0, 1 );
  
			var euler = new Euler();
  
			var q0 = new Quaternion();
  
			var q1 = new Quaternion( - Math.sqrt( 0.5 ), 0, 0, Math.sqrt( 0.5 ) ); // - PI/2 around the x-axis
  
			return function ( quaternion, alpha, beta, gamma, orient ) {
  
				euler.set( beta, alpha, - gamma, 'YXZ' ); // 'ZXY' for the device, but 'YXZ' for us
  
				quaternion.setFromEuler( euler ); // orient the device
  
				quaternion.multiply( q1 ); // camera looks out the back of the device, not the top
  
				quaternion.multiply( q0.setFromAxisAngle( zee, - orient ) ); // adjust for screen orientation
  
			};
  
		}();
  
		this.connect = function () {
  
			onScreenOrientationChangeEvent(); // run once on load
  
			window.addEventListener( 'orientationchange', onScreenOrientationChangeEvent, false );
			window.addEventListener( 'deviceorientation', onDeviceOrientationChangeEvent, false );
  
			scope.enabled = true;
  
		};
  
		this.disconnect = function () {
  
			window.removeEventListener( 'orientationchange', onScreenOrientationChangeEvent, false );
			window.removeEventListener( 'deviceorientation', onDeviceOrientationChangeEvent, false );
  
			scope.enabled = false;
  
		};
  
		this.update = function () {
  
			if ( scope.enabled === false ) return;
  
			var device = scope.deviceOrientation;
  
			if ( device ) {
  
				var alpha = device.alpha ? _Math.degToRad( device.alpha ) + scope.alphaOffset : 0; // Z
  
				var beta = device.beta ? _Math.degToRad( device.beta ) : 0; // X'
  
				var gamma = device.gamma ? _Math.degToRad( device.gamma ) : 0; // Y''
  
				var orient = scope.screenOrientation ? _Math.degToRad( scope.screenOrientation ) : 0; // O
  
				setObjectQuaternion( scope.object.quaternion, alpha, beta, gamma, orient );
  
			}
  
  
		};
  
		this.dispose = function () {
  
			scope.disconnect();
  
		};
  
		this.connect();
  
	};
  
	/**
	 * Convert a quaternion to an angle
	 *
	 * Taken from https://stackoverflow.com/a/35448946
	 * Thanks P. Ellul
	 */
  
	function Quat2Angle(x, y, z, w) {
	  var test = x * y + z * w; // singularity at north pole
  
	  if (test > 0.499) {
		var _yaw = 2 * Math.atan2(x, w);
  
		var _pitch = Math.PI / 2;
  
		var _roll = 0;
		return new Vector3(_pitch, _roll, _yaw);
	  } // singularity at south pole
  
  
	  if (test < -0.499) {
		var _yaw2 = -2 * Math.atan2(x, w);
  
		var _pitch2 = -Math.PI / 2;
  
		var _roll2 = 0;
		return new Vector3(_pitch2, _roll2, _yaw2);
	  }
  
	  var sqx = x * x;
	  var sqy = y * y;
	  var sqz = z * z;
	  var yaw = Math.atan2(2 * y * w - 2 * x * z, 1 - 2 * sqy - 2 * sqz);
	  var pitch = Math.asin(2 * test);
	  var roll = Math.atan2(2 * x * w - 2 * y * z, 1 - 2 * sqx - 2 * sqz);
	  return new Vector3(pitch, roll, yaw);
	}
  
	var OrbitOrientationControls =
	/*#__PURE__*/
	function () {
	  function OrbitOrientationControls(options) {
		this.object = options.camera;
		this.domElement = options.canvas;
		this.orbit = new OrbitControls(this.object, this.domElement);
		this.speed = 0.5;
		this.orbit.target.set(0, 0, -1);
		this.orbit.enableZoom = false;
		this.orbit.enablePan = false;
		this.orbit.rotateSpeed = -this.speed; // if orientation is supported
  
		if (options.orientation) {
		  this.orientation = new DeviceOrientationControls(this.object);
		} // if projection is not full view
		// limit the rotation angle in order to not display back half view
  
  
		if (options.halfView) {
		  this.orbit.minAzimuthAngle = -Math.PI / 4;
		  this.orbit.maxAzimuthAngle = Math.PI / 4;
		}
	  }
  
	  var _proto = OrbitOrientationControls.prototype;
  
	  _proto.update = function update() {
		// orientation updates the camera using quaternions and
		// orbit updates the camera using angles. They are incompatible
		// and one update overrides the other. So before
		// orbit overrides orientation we convert our quaternion changes to
		// an angle change. Then save the angle into orbit so that
		// it will take those into account when it updates the camera and overrides
		// our changes
		if (this.orientation) {
		  this.orientation.update();
		  var quat = this.orientation.object.quaternion;
		  var currentAngle = Quat2Angle(quat.x, quat.y, quat.z, quat.w); // we also have to store the last angle since quaternions are b
  
		  if (typeof this.lastAngle_ === 'undefined') {
			this.lastAngle_ = currentAngle;
		  }
  
		  this.orbit.rotateLeft((this.lastAngle_.z - currentAngle.z) * (1 + this.speed));
		  this.orbit.rotateUp((this.lastAngle_.y - currentAngle.y) * (1 + this.speed));
		  this.lastAngle_ = currentAngle;
		}
  
		this.orbit.update();
	  };
  
	  _proto.dispose = function dispose() {
		this.orbit.dispose();
  
		if (this.orientation) {
		  this.orientation.dispose();
		}
	  };
  
	  return OrbitOrientationControls;
	}();
  
	var corsSupport = function () {
	  var video = document$1.createElement('video');
	  video.crossOrigin = 'anonymous';
	  return video.hasAttribute('crossorigin');
	}();
	var validProjections = ['360', '360_LR', '360_TB', '360_CUBE', 'EAC', 'EAC_LR', 'NONE', 'AUTO', 'Sphere', 'Cube', 'equirectangular', '180', '180_LR', '180_MONO'];
	var getInternalProjectionName = function getInternalProjectionName(projection) {
	  if (!projection) {
		return;
	  }
  
	  projection = projection.toString().trim();
  
	  if (/sphere/i.test(projection)) {
		return '360';
	  }
  
	  if (/cube/i.test(projection)) {
		return '360_CUBE';
	  }
  
	  if (/equirectangular/i.test(projection)) {
		return '360';
	  }
  
	  for (var i = 0; i < validProjections.length; i++) {
		if (new RegExp('^' + validProjections[i] + '$', 'i').test(projection)) {
		  return validProjections[i];
		}
	  }
	};
  
	/**
	 * This class reacts to interactions with the canvas and
	 * triggers appropriate functionality on the player. Right now
	 * it does two things:
	 *
	 * 1. A `mousedown`/`touchstart` followed by `touchend`/`mouseup` without any
	 *    `touchmove` or `mousemove` toggles play/pause on the player
	 * 2. Only moving on/clicking the control bar or toggling play/pause should
	 *    show the control bar. Moving around the scene in the canvas should not.
	 */
  
	var CanvasPlayerControls =
	/*#__PURE__*/
	function (_videojs$EventTarget) {
	  inheritsLoose(CanvasPlayerControls, _videojs$EventTarget);
  
	  function CanvasPlayerControls(player, canvas) {
		var _this;
  
		_this = _videojs$EventTarget.call(this) || this;
		_this.player = player;
		_this.canvas = canvas;
		_this.onMoveEnd = videojs.bind(assertThisInitialized(_this), _this.onMoveEnd);
		_this.onMoveStart = videojs.bind(assertThisInitialized(_this), _this.onMoveStart);
		_this.onMove = videojs.bind(assertThisInitialized(_this), _this.onMove);
		_this.onControlBarMove = videojs.bind(assertThisInitialized(_this), _this.onControlBarMove);
  
		_this.player.controlBar.on(['mousedown', 'mousemove', 'mouseup', 'touchstart', 'touchmove', 'touchend'], _this.onControlBarMove); // we have to override these here because
		// video.js listens for user activity on the video element
		// and makes the user active when the mouse moves.
		// We don't want that for 3d videos
  
  
		_this.oldReportUserActivity = _this.player.reportUserActivity;
  
		_this.player.reportUserActivity = function () {}; // canvas movements
  
  
		_this.canvas.addEventListener('mousedown', _this.onMoveStart);
  
		_this.canvas.addEventListener('touchstart', _this.onMoveStart);
  
		_this.canvas.addEventListener('mousemove', _this.onMove);
  
		_this.canvas.addEventListener('touchmove', _this.onMove);
  
		_this.canvas.addEventListener('mouseup', _this.onMoveEnd);
  
		_this.canvas.addEventListener('touchend', _this.onMoveEnd);
  
		_this.shouldTogglePlay = false;
		return _this;
	  }
  
	  var _proto = CanvasPlayerControls.prototype;
  
	  _proto.togglePlay = function togglePlay() {
		if (this.player.paused()) {
		  this.player.play();
		} else {
		  this.player.pause();
		}
	  };
  
	  _proto.onMoveStart = function onMoveStart(e) {
		// if the player does not have a controlbar or
		// the move was a mouse click but not left click do not
		// toggle play.
		if (!this.player.controls() || e.type === 'mousedown' && !videojs.dom.isSingleLeftClick(e)) {
		  this.shouldTogglePlay = false;
		  return;
		}
  
		this.shouldTogglePlay = true;
		this.touchMoveCount_ = 0;
	  };
  
	  _proto.onMoveEnd = function onMoveEnd(e) {
		// We want to have the same behavior in VR360 Player and standard player.
		// in touchend we want to know if was a touch click, for a click we show the bar,
		// otherwise continue with the mouse logic.
		//
		// Maximum movement allowed during a touch event to still be considered a tap
		// Other popular libs use anywhere from 2 (hammer.js) to 15,
		// so 10 seems like a nice, round number.
		if (e.type === 'touchend' && this.touchMoveCount_ < 10) {
		  if (this.player.userActive() === false) {
			this.player.userActive(true);
			return;
		  }
  
		  this.player.userActive(false);
		  return;
		}
  
		if (!this.shouldTogglePlay) {
		  return;
		} // We want the same behavior in Desktop for VR360  and standard player
  
  
		if (e.type == 'mouseup') {
		  this.togglePlay();
		}
	  };
  
	  _proto.onMove = function onMove(e) {
		// Increase touchMoveCount_ since Android detects 1 - 6 touches when user click normally
		this.touchMoveCount_++;
		this.shouldTogglePlay = false;
	  };
  
	  _proto.onControlBarMove = function onControlBarMove(e) {
		this.player.userActive(true);
	  };
  
	  _proto.dispose = function dispose() {
		this.canvas.removeEventListener('mousedown', this.onMoveStart);
		this.canvas.removeEventListener('touchstart', this.onMoveStart);
		this.canvas.removeEventListener('mousemove', this.onMove);
		this.canvas.removeEventListener('touchmove', this.onMove);
		this.canvas.removeEventListener('mouseup', this.onMoveEnd);
		this.canvas.removeEventListener('touchend', this.onMoveEnd);
		this.player.controlBar.off(['mousedown', 'mousemove', 'mouseup', 'touchstart', 'touchmove', 'touchend'], this.onControlBarMove);
		this.player.reportUserActivity = this.oldReportUserActivity;
	  };
  
	  return CanvasPlayerControls;
	}(videojs.EventTarget);
  
	/**
	 * This class manages ambisonic decoding and binaural rendering via Omnitone library.
	 */
  
	var OmnitoneController =
	/*#__PURE__*/
	function (_videojs$EventTarget) {
	  inheritsLoose(OmnitoneController, _videojs$EventTarget);
  
	  /**
	   * Omnitone controller class.
	   *
	   * @class
	   * @param {AudioContext} audioContext - associated AudioContext.
	   * @param {Omnitone library} omnitone - Omnitone library element.
	   * @param {HTMLVideoElement} video - vidoe tag element.
	   * @param {Object} options - omnitone options.
	   */
	  function OmnitoneController(audioContext, omnitone, video, options) {
		var _this;
  
		_this = _videojs$EventTarget.call(this) || this;
		var settings = videojs.mergeOptions({
		  // Safari uses the different AAC decoder than FFMPEG. The channel order is
		  // The default 4ch AAC channel layout for FFMPEG AAC channel ordering.
		  channelMap: videojs.browser.IS_SAFARI ? [2, 0, 1, 3] : [0, 1, 2, 3],
		  ambisonicOrder: 1
		}, options);
		_this.videoElementSource = audioContext.createMediaElementSource(video);
		_this.foaRenderer = omnitone.createFOARenderer(audioContext, settings);
  
		_this.foaRenderer.initialize().then(function () {
		  if (audioContext.state === 'suspended') {
			_this.trigger({
			  type: 'audiocontext-suspended'
			});
		  }
  
		  _this.videoElementSource.connect(_this.foaRenderer.input);
  
		  _this.foaRenderer.output.connect(audioContext.destination);
  
		  _this.initialized = true;
  
		  _this.trigger({
			type: 'omnitone-ready'
		  });
		}, function (error) {
		  videojs.log.warn("videojs-vr: Omnitone initializes failed with the following error: " + error + ")");
		});
  
		return _this;
	  }
	  /**
	   * Updates the rotation of the Omnitone decoder based on three.js camera matrix.
	   *
	   * @param {Camera} camera Three.js camera object
	   */
  
  
	  var _proto = OmnitoneController.prototype;
  
	  _proto.update = function update(camera) {
		if (!this.initialized) {
		  return;
		}
  
		this.foaRenderer.setRotationMatrixFromCamera(camera.matrix);
	  }
	  /**
	   * Destroys the controller and does any necessary cleanup.
	   */
	  ;
  
	  _proto.dispose = function dispose() {
		this.initialized = false;
		this.foaRenderer.setRenderingMode('bypass');
		this.foaRenderer = null;
	  };
  
	  return OmnitoneController;
	}(videojs.EventTarget);
  
	var Button = videojs.getComponent('Button');
  
	var CardboardButton =
	/*#__PURE__*/
	function (_Button) {
	  inheritsLoose(CardboardButton, _Button);
  
	  function CardboardButton(player, options) {
		var _this;
  
		_this = _Button.call(this, player, options) || this;
		_this.handleVrDisplayActivate_ = videojs.bind(assertThisInitialized(_this), _this.handleVrDisplayActivate_);
		_this.handleVrDisplayDeactivate_ = videojs.bind(assertThisInitialized(_this), _this.handleVrDisplayDeactivate_);
		_this.handleVrDisplayPresentChange_ = videojs.bind(assertThisInitialized(_this), _this.handleVrDisplayPresentChange_);
		_this.handleOrientationChange_ = videojs.bind(assertThisInitialized(_this), _this.handleOrientationChange_);
		window$1.addEventListener('orientationchange', _this.handleOrientationChange_);
		window$1.addEventListener('vrdisplayactivate', _this.handleVrDisplayActivate_);
		window$1.addEventListener('vrdisplaydeactivate', _this.handleVrDisplayDeactivate_); // vrdisplaypresentchange does not fire activate or deactivate
		// and happens when hitting the back button during cardboard mode
		// so we need to make sure we stay in the correct state by
		// listening to it and checking if we are presenting it or not
  
		window$1.addEventListener('vrdisplaypresentchange', _this.handleVrDisplayPresentChange_); // we cannot show the cardboard button in fullscreen on
		// android as it breaks the controls, and makes it impossible
		// to exit cardboard mode
  
		if (videojs.browser.IS_ANDROID) {
		  _this.on(player, 'fullscreenchange', function () {
			if (player.isFullscreen()) {
			  _this.hide();
			} else {
			  _this.show();
			}
		  });
		}
  
		return _this;
	  }
  
	  var _proto = CardboardButton.prototype;
  
	  _proto.buildCSSClass = function buildCSSClass() {
		return "vjs-button-vr " + _Button.prototype.buildCSSClass.call(this);
	  };
  
	  _proto.handleVrDisplayPresentChange_ = function handleVrDisplayPresentChange_() {
		if (!this.player_.vr().vrDisplay.isPresenting && this.active_) {
		  this.handleVrDisplayDeactivate_();
		}
  
		if (this.player_.vr().vrDisplay.isPresenting && !this.active_) {
		  this.handleVrDisplayActivate_();
		}
	  };
  
	  _proto.handleOrientationChange_ = function handleOrientationChange_() {
		if (this.active_ && videojs.browser.IS_IOS) {
		  this.changeSize_();
		}
	  };
  
	  _proto.changeSize_ = function changeSize_() {
		this.player_.width(window$1.innerWidth);
		this.player_.height(window$1.innerHeight);
		window$1.dispatchEvent(new window$1.Event('resize'));
	  };
  
	  _proto.handleVrDisplayActivate_ = function handleVrDisplayActivate_() {
		// we mimic fullscreen on IOS
		if (videojs.browser.IS_IOS) {
		  this.oldWidth_ = this.player_.currentWidth();
		  this.oldHeight_ = this.player_.currentHeight();
		  this.player_.enterFullWindow();
		  this.changeSize_();
		}
  
		this.active_ = true;
	  };
  
	  _proto.handleVrDisplayDeactivate_ = function handleVrDisplayDeactivate_() {
		// un-mimic fullscreen on iOS
		if (videojs.browser.IS_IOS) {
		  if (this.oldWidth_) {
			this.player_.width(this.oldWidth_);
		  }
  
		  if (this.oldHeight_) {
			this.player_.height(this.oldHeight_);
		  }
  
		  this.player_.exitFullWindow();
		}
  
		this.active_ = false;
	  };
  
	  _proto.handleClick = function handleClick(event) {
		// if cardboard mode display is not active, activate it
		// otherwise deactivate it
		if (!this.active_) {
		  // This starts playback mode when the cardboard button
		  // is clicked on Android. We need to do this as the controls
		  // disappear
		  if (!this.player_.hasStarted() && videojs.browser.IS_ANDROID) {
			this.player_.play();
		  }
  
		  window$1.dispatchEvent(new window$1.Event('vrdisplayactivate'));
		} else {
		  window$1.dispatchEvent(new window$1.Event('vrdisplaydeactivate'));
		}
	  };
  
	  _proto.dispose = function dispose() {
		_Button.prototype.dispose.call(this);
  
		window$1.removeEventListener('vrdisplayactivate', this.handleVrDisplayActivate_);
		window$1.removeEventListener('vrdisplaydeactivate', this.handleVrDisplayDeactivate_);
		window$1.removeEventListener('vrdisplaypresentchange', this.handleVrDisplayPresentChange_);
	  };
  
	  return CardboardButton;
	}(Button);
  
	videojs.registerComponent('CardboardButton', CardboardButton);
  
	var BigPlayButton = videojs.getComponent('BigPlayButton');
  
	var BigVrPlayButton =
	/*#__PURE__*/
	function (_BigPlayButton) {
	  inheritsLoose(BigVrPlayButton, _BigPlayButton);
  
	  function BigVrPlayButton() {
		return _BigPlayButton.apply(this, arguments) || this;
	  }
  
	  var _proto = BigVrPlayButton.prototype;
  
	  _proto.buildCSSClass = function buildCSSClass() {
		return "vjs-big-vr-play-button " + _BigPlayButton.prototype.buildCSSClass.call(this);
	  };
  
	  return BigVrPlayButton;
	}(BigPlayButton);
  
	videojs.registerComponent('BigVrPlayButton', BigVrPlayButton);
  
	var defaults = {
	  debug: false,
	  omnitone: false,
	  forceCardboard: false,
	  omnitoneOptions: {},
	  projection: 'AUTO',
	  sphereDetail: 32
	};
	var errors = {
	  'web-vr-out-of-date': {
		headline: '360 is out of date',
		type: '360_OUT_OF_DATE',
		message: "Your browser supports 360 but not the latest version. See <a href='http://webvr.info'>http://webvr.info</a> for more info."
	  },
	  'web-vr-not-supported': {
		headline: '360 not supported on this device',
		type: '360_NOT_SUPPORTED',
		message: "Your browser does not support 360. See <a href='http://webvr.info'>http://webvr.info</a> for assistance."
	  },
	  'web-vr-hls-cors-not-supported': {
		headline: '360 HLS video not supported on this device',
		type: '360_NOT_SUPPORTED',
		message: "Your browser/device does not support HLS 360 video. See <a href='http://webvr.info'>http://webvr.info</a> for assistance."
	  }
	};
	var Plugin = videojs.getPlugin('plugin');
	var Component = videojs.getComponent('Component');
  
	var VR =
	/*#__PURE__*/
	function (_Plugin) {
	  inheritsLoose(VR, _Plugin);
  
	  function VR(player, options) {
		var _this;
  
		var settings = videojs.mergeOptions(defaults, options);
		_this = _Plugin.call(this, player, settings) || this;
		_this.options_ = settings;
		_this.player_ = player;
		_this.bigPlayButtonIndex_ = player.children().indexOf(player.getChild('BigPlayButton')) || 0; // custom videojs-errors integration boolean
  
		_this.videojsErrorsSupport_ = !!videojs.errors;
  
		if (_this.videojsErrorsSupport_) {
		  player.errors({
			errors: errors
		  });
		} // IE 11 does not support enough webgl to be supported
		// older safari does not support cors, so it wont work
  
  
		if (videojs.browser.IE_VERSION || !corsSupport) {
		  // if a player triggers error before 'loadstart' is fired
		  // video.js will reset the error overlay
		  _this.player_.on('loadstart', function () {
			_this.triggerError_({
			  code: 'web-vr-not-supported',
			  dismiss: false
			});
		  });
  
		  return assertThisInitialized(_this);
		}
  
		_this.polyfill_ = new WebVRPolyfill({
		  // do not show rotate instructions
		  ROTATE_INSTRUCTIONS_DISABLED: true
		});
		_this.polyfill_ = new WebVRPolyfill();
		_this.handleVrDisplayActivate_ = videojs.bind(assertThisInitialized(_this), _this.handleVrDisplayActivate_);
		_this.handleVrDisplayDeactivate_ = videojs.bind(assertThisInitialized(_this), _this.handleVrDisplayDeactivate_);
		_this.handleResize_ = videojs.bind(assertThisInitialized(_this), _this.handleResize_);
		_this.animate_ = videojs.bind(assertThisInitialized(_this), _this.animate_);
  
		_this.setProjection(_this.options_.projection); // any time the video element is recycled for ads
		// we have to reset the vr state and re-init after ad
  
  
		_this.on(player, 'adstart', function () {
		  return player.setTimeout(function () {
			// if the video element was recycled for this ad
			if (!player.ads || !player.ads.videoElementRecycled()) {
			  _this.log('video element not recycled for this ad, no need to reset');
  
			  return;
			}
  
			_this.log('video element recycled for this ad, reseting');
  
			_this.reset();
  
			_this.one(player, 'playing', _this.init);
		  });
		}, 1);
  
		_this.on(player, 'loadedmetadata', _this.init);
  
		return _this;
	  }
  
	  var _proto = VR.prototype;
  
	  _proto.changeProjection_ = function changeProjection_(projection) {
		var _this2 = this;
  
		projection = getInternalProjectionName(projection); // don't change to an invalid projection
  
		if (!projection) {
		  projection = 'NONE';
		}
  
		var position = {
		  x: 0,
		  y: 0,
		  z: 0
		};
  
		if (this.scene) {
		  this.scene.remove(this.movieScreen);
		}
  
		if (projection === 'AUTO') {
		  // mediainfo cannot be set to auto or we would infinite loop here
		  // each source should know whatever they are 360 or not, if using AUTO
		  if (this.player_.mediainfo && this.player_.mediainfo.projection && this.player_.mediainfo.projection !== 'AUTO') {
			var autoProjection = getInternalProjectionName(this.player_.mediainfo.projection);
			return this.changeProjection_(autoProjection);
		  }
  
		  return this.changeProjection_('NONE');
		} else if (projection === '360') {
		  this.movieGeometry = new SphereBufferGeometry(256, this.options_.sphereDetail, this.options_.sphereDetail);
		  this.movieMaterial = new MeshBasicMaterial({
			map: this.videoTexture,
			overdraw: true,
			side: BackSide
		  });
		  this.movieScreen = new Mesh(this.movieGeometry, this.movieMaterial);
		  this.movieScreen.position.set(position.x, position.y, position.z);
		  this.movieScreen.scale.x = -1;
		  this.movieScreen.quaternion.setFromAxisAngle({
			x: 0,
			y: 1,
			z: 0
		  }, -Math.PI / 2);
		  this.scene.add(this.movieScreen);
		} else if (projection === '360_LR' || projection === '360_TB') {
		  // Left eye view
		  var geometry = new SphereGeometry(256, this.options_.sphereDetail, this.options_.sphereDetail);
		  var uvs = geometry.faceVertexUvs[0];
  
		  for (var i = 0; i < uvs.length; i++) {
			for (var j = 0; j < 3; j++) {
			  if (projection === '360_LR') {
				uvs[i][j].x *= 0.5;
			  } else {
				uvs[i][j].y *= 0.5;
				uvs[i][j].y += 0.5;
			  }
			}
		  }
  
		  this.movieGeometry = new BufferGeometry().fromGeometry(geometry);
		  this.movieMaterial = new MeshBasicMaterial({
			map: this.videoTexture,
			overdraw: true,
			side: BackSide
		  });
		  this.movieScreen = new Mesh(this.movieGeometry, this.movieMaterial);
		  this.movieScreen.scale.x = -1;
		  this.movieScreen.quaternion.setFromAxisAngle({
			x: 0,
			y: 1,
			z: 0
		  }, -Math.PI / 2); // display in left eye only
  
		  this.movieScreen.layers.set(1);
		  this.scene.add(this.movieScreen); // Right eye view
  
		  geometry = new SphereGeometry(256, this.options_.sphereDetail, this.options_.sphereDetail);
		  uvs = geometry.faceVertexUvs[0];
  
		  for (var _i = 0; _i < uvs.length; _i++) {
			for (var _j = 0; _j < 3; _j++) {
			  if (projection === '360_LR') {
				uvs[_i][_j].x *= 0.5;
				uvs[_i][_j].x += 0.5;
			  } else {
				uvs[_i][_j].y *= 0.5;
			  }
			}
		  }
  
		  this.movieGeometry = new BufferGeometry().fromGeometry(geometry);
		  this.movieMaterial = new MeshBasicMaterial({
			map: this.videoTexture,
			overdraw: true,
			side: BackSide
		  });
		  this.movieScreen = new Mesh(this.movieGeometry, this.movieMaterial);
		  this.movieScreen.scale.x = -1;
		  this.movieScreen.quaternion.setFromAxisAngle({
			x: 0,
			y: 1,
			z: 0
		  }, -Math.PI / 2); // display in right eye only
  
		  this.movieScreen.layers.set(2);
		  this.scene.add(this.movieScreen);
		} else if (projection === '360_CUBE') {
		  this.movieGeometry = new BoxGeometry(256, 256, 256);
		  this.movieMaterial = new MeshBasicMaterial({
			map: this.videoTexture,
			overdraw: true,
			side: BackSide
		  });
		  var left = [new Vector2(0, 0.5), new Vector2(0.333, 0.5), new Vector2(0.333, 1), new Vector2(0, 1)];
		  var right = [new Vector2(0.333, 0.5), new Vector2(0.666, 0.5), new Vector2(0.666, 1), new Vector2(0.333, 1)];
		  var top = [new Vector2(0.666, 0.5), new Vector2(1, 0.5), new Vector2(1, 1), new Vector2(0.666, 1)];
		  var bottom = [new Vector2(0, 0), new Vector2(0.333, 0), new Vector2(0.333, 0.5), new Vector2(0, 0.5)];
		  var front = [new Vector2(0.333, 0), new Vector2(0.666, 0), new Vector2(0.666, 0.5), new Vector2(0.333, 0.5)];
		  var back = [new Vector2(0.666, 0), new Vector2(1, 0), new Vector2(1, 0.5), new Vector2(0.666, 0.5)];
		  this.movieGeometry.faceVertexUvs[0] = [];
		  this.movieGeometry.faceVertexUvs[0][0] = [right[2], right[1], right[3]];
		  this.movieGeometry.faceVertexUvs[0][1] = [right[1], right[0], right[3]];
		  this.movieGeometry.faceVertexUvs[0][2] = [left[2], left[1], left[3]];
		  this.movieGeometry.faceVertexUvs[0][3] = [left[1], left[0], left[3]];
		  this.movieGeometry.faceVertexUvs[0][4] = [top[2], top[1], top[3]];
		  this.movieGeometry.faceVertexUvs[0][5] = [top[1], top[0], top[3]];
		  this.movieGeometry.faceVertexUvs[0][6] = [bottom[2], bottom[1], bottom[3]];
		  this.movieGeometry.faceVertexUvs[0][7] = [bottom[1], bottom[0], bottom[3]];
		  this.movieGeometry.faceVertexUvs[0][8] = [front[2], front[1], front[3]];
		  this.movieGeometry.faceVertexUvs[0][9] = [front[1], front[0], front[3]];
		  this.movieGeometry.faceVertexUvs[0][10] = [back[2], back[1], back[3]];
		  this.movieGeometry.faceVertexUvs[0][11] = [back[1], back[0], back[3]];
		  this.movieScreen = new Mesh(this.movieGeometry, this.movieMaterial);
		  this.movieScreen.position.set(position.x, position.y, position.z);
		  this.movieScreen.rotation.y = -Math.PI;
		  this.scene.add(this.movieScreen);
		} else if (projection === '180' || projection === '180_LR' || projection === '180_MONO') {
		  var _geometry = new SphereGeometry(256, this.options_.sphereDetail, this.options_.sphereDetail, Math.PI, Math.PI); // Left eye view
  
  
		  _geometry.scale(-1, 1, 1);
  
		  var _uvs = _geometry.faceVertexUvs[0];
  
		  if (projection !== '180_MONO') {
			for (var _i2 = 0; _i2 < _uvs.length; _i2++) {
			  for (var _j2 = 0; _j2 < 3; _j2++) {
				_uvs[_i2][_j2].x *= 0.5;
			  }
			}
		  }
  
		  this.movieGeometry = new BufferGeometry().fromGeometry(_geometry);
		  this.movieMaterial = new MeshBasicMaterial({
			map: this.videoTexture,
			overdraw: true
		  });
		  this.movieScreen = new Mesh(this.movieGeometry, this.movieMaterial); // display in left eye only
  
		  this.movieScreen.layers.set(1);
		  this.scene.add(this.movieScreen); // Right eye view
  
		  _geometry = new SphereGeometry(256, this.options_.sphereDetail, this.options_.sphereDetail, Math.PI, Math.PI);
  
		  _geometry.scale(-1, 1, 1);
  
		  _uvs = _geometry.faceVertexUvs[0];
  
		  for (var _i3 = 0; _i3 < _uvs.length; _i3++) {
			for (var _j3 = 0; _j3 < 3; _j3++) {
			  _uvs[_i3][_j3].x *= 0.5;
			  _uvs[_i3][_j3].x += 0.5;
			}
		  }
  
		  this.movieGeometry = new BufferGeometry().fromGeometry(_geometry);
		  this.movieMaterial = new MeshBasicMaterial({
			map: this.videoTexture,
			overdraw: true
		  });
		  this.movieScreen = new Mesh(this.movieGeometry, this.movieMaterial); // display in right eye only
  
		  this.movieScreen.layers.set(2);
		  this.scene.add(this.movieScreen);
		} else if (projection === 'EAC' || projection === 'EAC_LR') {
		  var makeScreen = function makeScreen(mapMatrix, scaleMatrix) {
			// "Continuity correction?": because of discontinuous faces and aliasing,
			// we truncate the 2-pixel-wide strips on all discontinuous edges,
			var contCorrect = 2;
			_this2.movieGeometry = new BoxGeometry(256, 256, 256);
			_this2.movieMaterial = new ShaderMaterial({
			  overdraw: true,
			  side: BackSide,
			  uniforms: {
				mapped: {
				  value: _this2.videoTexture
				},
				mapMatrix: {
				  value: mapMatrix
				},
				contCorrect: {
				  value: contCorrect
				},
				faceWH: {
				  value: new Vector2(1 / 3, 1 / 2).applyMatrix3(scaleMatrix)
				},
				vidWH: {
				  value: new Vector2(_this2.videoTexture.image.videoWidth, _this2.videoTexture.image.videoHeight).applyMatrix3(scaleMatrix)
				}
			  },
			  vertexShader: "\nvarying vec2 vUv;\nuniform mat3 mapMatrix;\n\nvoid main() {\n  vUv = (mapMatrix * vec3(uv, 1.)).xy;\n  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.);\n}",
			  fragmentShader: "\nvarying vec2 vUv;\nuniform sampler2D mapped;\nuniform vec2 faceWH;\nuniform vec2 vidWH;\nuniform float contCorrect;\n\nconst float PI = 3.1415926535897932384626433832795;\n\nvoid main() {\n  vec2 corner = vUv - mod(vUv, faceWH) + vec2(0, contCorrect / vidWH.y);\n\n  vec2 faceWHadj = faceWH - vec2(0, contCorrect * 2. / vidWH.y);\n\n  vec2 p = (vUv - corner) / faceWHadj - .5;\n  vec2 q = 2. / PI * atan(2. * p) + .5;\n\n  vec2 eUv = corner + q * faceWHadj;\n\n  gl_FragColor = texture2D(mapped, eUv);\n}"
			});
			var right = [new Vector2(0, 1 / 2), new Vector2(1 / 3, 1 / 2), new Vector2(1 / 3, 1), new Vector2(0, 1)];
			var front = [new Vector2(1 / 3, 1 / 2), new Vector2(2 / 3, 1 / 2), new Vector2(2 / 3, 1), new Vector2(1 / 3, 1)];
			var left = [new Vector2(2 / 3, 1 / 2), new Vector2(1, 1 / 2), new Vector2(1, 1), new Vector2(2 / 3, 1)];
			var bottom = [new Vector2(1 / 3, 0), new Vector2(1 / 3, 1 / 2), new Vector2(0, 1 / 2), new Vector2(0, 0)];
			var back = [new Vector2(1 / 3, 1 / 2), new Vector2(1 / 3, 0), new Vector2(2 / 3, 0), new Vector2(2 / 3, 1 / 2)];
			var top = [new Vector2(1, 0), new Vector2(1, 1 / 2), new Vector2(2 / 3, 1 / 2), new Vector2(2 / 3, 0)];
  
			for (var _i4 = 0, _arr = [right, front, left, bottom, back, top]; _i4 < _arr.length; _i4++) {
			  var face = _arr[_i4];
			  var height = _this2.videoTexture.image.videoHeight;
			  var lowY = 1;
			  var highY = 0;
  
			  for (var _iterator = face, _isArray = Array.isArray(_iterator), _i5 = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {
				var _ref;
  
				if (_isArray) {
				  if (_i5 >= _iterator.length) break;
				  _ref = _iterator[_i5++];
				} else {
				  _i5 = _iterator.next();
				  if (_i5.done) break;
				  _ref = _i5.value;
				}
  
				var vector = _ref;
  
				if (vector.y < lowY) {
				  lowY = vector.y;
				}
  
				if (vector.y > highY) {
				  highY = vector.y;
				}
			  }
  
			  for (var _iterator2 = face, _isArray2 = Array.isArray(_iterator2), _i6 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) {
				var _ref2;
  
				if (_isArray2) {
				  if (_i6 >= _iterator2.length) break;
				  _ref2 = _iterator2[_i6++];
				} else {
				  _i6 = _iterator2.next();
				  if (_i6.done) break;
				  _ref2 = _i6.value;
				}
  
				var _vector = _ref2;
  
				if (Math.abs(_vector.y - lowY) < Number.EPSILON) {
				  _vector.y += contCorrect / height;
				}
  
				if (Math.abs(_vector.y - highY) < Number.EPSILON) {
				  _vector.y -= contCorrect / height;
				}
  
				_vector.x = _vector.x / height * (height - contCorrect * 2) + contCorrect / height;
			  }
			}
  
			_this2.movieGeometry.faceVertexUvs[0] = [];
			_this2.movieGeometry.faceVertexUvs[0][0] = [right[2], right[1], right[3]];
			_this2.movieGeometry.faceVertexUvs[0][1] = [right[1], right[0], right[3]];
			_this2.movieGeometry.faceVertexUvs[0][2] = [left[2], left[1], left[3]];
			_this2.movieGeometry.faceVertexUvs[0][3] = [left[1], left[0], left[3]];
			_this2.movieGeometry.faceVertexUvs[0][4] = [top[2], top[1], top[3]];
			_this2.movieGeometry.faceVertexUvs[0][5] = [top[1], top[0], top[3]];
			_this2.movieGeometry.faceVertexUvs[0][6] = [bottom[2], bottom[1], bottom[3]];
			_this2.movieGeometry.faceVertexUvs[0][7] = [bottom[1], bottom[0], bottom[3]];
			_this2.movieGeometry.faceVertexUvs[0][8] = [front[2], front[1], front[3]];
			_this2.movieGeometry.faceVertexUvs[0][9] = [front[1], front[0], front[3]];
			_this2.movieGeometry.faceVertexUvs[0][10] = [back[2], back[1], back[3]];
			_this2.movieGeometry.faceVertexUvs[0][11] = [back[1], back[0], back[3]];
			_this2.movieScreen = new Mesh(_this2.movieGeometry, _this2.movieMaterial);
  
			_this2.movieScreen.position.set(position.x, position.y, position.z);
  
			_this2.movieScreen.rotation.y = -Math.PI;
			return _this2.movieScreen;
		  };
  
		  if (projection === 'EAC') {
			this.scene.add(makeScreen(new Matrix3(), new Matrix3()));
		  } else {
			var scaleMatrix = new Matrix3().set(0, 0.5, 0, 1, 0, 0, 0, 0, 1);
			makeScreen(new Matrix3().set(0, -0.5, 0.5, 1, 0, 0, 0, 0, 1), scaleMatrix); // display in left eye only
  
			this.movieScreen.layers.set(1);
			this.scene.add(this.movieScreen);
			makeScreen(new Matrix3().set(0, -0.5, 1, 1, 0, 0, 0, 0, 1), scaleMatrix); // display in right eye only
  
			this.movieScreen.layers.set(2);
			this.scene.add(this.movieScreen);
		  }
		}
  
		this.currentProjection_ = projection;
	  };
  
	  _proto.triggerError_ = function triggerError_(errorObj) {
		// if we have videojs-errors use it
		if (this.videojsErrorsSupport_) {
		  this.player_.error(errorObj); // if we don't have videojs-errors just use a normal player error
		} else {
		  // strip any html content from the error message
		  // as it is not supported outside of videojs-errors
		  var div = document$1.createElement('div');
		  div.innerHTML = errors[errorObj.code].message;
		  var message = div.textContent || div.innerText || '';
		  this.player_.error({
			code: errorObj.code,
			message: message
		  });
		}
	  };
  
	  _proto.log = function log() {
		if (!this.options_.debug) {
		  return;
		}
  
		for (var _len = arguments.length, msgs = new Array(_len), _key = 0; _key < _len; _key++) {
		  msgs[_key] = arguments[_key];
		}
  
		msgs.forEach(function (msg) {
		  videojs.log('VR: ', msg);
		});
	  };
  
	  _proto.handleVrDisplayActivate_ = function handleVrDisplayActivate_() {
		var _this3 = this;
  
		if (!this.vrDisplay) {
		  return;
		}
  
		this.vrDisplay.requestPresent([{
		  source: this.renderedCanvas
		}]).then(function () {
		  if (!_this3.vrDisplay.cardboardUI_ || !videojs.browser.IS_IOS) {
			return;
		  } // webvr-polyfill/cardboard ui only watches for click events
		  // to tell that the back arrow button is pressed during cardboard vr.
		  // but somewhere along the line these events are silenced with preventDefault
		  // but only on iOS, so we translate them ourselves here
  
  
		  var touches = [];
  
		  var iosCardboardTouchStart_ = function iosCardboardTouchStart_(e) {
			for (var i = 0; i < e.touches.length; i++) {
			  touches.push(e.touches[i]);
			}
		  };
  
		  var iosCardboardTouchEnd_ = function iosCardboardTouchEnd_(e) {
			if (!touches.length) {
			  return;
			}
  
			touches.forEach(function (t) {
			  var simulatedClick = new window$1.MouseEvent('click', {
				screenX: t.screenX,
				screenY: t.screenY,
				clientX: t.clientX,
				clientY: t.clientY
			  });
  
			  _this3.renderedCanvas.dispatchEvent(simulatedClick);
			});
			touches = [];
		  };
  
		  _this3.renderedCanvas.addEventListener('touchstart', iosCardboardTouchStart_);
  
		  _this3.renderedCanvas.addEventListener('touchend', iosCardboardTouchEnd_);
  
		  _this3.iosRevertTouchToClick_ = function () {
			_this3.renderedCanvas.removeEventListener('touchstart', iosCardboardTouchStart_);
  
			_this3.renderedCanvas.removeEventListener('touchend', iosCardboardTouchEnd_);
  
			_this3.iosRevertTouchToClick_ = null;
		  };
		});
	  };
  
	  _proto.handleVrDisplayDeactivate_ = function handleVrDisplayDeactivate_() {
		if (!this.vrDisplay || !this.vrDisplay.isPresenting) {
		  return;
		}
  
		if (this.iosRevertTouchToClick_) {
		  this.iosRevertTouchToClick_();
		}
  
		this.vrDisplay.exitPresent();
	  };
  
	  _proto.requestAnimationFrame = function requestAnimationFrame(fn) {
		if (this.vrDisplay) {
		  return this.vrDisplay.requestAnimationFrame(fn);
		}
  
		return this.player_.requestAnimationFrame(fn);
	  };
  
	  _proto.cancelAnimationFrame = function cancelAnimationFrame(id) {
		if (this.vrDisplay) {
		  return this.vrDisplay.cancelAnimationFrame(id);
		}
  
		return this.player_.cancelAnimationFrame(id);
	  };
  
	  _proto.togglePlay_ = function togglePlay_() {
		if (this.player_.paused()) {
		  this.player_.play();
		} else {
		  this.player_.pause();
		}
	  };
  
	  _proto.animate_ = function animate_() {
		if (!this.initialized_) {
		  return;
		}
  
		if (this.getVideoEl_().readyState === this.getVideoEl_().HAVE_ENOUGH_DATA) {
		  if (this.videoTexture) {
			this.videoTexture.needsUpdate = true;
		  }
		}
  
		this.controls3d.update();
  
		if (this.omniController) {
		  this.omniController.update(this.camera);
		}
  
		this.effect.render(this.scene, this.camera);
  
		if (window$1.navigator.getGamepads) {
		  // Grab all gamepads
		  var gamepads = window$1.navigator.getGamepads();
  
		  for (var i = 0; i < gamepads.length; ++i) {
			var gamepad = gamepads[i]; // Make sure gamepad is defined
			// Only take input if state has changed since we checked last
  
			if (!gamepad || !gamepad.timestamp || gamepad.timestamp === this.prevTimestamps_[i]) {
			  continue;
			}
  
			for (var j = 0; j < gamepad.buttons.length; ++j) {
			  if (gamepad.buttons[j].pressed) {
				this.togglePlay_();
				this.prevTimestamps_[i] = gamepad.timestamp;
				break;
			  }
			}
		  }
		}
  
		this.camera.getWorldDirection(this.cameraVector);
		this.animationFrameId_ = this.requestAnimationFrame(this.animate_);
	  };
  
	  _proto.handleResize_ = function handleResize_() {
		var width = this.player_.currentWidth();
		var height = this.player_.currentHeight();
		this.effect.setSize(width, height, false);
		this.camera.aspect = width / height;
		this.camera.updateProjectionMatrix();
	  };
  
	  _proto.setProjection = function setProjection(projection) {
		if (!getInternalProjectionName(projection)) {
		  videojs.log.error('videojs-vr: please pass a valid projection ' + validProjections.join(', '));
		  return;
		}
  
		this.currentProjection_ = projection;
		this.defaultProjection_ = projection;
	  };
  
	  _proto.init = function init() {
		var _this4 = this;
  
		this.reset();
		this.camera = new PerspectiveCamera(75, this.player_.currentWidth() / this.player_.currentHeight(), 1, 1000); // Store vector representing the direction in which the camera is looking, in world space.
  
		this.cameraVector = new Vector3();
  
		if (this.currentProjection_ === '360_LR' || this.currentProjection_ === '360_TB' || this.currentProjection_ === '180' || this.currentProjection_ === '180_LR' || this.currentProjection_ === '180_MONO' || this.currentProjection_ === 'EAC_LR') {
		  // Render left eye when not in VR mode
		  this.camera.layers.enable(1);
		}
  
		this.scene = new Scene();
		this.videoTexture = new VideoTexture(this.getVideoEl_()); // shared regardless of wether VideoTexture is used or
		// an image canvas is used
  
		this.videoTexture.generateMipmaps = false;
		this.videoTexture.minFilter = LinearFilter;
		this.videoTexture.magFilter = LinearFilter;
		this.videoTexture.format = RGBFormat;
		this.changeProjection_(this.currentProjection_);
  
		if (this.currentProjection_ === 'NONE') {
		  this.log('Projection is NONE, dont init');
		  this.reset();
		  return;
		}
  
		this.player_.removeChild('BigPlayButton');
		this.player_.addChild('BigVrPlayButton', {}, this.bigPlayButtonIndex_);
		this.player_.bigPlayButton = this.player_.getChild('BigVrPlayButton'); // mobile devices, or cardboard forced to on
  
		if (this.options_.forceCardboard || videojs.browser.IS_ANDROID || videojs.browser.IS_IOS) {
		  this.addCardboardButton_();
		} // if ios remove full screen toggle
  
  
		if (videojs.browser.IS_IOS && this.player_.controlBar && this.player_.controlBar.fullscreenToggle) {
		  this.player_.controlBar.fullscreenToggle.hide();
		}
  
		this.camera.position.set(0, 0, 0);
		this.renderer = new WebGLRenderer({
		  devicePixelRatio: window$1.devicePixelRatio,
		  alpha: false,
		  clearColor: 0xffffff,
		  antialias: true
		});
		var webglContext = this.renderer.getContext('webgl');
		var oldTexImage2D = webglContext.texImage2D;
		/* this is a workaround since threejs uses try catch */
  
		webglContext.texImage2D = function () {
		  try {
			for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
			  args[_key2] = arguments[_key2];
			}
  
			return oldTexImage2D.apply(webglContext, args);
		  } catch (e) {
			_this4.reset();
  
			_this4.player_.pause();
  
			_this4.triggerError_({
			  code: 'web-vr-hls-cors-not-supported',
			  dismiss: false
			});
  
			throw new Error(e);
		  }
		};
  
		this.renderer.setSize(this.player_.currentWidth(), this.player_.currentHeight(), false);
		this.effect = new VREffect(this.renderer);
		this.effect.setSize(this.player_.currentWidth(), this.player_.currentHeight(), false);
		this.vrDisplay = null; // Previous timestamps for gamepad updates
  
		this.prevTimestamps_ = [];
		this.renderedCanvas = this.renderer.domElement;
		this.renderedCanvas.setAttribute('style', 'width: 100%; height: 100%; position: absolute; top:0;');
		var videoElStyle = this.getVideoEl_().style;
		this.player_.el().insertBefore(this.renderedCanvas, this.player_.el().firstChild);
		videoElStyle.zIndex = '-1';
		videoElStyle.opacity = '0';
  
		if (window$1.navigator.getVRDisplays) {
		  this.log('is supported, getting vr displays');
		  window$1.navigator.getVRDisplays().then(function (displays) {
			if (displays.length > 0) {
			  _this4.log('Displays found', displays);
  
			  _this4.vrDisplay = displays[0]; // Native WebVR Head Mounted Displays (HMDs) like the HTC Vive
			  // also need the cardboard button to enter fully immersive mode
			  // so, we want to add the button if we're not polyfilled.
  
			  if (!_this4.vrDisplay.isPolyfilled) {
				_this4.log('Real HMD found using VRControls', _this4.vrDisplay);
  
				_this4.addCardboardButton_(); // We use VRControls here since we are working with an HMD
				// and we only want orientation controls.
  
  
				_this4.controls3d = new VRControls(_this4.camera);
			  }
			}
  
			if (!_this4.controls3d) {
			  _this4.log('no HMD found Using Orbit & Orientation Controls');
  
			  var options = {
				camera: _this4.camera,
				canvas: _this4.renderedCanvas,
				// check if its a half sphere view projection
				halfView: _this4.currentProjection_.indexOf('180') === 0,
				orientation: videojs.browser.IS_IOS || videojs.browser.IS_ANDROID || false
			  };
  
			  if (_this4.options_.motionControls === false) {
				options.orientation = false;
			  }
  
			  _this4.controls3d = new OrbitOrientationControls(options);
			  _this4.canvasPlayerControls = new CanvasPlayerControls(_this4.player_, _this4.renderedCanvas);
			}
  
			_this4.animationFrameId_ = _this4.requestAnimationFrame(_this4.animate_);
		  });
		} else if (window$1.navigator.getVRDevices) {
		  this.triggerError_({
			code: 'web-vr-out-of-date',
			dismiss: false
		  });
		} else {
		  this.triggerError_({
			code: 'web-vr-not-supported',
			dismiss: false
		  });
		}
  
		if (this.options_.omnitone) {
		  var audiocontext = AudioContext.getContext();
		  this.omniController = new OmnitoneController(audiocontext, this.options_.omnitone, this.getVideoEl_(), this.options_.omnitoneOptions);
		  this.omniController.one('audiocontext-suspended', function () {
			_this4.player.pause();
  
			_this4.player.one('playing', function () {
			  audiocontext.resume();
			});
		  });
		}
  
		this.on(this.player_, 'fullscreenchange', this.handleResize_);
		window$1.addEventListener('vrdisplaypresentchange', this.handleResize_, true);
		window$1.addEventListener('resize', this.handleResize_, true);
		window$1.addEventListener('vrdisplayactivate', this.handleVrDisplayActivate_, true);
		window$1.addEventListener('vrdisplaydeactivate', this.handleVrDisplayDeactivate_, true);
		this.initialized_ = true;
		this.trigger('initialized');
	  };
  
	  _proto.addCardboardButton_ = function addCardboardButton_() {
		if (!this.player_.controlBar.getChild('CardboardButton')) {
		  this.player_.controlBar.addChild('CardboardButton', {});
		}
	  };
  
	  _proto.getVideoEl_ = function getVideoEl_() {
		return this.player_.el().getElementsByTagName('video')[0];
	  };
  
	  _proto.reset = function reset() {
		if (!this.initialized_) {
		  return;
		}
  
		if (this.omniController) {
		  this.omniController.off('audiocontext-suspended');
		  this.omniController.dispose();
		  this.omniController = undefined;
		}
  
		if (this.controls3d) {
		  this.controls3d.dispose();
		  this.controls3d = null;
		}
  
		if (this.canvasPlayerControls) {
		  this.canvasPlayerControls.dispose();
		  this.canvasPlayerControls = null;
		}
  
		if (this.effect) {
		  this.effect.dispose();
		  this.effect = null;
		}
  
		window$1.removeEventListener('resize', this.handleResize_, true);
		window$1.removeEventListener('vrdisplaypresentchange', this.handleResize_, true);
		window$1.removeEventListener('vrdisplayactivate', this.handleVrDisplayActivate_, true);
		window$1.removeEventListener('vrdisplaydeactivate', this.handleVrDisplayDeactivate_, true); // re-add the big play button to player
  
		if (!this.player_.getChild('BigPlayButton')) {
		  this.player_.addChild('BigPlayButton', {}, this.bigPlayButtonIndex_);
		}
  
		if (this.player_.getChild('BigVrPlayButton')) {
		  this.player_.removeChild('BigVrPlayButton');
		} // remove the cardboard button
  
  
		if (this.player_.getChild('CardboardButton')) {
		  this.player_.controlBar.removeChild('CardboardButton');
		} // show the fullscreen again
  
  
		if (videojs.browser.IS_IOS && this.player_.controlBar && this.player_.controlBar.fullscreenToggle) {
		  this.player_.controlBar.fullscreenToggle.show();
		} // reset the video element style so that it will be displayed
  
  
		var videoElStyle = this.getVideoEl_().style;
		videoElStyle.zIndex = '';
		videoElStyle.opacity = ''; // set the current projection to the default
  
		this.currentProjection_ = this.defaultProjection_; // reset the ios touch to click workaround
  
		if (this.iosRevertTouchToClick_) {
		  this.iosRevertTouchToClick_();
		} // remove the old canvas
  
  
		if (this.renderedCanvas) {
		  this.renderedCanvas.parentNode.removeChild(this.renderedCanvas);
		}
  
		if (this.animationFrameId_) {
		  this.cancelAnimationFrame(this.animationFrameId_);
		}
  
		this.initialized_ = false;
	  };
  
	  _proto.dispose = function dispose() {
		_Plugin.prototype.dispose.call(this);
  
		this.reset();
	  };
  
	  _proto.polyfillVersion = function polyfillVersion() {
		return WebVRPolyfill.version;
	  };
  
	  return VR;
	}(Plugin);
  
	VR.prototype.setTimeout = Component.prototype.setTimeout;
	VR.prototype.clearTimeout = Component.prototype.clearTimeout;
	VR.VERSION = version;
	videojs.registerPlugin('vr', VR);
  
	return VR;
  
  })));
// source --> https://eternize360.com.br/wp-content/plugins/wpvr-pro/admin/lib/pannellum/src/js/videojs-pannellum-plugin.js?ver=1 
/*
 * Video.js plugin for Pannellum
 * Copyright (c) 2015-2018 Matthew Petroff
 * MIT License
 */

(function(document, videojs, pannellum) {
'use strict';

var registerPlugin = videojs.registerPlugin || videojs.plugin;  // Use registerPlugin for Video.js >= 6
registerPlugin('pannellum', function(config) {
    // Create Pannellum instance
    var player = this;
    var container = player.el();
    var vid = container.getElementsByTagName('video')[0],
        pnlmContainer = document.createElement('div');
    pnlmContainer.style.zIndex = '0';
    config = config || {};
    config.type = 'equirectangular';
    config.dynamic = true;
    config.showZoomCtrl = false;
    config.showFullscreenCtrl = false;
    config.autoLoad = true;
    config.panorama = vid;
    pnlmContainer.style.visibility = 'hidden';
    player.pnlmViewer = pannellum.viewer(pnlmContainer, config);
    container.insertBefore(pnlmContainer, container.firstChild);
    vid.style.display = 'none';

    // Handle update settings
    player.on('play', function() {
        if (vid.readyState > 1)
            player.pnlmViewer.setUpdate(true);
    });
    player.on('canplay', function() {
        if (!player.paused())
            player.pnlmViewer.setUpdate(true);
    });
    player.on('pause', function() {
        player.pnlmViewer.setUpdate(false);
    });
    player.on('loadeddata', function() {
        pnlmContainer.style.visibility = 'visible';
    });
    player.on('seeking', function() {
        if (player.paused())
            player.pnlmViewer.setUpdate(true);
    });
    player.on('seeked', function() {
        if (player.paused())
            player.pnlmViewer.setUpdate(false);
    });
});

})(document, videojs, pannellum);
// source --> https://eternize360.com.br/wp-content/plugins/wpvr-pro/admin/js/owl.carousel.js?ver=6.9.4 
/**
 * Owl carousel
 * @version 2.3.4
 * @author Bartosz Wojciechowski
 * @author David Deutsch
 * @license The MIT License (MIT)
 * @todo Lazy Load Icon
 * @todo prevent animationend bubling
 * @todo itemsScaleUp
 * @todo Test Zepto
 * @todo stagePadding calculate wrong active classes
 */
;(function($, window, document, undefined) {

	/**
	 * Creates a carousel.
	 * @class The Owl Carousel.
	 * @public
	 * @param {HTMLElement|jQuery} element - The element to create the carousel for.
	 * @param {Object} [options] - The options
	 */
	function Owl(element, options) {

		/**
		 * Current settings for the carousel.
		 * @public
		 */
		this.settings = null;

		/**
		 * Current options set by the caller including defaults.
		 * @public
		 */
		this.options = $.extend({}, Owl.Defaults, options);

		/**
		 * Plugin element.
		 * @public
		 */
		this.$element = $(element);

		/**
		 * Proxied event handlers.
		 * @protected
		 */
		this._handlers = {};

		/**
		 * References to the running plugins of this carousel.
		 * @protected
		 */
		this._plugins = {};

		/**
		 * Currently suppressed events to prevent them from being retriggered.
		 * @protected
		 */
		this._supress = {};

		/**
		 * Absolute current position.
		 * @protected
		 */
		this._current = null;

		/**
		 * Animation speed in milliseconds.
		 * @protected
		 */
		this._speed = null;

		/**
		 * Coordinates of all items in pixel.
		 * @todo The name of this member is missleading.
		 * @protected
		 */
		this._coordinates = [];

		/**
		 * Current breakpoint.
		 * @todo Real media queries would be nice.
		 * @protected
		 */
		this._breakpoint = null;

		/**
		 * Current width of the plugin element.
		 */
		this._width = null;

		/**
		 * All real items.
		 * @protected
		 */
		this._items = [];

		/**
		 * All cloned items.
		 * @protected
		 */
		this._clones = [];

		/**
		 * Merge values of all items.
		 * @todo Maybe this could be part of a plugin.
		 * @protected
		 */
		this._mergers = [];

		/**
		 * Widths of all items.
		 */
		this._widths = [];

		/**
		 * Invalidated parts within the update process.
		 * @protected
		 */
		this._invalidated = {};

		/**
		 * Ordered list of workers for the update process.
		 * @protected
		 */
		this._pipe = [];

		/**
		 * Current state information for the drag operation.
		 * @todo #261
		 * @protected
		 */
		this._drag = {
			time: null,
			target: null,
			pointer: null,
			stage: {
				start: null,
				current: null
			},
			direction: null
		};

		/**
		 * Current state information and their tags.
		 * @type {Object}
		 * @protected
		 */
		this._states = {
			current: {},
			tags: {
				'initializing': [ 'busy' ],
				'animating': [ 'busy' ],
				'dragging': [ 'interacting' ]
			}
		};

		$.each([ 'onResize', 'onThrottledResize' ], $.proxy(function(i, handler) {
			this._handlers[handler] = $.proxy(this[handler], this);
		}, this));

		$.each(Owl.Plugins, $.proxy(function(key, plugin) {
			this._plugins[key.charAt(0).toLowerCase() + key.slice(1)]
				= new plugin(this);
		}, this));

		$.each(Owl.Workers, $.proxy(function(priority, worker) {
			this._pipe.push({
				'filter': worker.filter,
				'run': $.proxy(worker.run, this)
			});
		}, this));

		this.setup();
		this.initialize();
	}

	/**
	 * Default options for the carousel.
	 * @public
	 */
	Owl.Defaults = {
		items: 3,
		loop: false,
		center: false,
		rewind: false,
		checkVisibility: true,

		mouseDrag: true,
		touchDrag: true,
		pullDrag: true,
		freeDrag: false,

		margin: 0,
		stagePadding: 0,

		merge: false,
		mergeFit: true,
		autoWidth: false,

		startPosition: 0,
		rtl: false,

		smartSpeed: 250,
		fluidSpeed: false,
		dragEndSpeed: false,

		responsive: {},
		responsiveRefreshRate: 200,
		responsiveBaseElement: window,

		fallbackEasing: 'swing',
		slideTransition: '',

		info: false,

		nestedItemSelector: false,
		itemElement: 'div',
		stageElement: 'div',

		refreshClass: 'owl-refresh',
		loadedClass: 'owl-loaded',
		loadingClass: 'owl-loading',
		rtlClass: 'owl-rtl',
		responsiveClass: 'owl-responsive',
		dragClass: 'owl-drag',
		itemClass: 'owl-item',
		stageClass: 'owl-stage',
		stageOuterClass: 'owl-stage-outer',
		grabClass: 'owl-grab'
	};

	/**
	 * Enumeration for width.
	 * @public
	 * @readonly
	 * @enum {String}
	 */
	Owl.Width = {
		Default: 'default',
		Inner: 'inner',
		Outer: 'outer'
	};

	/**
	 * Enumeration for types.
	 * @public
	 * @readonly
	 * @enum {String}
	 */
	Owl.Type = {
		Event: 'event',
		State: 'state'
	};

	/**
	 * Contains all registered plugins.
	 * @public
	 */
	Owl.Plugins = {};

	/**
	 * List of workers involved in the update process.
	 */
	Owl.Workers = [ {
		filter: [ 'width', 'settings' ],
		run: function() {
			this._width = this.$element.width();
		}
	}, {
		filter: [ 'width', 'items', 'settings' ],
		run: function(cache) {
			cache.current = this._items && this._items[this.relative(this._current)];
		}
	}, {
		filter: [ 'items', 'settings' ],
		run: function() {
			this.$stage.children('.cloned').remove();
		}
	}, {
		filter: [ 'width', 'items', 'settings' ],
		run: function(cache) {
			var margin = this.settings.margin || '',
				grid = !this.settings.autoWidth,
				rtl = this.settings.rtl,
				css = {
					'width': 'auto',
					'margin-left': rtl ? margin : '',
					'margin-right': rtl ? '' : margin
				};

			!grid && this.$stage.children().css(css);

			cache.css = css;
		}
	}, {
		filter: [ 'width', 'items', 'settings' ],
		run: function(cache) {
			var width = (this.width() / this.settings.items).toFixed(3) - this.settings.margin,
				merge = null,
				iterator = this._items.length,
				grid = !this.settings.autoWidth,
				widths = [];

			cache.items = {
				merge: false,
				width: width
			};

			while (iterator--) {
				merge = this._mergers[iterator];
				merge = this.settings.mergeFit && Math.min(merge, this.settings.items) || merge;

				cache.items.merge = merge > 1 || cache.items.merge;

				widths[iterator] = !grid ? this._items[iterator].width() : width * merge;
			}

			this._widths = widths;
		}
	}, {
		filter: [ 'items', 'settings' ],
		run: function() {
			var clones = [],
				items = this._items,
				settings = this.settings,
				// TODO: Should be computed from number of min width items in stage
				view = Math.max(settings.items * 2, 4),
				size = Math.ceil(items.length / 2) * 2,
				repeat = settings.loop && items.length ? settings.rewind ? view : Math.max(view, size) : 0,
				append = '',
				prepend = '';

			repeat /= 2;

			while (repeat > 0) {
				// Switch to only using appended clones
				clones.push(this.normalize(clones.length / 2, true));
				$(items[clones[clones.length - 1]][0]).clone(true).addClass('cloned').appendTo(this.$stage);
				clones.push(this.normalize(items.length - 1 - (clones.length - 1) / 2, true));
				$(items[clones[clones.length - 1]][0]).clone(true).addClass('cloned').prependTo(this.$stage);
				repeat -= 1;
			}
			this._clones = clones;
		}
	}, {
		filter: [ 'width', 'items', 'settings' ],
		run: function() {
			var rtl = this.settings.rtl ? 1 : -1,
				size = this._clones.length + this._items.length,
				iterator = -1,
				previous = 0,
				current = 0,
				coordinates = [];

			while (++iterator < size) {
				previous = coordinates[iterator - 1] || 0;
				current = this._widths[this.relative(iterator)] + this.settings.margin;
				coordinates.push(previous + current * rtl);
			}

			this._coordinates = coordinates;
		}
	}, {
		filter: [ 'width', 'items', 'settings' ],
		run: function() {
			var padding = this.settings.stagePadding,
				coordinates = this._coordinates,
				css = {
					'width': Math.ceil(Math.abs(coordinates[coordinates.length - 1])) + padding * 2,
					'padding-left': padding || '',
					'padding-right': padding || ''
				};

			this.$stage.css(css);
		}
	}, {
		filter: [ 'width', 'items', 'settings' ],
		run: function(cache) {
			var iterator = this._coordinates.length,
				grid = !this.settings.autoWidth,
				items = this.$stage.children();

			if (grid && cache.items.merge) {
				while (iterator--) {
					cache.css.width = this._widths[this.relative(iterator)];
					items.eq(iterator).css(cache.css);
				}
			} else if (grid) {
				cache.css.width = cache.items.width;
				items.css(cache.css);
			}
		}
	}, {
		filter: [ 'items' ],
		run: function() {
			this._coordinates.length < 1 && this.$stage.removeAttr('style');
		}
	}, {
		filter: [ 'width', 'items', 'settings' ],
		run: function(cache) {
			cache.current = cache.current ? this.$stage.children().index(cache.current) : 0;
			cache.current = Math.max(this.minimum(), Math.min(this.maximum(), cache.current));
			this.reset(cache.current);
		}
	}, {
		filter: [ 'position' ],
		run: function() {
			this.animate(this.coordinates(this._current));
		}
	}, {
		filter: [ 'width', 'position', 'items', 'settings' ],
		run: function() {
			var rtl = this.settings.rtl ? 1 : -1,
				padding = this.settings.stagePadding * 2,
				begin = this.coordinates(this.current()) + padding,
				end = begin + this.width() * rtl,
				inner, outer, matches = [], i, n;

			for (i = 0, n = this._coordinates.length; i < n; i++) {
				inner = this._coordinates[i - 1] || 0;
				outer = Math.abs(this._coordinates[i]) + padding * rtl;

				if ((this.op(inner, '<=', begin) && (this.op(inner, '>', end)))
					|| (this.op(outer, '<', begin) && this.op(outer, '>', end))) {
					matches.push(i);
				}
			}

			this.$stage.children('.active').removeClass('active');
			this.$stage.children(':eq(' + matches.join('), :eq(') + ')').addClass('active');

			this.$stage.children('.center').removeClass('center');
			if (this.settings.center) {
				this.$stage.children().eq(this.current()).addClass('center');
			}
		}
	} ];

	/**
	 * Create the stage DOM element
	 */
	Owl.prototype.initializeStage = function() {
		this.$stage = this.$element.find('.' + this.settings.stageClass);

		// if the stage is already in the DOM, grab it and skip stage initialization
		if (this.$stage.length) {
			return;
		}

		this.$element.addClass(this.options.loadingClass);

		// create stage
		this.$stage = $('<' + this.settings.stageElement + '>', {
			"class": this.settings.stageClass
		}).wrap( $( '<div/>', {
			"class": this.settings.stageOuterClass
		}));

		// append stage
		this.$element.append(this.$stage.parent());
	};

	/**
	 * Create item DOM elements
	 */
	Owl.prototype.initializeItems = function() {
		var $items = this.$element.find('.owl-item');

		// if the items are already in the DOM, grab them and skip item initialization
		if ($items.length) {
			this._items = $items.get().map(function(item) {
				return $(item);
			});

			this._mergers = this._items.map(function() {
				return 1;
			});

			this.refresh();

			return;
		}

		// append content
		this.replace(this.$element.children().not(this.$stage.parent()));

		// check visibility
		if (this.isVisible()) {
			// update view
			this.refresh();
		} else {
			// invalidate width
			this.invalidate('width');
		}

		this.$element
			.removeClass(this.options.loadingClass)
			.addClass(this.options.loadedClass);
	};

	/**
	 * Initializes the carousel.
	 * @protected
	 */
	Owl.prototype.initialize = function() {
		this.enter('initializing');
		this.trigger('initialize');

		this.$element.toggleClass(this.settings.rtlClass, this.settings.rtl);

		if (this.settings.autoWidth && !this.is('pre-loading')) {
			var imgs, nestedSelector, width;
			imgs = this.$element.find('img');
			nestedSelector = this.settings.nestedItemSelector ? '.' + this.settings.nestedItemSelector : undefined;
			width = this.$element.children(nestedSelector).width();

			if (imgs.length && width <= 0) {
				this.preloadAutoWidthImages(imgs);
			}
		}

		this.initializeStage();
		this.initializeItems();

		// register event handlers
		this.registerEventHandlers();

		this.leave('initializing');
		this.trigger('initialized');
	};

	/**
	 * @returns {Boolean} visibility of $element
	 *                    if you know the carousel will always be visible you can set `checkVisibility` to `false` to
	 *                    prevent the expensive browser layout forced reflow the $element.is(':visible') does
	 */
	Owl.prototype.isVisible = function() {
		return this.settings.checkVisibility
			? this.$element.is(':visible')
			: true;
	};

	/**
	 * Setups the current settings.
	 * @todo Remove responsive classes. Why should adaptive designs be brought into IE8?
	 * @todo Support for media queries by using `matchMedia` would be nice.
	 * @public
	 */
	Owl.prototype.setup = function() {
		var viewport = this.viewport(),
			overwrites = this.options.responsive,
			match = -1,
			settings = null;

		if (!overwrites) {
			settings = $.extend({}, this.options);
		} else {
			$.each(overwrites, function(breakpoint) {
				if (breakpoint <= viewport && breakpoint > match) {
					match = Number(breakpoint);
				}
			});

			settings = $.extend({}, this.options, overwrites[match]);
			if (typeof settings.stagePadding === 'function') {
				settings.stagePadding = settings.stagePadding();
			}
			delete settings.responsive;

			// responsive class
			if (settings.responsiveClass) {
				this.$element.attr('class',
					this.$element.attr('class').replace(new RegExp('(' + this.options.responsiveClass + '-)\\S+\\s', 'g'), '$1' + match)
				);
			}
		}

		this.trigger('change', { property: { name: 'settings', value: settings } });
		this._breakpoint = match;
		this.settings = settings;
		this.invalidate('settings');
		this.trigger('changed', { property: { name: 'settings', value: this.settings } });
	};

	/**
	 * Updates option logic if necessery.
	 * @protected
	 */
	Owl.prototype.optionsLogic = function() {
		if (this.settings.autoWidth) {
			this.settings.stagePadding = false;
			this.settings.merge = false;
		}
	};

	/**
	 * Prepares an item before add.
	 * @todo Rename event parameter `content` to `item`.
	 * @protected
	 * @returns {jQuery|HTMLElement} - The item container.
	 */
	Owl.prototype.prepare = function(item) {
		var event = this.trigger('prepare', { content: item });

		if (!event.data) {
			event.data = $('<' + this.settings.itemElement + '/>')
				.addClass(this.options.itemClass).append(item)
		}

		this.trigger('prepared', { content: event.data });

		return event.data;
	};

	/**
	 * Updates the view.
	 * @public
	 */
	Owl.prototype.update = function() {
		var i = 0,
			n = this._pipe.length,
			filter = $.proxy(function(p) { return this[p] }, this._invalidated),
			cache = {};

		while (i < n) {
			if (this._invalidated.all || $.grep(this._pipe[i].filter, filter).length > 0) {
				this._pipe[i].run(cache);
			}
			i++;
		}

		this._invalidated = {};

		!this.is('valid') && this.enter('valid');
	};

	/**
	 * Gets the width of the view.
	 * @public
	 * @param {Owl.Width} [dimension=Owl.Width.Default] - The dimension to return.
	 * @returns {Number} - The width of the view in pixel.
	 */
	Owl.prototype.width = function(dimension) {
		dimension = dimension || Owl.Width.Default;
		switch (dimension) {
			case Owl.Width.Inner:
			case Owl.Width.Outer:
				return this._width;
			default:
				return this._width - this.settings.stagePadding * 2 + this.settings.margin;
		}
	};

	/**
	 * Refreshes the carousel primarily for adaptive purposes.
	 * @public
	 */
	Owl.prototype.refresh = function(resizing) {
		resizing = resizing || false;

		this.enter('refreshing');
		this.trigger('refresh');

		this.setup();

		this.optionsLogic();

		this.$element.addClass(this.options.refreshClass);

		this.update();

		if (!resizing) {
			this.onResize();
		}

		this.$element.removeClass(this.options.refreshClass);

		this.leave('refreshing');
		this.trigger('refreshed');
	};

	/**
	 * Checks window `resize` event.
	 * @protected
	 */
	Owl.prototype.onThrottledResize = function() {
		window.clearTimeout(this.resizeTimer);
		this.resizeTimer = window.setTimeout(this._handlers.onResize, this.settings.responsiveRefreshRate);
	};

	/**
	 * Checks window `resize` event.
	 * @protected
	 */
	Owl.prototype.onResize = function() {
		var resizing = true;

		if (!this._items.length) {
			return false;
		}

		if (this._width === this.$element.width()) {
			return false;
		}

		if (!this.isVisible()) {
			return false;
		}

		this.enter('resizing');

		if (this.trigger('resize').isDefaultPrevented()) {
			this.leave('resizing');
			return false;
		}

		this.invalidate('width');

		this.refresh(resizing);

		this.leave('resizing');
		this.trigger('resized');
	};

	/**
	 * Registers event handlers.
	 * @todo Check `msPointerEnabled`
	 * @todo #261
	 * @protected
	 */
	Owl.prototype.registerEventHandlers = function() {
		if ($.support.transition) {
			this.$stage.on($.support.transition.end + '.owl.core', $.proxy(this.onTransitionEnd, this));
		}

		if (this.settings.responsive !== false) {
			this.on(window, 'resize', this._handlers.onThrottledResize);
		}

		if (this.settings.mouseDrag) {
			this.$element.addClass(this.options.dragClass);
			this.$stage.on('mousedown.owl.core', $.proxy(this.onDragStart, this));
			this.$stage.on('dragstart.owl.core selectstart.owl.core', function() { return false });
		}

		if (this.settings.touchDrag){
			this.$stage.on('touchstart.owl.core', $.proxy(this.onDragStart, this));
			this.$stage.on('touchcancel.owl.core', $.proxy(this.onDragEnd, this));
		}
	};

	/**
	 * Handles `touchstart` and `mousedown` events.
	 * @todo Horizontal swipe threshold as option
	 * @todo #261
	 * @protected
	 * @param {Event} event - The event arguments.
	 */
	Owl.prototype.onDragStart = function(event) {
		var stage = null;

		if (event.which === 3) {
			return;
		}

		if ($.support.transform) {
			stage = this.$stage.css('transform').replace(/.*\(|\)| /g, '').split(',');
			stage = {
				x: stage[stage.length === 16 ? 12 : 4],
				y: stage[stage.length === 16 ? 13 : 5]
			};
		} else {
			stage = this.$stage.position();
			stage = {
				x: this.settings.rtl ?
					stage.left + this.$stage.width() - this.width() + this.settings.margin :
					stage.left,
				y: stage.top
			};
		}

		if (this.is('animating')) {
			$.support.transform ? this.animate(stage.x) : this.$stage.stop()
			this.invalidate('position');
		}

		this.$element.toggleClass(this.options.grabClass, event.type === 'mousedown');

		this.speed(0);

		this._drag.time = new Date().getTime();
		this._drag.target = $(event.target);
		this._drag.stage.start = stage;
		this._drag.stage.current = stage;
		this._drag.pointer = this.pointer(event);

		$(document).on('mouseup.owl.core touchend.owl.core', $.proxy(this.onDragEnd, this));

		$(document).one('mousemove.owl.core touchmove.owl.core', $.proxy(function(event) {
			var delta = this.difference(this._drag.pointer, this.pointer(event));

			$(document).on('mousemove.owl.core touchmove.owl.core', $.proxy(this.onDragMove, this));

			if (Math.abs(delta.x) < Math.abs(delta.y) && this.is('valid')) {
				return;
			}

			event.preventDefault();

			this.enter('dragging');
			this.trigger('drag');
		}, this));
	};

	/**
	 * Handles the `touchmove` and `mousemove` events.
	 * @todo #261
	 * @protected
	 * @param {Event} event - The event arguments.
	 */
	Owl.prototype.onDragMove = function(event) {
		var minimum = null,
			maximum = null,
			pull = null,
			delta = this.difference(this._drag.pointer, this.pointer(event)),
			stage = this.difference(this._drag.stage.start, delta);

		if (!this.is('dragging')) {
			return;
		}

		event.preventDefault();

		if (this.settings.loop) {
			minimum = this.coordinates(this.minimum());
			maximum = this.coordinates(this.maximum() + 1) - minimum;
			stage.x = (((stage.x - minimum) % maximum + maximum) % maximum) + minimum;
		} else {
			minimum = this.settings.rtl ? this.coordinates(this.maximum()) : this.coordinates(this.minimum());
			maximum = this.settings.rtl ? this.coordinates(this.minimum()) : this.coordinates(this.maximum());
			pull = this.settings.pullDrag ? -1 * delta.x / 5 : 0;
			stage.x = Math.max(Math.min(stage.x, minimum + pull), maximum + pull);
		}

		this._drag.stage.current = stage;

		this.animate(stage.x);
	};

	/**
	 * Handles the `touchend` and `mouseup` events.
	 * @todo #261
	 * @todo Threshold for click event
	 * @protected
	 * @param {Event} event - The event arguments.
	 */
	Owl.prototype.onDragEnd = function(event) {
		var delta = this.difference(this._drag.pointer, this.pointer(event)),
			stage = this._drag.stage.current,
			direction = delta.x > 0 ^ this.settings.rtl ? 'left' : 'right';

		$(document).off('.owl.core');

		this.$element.removeClass(this.options.grabClass);

		if (delta.x !== 0 && this.is('dragging') || !this.is('valid')) {
			this.speed(this.settings.dragEndSpeed || this.settings.smartSpeed);
			this.current(this.closest(stage.x, delta.x !== 0 ? direction : this._drag.direction));
			this.invalidate('position');
			this.update();

			this._drag.direction = direction;

			if (Math.abs(delta.x) > 3 || new Date().getTime() - this._drag.time > 300) {
				this._drag.target.one('click.owl.core', function() { return false; });
			}
		}

		if (!this.is('dragging')) {
			return;
		}

		this.leave('dragging');
		this.trigger('dragged');
	};

	/**
	 * Gets absolute position of the closest item for a coordinate.
	 * @todo Setting `freeDrag` makes `closest` not reusable. See #165.
	 * @protected
	 * @param {Number} coordinate - The coordinate in pixel.
	 * @param {String} direction - The direction to check for the closest item. Ether `left` or `right`.
	 * @return {Number} - The absolute position of the closest item.
	 */
	Owl.prototype.closest = function(coordinate, direction) {
		var position = -1,
			pull = 30,
			width = this.width(), // visible carousel width
			count = this.settings.items,
			itemWidth = Math.round(width / count),
			coordinates = this.coordinates();

		if (!this.settings.freeDrag) {
			// check closest item
			$.each(coordinates, $.proxy(function(index, value) {
				// on a left pull, check on current index
				if (direction === 'left' && coordinate > value - pull && coordinate < value + pull) {
					position = index;
				// on a right pull, check on previous index
				// to do so, subtract width from value and set position = index + 1
				} else if (direction === 'right' && coordinate > value - itemWidth - pull && coordinate < value - itemWidth + pull) {
					position = index + 1;
				} else if (this.op(coordinate, '<', value)
					&& this.op(coordinate, '>', coordinates[index + 1] !== undefined ? coordinates[index + 1] : value - width)) {
					position = direction === 'left' ? index + 1 : index;
				}
				return position === -1;
			}, this));
		}

		if (!this.settings.loop) {
			// non loop boundries
			if (this.op(coordinate, '>', coordinates[this.minimum()])) {
				position = coordinate = this.minimum();
			} else if (this.op(coordinate, '<', coordinates[this.maximum()])) {
				position = coordinate = this.maximum();
			}
		}

		return position;
	};

	/**
	 * Animates the stage.
	 * @todo #270
	 * @public
	 * @param {Number} coordinate - The coordinate in pixels.
	 */
	Owl.prototype.animate = function(coordinate) {
		var animate = this.speed() > 0;

		this.is('animating') && this.onTransitionEnd();

		if (animate) {
			this.enter('animating');
			this.trigger('translate');
		}

		if ($.support.transform3d && $.support.transition) {
			this.$stage.css({
				transform: 'translate3d(' + coordinate + 'px,0px,0px)',
				transition: (this.speed() / 1000) + 's' + (
					this.settings.slideTransition ? ' ' + this.settings.slideTransition : ''
				)
			});
		} else if (animate) {
			this.$stage.animate({
				left: coordinate + 'px'
			}, this.speed(), this.settings.fallbackEasing, $.proxy(this.onTransitionEnd, this));
		} else {
			this.$stage.css({
				left: coordinate + 'px'
			});
		}
	};

	/**
	 * Checks whether the carousel is in a specific state or not.
	 * @param {String} state - The state to check.
	 * @returns {Boolean} - The flag which indicates if the carousel is busy.
	 */
	Owl.prototype.is = function(state) {
		return this._states.current[state] && this._states.current[state] > 0;
	};

	/**
	 * Sets the absolute position of the current item.
	 * @public
	 * @param {Number} [position] - The new absolute position or nothing to leave it unchanged.
	 * @returns {Number} - The absolute position of the current item.
	 */
	Owl.prototype.current = function(position) {
		if (position === undefined) {
			return this._current;
		}

		if (this._items.length === 0) {
			return undefined;
		}

		position = this.normalize(position);

		if (this._current !== position) {
			var event = this.trigger('change', { property: { name: 'position', value: position } });

			if (event.data !== undefined) {
				position = this.normalize(event.data);
			}

			this._current = position;

			this.invalidate('position');

			this.trigger('changed', { property: { name: 'position', value: this._current } });
		}

		return this._current;
	};

	/**
	 * Invalidates the given part of the update routine.
	 * @param {String} [part] - The part to invalidate.
	 * @returns {Array.<String>} - The invalidated parts.
	 */
	Owl.prototype.invalidate = function(part) {
		if ($.type(part) === 'string') {
			this._invalidated[part] = true;
			this.is('valid') && this.leave('valid');
		}
		return $.map(this._invalidated, function(v, i) { return i });
	};

	/**
	 * Resets the absolute position of the current item.
	 * @public
	 * @param {Number} position - The absolute position of the new item.
	 */
	Owl.prototype.reset = function(position) {
		position = this.normalize(position);

		if (position === undefined) {
			return;
		}

		this._speed = 0;
		this._current = position;

		this.suppress([ 'translate', 'translated' ]);

		this.animate(this.coordinates(position));

		this.release([ 'translate', 'translated' ]);
	};

	/**
	 * Normalizes an absolute or a relative position of an item.
	 * @public
	 * @param {Number} position - The absolute or relative position to normalize.
	 * @param {Boolean} [relative=false] - Whether the given position is relative or not.
	 * @returns {Number} - The normalized position.
	 */
	Owl.prototype.normalize = function(position, relative) {
		var n = this._items.length,
			m = relative ? 0 : this._clones.length;

		if (!this.isNumeric(position) || n < 1) {
			position = undefined;
		} else if (position < 0 || position >= n + m) {
			position = ((position - m / 2) % n + n) % n + m / 2;
		}

		return position;
	};

	/**
	 * Converts an absolute position of an item into a relative one.
	 * @public
	 * @param {Number} position - The absolute position to convert.
	 * @returns {Number} - The converted position.
	 */
	Owl.prototype.relative = function(position) {
		position -= this._clones.length / 2;
		return this.normalize(position, true);
	};

	/**
	 * Gets the maximum position for the current item.
	 * @public
	 * @param {Boolean} [relative=false] - Whether to return an absolute position or a relative position.
	 * @returns {Number}
	 */
	Owl.prototype.maximum = function(relative) {
		var settings = this.settings,
			maximum = this._coordinates.length,
			iterator,
			reciprocalItemsWidth,
			elementWidth;

		if (settings.loop) {
			maximum = this._clones.length / 2 + this._items.length - 1;
		} else if (settings.autoWidth || settings.merge) {
			iterator = this._items.length;
			if (iterator) {
				reciprocalItemsWidth = this._items[--iterator].width();
				elementWidth = this.$element.width();
				while (iterator--) {
					reciprocalItemsWidth += this._items[iterator].width() + this.settings.margin;
					if (reciprocalItemsWidth > elementWidth) {
						break;
					}
				}
			}
			maximum = iterator + 1;
		} else if (settings.center) {
			maximum = this._items.length - 1;
		} else {
			maximum = this._items.length - settings.items;
		}

		if (relative) {
			maximum -= this._clones.length / 2;
		}

		return Math.max(maximum, 0);
	};

	/**
	 * Gets the minimum position for the current item.
	 * @public
	 * @param {Boolean} [relative=false] - Whether to return an absolute position or a relative position.
	 * @returns {Number}
	 */
	Owl.prototype.minimum = function(relative) {
		return relative ? 0 : this._clones.length / 2;
	};

	/**
	 * Gets an item at the specified relative position.
	 * @public
	 * @param {Number} [position] - The relative position of the item.
	 * @return {jQuery|Array.<jQuery>} - The item at the given position or all items if no position was given.
	 */
	Owl.prototype.items = function(position) {
		if (position === undefined) {
			return this._items.slice();
		}

		position = this.normalize(position, true);
		return this._items[position];
	};

	/**
	 * Gets an item at the specified relative position.
	 * @public
	 * @param {Number} [position] - The relative position of the item.
	 * @return {jQuery|Array.<jQuery>} - The item at the given position or all items if no position was given.
	 */
	Owl.prototype.mergers = function(position) {
		if (position === undefined) {
			return this._mergers.slice();
		}

		position = this.normalize(position, true);
		return this._mergers[position];
	};

	/**
	 * Gets the absolute positions of clones for an item.
	 * @public
	 * @param {Number} [position] - The relative position of the item.
	 * @returns {Array.<Number>} - The absolute positions of clones for the item or all if no position was given.
	 */
	Owl.prototype.clones = function(position) {
		var odd = this._clones.length / 2,
			even = odd + this._items.length,
			map = function(index) { return index % 2 === 0 ? even + index / 2 : odd - (index + 1) / 2 };

		if (position === undefined) {
			return $.map(this._clones, function(v, i) { return map(i) });
		}

		return $.map(this._clones, function(v, i) { return v === position ? map(i) : null });
	};

	/**
	 * Sets the current animation speed.
	 * @public
	 * @param {Number} [speed] - The animation speed in milliseconds or nothing to leave it unchanged.
	 * @returns {Number} - The current animation speed in milliseconds.
	 */
	Owl.prototype.speed = function(speed) {
		if (speed !== undefined) {
			this._speed = speed;
		}

		return this._speed;
	};

	/**
	 * Gets the coordinate of an item.
	 * @todo The name of this method is missleanding.
	 * @public
	 * @param {Number} position - The absolute position of the item within `minimum()` and `maximum()`.
	 * @returns {Number|Array.<Number>} - The coordinate of the item in pixel or all coordinates.
	 */
	Owl.prototype.coordinates = function(position) {
		var multiplier = 1,
			newPosition = position - 1,
			coordinate;

		if (position === undefined) {
			return $.map(this._coordinates, $.proxy(function(coordinate, index) {
				return this.coordinates(index);
			}, this));
		}

		if (this.settings.center) {
			if (this.settings.rtl) {
				multiplier = -1;
				newPosition = position + 1;
			}

			coordinate = this._coordinates[position];
			coordinate += (this.width() - coordinate + (this._coordinates[newPosition] || 0)) / 2 * multiplier;
		} else {
			coordinate = this._coordinates[newPosition] || 0;
		}

		coordinate = Math.ceil(coordinate);

		return coordinate;
	};

	/**
	 * Calculates the speed for a translation.
	 * @protected
	 * @param {Number} from - The absolute position of the start item.
	 * @param {Number} to - The absolute position of the target item.
	 * @param {Number} [factor=undefined] - The time factor in milliseconds.
	 * @returns {Number} - The time in milliseconds for the translation.
	 */
	Owl.prototype.duration = function(from, to, factor) {
		if (factor === 0) {
			return 0;
		}

		return Math.min(Math.max(Math.abs(to - from), 1), 6) * Math.abs((factor || this.settings.smartSpeed));
	};

	/**
	 * Slides to the specified item.
	 * @public
	 * @param {Number} position - The position of the item.
	 * @param {Number} [speed] - The time in milliseconds for the transition.
	 */
	Owl.prototype.to = function(position, speed) {
		var current = this.current(),
			revert = null,
			distance = position - this.relative(current),
			direction = (distance > 0) - (distance < 0),
			items = this._items.length,
			minimum = this.minimum(),
			maximum = this.maximum();

		if (this.settings.loop) {
			if (!this.settings.rewind && Math.abs(distance) > items / 2) {
				distance += direction * -1 * items;
			}

			position = current + distance;
			revert = ((position - minimum) % items + items) % items + minimum;

			if (revert !== position && revert - distance <= maximum && revert - distance > 0) {
				current = revert - distance;
				position = revert;
				this.reset(current);
			}
		} else if (this.settings.rewind) {
			maximum += 1;
			position = (position % maximum + maximum) % maximum;
		} else {
			position = Math.max(minimum, Math.min(maximum, position));
		}

		this.speed(this.duration(current, position, speed));
		this.current(position);

		if (this.isVisible()) {
			this.update();
		}
	};

	/**
	 * Slides to the next item.
	 * @public
	 * @param {Number} [speed] - The time in milliseconds for the transition.
	 */
	Owl.prototype.next = function(speed) {
		speed = speed || false;
		this.to(this.relative(this.current()) + 1, speed);
	};

	/**
	 * Slides to the previous item.
	 * @public
	 * @param {Number} [speed] - The time in milliseconds for the transition.
	 */
	Owl.prototype.prev = function(speed) {
		speed = speed || false;
		this.to(this.relative(this.current()) - 1, speed);
	};

	/**
	 * Handles the end of an animation.
	 * @protected
	 * @param {Event} event - The event arguments.
	 */
	Owl.prototype.onTransitionEnd = function(event) {

		// if css2 animation then event object is undefined
		if (event !== undefined) {
			event.stopPropagation();

			// Catch only owl-stage transitionEnd event
			if ((event.target || event.srcElement || event.originalTarget) !== this.$stage.get(0)) {
				return false;
			}
		}

		this.leave('animating');
		this.trigger('translated');
	};

	/**
	 * Gets viewport width.
	 * @protected
	 * @return {Number} - The width in pixel.
	 */
	Owl.prototype.viewport = function() {
		var width;
		if (this.options.responsiveBaseElement !== window) {
			width = $(this.options.responsiveBaseElement).width();
		} else if (window.innerWidth) {
			width = window.innerWidth;
		} else if (document.documentElement && document.documentElement.clientWidth) {
			width = document.documentElement.clientWidth;
		} else {
			console.warn('Can not detect viewport width.');
		}
		return width;
	};

	/**
	 * Replaces the current content.
	 * @public
	 * @param {HTMLElement|jQuery|String} content - The new content.
	 */
	Owl.prototype.replace = function(content) {
		this.$stage.empty();
		this._items = [];

		if (content) {
			content = (content instanceof jQuery) ? content : $(content);
		}

		if (this.settings.nestedItemSelector) {
			content = content.find('.' + this.settings.nestedItemSelector);
		}

		content.filter(function() {
			return this.nodeType === 1;
		}).each($.proxy(function(index, item) {
			item = this.prepare(item);
			this.$stage.append(item);
			this._items.push(item);
			this._mergers.push(item.find('[data-merge]').addBack('[data-merge]').attr('data-merge') * 1 || 1);
		}, this));

		this.reset(this.isNumeric(this.settings.startPosition) ? this.settings.startPosition : 0);

		this.invalidate('items');
	};

	/**
	 * Adds an item.
	 * @todo Use `item` instead of `content` for the event arguments.
	 * @public
	 * @param {HTMLElement|jQuery|String} content - The item content to add.
	 * @param {Number} [position] - The relative position at which to insert the item otherwise the item will be added to the end.
	 */
	Owl.prototype.add = function(content, position) {
		var current = this.relative(this._current);

		position = position === undefined ? this._items.length : this.normalize(position, true);
		content = content instanceof jQuery ? content : $(content);

		this.trigger('add', { content: content, position: position });

		content = this.prepare(content);

		if (this._items.length === 0 || position === this._items.length) {
			this._items.length === 0 && this.$stage.append(content);
			this._items.length !== 0 && this._items[position - 1].after(content);
			this._items.push(content);
			this._mergers.push(content.find('[data-merge]').addBack('[data-merge]').attr('data-merge') * 1 || 1);
		} else {
			this._items[position].before(content);
			this._items.splice(position, 0, content);
			this._mergers.splice(position, 0, content.find('[data-merge]').addBack('[data-merge]').attr('data-merge') * 1 || 1);
		}

		this._items[current] && this.reset(this._items[current].index());

		this.invalidate('items');

		this.trigger('added', { content: content, position: position });
	};

	/**
	 * Removes an item by its position.
	 * @todo Use `item` instead of `content` for the event arguments.
	 * @public
	 * @param {Number} position - The relative position of the item to remove.
	 */
	Owl.prototype.remove = function(position) {
		position = this.normalize(position, true);

		if (position === undefined) {
			return;
		}

		this.trigger('remove', { content: this._items[position], position: position });

		this._items[position].remove();
		this._items.splice(position, 1);
		this._mergers.splice(position, 1);

		this.invalidate('items');

		this.trigger('removed', { content: null, position: position });
	};

	/**
	 * Preloads images with auto width.
	 * @todo Replace by a more generic approach
	 * @protected
	 */
	Owl.prototype.preloadAutoWidthImages = function(images) {
		images.each($.proxy(function(i, element) {
			this.enter('pre-loading');
			element = $(element);
			$(new Image()).one('load', $.proxy(function(e) {
				element.attr('src', e.target.src);
				element.css('opacity', 1);
				this.leave('pre-loading');
				!this.is('pre-loading') && !this.is('initializing') && this.refresh();
			}, this)).attr('src', (window.devicePixelRatio > 1) ? element.attr('data-src-retina') : element.attr('data-src') || element.attr('src'));
		}, this));
	};

	/**
	 * Destroys the carousel.
	 * @public
	 */
	Owl.prototype.destroy = function() {

		this.$element.off('.owl.core');
		this.$stage.off('.owl.core');
		$(document).off('.owl.core');

		if (this.settings.responsive !== false) {
			window.clearTimeout(this.resizeTimer);
			this.off(window, 'resize', this._handlers.onThrottledResize);
		}

		for (var i in this._plugins) {
			this._plugins[i].destroy();
		}

		this.$stage.children('.cloned').remove();

		this.$stage.unwrap();
		this.$stage.children().contents().unwrap();
		this.$stage.children().unwrap();
		this.$stage.remove();
		this.$element
			.removeClass(this.options.refreshClass)
			.removeClass(this.options.loadingClass)
			.removeClass(this.options.loadedClass)
			.removeClass(this.options.rtlClass)
			.removeClass(this.options.dragClass)
			.removeClass(this.options.grabClass)
			.attr('class', this.$element.attr('class').replace(new RegExp(this.options.responsiveClass + '-\\S+\\s', 'g'), ''))
			.removeData('owl.carousel');
	};

	/**
	 * Operators to calculate right-to-left and left-to-right.
	 * @protected
	 * @param {Number} [a] - The left side operand.
	 * @param {String} [o] - The operator.
	 * @param {Number} [b] - The right side operand.
	 */
	Owl.prototype.op = function(a, o, b) {
		var rtl = this.settings.rtl;
		switch (o) {
			case '<':
				return rtl ? a > b : a < b;
			case '>':
				return rtl ? a < b : a > b;
			case '>=':
				return rtl ? a <= b : a >= b;
			case '<=':
				return rtl ? a >= b : a <= b;
			default:
				break;
		}
	};

	/**
	 * Attaches to an internal event.
	 * @protected
	 * @param {HTMLElement} element - The event source.
	 * @param {String} event - The event name.
	 * @param {Function} listener - The event handler to attach.
	 * @param {Boolean} capture - Wether the event should be handled at the capturing phase or not.
	 */
	Owl.prototype.on = function(element, event, listener, capture) {
		if (element.addEventListener) {
			element.addEventListener(event, listener, capture);
		} else if (element.attachEvent) {
			element.attachEvent('on' + event, listener);
		}
	};

	/**
	 * Detaches from an internal event.
	 * @protected
	 * @param {HTMLElement} element - The event source.
	 * @param {String} event - The event name.
	 * @param {Function} listener - The attached event handler to detach.
	 * @param {Boolean} capture - Wether the attached event handler was registered as a capturing listener or not.
	 */
	Owl.prototype.off = function(element, event, listener, capture) {
		if (element.removeEventListener) {
			element.removeEventListener(event, listener, capture);
		} else if (element.detachEvent) {
			element.detachEvent('on' + event, listener);
		}
	};

	/**
	 * Triggers a public event.
	 * @todo Remove `status`, `relatedTarget` should be used instead.
	 * @protected
	 * @param {String} name - The event name.
	 * @param {*} [data=null] - The event data.
	 * @param {String} [namespace=carousel] - The event namespace.
	 * @param {String} [state] - The state which is associated with the event.
	 * @param {Boolean} [enter=false] - Indicates if the call enters the specified state or not.
	 * @returns {Event} - The event arguments.
	 */
	Owl.prototype.trigger = function(name, data, namespace, state, enter) {
		var status = {
			item: { count: this._items.length, index: this.current() }
		}, handler = $.camelCase(
			$.grep([ 'on', name, namespace ], function(v) { return v })
				.join('-').toLowerCase()
		), event = $.Event(
			[ name, 'owl', namespace || 'carousel' ].join('.').toLowerCase(),
			$.extend({ relatedTarget: this }, status, data)
		);

		if (!this._supress[name]) {
			$.each(this._plugins, function(name, plugin) {
				if (plugin.onTrigger) {
					plugin.onTrigger(event);
				}
			});

			this.register({ type: Owl.Type.Event, name: name });
			this.$element.trigger(event);

			if (this.settings && typeof this.settings[handler] === 'function') {
				this.settings[handler].call(this, event);
			}
		}

		return event;
	};

	/**
	 * Enters a state.
	 * @param name - The state name.
	 */
	Owl.prototype.enter = function(name) {
		$.each([ name ].concat(this._states.tags[name] || []), $.proxy(function(i, name) {
			if (this._states.current[name] === undefined) {
				this._states.current[name] = 0;
			}

			this._states.current[name]++;
		}, this));
	};

	/**
	 * Leaves a state.
	 * @param name - The state name.
	 */
	Owl.prototype.leave = function(name) {
		$.each([ name ].concat(this._states.tags[name] || []), $.proxy(function(i, name) {
			this._states.current[name]--;
		}, this));
	};

	/**
	 * Registers an event or state.
	 * @public
	 * @param {Object} object - The event or state to register.
	 */
	Owl.prototype.register = function(object) {
		if (object.type === Owl.Type.Event) {
			if (!$.event.special[object.name]) {
				$.event.special[object.name] = {};
			}

			if (!$.event.special[object.name].owl) {
				var _default = $.event.special[object.name]._default;
				$.event.special[object.name]._default = function(e) {
					if (_default && _default.apply && (!e.namespace || e.namespace.indexOf('owl') === -1)) {
						return _default.apply(this, arguments);
					}
					return e.namespace && e.namespace.indexOf('owl') > -1;
				};
				$.event.special[object.name].owl = true;
			}
		} else if (object.type === Owl.Type.State) {
			if (!this._states.tags[object.name]) {
				this._states.tags[object.name] = object.tags;
			} else {
				this._states.tags[object.name] = this._states.tags[object.name].concat(object.tags);
			}

			this._states.tags[object.name] = $.grep(this._states.tags[object.name], $.proxy(function(tag, i) {
				return $.inArray(tag, this._states.tags[object.name]) === i;
			}, this));
		}
	};

	/**
	 * Suppresses events.
	 * @protected
	 * @param {Array.<String>} events - The events to suppress.
	 */
	Owl.prototype.suppress = function(events) {
		$.each(events, $.proxy(function(index, event) {
			this._supress[event] = true;
		}, this));
	};

	/**
	 * Releases suppressed events.
	 * @protected
	 * @param {Array.<String>} events - The events to release.
	 */
	Owl.prototype.release = function(events) {
		$.each(events, $.proxy(function(index, event) {
			delete this._supress[event];
		}, this));
	};

	/**
	 * Gets unified pointer coordinates from event.
	 * @todo #261
	 * @protected
	 * @param {Event} - The `mousedown` or `touchstart` event.
	 * @returns {Object} - Contains `x` and `y` coordinates of current pointer position.
	 */
	Owl.prototype.pointer = function(event) {
		var result = { x: null, y: null };

		event = event.originalEvent || event || window.event;

		event = event.touches && event.touches.length ?
			event.touches[0] : event.changedTouches && event.changedTouches.length ?
				event.changedTouches[0] : event;

		if (event.pageX) {
			result.x = event.pageX;
			result.y = event.pageY;
		} else {
			result.x = event.clientX;
			result.y = event.clientY;
		}

		return result;
	};

	/**
	 * Determines if the input is a Number or something that can be coerced to a Number
	 * @protected
	 * @param {Number|String|Object|Array|Boolean|RegExp|Function|Symbol} - The input to be tested
	 * @returns {Boolean} - An indication if the input is a Number or can be coerced to a Number
	 */
	Owl.prototype.isNumeric = function(number) {
		return !isNaN(parseFloat(number));
	};

	/**
	 * Gets the difference of two vectors.
	 * @todo #261
	 * @protected
	 * @param {Object} - The first vector.
	 * @param {Object} - The second vector.
	 * @returns {Object} - The difference.
	 */
	Owl.prototype.difference = function(first, second) {
		return {
			x: first.x - second.x,
			y: first.y - second.y
		};
	};

	/**
	 * The jQuery Plugin for the Owl Carousel
	 * @todo Navigation plugin `next` and `prev`
	 * @public
	 */
	$.fn.owlCarousel = function(option) {
		var args = Array.prototype.slice.call(arguments, 1);

		return this.each(function() {
			var $this = $(this),
				data = $this.data('owl.carousel');

			if (!data) {
				data = new Owl(this, typeof option == 'object' && option);
				$this.data('owl.carousel', data);

				$.each([
					'next', 'prev', 'to', 'destroy', 'refresh', 'replace', 'add', 'remove'
				], function(i, event) {
					data.register({ type: Owl.Type.Event, name: event });
					data.$element.on(event + '.owl.carousel.core', $.proxy(function(e) {
						if (e.namespace && e.relatedTarget !== this) {
							this.suppress([ event ]);
							data[event].apply(this, [].slice.call(arguments, 1));
							this.release([ event ]);
						}
					}, data));
				});
			}

			if (typeof option == 'string' && option.charAt(0) !== '_') {
				data[option].apply(data, args);
			}
		});
	};

	/**
	 * The constructor for the jQuery Plugin
	 * @public
	 */
	$.fn.owlCarousel.Constructor = Owl;

})(window.Zepto || window.jQuery, window, document);
// source --> https://eternize360.com.br/wp-content/plugins/wpvr-pro/admin/js/jquery.cookie.js?ver=1 
/*!
 * jQuery Cookie Plugin v1.4.1
 * https://github.com/carhartl/jquery-cookie
 *
 * Copyright 2013 Klaus Hartl
 * Released under the MIT license
 */
(function (factory) {
	if (typeof define === 'function' && define.amd) {
		// AMD
		define(['jquery'], factory);
	} else if (typeof exports === 'object') {
		// CommonJS
		factory(require('jquery'));
	} else {
		// Browser globals
		factory(jQuery);
	}
}(function ($) {

	var pluses = /\+/g;

	function encode(s) {
		return config.raw ? s : encodeURIComponent(s);
	}

	function decode(s) {
		return config.raw ? s : decodeURIComponent(s);
	}

	function stringifyCookieValue(value) {
		return encode(config.json ? JSON.stringify(value) : String(value));
	}

	function parseCookieValue(s) {
		if (s.indexOf('"') === 0) {
			// This is a quoted cookie as according to RFC2068, unescape...
			s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
		}

		try {
			// Replace server-side written pluses with spaces.
			// If we can't decode the cookie, ignore it, it's unusable.
			// If we can't parse the cookie, ignore it, it's unusable.
			s = decodeURIComponent(s.replace(pluses, ' '));
			return config.json ? JSON.parse(s) : s;
		} catch(e) {}
	}

	function read(s, converter) {
		var value = config.raw ? s : parseCookieValue(s);
		return $.isFunction(converter) ? converter(value) : value;
	}

	var config = $.cookie = function (key, value, options) {

		// Write

		if (value !== undefined && !$.isFunction(value)) {
			options = $.extend({}, config.defaults, options);

			if (typeof options.expires === 'number') {
				var days = options.expires, t = options.expires = new Date();
				t.setTime(+t + days * 864e+5);
			}

			return (document.cookie = [
				encode(key), '=', stringifyCookieValue(value),
				options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
				options.path    ? '; path=' + options.path : '',
				options.domain  ? '; domain=' + options.domain : '',
				options.secure  ? '; secure' : ''
			].join(''));
		}

		// Read

		var result = key ? undefined : {};

		// To prevent the for loop in the first place assign an empty array
		// in case there are no cookies at all. Also prevents odd result when
		// calling $.cookie().
		var cookies = document.cookie ? document.cookie.split('; ') : [];

		for (var i = 0, l = cookies.length; i < l; i++) {
			var parts = cookies[i].split('=');
			var name = decode(parts.shift());
			var cookie = parts.join('=');

			if (key && key === name) {
				// If second argument (value) is a function it's a converter...
				result = read(cookie, value);
				break;
			}

			// Prevent storing a cookie that we couldn't decode.
			if (!key && (cookie = read(cookie)) !== undefined) {
				result[name] = cookie;
			}
		}

		return result;
	};

	config.defaults = {};

	$.removeCookie = function (key, options) {
		if ($.cookie(key) === undefined) {
			return false;
		}

		// Must not alter options, thus extending a fresh object...
		$.cookie(key, '', $.extend({}, options, { expires: -1 }));
		return !$.cookie(key);
	};

}));