diff --git a/public/css/segoepr.ttf b/public/css/segoepr.ttf new file mode 100644 index 0000000000000000000000000000000000000000..38f696d4f4cbcd6d4b78614ed82db86c604a2c18 Binary files /dev/null and b/public/css/segoepr.ttf differ diff --git a/public/css/segoeprb.ttf b/public/css/segoeprb.ttf new file mode 100644 index 0000000000000000000000000000000000000000..c7e267ec70457e35ce75980c07c2db18769c220d Binary files /dev/null and b/public/css/segoeprb.ttf differ diff --git a/public/css/style.css b/public/css/style.css new file mode 100644 index 0000000000000000000000000000000000000000..14d06aa0ee07c3329c9fd5f5a8e8cc07dd007181 --- /dev/null +++ b/public/css/style.css @@ -0,0 +1,201 @@ +#mainLogo +{ + display: block; + margin: auto; + height: 352px; + width: 777px; +} +#smallLogo +{ + display: block; + margin-left: 20px; + height: 175px; + width: 200px; +} + +/* the different container used */ +.container +{ + display: flex; +} +.optionDescription +{ + display: flex; +} +.optionDescription * +{ + flex: 4; +} + + +@font-face +{ + font-family: 'segoepr'; + src: url('./segoepr.ttf'); +} + +/* default parameters */ +body +{ + overflow : scroll; + padding : 1; + margin : 0; + + text-align: left; + text-decoration: none; + + font-style: normal; + font-weight: normal; + font-family: Verdana; + font-size: 1em; + + color: black; + background-color: white; +} + + +footer +{ + margin: 10px; + + border-style: solid; + border-width: 2px; + border-color: black; + border-radius: 20px; + + background-color: #c9c9c9; + + text-align: center; +} +nav +{ + border-style: solid; + border-width: 2px; + border-color: black; + border-radius: 20px; + + background-color: #c9c9c9; + + margin: 10px; + + width: 25%; + min-width: 200px; + max-width: 300px; + flex: 1; +} +section +{ + margin: 10px; + + width: 75%; + min-width: 777px; + flex: 4; +} +header +{ + margin: 10px; + + border-style: solid; + border-width: 2px; + border-color: black; + border-radius: 20px; + + background-color: #c9c9c9; +} + + +/* lists parameters */ +ul.navlist +{ + list-style-type: none; +} + + + +/* links parameters */ +a +{ + text-decoration: underline; + color: blue; +} +a:hover, a:hover:visited +{ + text-decoration: none; + color: green; +} +a:visited +{ + color: purple; +} +a.hiddenlink, a.hiddenlink:hover, a.hiddenlink:hover:visited, a.hiddenlink:visited +{ + text-decoration: none; + color: black; +} +a.navlink, a.navlink:visited +{ + text-decoration: none; + color: black; +} +a.navlink:hover, a.navlink:hover:visited +{ + text-decoration: none; + color: red; +} + +/* parameters titles */ +h1 +{ + margin: auto; + text-align: center; + font-weight: bold; + font-family: segoepr; + font-size: 4em; + flex: 1; +} +h2 +{ + text-align: center; + font-size: 2em; +} +h3 +{ + font-size: 1.2em; +} + + +/* special word */ +em +{ + font-style: italic; + color: silver; +} +strong +{ + font-weight: bold; +} + + + +/* special window */ +#javascriptWindow +{ + display: block; + text-align: center; + + min-width: 1200px; + min-height: 800px; + background-color: black; +} + + + + + + + + + + + + diff --git a/public/js/javascriptWindow.js b/public/js/javascriptWindow.js new file mode 100644 index 0000000000000000000000000000000000000000..4fd38d723685f703e478a93ea32d0e6151af64ab --- /dev/null +++ b/public/js/javascriptWindow.js @@ -0,0 +1,134 @@ + +/* + global attributes and variables +*/ +var canvasContainer; + +var gravity = 2; +var lifeTime = 500; +var endLife = 0.1; +var preEndLife = 0.3 + endLife; +var circleList = []; + +/* + setup functions called once for instanciation +*/ +function setup() +{ + // p5js and other stuf + canvasContainer = document.getElementById('javascriptWindow'); + var myCanvas = createCanvas(canvasContainer.clientWidth, canvasContainer.clientHeight); + myCanvas.parent('javascriptWindow'); + + frameRate(30); + colorMode(RGB, 255, 255, 255, 1); + textSize(24); + textAlign(CENTER, CENTER); + noStroke(); + //noFill(); +} + + +/* + callback functions : used by p5.js for user interactions +*/ +function windowResized() +{ + resizeCanvas(canvasContainer.clientWidth, canvasContainer.clientHeight); +} +function mousePressed() +{ + circleList.push({ x:mouseX, y:mouseY, r:random(10,50), vx:0, vy:0, life:lifeTime }); +} + + +/* + draw function called once per frame : 30 times by seconds +*/ +var initialColor = {r:255.0, g:255.0, b:255.0, a:1.0}; +var finalColor = {r:255.0, g:0.0, b:0.0, a:1.0}; +function lerp2(a, b, t) +{ + return {r:lerp(a.r, b.r, t), g:lerp(a.g, b.g, t), b:lerp(a.b, b.b, t), a:lerp(a.a, b.a, t)}; +} +function reflect(v, n) +{ + // v = v-2*dot(v, n)*n + p = v.x*n.x + v.y*n.y; + if(p<0) p=0; + return {x : v.x - 2*p*n.x, y : v.y - 2*p*n.y}; +} +function draw() +{ + // Begin frame + clear(); + background(200); + + //text("Hello friend !", width/2, height/2); + for (var i = 0; i < circleList.length; i++) + { + // color + var c = Object.assign({}, initialColor); + if(circleList[i].life < endLife*lifeTime) + { + c = Object.assign({}, finalColor); + c.a = 1.0*circleList[i].life / (endLife*lifeTime); + } + else if(circleList[i].life < preEndLife*lifeTime) + { + var t = (circleList[i].life - endLife*lifeTime)/((preEndLife-endLife)*lifeTime); + c = lerp2(initialColor, finalColor, 1-t); + } + fill(c.r, c.g, c.b, c.a); + + // apply gravity + if(circleList[i].y + circleList[i].vy + gravity < height - 0.5*circleList[i].r) + { + circleList[i].vy += gravity; + circleList[i].y += circleList[i].vy; + circleList[i].x += circleList[i].vx; + } + else + { + circleList[i].y = height - 0.5*circleList[i].r; + circleList[i].vy = -0.7*circleList[i].vy; + circleList[i].x += circleList[i].vx; + } + + // draw particle + ellipse(circleList[i].x, circleList[i].y, circleList[i].r, circleList[i].r); + + // collision + for (var j = i+1; j < circleList.length; j++) + { + var nx = 1.0*circleList[i].x - circleList[j].x; + var ny = 1.0*circleList[i].y - circleList[j].y; + var d = 0.5*(circleList[i].r + circleList[j].r); + + if(nx*nx + ny*ny < d*d && (circleList[i].vx*circleList[j].vx <= 0 || circleList[i].vy*circleList[j].vy <= 0)) // collision and directions are not the same + { + var nn = sqrt(nx*nx + ny*ny); + var v = reflect({x:circleList[i].vx, y:circleList[i].vy}, {x:-nx/nn, y:-ny/nn}); + circleList[i].vx = v.x; + circleList[i].vy = v.y; + + v = reflect({x:circleList[j].vx, y:circleList[j].vy}, {x:nx/nn, y:ny/nn}); + circleList[j].vx = v.x; + circleList[j].vy = v.y; + + //line(circleList[i].x, circleList[i].y, circleList[i].x + 5*circleList[i].vx, circleList[i].y + 5*circleList[i].vy); + //line(circleList[j].x, circleList[j].y, circleList[j].x + 5*circleList[j].vx, circleList[j].y + 5*circleList[j].vy); + //line(circleList[i].x, circleList[i].y, circleList[j].x, circleList[j].y); + } + } + + + + // reduce life + circleList[i].life--; + if(circleList[i].life <= 0) + { + circleList.shift(); + } + } +} \ No newline at end of file diff --git a/public/js/jquery-3.1.1.min.js b/public/js/jquery-3.1.1.min.js new file mode 100644 index 0000000000000000000000000000000000000000..4c5be4c0fbe230e81d95718a18829e965a2d14b2 --- /dev/null +++ b/public/js/jquery-3.1.1.min.js @@ -0,0 +1,4 @@ +/*! jQuery v3.1.1 | (c) jQuery Foundation | jquery.org/license */ +!function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.1.1",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null==a?f.call(this):a<0?this[a+this.length]:this[a]},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(a<0?b:0);return this.pushStack(c>=0&&c<b?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:h,sort:c.sort,splice:c.splice},r.extend=r.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||r.isFunction(g)||(g={}),h===i&&(g=this,h--);h<i;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(r.isPlainObject(d)||(e=r.isArray(d)))?(e?(e=!1,f=c&&r.isArray(c)?c:[]):f=c&&r.isPlainObject(c)?c:{},g[b]=r.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},r.extend({expando:"jQuery"+(q+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===r.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){var b=r.type(a);return("number"===b||"string"===b)&&!isNaN(a-parseFloat(a))},isPlainObject:function(a){var b,c;return!(!a||"[object Object]"!==k.call(a))&&(!(b=e(a))||(c=l.call(b,"constructor")&&b.constructor,"function"==typeof c&&m.call(c)===n))},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?j[k.call(a)]||"object":typeof a},globalEval:function(a){p(a)},camelCase:function(a){return a.replace(t,"ms-").replace(u,v)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b){var c,d=0;if(w(a)){for(c=a.length;d<c;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(s,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(w(Object(a))?r.merge(c,"string"==typeof a?[a]:a):h.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:i.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;d<c;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;f<g;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,f=0,h=[];if(w(a))for(d=a.length;f<d;f++)e=b(a[f],f,c),null!=e&&h.push(e);else for(f in a)e=b(a[f],f,c),null!=e&&h.push(e);return g.apply([],h)},guid:1,proxy:function(a,b){var c,d,e;if("string"==typeof b&&(c=a[b],b=a,a=c),r.isFunction(a))return d=f.call(arguments,2),e=function(){return a.apply(b||this,d.concat(f.call(arguments)))},e.guid=a.guid=a.guid||r.guid++,e},now:Date.now,support:o}),"function"==typeof Symbol&&(r.fn[Symbol.iterator]=c[Symbol.iterator]),r.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){j["[object "+b+"]"]=b.toLowerCase()});function w(a){var b=!!a&&"length"in a&&a.length,c=r.type(a);return"function"!==c&&!r.isWindow(a)&&("array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c<d;c++)if(a[c]===b)return c;return-1},J="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",K="[\\x20\\t\\r\\n\\f]",L="(?:\\\\.|[\\w-]|[^\0-\\xa0])+",M="\\["+K+"*("+L+")(?:"+K+"*([*^$|!~]?=)"+K+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+L+"))|)"+K+"*\\]",N=":("+L+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+M+")*)|.*)\\)|)",O=new RegExp(K+"+","g"),P=new RegExp("^"+K+"+|((?:^|[^\\\\])(?:\\\\.)*)"+K+"+$","g"),Q=new RegExp("^"+K+"*,"+K+"*"),R=new RegExp("^"+K+"*([>+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ca=function(a,b){return b?"\0"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0&&("form"in a||"label"in a)},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"form"in b?b.parentNode&&b.disabled===!1?"label"in b?"label"in b.parentNode?b.parentNode.disabled===a:b.disabled===a:b.isDisabled===a||b.isDisabled!==!a&&ea(b)===a:b.disabled===a:"label"in b&&b.disabled===a}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}}):(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c,d,e,f=b.getElementById(a);if(f){if(c=f.getAttributeNode("id"),c&&c.value===a)return[f];e=b.getElementsByName(a),d=0;while(f=e[d++])if(c=f.getAttributeNode("id"),c&&c.value===a)return[f]}return[]}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){if("undefined"!=typeof b.getElementsByClassName&&p)return b.getElementsByClassName(a)},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="<a id='"+u+"'></a><select id='"+u+"-\r\\' msallowcapture=''><option selected=''></option></select>",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c<b;c+=2)a.push(c);return a}),odd:pa(function(a,b){for(var c=1;c<b;c+=2)a.push(c);return a}),lt:pa(function(a,b,c){for(var d=c<0?c+b:c;--d>=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d<b;)a.push(d);return a})}},d.pseudos.nth=d.pseudos.eq;for(b in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})d.pseudos[b]=ma(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=na(b);function ra(){}ra.prototype=d.filters=d.pseudos,d.setFilters=new ra,g=ga.tokenize=function(a,b){var c,e,f,g,h,i,j,k=z[a+" "];if(k)return b?0:k.slice(0);h=a,i=[],j=d.preFilter;while(h){c&&!(e=Q.exec(h))||(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),c=!1,(e=R.exec(h))&&(c=e.shift(),f.push({value:c,type:e[0].replace(P," ")}),h=h.slice(c.length));for(g in d.filter)!(e=V[g].exec(h))||j[g]&&!(e=j[g](e))||(c=e.shift(),f.push({value:c,type:g,matches:e}),h=h.slice(c.length));if(!c)break}return b?h.length:h?ga.error(a):z(a,i).slice(0)};function sa(a){for(var b=0,c=a.length,d="";b<c;b++)d+=a[b].value;return d}function ta(a,b,c){var d=b.dir,e=b.next,f=e||d,g=c&&"parentNode"===f,h=x++;return b.first?function(b,c,e){while(b=b[d])if(1===b.nodeType||g)return a(b,c,e);return!1}:function(b,c,i){var j,k,l,m=[w,h];if(i){while(b=b[d])if((1===b.nodeType||g)&&a(b,c,i))return!0}else while(b=b[d])if(1===b.nodeType||g)if(l=b[u]||(b[u]={}),k=l[b.uniqueID]||(l[b.uniqueID]={}),e&&e===b.nodeName.toLowerCase())b=b[d]||b;else{if((j=k[f])&&j[0]===w&&j[1]===h)return m[2]=j[2];if(k[f]=m,m[2]=a(b,c,i))return!0}return!1}}function ua(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d<e;d++)ga(a,b[d],c);return c}function wa(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;h<i;h++)(f=a[h])&&(c&&!c(f,d,e)||(g.push(f),j&&b.push(h)));return g}function xa(a,b,c,d,e,f){return d&&!d[u]&&(d=xa(d)),e&&!e[u]&&(e=xa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||va(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:wa(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=wa(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?I(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i<f;i++)if(c=d.relative[a[i].type])m=[ta(ua(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;e<f;e++)if(d.relative[a[e].type])break;return xa(i>1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i<e&&ya(a.slice(i,e)),e<f&&ya(a=a.slice(e)),e<f&&sa(a))}m.push(c)}return ua(m)}function za(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,c,e){var f,i,j,k,l,m="function"==typeof a&&a,n=!e&&g(a=m.selector||a);if(c=c||[],1===n.length){if(i=n[0]=n[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&9===b.nodeType&&p&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(_,aa),b)||[])[0],!b)return c;m&&(b=b.parentNode),a=a.slice(i.shift().value.length)}f=V.needsContext.test(a)?0:i.length;while(f--){if(j=i[f],d.relative[k=j.type])break;if((l=d.find[k])&&(e=l(j.matches[0].replace(_,aa),$.test(i[0].type)&&qa(b.parentNode)||b))){if(i.splice(f,1),a=e.length&&sa(i),!a)return G.apply(c,e),c;break}}}return(m||h(a,n))(e,b,!p,c,!b||$.test(a)&&qa(b.parentNode)||b),c},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext,B=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,C=/^.[^:#\[\.,]*$/;function D(a,b,c){return r.isFunction(b)?r.grep(a,function(a,d){return!!b.call(a,d,a)!==c}):b.nodeType?r.grep(a,function(a){return a===b!==c}):"string"!=typeof b?r.grep(a,function(a){return i.call(b,a)>-1!==c}):C.test(b)?r.filter(b,a,c):(b=r.filter(b,a),r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType}))}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b<d;b++)if(r.contains(e[b],this))return!0}));for(c=this.pushStack([]),b=0;b<d;b++)r.find(a,e[b],c);return d>1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(D(this,a||[],!1))},not:function(a){return this.pushStack(D(this,a||[],!0))},is:function(a){return!!D(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var E,F=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,G=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||E,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:F.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),B.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};G.prototype=r.fn,E=r(d);var H=/^(?:parents|prev(?:Until|All))/,I={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a<c;a++)if(r.contains(this,b[a]))return!0})},closest:function(a,b){var c,d=0,e=this.length,f=[],g="string"!=typeof a&&r(a);if(!A.test(a))for(;d<e;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function J(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return J(a,"nextSibling")},prev:function(a){return J(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return a.contentDocument||r.merge([],a.childNodes)}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(I[a]||r.uniqueSort(e),H.test(a)&&e.reverse()),this.pushStack(e)}});var K=/[^\x20\t\r\n\f]+/g;function L(a){var b={};return r.each(a.match(K)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?L(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h<f.length)f[h].apply(c[0],c[1])===!1&&a.stopOnFalse&&(h=f.length,c=!1)}a.memory||(c=!1),b=!1,e&&(f=c?[]:"")},j={add:function(){return f&&(c&&!b&&(h=f.length-1,g.push(c)),function d(b){r.each(b,function(b,c){r.isFunction(c)?a.unique&&j.has(c)||f.push(c):c&&c.length&&"string"!==r.type(c)&&d(c)})}(arguments),c&&!b&&i()),this},remove:function(){return r.each(arguments,function(a,b){var c;while((c=r.inArray(b,f,c))>-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function M(a){return a}function N(a){throw a}function O(a,b,c){var d;try{a&&r.isFunction(d=a.promise)?d.call(a).done(b).fail(c):a&&r.isFunction(d=a.then)?d.call(a,b,c):b.call(void 0,a)}catch(a){c.call(void 0,a)}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b<f)){if(a=d.apply(h,i),a===c.promise())throw new TypeError("Thenable self-resolution");j=a&&("object"==typeof a||"function"==typeof a)&&a.then,r.isFunction(j)?e?j.call(a,g(f,c,M,e),g(f,c,N,e)):(f++,j.call(a,g(f,c,M,e),g(f,c,N,e),g(f,c,M,c.notifyWith))):(d!==M&&(h=void 0,i=[a]),(e||c.resolveWith)(h,i))}},k=e?j:function(){try{j()}catch(a){r.Deferred.exceptionHook&&r.Deferred.exceptionHook(a,k.stackTrace),b+1>=f&&(d!==N&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:M,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:M)),c[2][3].add(g(0,a,r.isFunction(d)?d:N))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(O(a,g.done(h(c)).resolve,g.reject),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)O(e[c],h(c),g.reject);return g.promise()}});var P=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&P.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var Q=r.Deferred();r.fn.ready=function(a){return Q.then(a)["catch"](function(a){r.readyException(a)}),this},r.extend({isReady:!1,readyWait:1,holdReady:function(a){a?r.readyWait++:r.ready(!0)},ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||Q.resolveWith(d,[r]))}}),r.ready.then=Q.then;function R(){d.removeEventListener("DOMContentLoaded",R), +a.removeEventListener("load",R),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",R),a.addEventListener("load",R));var S=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)S(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h<i;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},T=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function U(){this.expando=r.expando+U.uid++}U.uid=1,U.prototype={cache:function(a){var b=a[this.expando];return b||(b={},T(a)&&(a.nodeType?a[this.expando]=b:Object.defineProperty(a,this.expando,{value:b,configurable:!0}))),b},set:function(a,b,c){var d,e=this.cache(a);if("string"==typeof b)e[r.camelCase(b)]=c;else for(d in b)e[r.camelCase(d)]=b[d];return e},get:function(a,b){return void 0===b?this.cache(a):a[this.expando]&&a[this.expando][r.camelCase(b)]},access:function(a,b,c){return void 0===b||b&&"string"==typeof b&&void 0===c?this.get(a,b):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d=a[this.expando];if(void 0!==d){if(void 0!==b){r.isArray(b)?b=b.map(r.camelCase):(b=r.camelCase(b),b=b in d?[b]:b.match(K)||[]),c=b.length;while(c--)delete d[b[c]]}(void 0===b||r.isEmptyObject(d))&&(a.nodeType?a[this.expando]=void 0:delete a[this.expando])}},hasData:function(a){var b=a[this.expando];return void 0!==b&&!r.isEmptyObject(b)}};var V=new U,W=new U,X=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,Y=/[A-Z]/g;function Z(a){return"true"===a||"false"!==a&&("null"===a?null:a===+a+""?+a:X.test(a)?JSON.parse(a):a)}function $(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(Y,"-$&").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c=Z(c)}catch(e){}W.set(a,b,c)}else c=void 0;return c}r.extend({hasData:function(a){return W.hasData(a)||V.hasData(a)},data:function(a,b,c){return W.access(a,b,c)},removeData:function(a,b){W.remove(a,b)},_data:function(a,b,c){return V.access(a,b,c)},_removeData:function(a,b){V.remove(a,b)}}),r.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=W.get(f),1===f.nodeType&&!V.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=r.camelCase(d.slice(5)),$(f,d,e[d])));V.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){W.set(this,a)}):S(this,function(b){var c;if(f&&void 0===b){if(c=W.get(f,a),void 0!==c)return c;if(c=$(f,a),void 0!==c)return c}else this.each(function(){W.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){W.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=V.get(a,b),c&&(!d||r.isArray(c)?d=V.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return V.get(a,c)||V.access(a,c,{empty:r.Callbacks("once memory").add(function(){V.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?r.queue(this[0],a):void 0===b?this:this.each(function(){var c=r.queue(this,a,b);r._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&r.dequeue(this,a)})},dequeue:function(a){return this.each(function(){r.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=r.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=V.get(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var _=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,aa=new RegExp("^(?:([+-])=|)("+_+")([a-z%]*)$","i"),ba=["Top","Right","Bottom","Left"],ca=function(a,b){return a=b||a,"none"===a.style.display||""===a.style.display&&r.contains(a.ownerDocument,a)&&"none"===r.css(a,"display")},da=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};function ea(a,b,c,d){var e,f=1,g=20,h=d?function(){return d.cur()}:function(){return r.css(a,b,"")},i=h(),j=c&&c[3]||(r.cssNumber[b]?"":"px"),k=(r.cssNumber[b]||"px"!==j&&+i)&&aa.exec(r.css(a,b));if(k&&k[3]!==j){j=j||k[3],c=c||[],k=+i||1;do f=f||".5",k/=f,r.style(a,b,k+j);while(f!==(f=h()/i)&&1!==f&&--g)}return c&&(k=+k||+i||0,e=c[1]?k+(c[1]+1)*c[2]:+c[2],d&&(d.unit=j,d.start=k,d.end=e)),e}var fa={};function ga(a){var b,c=a.ownerDocument,d=a.nodeName,e=fa[d];return e?e:(b=c.body.appendChild(c.createElement(d)),e=r.css(b,"display"),b.parentNode.removeChild(b),"none"===e&&(e="block"),fa[d]=e,e)}function ha(a,b){for(var c,d,e=[],f=0,g=a.length;f<g;f++)d=a[f],d.style&&(c=d.style.display,b?("none"===c&&(e[f]=V.get(d,"display")||null,e[f]||(d.style.display="")),""===d.style.display&&ca(d)&&(e[f]=ga(d))):"none"!==c&&(e[f]="none",V.set(d,"display",c)));for(f=0;f<g;f++)null!=e[f]&&(a[f].style.display=e[f]);return a}r.fn.extend({show:function(){return ha(this,!0)},hide:function(){return ha(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){ca(this)?r(this).show():r(this).hide()})}});var ia=/^(?:checkbox|radio)$/i,ja=/<([a-z][^\/\0>\x20\t\r\n\f]+)/i,ka=/^$|\/(?:java|ecma)script/i,la={option:[1,"<select multiple='multiple'>","</select>"],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,"",""]};la.optgroup=la.option,la.tbody=la.tfoot=la.colgroup=la.caption=la.thead,la.th=la.td;function ma(a,b){var c;return c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[],void 0===b||b&&r.nodeName(a,b)?r.merge([a],c):c}function na(a,b){for(var c=0,d=a.length;c<d;c++)V.set(a[c],"globalEval",!b||V.get(b[c],"globalEval"))}var oa=/<|&#?\w+;/;function pa(a,b,c,d,e){for(var f,g,h,i,j,k,l=b.createDocumentFragment(),m=[],n=0,o=a.length;n<o;n++)if(f=a[n],f||0===f)if("object"===r.type(f))r.merge(m,f.nodeType?[f]:f);else if(oa.test(f)){g=g||l.appendChild(b.createElement("div")),h=(ja.exec(f)||["",""])[1].toLowerCase(),i=la[h]||la._default,g.innerHTML=i[1]+r.htmlPrefilter(f)+i[2],k=i[0];while(k--)g=g.lastChild;r.merge(m,g.childNodes),g=l.firstChild,g.textContent=""}else m.push(b.createTextNode(f));l.textContent="",n=0;while(f=m[n++])if(d&&r.inArray(f,d)>-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=ma(l.appendChild(f),"script"),j&&na(g),c){k=0;while(f=g[k++])ka.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="<textarea>x</textarea>",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var qa=d.documentElement,ra=/^key/,sa=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ta=/^([^.]*)(?:\.(.+)|)/;function ua(){return!0}function va(){return!1}function wa(){try{return d.activeElement}catch(a){}}function xa(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)xa(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=va;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(qa,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(K)||[""],j=b.length;while(j--)h=ta.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.hasData(a)&&V.get(a);if(q&&(i=q.events)){b=(b||"").match(K)||[""],j=b.length;while(j--)if(h=ta.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&V.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(V.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c<arguments.length;c++)i[c]=arguments[c];if(b.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,b)!==!1){h=r.event.handlers.call(this,b,j),c=0;while((f=h[c++])&&!b.isPropagationStopped()){b.currentTarget=f.elem,d=0;while((g=f.handlers[d++])&&!b.isImmediatePropagationStopped())b.rnamespace&&!b.rnamespace.test(g.namespace)||(b.handleObj=g,b.data=g.data,e=((r.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(b.result=e)===!1&&(b.preventDefault(),b.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,b),b.result}},handlers:function(a,b){var c,d,e,f,g,h=[],i=b.delegateCount,j=a.target;if(i&&j.nodeType&&!("click"===a.type&&a.button>=1))for(;j!==this;j=j.parentNode||this)if(1===j.nodeType&&("click"!==a.type||j.disabled!==!0)){for(f=[],g={},c=0;c<i;c++)d=b[c],e=d.selector+" ",void 0===g[e]&&(g[e]=d.needsContext?r(e,this).index(j)>-1:r.find(e,this,null,[j]).length),g[e]&&f.push(d);f.length&&h.push({elem:j,handlers:f})}return j=this,i<b.length&&h.push({elem:j,handlers:b.slice(i)}),h},addProp:function(a,b){Object.defineProperty(r.Event.prototype,a,{enumerable:!0,configurable:!0,get:r.isFunction(b)?function(){if(this.originalEvent)return b(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[a]},set:function(b){Object.defineProperty(this,a,{enumerable:!0,configurable:!0,writable:!0,value:b})}})},fix:function(a){return a[r.expando]?a:new r.Event(a)},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==wa()&&this.focus)return this.focus(),!1},delegateType:"focusin"},blur:{trigger:function(){if(this===wa()&&this.blur)return this.blur(),!1},delegateType:"focusout"},click:{trigger:function(){if("checkbox"===this.type&&this.click&&r.nodeName(this,"input"))return this.click(),!1},_default:function(a){return r.nodeName(a.target,"a")}},beforeunload:{postDispatch:function(a){void 0!==a.result&&a.originalEvent&&(a.originalEvent.returnValue=a.result)}}}},r.removeEvent=function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c)},r.Event=function(a,b){return this instanceof r.Event?(a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||void 0===a.defaultPrevented&&a.returnValue===!1?ua:va,this.target=a.target&&3===a.target.nodeType?a.target.parentNode:a.target,this.currentTarget=a.currentTarget,this.relatedTarget=a.relatedTarget):this.type=a,b&&r.extend(this,b),this.timeStamp=a&&a.timeStamp||r.now(),void(this[r.expando]=!0)):new r.Event(a,b)},r.Event.prototype={constructor:r.Event,isDefaultPrevented:va,isPropagationStopped:va,isImmediatePropagationStopped:va,isSimulated:!1,preventDefault:function(){var a=this.originalEvent;this.isDefaultPrevented=ua,a&&!this.isSimulated&&a.preventDefault()},stopPropagation:function(){var a=this.originalEvent;this.isPropagationStopped=ua,a&&!this.isSimulated&&a.stopPropagation()},stopImmediatePropagation:function(){var a=this.originalEvent;this.isImmediatePropagationStopped=ua,a&&!this.isSimulated&&a.stopImmediatePropagation(),this.stopPropagation()}},r.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,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:function(a){var b=a.button;return null==a.which&&ra.test(a.type)?null!=a.charCode?a.charCode:a.keyCode:!a.which&&void 0!==b&&sa.test(a.type)?1&b?1:2&b?3:4&b?2:0:a.which}},r.event.addProp),r.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(a,b){r.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj;return e&&(e===d||r.contains(d,e))||(a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b),c}}}),r.fn.extend({on:function(a,b,c,d){return xa(this,a,b,c,d)},one:function(a,b,c,d){return xa(this,a,b,c,d,1)},off:function(a,b,c){var d,e;if(a&&a.preventDefault&&a.handleObj)return d=a.handleObj,r(a.delegateTarget).off(d.namespace?d.origType+"."+d.namespace:d.origType,d.selector,d.handler),this;if("object"==typeof a){for(e in a)this.off(e,b,a[e]);return this}return b!==!1&&"function"!=typeof b||(c=b,b=void 0),c===!1&&(c=va),this.each(function(){r.event.remove(this,a,c,b)})}});var ya=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi,za=/<script|<style|<link/i,Aa=/checked\s*(?:[^=]|=\s*.checked.)/i,Ba=/^true\/(.*)/,Ca=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;function Da(a,b){return r.nodeName(a,"table")&&r.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a:a}function Ea(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function Fa(a){var b=Ba.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Ga(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(V.hasData(a)&&(f=V.access(a),g=V.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;c<d;c++)r.event.add(b,e,j[e][c])}W.hasData(a)&&(h=W.access(a),i=r.extend({},h),W.set(b,i))}}function Ha(a,b){var c=b.nodeName.toLowerCase();"input"===c&&ia.test(a.type)?b.checked=a.checked:"input"!==c&&"textarea"!==c||(b.defaultValue=a.defaultValue)}function Ia(a,b,c,d){b=g.apply([],b);var e,f,h,i,j,k,l=0,m=a.length,n=m-1,q=b[0],s=r.isFunction(q);if(s||m>1&&"string"==typeof q&&!o.checkClone&&Aa.test(q))return a.each(function(e){var f=a.eq(e);s&&(b[0]=q.call(this,e,f.html())),Ia(f,b,c,d)});if(m&&(e=pa(b,a[0].ownerDocument,!1,a,d),f=e.firstChild,1===e.childNodes.length&&(e=f),f||d)){for(h=r.map(ma(e,"script"),Ea),i=h.length;l<m;l++)j=e,l!==n&&(j=r.clone(j,!0,!0),i&&r.merge(h,ma(j,"script"))),c.call(a[l],j,l);if(i)for(k=h[h.length-1].ownerDocument,r.map(h,Fa),l=0;l<i;l++)j=h[l],ka.test(j.type||"")&&!V.access(j,"globalEval")&&r.contains(k,j)&&(j.src?r._evalUrl&&r._evalUrl(j.src):p(j.textContent.replace(Ca,""),k))}return a}function Ja(a,b,c){for(var d,e=b?r.filter(b,a):a,f=0;null!=(d=e[f]);f++)c||1!==d.nodeType||r.cleanData(ma(d)),d.parentNode&&(c&&r.contains(d.ownerDocument,d)&&na(ma(d,"script")),d.parentNode.removeChild(d));return a}r.extend({htmlPrefilter:function(a){return a.replace(ya,"<$1></$2>")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=r.contains(a.ownerDocument,a);if(!(o.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||r.isXMLDoc(a)))for(g=ma(h),f=ma(a),d=0,e=f.length;d<e;d++)Ha(f[d],g[d]);if(b)if(c)for(f=f||ma(a),g=g||ma(h),d=0,e=f.length;d<e;d++)Ga(f[d],g[d]);else Ga(a,h);return g=ma(h,"script"),g.length>0&&na(g,!i&&ma(a,"script")),h},cleanData:function(a){for(var b,c,d,e=r.event.special,f=0;void 0!==(c=a[f]);f++)if(T(c)){if(b=c[V.expando]){if(b.events)for(d in b.events)e[d]?r.event.remove(c,d):r.removeEvent(c,d,b.handle);c[V.expando]=void 0}c[W.expando]&&(c[W.expando]=void 0)}}}),r.fn.extend({detach:function(a){return Ja(this,a,!0)},remove:function(a){return Ja(this,a)},text:function(a){return S(this,function(a){return void 0===a?r.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return Ia(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Da(this,a);b.appendChild(a)}})},prepend:function(){return Ia(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Da(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ia(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ia(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(r.cleanData(ma(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null!=a&&a,b=null==b?a:b,this.map(function(){return r.clone(this,a,b)})},html:function(a){return S(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!za.test(a)&&!la[(ja.exec(a)||["",""])[1].toLowerCase()]){a=r.htmlPrefilter(a);try{for(;c<d;c++)b=this[c]||{},1===b.nodeType&&(r.cleanData(ma(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=[];return Ia(this,arguments,function(b){var c=this.parentNode;r.inArray(this,a)<0&&(r.cleanData(ma(this)),c&&c.replaceChild(b,this))},a)}}),r.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){r.fn[a]=function(a){for(var c,d=[],e=r(a),f=e.length-1,g=0;g<=f;g++)c=g===f?this:this.clone(!0),r(e[g])[b](c),h.apply(d,c.get());return this.pushStack(d)}});var Ka=/^margin/,La=new RegExp("^("+_+")(?!px)[a-z%]+$","i"),Ma=function(b){var c=b.ownerDocument.defaultView;return c&&c.opener||(c=a),c.getComputedStyle(b)};!function(){function b(){if(i){i.style.cssText="box-sizing:border-box;position:relative;display:block;margin:auto;border:1px;padding:1px;top:1%;width:50%",i.innerHTML="",qa.appendChild(h);var b=a.getComputedStyle(i);c="1%"!==b.top,g="2px"===b.marginLeft,e="4px"===b.width,i.style.marginRight="50%",f="4px"===b.marginRight,qa.removeChild(h),i=null}}var c,e,f,g,h=d.createElement("div"),i=d.createElement("div");i.style&&(i.style.backgroundClip="content-box",i.cloneNode(!0).style.backgroundClip="",o.clearCloneStyle="content-box"===i.style.backgroundClip,h.style.cssText="border:0;width:8px;height:0;top:0;left:-9999px;padding:0;margin-top:1px;position:absolute",h.appendChild(i),r.extend(o,{pixelPosition:function(){return b(),c},boxSizingReliable:function(){return b(),e},pixelMarginRight:function(){return b(),f},reliableMarginLeft:function(){return b(),g}}))}();function Na(a,b,c){var d,e,f,g,h=a.style;return c=c||Ma(a),c&&(g=c.getPropertyValue(b)||c[b],""!==g||r.contains(a.ownerDocument,a)||(g=r.style(a,b)),!o.pixelMarginRight()&&La.test(g)&&Ka.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0!==g?g+"":g}function Oa(a,b){return{get:function(){return a()?void delete this.get:(this.get=b).apply(this,arguments)}}}var Pa=/^(none|table(?!-c[ea]).+)/,Qa={position:"absolute",visibility:"hidden",display:"block"},Ra={letterSpacing:"0",fontWeight:"400"},Sa=["Webkit","Moz","ms"],Ta=d.createElement("div").style;function Ua(a){if(a in Ta)return a;var b=a[0].toUpperCase()+a.slice(1),c=Sa.length;while(c--)if(a=Sa[c]+b,a in Ta)return a}function Va(a,b,c){var d=aa.exec(b);return d?Math.max(0,d[2]-(c||0))+(d[3]||"px"):b}function Wa(a,b,c,d,e){var f,g=0;for(f=c===(d?"border":"content")?4:"width"===b?1:0;f<4;f+=2)"margin"===c&&(g+=r.css(a,c+ba[f],!0,e)),d?("content"===c&&(g-=r.css(a,"padding"+ba[f],!0,e)),"margin"!==c&&(g-=r.css(a,"border"+ba[f]+"Width",!0,e))):(g+=r.css(a,"padding"+ba[f],!0,e),"padding"!==c&&(g+=r.css(a,"border"+ba[f]+"Width",!0,e)));return g}function Xa(a,b,c){var d,e=!0,f=Ma(a),g="border-box"===r.css(a,"boxSizing",!1,f);if(a.getClientRects().length&&(d=a.getBoundingClientRect()[b]),d<=0||null==d){if(d=Na(a,b,f),(d<0||null==d)&&(d=a.style[b]),La.test(d))return d;e=g&&(o.boxSizingReliable()||d===a.style[b]),d=parseFloat(d)||0}return d+Wa(a,b,c||(g?"border":"content"),e,f)+"px"}r.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=Na(a,"opacity");return""===c?"1":c}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":"cssFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=r.camelCase(b),i=a.style;return b=r.cssProps[h]||(r.cssProps[h]=Ua(h)||h),g=r.cssHooks[b]||r.cssHooks[h],void 0===c?g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b]:(f=typeof c,"string"===f&&(e=aa.exec(c))&&e[1]&&(c=ea(a,b,e),f="number"),null!=c&&c===c&&("number"===f&&(c+=e&&e[3]||(r.cssNumber[h]?"":"px")),o.clearCloneStyle||""!==c||0!==b.indexOf("background")||(i[b]="inherit"),g&&"set"in g&&void 0===(c=g.set(a,c,d))||(i[b]=c)),void 0)}},css:function(a,b,c,d){var e,f,g,h=r.camelCase(b);return b=r.cssProps[h]||(r.cssProps[h]=Ua(h)||h),g=r.cssHooks[b]||r.cssHooks[h],g&&"get"in g&&(e=g.get(a,!0,c)),void 0===e&&(e=Na(a,b,d)),"normal"===e&&b in Ra&&(e=Ra[b]),""===c||c?(f=parseFloat(e),c===!0||isFinite(f)?f||0:e):e}}),r.each(["height","width"],function(a,b){r.cssHooks[b]={get:function(a,c,d){if(c)return!Pa.test(r.css(a,"display"))||a.getClientRects().length&&a.getBoundingClientRect().width?Xa(a,b,d):da(a,Qa,function(){return Xa(a,b,d)})},set:function(a,c,d){var e,f=d&&Ma(a),g=d&&Wa(a,b,d,"border-box"===r.css(a,"boxSizing",!1,f),f);return g&&(e=aa.exec(c))&&"px"!==(e[3]||"px")&&(a.style[b]=c,c=r.css(a,b)),Va(a,c,g)}}}),r.cssHooks.marginLeft=Oa(o.reliableMarginLeft,function(a,b){if(b)return(parseFloat(Na(a,"marginLeft"))||a.getBoundingClientRect().left-da(a,{marginLeft:0},function(){return a.getBoundingClientRect().left}))+"px"}),r.each({margin:"",padding:"",border:"Width"},function(a,b){r.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];d<4;d++)e[a+ba[d]+b]=f[d]||f[d-2]||f[0];return e}},Ka.test(a)||(r.cssHooks[a+b].set=Va)}),r.fn.extend({css:function(a,b){return S(this,function(a,b,c){var d,e,f={},g=0;if(r.isArray(b)){for(d=Ma(a),e=b.length;g<e;g++)f[b[g]]=r.css(a,b[g],!1,d);return f}return void 0!==c?r.style(a,b,c):r.css(a,b)},a,b,arguments.length>1)}});function Ya(a,b,c,d,e){return new Ya.prototype.init(a,b,c,d,e)}r.Tween=Ya,Ya.prototype={constructor:Ya,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||r.easing._default,this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(r.cssNumber[c]?"":"px")},cur:function(){var a=Ya.propHooks[this.prop];return a&&a.get?a.get(this):Ya.propHooks._default.get(this)},run:function(a){var b,c=Ya.propHooks[this.prop];return this.options.duration?this.pos=b=r.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Ya.propHooks._default.set(this),this}},Ya.prototype.init.prototype=Ya.prototype,Ya.propHooks={_default:{get:function(a){var b;return 1!==a.elem.nodeType||null!=a.elem[a.prop]&&null==a.elem.style[a.prop]?a.elem[a.prop]:(b=r.css(a.elem,a.prop,""),b&&"auto"!==b?b:0)},set:function(a){r.fx.step[a.prop]?r.fx.step[a.prop](a):1!==a.elem.nodeType||null==a.elem.style[r.cssProps[a.prop]]&&!r.cssHooks[a.prop]?a.elem[a.prop]=a.now:r.style(a.elem,a.prop,a.now+a.unit)}}},Ya.propHooks.scrollTop=Ya.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},r.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2},_default:"swing"},r.fx=Ya.prototype.init,r.fx.step={};var Za,$a,_a=/^(?:toggle|show|hide)$/,ab=/queueHooks$/;function bb(){$a&&(a.requestAnimationFrame(bb),r.fx.tick())}function cb(){return a.setTimeout(function(){Za=void 0}),Za=r.now()}function db(a,b){var c,d=0,e={height:a};for(b=b?1:0;d<4;d+=2-b)c=ba[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function eb(a,b,c){for(var d,e=(hb.tweeners[b]||[]).concat(hb.tweeners["*"]),f=0,g=e.length;f<g;f++)if(d=e[f].call(c,b,a))return d}function fb(a,b,c){var d,e,f,g,h,i,j,k,l="width"in b||"height"in b,m=this,n={},o=a.style,p=a.nodeType&&ca(a),q=V.get(a,"fxshow");c.queue||(g=r._queueHooks(a,"fx"),null==g.unqueued&&(g.unqueued=0,h=g.empty.fire,g.empty.fire=function(){g.unqueued||h()}),g.unqueued++,m.always(function(){m.always(function(){g.unqueued--,r.queue(a,"fx").length||g.empty.fire()})}));for(d in b)if(e=b[d],_a.test(e)){if(delete b[d],f=f||"toggle"===e,e===(p?"hide":"show")){if("show"!==e||!q||void 0===q[d])continue;p=!0}n[d]=q&&q[d]||r.style(a,d)}if(i=!r.isEmptyObject(b),i||!r.isEmptyObject(n)){l&&1===a.nodeType&&(c.overflow=[o.overflow,o.overflowX,o.overflowY],j=q&&q.display,null==j&&(j=V.get(a,"display")),k=r.css(a,"display"),"none"===k&&(j?k=j:(ha([a],!0),j=a.style.display||j,k=r.css(a,"display"),ha([a]))),("inline"===k||"inline-block"===k&&null!=j)&&"none"===r.css(a,"float")&&(i||(m.done(function(){o.display=j}),null==j&&(k=o.display,j="none"===k?"":k)),o.display="inline-block")),c.overflow&&(o.overflow="hidden",m.always(function(){o.overflow=c.overflow[0],o.overflowX=c.overflow[1],o.overflowY=c.overflow[2]})),i=!1;for(d in n)i||(q?"hidden"in q&&(p=q.hidden):q=V.access(a,"fxshow",{display:j}),f&&(q.hidden=!p),p&&ha([a],!0),m.done(function(){p||ha([a]),V.remove(a,"fxshow");for(d in n)r.style(a,d,n[d])})),i=eb(p?q[d]:0,d,m),d in q||(q[d]=i.start,p&&(i.end=i.start,i.start=0))}}function gb(a,b){var c,d,e,f,g;for(c in a)if(d=r.camelCase(c),e=b[d],f=a[c],r.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=r.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function hb(a,b,c){var d,e,f=0,g=hb.prefilters.length,h=r.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=Za||cb(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;g<i;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),f<1&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:r.extend({},b),opts:r.extend(!0,{specialEasing:{},easing:r.easing._default},c),originalProperties:b,originalOptions:c,startTime:Za||cb(),duration:c.duration,tweens:[],createTween:function(b,c){var d=r.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;c<d;c++)j.tweens[c].run(1);return b?(h.notifyWith(a,[j,1,0]),h.resolveWith(a,[j,b])):h.rejectWith(a,[j,b]),this}}),k=j.props;for(gb(k,j.opts.specialEasing);f<g;f++)if(d=hb.prefilters[f].call(j,a,k,j.opts))return r.isFunction(d.stop)&&(r._queueHooks(j.elem,j.opts.queue).stop=r.proxy(d.stop,d)),d;return r.map(k,eb,j),r.isFunction(j.opts.start)&&j.opts.start.call(a,j),r.fx.timer(r.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}r.Animation=r.extend(hb,{tweeners:{"*":[function(a,b){var c=this.createTween(a,b);return ea(c.elem,a,aa.exec(b),c),c}]},tweener:function(a,b){r.isFunction(a)?(b=a,a=["*"]):a=a.match(K);for(var c,d=0,e=a.length;d<e;d++)c=a[d],hb.tweeners[c]=hb.tweeners[c]||[],hb.tweeners[c].unshift(b)},prefilters:[fb],prefilter:function(a,b){b?hb.prefilters.unshift(a):hb.prefilters.push(a)}}),r.speed=function(a,b,c){var e=a&&"object"==typeof a?r.extend({},a):{complete:c||!c&&b||r.isFunction(a)&&a,duration:a,easing:c&&b||b&&!r.isFunction(b)&&b};return r.fx.off||d.hidden?e.duration=0:"number"!=typeof e.duration&&(e.duration in r.fx.speeds?e.duration=r.fx.speeds[e.duration]:e.duration=r.fx.speeds._default),null!=e.queue&&e.queue!==!0||(e.queue="fx"),e.old=e.complete,e.complete=function(){r.isFunction(e.old)&&e.old.call(this),e.queue&&r.dequeue(this,e.queue)},e},r.fn.extend({fadeTo:function(a,b,c,d){return this.filter(ca).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=r.isEmptyObject(a),f=r.speed(b,c,d),g=function(){var b=hb(this,r.extend({},a),f);(e||V.get(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=r.timers,g=V.get(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&ab.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));!b&&c||r.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=V.get(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=r.timers,g=d?d.length:0;for(c.finish=!0,r.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;b<g;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),r.each(["toggle","show","hide"],function(a,b){var c=r.fn[b];r.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(db(b,!0),a,d,e)}}),r.each({slideDown:db("show"),slideUp:db("hide"),slideToggle:db("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){r.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),r.timers=[],r.fx.tick=function(){var a,b=0,c=r.timers;for(Za=r.now();b<c.length;b++)a=c[b],a()||c[b]!==a||c.splice(b--,1);c.length||r.fx.stop(),Za=void 0},r.fx.timer=function(a){r.timers.push(a),a()?r.fx.start():r.timers.pop()},r.fx.interval=13,r.fx.start=function(){$a||($a=a.requestAnimationFrame?a.requestAnimationFrame(bb):a.setInterval(r.fx.tick,r.fx.interval))},r.fx.stop=function(){a.cancelAnimationFrame?a.cancelAnimationFrame($a):a.clearInterval($a),$a=null},r.fx.speeds={slow:600,fast:200,_default:400},r.fn.delay=function(b,c){return b=r.fx?r.fx.speeds[b]||b:b,c=c||"fx",this.queue(c,function(c,d){var e=a.setTimeout(c,b);d.stop=function(){a.clearTimeout(e)}})},function(){var a=d.createElement("input"),b=d.createElement("select"),c=b.appendChild(d.createElement("option"));a.type="checkbox",o.checkOn=""!==a.value,o.optSelected=c.selected,a=d.createElement("input"),a.value="t",a.type="radio",o.radioValue="t"===a.value}();var ib,jb=r.expr.attrHandle;r.fn.extend({attr:function(a,b){return S(this,r.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){r.removeAttr(this,a)})}}),r.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return"undefined"==typeof a.getAttribute?r.prop(a,b,c):(1===f&&r.isXMLDoc(a)||(e=r.attrHooks[b.toLowerCase()]||(r.expr.match.bool.test(b)?ib:void 0)), +void 0!==c?null===c?void r.removeAttr(a,b):e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:(a.setAttribute(b,c+""),c):e&&"get"in e&&null!==(d=e.get(a,b))?d:(d=r.find.attr(a,b),null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!o.radioValue&&"radio"===b&&r.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}},removeAttr:function(a,b){var c,d=0,e=b&&b.match(K);if(e&&1===a.nodeType)while(c=e[d++])a.removeAttribute(c)}}),ib={set:function(a,b,c){return b===!1?r.removeAttr(a,c):a.setAttribute(c,c),c}},r.each(r.expr.match.bool.source.match(/\w+/g),function(a,b){var c=jb[b]||r.find.attr;jb[b]=function(a,b,d){var e,f,g=b.toLowerCase();return d||(f=jb[g],jb[g]=e,e=null!=c(a,b,d)?g:null,jb[g]=f),e}});var kb=/^(?:input|select|textarea|button)$/i,lb=/^(?:a|area)$/i;r.fn.extend({prop:function(a,b){return S(this,r.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[r.propFix[a]||a]})}}),r.extend({prop:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return 1===f&&r.isXMLDoc(a)||(b=r.propFix[b]||b,e=r.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=r.find.attr(a,"tabindex");return b?parseInt(b,10):kb.test(a.nodeName)||lb.test(a.nodeName)&&a.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),o.optSelected||(r.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null},set:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}}),r.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){r.propFix[this.toLowerCase()]=this});function mb(a){var b=a.match(K)||[];return b.join(" ")}function nb(a){return a.getAttribute&&a.getAttribute("class")||""}r.fn.extend({addClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).addClass(a.call(this,b,nb(this)))});if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=nb(c),d=1===c.nodeType&&" "+mb(e)+" "){g=0;while(f=b[g++])d.indexOf(" "+f+" ")<0&&(d+=f+" ");h=mb(d),e!==h&&c.setAttribute("class",h)}}return this},removeClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).removeClass(a.call(this,b,nb(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=nb(c),d=1===c.nodeType&&" "+mb(e)+" "){g=0;while(f=b[g++])while(d.indexOf(" "+f+" ")>-1)d=d.replace(" "+f+" "," ");h=mb(d),e!==h&&c.setAttribute("class",h)}}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):r.isFunction(a)?this.each(function(c){r(this).toggleClass(a.call(this,c,nb(this),b),b)}):this.each(function(){var b,d,e,f;if("string"===c){d=0,e=r(this),f=a.match(K)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else void 0!==a&&"boolean"!==c||(b=nb(this),b&&V.set(this,"__className__",b),this.setAttribute&&this.setAttribute("class",b||a===!1?"":V.get(this,"__className__")||""))})},hasClass:function(a){var b,c,d=0;b=" "+a+" ";while(c=this[d++])if(1===c.nodeType&&(" "+mb(nb(c))+" ").indexOf(b)>-1)return!0;return!1}});var ob=/\r/g;r.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=r.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,r(this).val()):a,null==e?e="":"number"==typeof e?e+="":r.isArray(e)&&(e=r.map(e,function(a){return null==a?"":a+""})),b=r.valHooks[this.type]||r.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=r.valHooks[e.type]||r.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(ob,""):null==c?"":c)}}}),r.extend({valHooks:{option:{get:function(a){var b=r.find.attr(a,"value");return null!=b?b:mb(r.text(a))}},select:{get:function(a){var b,c,d,e=a.options,f=a.selectedIndex,g="select-one"===a.type,h=g?null:[],i=g?f+1:e.length;for(d=f<0?i:g?f:0;d<i;d++)if(c=e[d],(c.selected||d===f)&&!c.disabled&&(!c.parentNode.disabled||!r.nodeName(c.parentNode,"optgroup"))){if(b=r(c).val(),g)return b;h.push(b)}return h},set:function(a,b){var c,d,e=a.options,f=r.makeArray(b),g=e.length;while(g--)d=e[g],(d.selected=r.inArray(r.valHooks.option.get(d),f)>-1)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),r.each(["radio","checkbox"],function(){r.valHooks[this]={set:function(a,b){if(r.isArray(b))return a.checked=r.inArray(r(a).val(),b)>-1}},o.checkOn||(r.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var pb=/^(?:focusinfocus|focusoutblur)$/;r.extend(r.event,{trigger:function(b,c,e,f){var g,h,i,j,k,m,n,o=[e||d],p=l.call(b,"type")?b.type:b,q=l.call(b,"namespace")?b.namespace.split("."):[];if(h=i=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!pb.test(p+r.event.triggered)&&(p.indexOf(".")>-1&&(q=p.split("."),p=q.shift(),q.sort()),k=p.indexOf(":")<0&&"on"+p,b=b[r.expando]?b:new r.Event(p,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=q.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:r.makeArray(c,[b]),n=r.event.special[p]||{},f||!n.trigger||n.trigger.apply(e,c)!==!1)){if(!f&&!n.noBubble&&!r.isWindow(e)){for(j=n.delegateType||p,pb.test(j+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),i=h;i===(e.ownerDocument||d)&&o.push(i.defaultView||i.parentWindow||a)}g=0;while((h=o[g++])&&!b.isPropagationStopped())b.type=g>1?j:n.bindType||p,m=(V.get(h,"events")||{})[b.type]&&V.get(h,"handle"),m&&m.apply(h,c),m=k&&h[k],m&&m.apply&&T(h)&&(b.result=m.apply(h,c),b.result===!1&&b.preventDefault());return b.type=p,f||b.isDefaultPrevented()||n._default&&n._default.apply(o.pop(),c)!==!1||!T(e)||k&&r.isFunction(e[p])&&!r.isWindow(e)&&(i=e[k],i&&(e[k]=null),r.event.triggered=p,e[p](),r.event.triggered=void 0,i&&(e[k]=i)),b.result}},simulate:function(a,b,c){var d=r.extend(new r.Event,c,{type:a,isSimulated:!0});r.event.trigger(d,null,b)}}),r.fn.extend({trigger:function(a,b){return this.each(function(){r.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];if(c)return r.event.trigger(a,b,c,!0)}}),r.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(a,b){r.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),r.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),o.focusin="onfocusin"in a,o.focusin||r.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){r.event.simulate(b,a.target,r.event.fix(a))};r.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=V.access(d,b);e||d.addEventListener(a,c,!0),V.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=V.access(d,b)-1;e?V.access(d,b,e):(d.removeEventListener(a,c,!0),V.remove(d,b))}}});var qb=a.location,rb=r.now(),sb=/\?/;r.parseXML=function(b){var c;if(!b||"string"!=typeof b)return null;try{c=(new a.DOMParser).parseFromString(b,"text/xml")}catch(d){c=void 0}return c&&!c.getElementsByTagName("parsererror").length||r.error("Invalid XML: "+b),c};var tb=/\[\]$/,ub=/\r?\n/g,vb=/^(?:submit|button|image|reset|file)$/i,wb=/^(?:input|select|textarea|keygen)/i;function xb(a,b,c,d){var e;if(r.isArray(b))r.each(b,function(b,e){c||tb.test(a)?d(a,e):xb(a+"["+("object"==typeof e&&null!=e?b:"")+"]",e,c,d)});else if(c||"object"!==r.type(b))d(a,b);else for(e in b)xb(a+"["+e+"]",b[e],c,d)}r.param=function(a,b){var c,d=[],e=function(a,b){var c=r.isFunction(b)?b():b;d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(null==c?"":c)};if(r.isArray(a)||a.jquery&&!r.isPlainObject(a))r.each(a,function(){e(this.name,this.value)});else for(c in a)xb(c,a[c],b,e);return d.join("&")},r.fn.extend({serialize:function(){return r.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=r.prop(this,"elements");return a?r.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!r(this).is(":disabled")&&wb.test(this.nodeName)&&!vb.test(a)&&(this.checked||!ia.test(a))}).map(function(a,b){var c=r(this).val();return null==c?null:r.isArray(c)?r.map(c,function(a){return{name:b.name,value:a.replace(ub,"\r\n")}}):{name:b.name,value:c.replace(ub,"\r\n")}}).get()}});var yb=/%20/g,zb=/#.*$/,Ab=/([?&])_=[^&]*/,Bb=/^(.*?):[ \t]*([^\r\n]*)$/gm,Cb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Db=/^(?:GET|HEAD)$/,Eb=/^\/\//,Fb={},Gb={},Hb="*/".concat("*"),Ib=d.createElement("a");Ib.href=qb.href;function Jb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(K)||[];if(r.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Kb(a,b,c,d){var e={},f=a===Gb;function g(h){var i;return e[h]=!0,r.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Lb(a,b){var c,d,e=r.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&r.extend(!0,a,d),a}function Mb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}if(f)return f!==i[0]&&i.unshift(f),c[f]}function Nb(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}r.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:qb.href,type:"GET",isLocal:Cb.test(qb.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Hb,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":r.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Lb(Lb(a,r.ajaxSettings),b):Lb(r.ajaxSettings,a)},ajaxPrefilter:Jb(Fb),ajaxTransport:Jb(Gb),ajax:function(b,c){"object"==typeof b&&(c=b,b=void 0),c=c||{};var e,f,g,h,i,j,k,l,m,n,o=r.ajaxSetup({},c),p=o.context||o,q=o.context&&(p.nodeType||p.jquery)?r(p):r.event,s=r.Deferred(),t=r.Callbacks("once memory"),u=o.statusCode||{},v={},w={},x="canceled",y={readyState:0,getResponseHeader:function(a){var b;if(k){if(!h){h={};while(b=Bb.exec(g))h[b[1].toLowerCase()]=b[2]}b=h[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return k?g:null},setRequestHeader:function(a,b){return null==k&&(a=w[a.toLowerCase()]=w[a.toLowerCase()]||a,v[a]=b),this},overrideMimeType:function(a){return null==k&&(o.mimeType=a),this},statusCode:function(a){var b;if(a)if(k)y.always(a[y.status]);else for(b in a)u[b]=[u[b],a[b]];return this},abort:function(a){var b=a||x;return e&&e.abort(b),A(0,b),this}};if(s.promise(y),o.url=((b||o.url||qb.href)+"").replace(Eb,qb.protocol+"//"),o.type=c.method||c.type||o.method||o.type,o.dataTypes=(o.dataType||"*").toLowerCase().match(K)||[""],null==o.crossDomain){j=d.createElement("a");try{j.href=o.url,j.href=j.href,o.crossDomain=Ib.protocol+"//"+Ib.host!=j.protocol+"//"+j.host}catch(z){o.crossDomain=!0}}if(o.data&&o.processData&&"string"!=typeof o.data&&(o.data=r.param(o.data,o.traditional)),Kb(Fb,o,c,y),k)return y;l=r.event&&o.global,l&&0===r.active++&&r.event.trigger("ajaxStart"),o.type=o.type.toUpperCase(),o.hasContent=!Db.test(o.type),f=o.url.replace(zb,""),o.hasContent?o.data&&o.processData&&0===(o.contentType||"").indexOf("application/x-www-form-urlencoded")&&(o.data=o.data.replace(yb,"+")):(n=o.url.slice(f.length),o.data&&(f+=(sb.test(f)?"&":"?")+o.data,delete o.data),o.cache===!1&&(f=f.replace(Ab,"$1"),n=(sb.test(f)?"&":"?")+"_="+rb++ +n),o.url=f+n),o.ifModified&&(r.lastModified[f]&&y.setRequestHeader("If-Modified-Since",r.lastModified[f]),r.etag[f]&&y.setRequestHeader("If-None-Match",r.etag[f])),(o.data&&o.hasContent&&o.contentType!==!1||c.contentType)&&y.setRequestHeader("Content-Type",o.contentType),y.setRequestHeader("Accept",o.dataTypes[0]&&o.accepts[o.dataTypes[0]]?o.accepts[o.dataTypes[0]]+("*"!==o.dataTypes[0]?", "+Hb+"; q=0.01":""):o.accepts["*"]);for(m in o.headers)y.setRequestHeader(m,o.headers[m]);if(o.beforeSend&&(o.beforeSend.call(p,y,o)===!1||k))return y.abort();if(x="abort",t.add(o.complete),y.done(o.success),y.fail(o.error),e=Kb(Gb,o,c,y)){if(y.readyState=1,l&&q.trigger("ajaxSend",[y,o]),k)return y;o.async&&o.timeout>0&&(i=a.setTimeout(function(){y.abort("timeout")},o.timeout));try{k=!1,e.send(v,A)}catch(z){if(k)throw z;A(-1,z)}}else A(-1,"No Transport");function A(b,c,d,h){var j,m,n,v,w,x=c;k||(k=!0,i&&a.clearTimeout(i),e=void 0,g=h||"",y.readyState=b>0?4:0,j=b>=200&&b<300||304===b,d&&(v=Mb(o,y,d)),v=Nb(o,v,y,j),j?(o.ifModified&&(w=y.getResponseHeader("Last-Modified"),w&&(r.lastModified[f]=w),w=y.getResponseHeader("etag"),w&&(r.etag[f]=w)),204===b||"HEAD"===o.type?x="nocontent":304===b?x="notmodified":(x=v.state,m=v.data,n=v.error,j=!n)):(n=x,!b&&x||(x="error",b<0&&(b=0))),y.status=b,y.statusText=(c||x)+"",j?s.resolveWith(p,[m,x,y]):s.rejectWith(p,[y,x,n]),y.statusCode(u),u=void 0,l&&q.trigger(j?"ajaxSuccess":"ajaxError",[y,o,j?m:n]),t.fireWith(p,[y,x]),l&&(q.trigger("ajaxComplete",[y,o]),--r.active||r.event.trigger("ajaxStop")))}return y},getJSON:function(a,b,c){return r.get(a,b,c,"json")},getScript:function(a,b){return r.get(a,void 0,b,"script")}}),r.each(["get","post"],function(a,b){r[b]=function(a,c,d,e){return r.isFunction(c)&&(e=e||d,d=c,c=void 0),r.ajax(r.extend({url:a,type:b,dataType:e,data:c,success:d},r.isPlainObject(a)&&a))}}),r._evalUrl=function(a){return r.ajax({url:a,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},r.fn.extend({wrapAll:function(a){var b;return this[0]&&(r.isFunction(a)&&(a=a.call(this[0])),b=r(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this},wrapInner:function(a){return r.isFunction(a)?this.each(function(b){r(this).wrapInner(a.call(this,b))}):this.each(function(){var b=r(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=r.isFunction(a);return this.each(function(c){r(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(a){return this.parent(a).not("body").each(function(){r(this).replaceWith(this.childNodes)}),this}}),r.expr.pseudos.hidden=function(a){return!r.expr.pseudos.visible(a)},r.expr.pseudos.visible=function(a){return!!(a.offsetWidth||a.offsetHeight||a.getClientRects().length)},r.ajaxSettings.xhr=function(){try{return new a.XMLHttpRequest}catch(b){}};var Ob={0:200,1223:204},Pb=r.ajaxSettings.xhr();o.cors=!!Pb&&"withCredentials"in Pb,o.ajax=Pb=!!Pb,r.ajaxTransport(function(b){var c,d;if(o.cors||Pb&&!b.crossDomain)return{send:function(e,f){var g,h=b.xhr();if(h.open(b.type,b.url,b.async,b.username,b.password),b.xhrFields)for(g in b.xhrFields)h[g]=b.xhrFields[g];b.mimeType&&h.overrideMimeType&&h.overrideMimeType(b.mimeType),b.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest");for(g in e)h.setRequestHeader(g,e[g]);c=function(a){return function(){c&&(c=d=h.onload=h.onerror=h.onabort=h.onreadystatechange=null,"abort"===a?h.abort():"error"===a?"number"!=typeof h.status?f(0,"error"):f(h.status,h.statusText):f(Ob[h.status]||h.status,h.statusText,"text"!==(h.responseType||"text")||"string"!=typeof h.responseText?{binary:h.response}:{text:h.responseText},h.getAllResponseHeaders()))}},h.onload=c(),d=h.onerror=c("error"),void 0!==h.onabort?h.onabort=d:h.onreadystatechange=function(){4===h.readyState&&a.setTimeout(function(){c&&d()})},c=c("abort");try{h.send(b.hasContent&&b.data||null)}catch(i){if(c)throw i}},abort:function(){c&&c()}}}),r.ajaxPrefilter(function(a){a.crossDomain&&(a.contents.script=!1)}),r.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(a){return r.globalEval(a),a}}}),r.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),r.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(e,f){b=r("<script>").prop({charset:a.scriptCharset,src:a.url}).on("load error",c=function(a){b.remove(),c=null,a&&f("error"===a.type?404:200,a.type)}),d.head.appendChild(b[0])},abort:function(){c&&c()}}}});var Qb=[],Rb=/(=)\?(?=&|$)|\?\?/;r.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=Qb.pop()||r.expando+"_"+rb++;return this[a]=!0,a}}),r.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(Rb.test(b.url)?"url":"string"==typeof b.data&&0===(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&Rb.test(b.data)&&"data");if(h||"jsonp"===b.dataTypes[0])return e=b.jsonpCallback=r.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(Rb,"$1"+e):b.jsonp!==!1&&(b.url+=(sb.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||r.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){void 0===f?r(a).removeProp(e):a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,Qb.push(e)),g&&r.isFunction(f)&&f(g[0]),g=f=void 0}),"script"}),o.createHTMLDocument=function(){var a=d.implementation.createHTMLDocument("").body;return a.innerHTML="<form></form><form></form>",2===a.childNodes.length}(),r.parseHTML=function(a,b,c){if("string"!=typeof a)return[];"boolean"==typeof b&&(c=b,b=!1);var e,f,g;return b||(o.createHTMLDocument?(b=d.implementation.createHTMLDocument(""),e=b.createElement("base"),e.href=d.location.href,b.head.appendChild(e)):b=d),f=B.exec(a),g=!c&&[],f?[b.createElement(f[1])]:(f=pa([a],b,g),g&&g.length&&r(g).remove(),r.merge([],f.childNodes))},r.fn.load=function(a,b,c){var d,e,f,g=this,h=a.indexOf(" ");return h>-1&&(d=mb(a.slice(h)),a=a.slice(0,h)),r.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(e="POST"),g.length>0&&r.ajax({url:a,type:e||"GET",dataType:"html",data:b}).done(function(a){f=arguments,g.html(d?r("<div>").append(r.parseHTML(a)).find(d):a)}).always(c&&function(a,b){g.each(function(){c.apply(this,f||[a.responseText,b,a])})}),this},r.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){r.fn[b]=function(a){return this.on(b,a)}}),r.expr.pseudos.animated=function(a){return r.grep(r.timers,function(b){return a===b.elem}).length};function Sb(a){return r.isWindow(a)?a:9===a.nodeType&&a.defaultView}r.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=r.css(a,"position"),l=r(a),m={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=r.css(a,"top"),i=r.css(a,"left"),j=("absolute"===k||"fixed"===k)&&(f+i).indexOf("auto")>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),r.isFunction(b)&&(b=b.call(a,c,r.extend({},h))),null!=b.top&&(m.top=b.top-h.top+g),null!=b.left&&(m.left=b.left-h.left+e),"using"in b?b.using.call(a,m):l.css(m)}},r.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){r.offset.setOffset(this,a,b)});var b,c,d,e,f=this[0];if(f)return f.getClientRects().length?(d=f.getBoundingClientRect(),d.width||d.height?(e=f.ownerDocument,c=Sb(e),b=e.documentElement,{top:d.top+c.pageYOffset-b.clientTop,left:d.left+c.pageXOffset-b.clientLeft}):d):{top:0,left:0}},position:function(){if(this[0]){var a,b,c=this[0],d={top:0,left:0};return"fixed"===r.css(c,"position")?b=c.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),r.nodeName(a[0],"html")||(d=a.offset()),d={top:d.top+r.css(a[0],"borderTopWidth",!0),left:d.left+r.css(a[0],"borderLeftWidth",!0)}),{top:b.top-d.top-r.css(c,"marginTop",!0),left:b.left-d.left-r.css(c,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent;while(a&&"static"===r.css(a,"position"))a=a.offsetParent;return a||qa})}}),r.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,b){var c="pageYOffset"===b;r.fn[a]=function(d){return S(this,function(a,d,e){var f=Sb(a);return void 0===e?f?f[b]:a[d]:void(f?f.scrollTo(c?f.pageXOffset:e,c?e:f.pageYOffset):a[d]=e)},a,d,arguments.length)}}),r.each(["top","left"],function(a,b){r.cssHooks[b]=Oa(o.pixelPosition,function(a,c){if(c)return c=Na(a,b),La.test(c)?r(a).position()[b]+"px":c})}),r.each({Height:"height",Width:"width"},function(a,b){r.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){r.fn[d]=function(e,f){var g=arguments.length&&(c||"boolean"!=typeof e),h=c||(e===!0||f===!0?"margin":"border");return S(this,function(b,c,e){var f;return r.isWindow(b)?0===d.indexOf("outer")?b["inner"+a]:b.document.documentElement["client"+a]:9===b.nodeType?(f=b.documentElement,Math.max(b.body["scroll"+a],f["scroll"+a],b.body["offset"+a],f["offset"+a],f["client"+a])):void 0===e?r.css(b,c,h):r.style(b,c,e,h)},b,g?e:void 0,g)}})}),r.fn.extend({bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}}),r.parseJSON=JSON.parse,"function"==typeof define&&define.amd&&define("jquery",[],function(){return r});var Tb=a.jQuery,Ub=a.$;return r.noConflict=function(b){return a.$===r&&(a.$=Ub),b&&a.jQuery===r&&(a.jQuery=Tb),r},b||(a.jQuery=a.$=r),r}); diff --git a/public/js/p5.js b/public/js/p5.js new file mode 100644 index 0000000000000000000000000000000000000000..c90badc19c1bc9f84a5f48a5168ecf8e4b42c240 --- /dev/null +++ b/public/js/p5.js @@ -0,0 +1,33029 @@ +/*! p5.js v0.5.4 October 01, 2016 */ +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.p5 = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){ + +},{}],2:[function(_dereq_,module,exports){ +// Run-time checking of preconditions. + +'use strict'; + +// Precondition function that checks if the given predicate is true. +// If not, it will throw an error. +exports.argument = function(predicate, message) { + if (!predicate) { + throw new Error(message); + } +}; + +// Precondition function that checks if the given assertion is true. +// If not, it will throw an error. +exports.assert = exports.argument; + +},{}],3:[function(_dereq_,module,exports){ +// Drawing utility functions. + +'use strict'; + +// Draw a line on the given context from point `x1,y1` to point `x2,y2`. +function line(ctx, x1, y1, x2, y2) { + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.stroke(); +} + +exports.line = line; + +},{}],4:[function(_dereq_,module,exports){ +// Glyph encoding + +'use strict'; + +var cffStandardStrings = [ + '.notdef', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', 'ampersand', 'quoteright', + 'parenleft', 'parenright', 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one', 'two', + 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less', 'equal', 'greater', + 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', + 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright', 'asciicircum', 'underscore', + 'quoteleft', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', 'asciitilde', 'exclamdown', 'cent', 'sterling', + 'fraction', 'yen', 'florin', 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft', + 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger', 'daggerdbl', 'periodcentered', 'paragraph', + 'bullet', 'quotesinglbase', 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand', + 'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve', 'dotaccent', 'dieresis', 'ring', + 'cedilla', 'hungarumlaut', 'ogonek', 'caron', 'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', + 'ordmasculine', 'ae', 'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior', 'logicalnot', 'mu', + 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn', 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', + 'threequarters', 'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior', 'copyright', + 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring', 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', + 'Edieresis', 'Egrave', 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute', 'Ocircumflex', + 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute', 'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', + 'Ydieresis', 'Zcaron', 'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla', 'eacute', + 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex', 'idieresis', 'igrave', 'ntilde', 'oacute', + 'ocircumflex', 'odieresis', 'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave', + 'yacute', 'ydieresis', 'zcaron', 'exclamsmall', 'Hungarumlautsmall', 'dollaroldstyle', 'dollarsuperior', + 'ampersandsmall', 'Acutesmall', 'parenleftsuperior', 'parenrightsuperior', '266 ff', 'onedotenleader', + 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle', 'fiveoldstyle', 'sixoldstyle', + 'sevenoldstyle', 'eightoldstyle', 'nineoldstyle', 'commasuperior', 'threequartersemdash', 'periodsuperior', + 'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior', 'esuperior', 'isuperior', 'lsuperior', + 'msuperior', 'nsuperior', 'osuperior', 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'ffi', 'ffl', + 'parenleftinferior', 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall', 'Asmall', + 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall', 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', + 'Msmall', 'Nsmall', 'Osmall', 'Psmall', 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', + 'Xsmall', 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall', 'exclamdownsmall', + 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall', 'Dieresissmall', 'Brevesmall', 'Caronsmall', + 'Dotaccentsmall', 'Macronsmall', 'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall', + 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths', 'onethird', 'twothirds', + 'zerosuperior', 'foursuperior', 'fivesuperior', 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', + 'zeroinferior', 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior', 'sixinferior', + 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior', 'dollarinferior', 'periodinferior', + 'commainferior', 'Agravesmall', 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', + 'Aringsmall', 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall', 'Edieresissmall', + 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall', 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', + 'Oacutesmall', 'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall', 'Ugravesmall', + 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall', 'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000', + '001.001', '001.002', '001.003', 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman', 'Semibold']; + +var cffStandardEncoding = [ + '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', + '', '', '', '', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', 'ampersand', 'quoteright', + 'parenleft', 'parenright', 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one', 'two', + 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less', 'equal', 'greater', + 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', + 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright', 'asciicircum', 'underscore', + 'quoteleft', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', 'asciitilde', '', '', '', '', '', '', '', '', + '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', + 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin', 'section', 'currency', 'quotesingle', + 'quotedblleft', 'guillemotleft', 'guilsinglleft', 'guilsinglright', 'fi', 'fl', '', 'endash', 'dagger', + 'daggerdbl', 'periodcentered', '', 'paragraph', 'bullet', 'quotesinglbase', 'quotedblbase', 'quotedblright', + 'guillemotright', 'ellipsis', 'perthousand', '', 'questiondown', '', 'grave', 'acute', 'circumflex', 'tilde', + 'macron', 'breve', 'dotaccent', 'dieresis', '', 'ring', 'cedilla', '', 'hungarumlaut', 'ogonek', 'caron', + 'emdash', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'AE', '', 'ordfeminine', '', '', '', + '', 'Lslash', 'Oslash', 'OE', 'ordmasculine', '', '', '', '', '', 'ae', '', '', '', 'dotlessi', '', '', + 'lslash', 'oslash', 'oe', 'germandbls']; + +var cffExpertEncoding = [ + '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', + '', '', '', '', 'space', 'exclamsmall', 'Hungarumlautsmall', '', 'dollaroldstyle', 'dollarsuperior', + 'ampersandsmall', 'Acutesmall', 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader', + 'comma', 'hyphen', 'period', 'fraction', 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', + 'fouroldstyle', 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle', 'nineoldstyle', 'colon', + 'semicolon', 'commasuperior', 'threequartersemdash', 'periodsuperior', 'questionsmall', '', 'asuperior', + 'bsuperior', 'centsuperior', 'dsuperior', 'esuperior', '', '', 'isuperior', '', '', 'lsuperior', 'msuperior', + 'nsuperior', 'osuperior', '', '', 'rsuperior', 'ssuperior', 'tsuperior', '', 'ff', 'fi', 'fl', 'ffi', 'ffl', + 'parenleftinferior', '', 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall', 'Asmall', + 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall', 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', + 'Msmall', 'Nsmall', 'Osmall', 'Psmall', 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', + 'Xsmall', 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall', '', '', '', '', '', '', '', + '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', + 'exclamdownsmall', 'centoldstyle', 'Lslashsmall', '', '', 'Scaronsmall', 'Zcaronsmall', 'Dieresissmall', + 'Brevesmall', 'Caronsmall', '', 'Dotaccentsmall', '', '', 'Macronsmall', '', '', 'figuredash', 'hypheninferior', + '', '', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall', '', '', '', 'onequarter', 'onehalf', 'threequarters', + 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths', 'onethird', 'twothirds', '', + '', 'zerosuperior', 'onesuperior', 'twosuperior', 'threesuperior', 'foursuperior', 'fivesuperior', + 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior', 'oneinferior', 'twoinferior', + 'threeinferior', 'fourinferior', 'fiveinferior', 'sixinferior', 'seveninferior', 'eightinferior', + 'nineinferior', 'centinferior', 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall', + 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall', 'AEsmall', 'Ccedillasmall', + 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall', 'Edieresissmall', 'Igravesmall', 'Iacutesmall', + 'Icircumflexsmall', 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall', + 'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall', 'Ugravesmall', 'Uacutesmall', + 'Ucircumflexsmall', 'Udieresissmall', 'Yacutesmall', 'Thornsmall', 'Ydieresissmall']; + +var standardNames = [ + '.notdef', '.null', 'nonmarkingreturn', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', + 'ampersand', 'quotesingle', 'parenleft', 'parenright', 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', + 'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less', + 'equal', 'greater', 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright', + 'asciicircum', 'underscore', 'grave', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', 'asciitilde', + 'Adieresis', 'Aring', 'Ccedilla', 'Eacute', 'Ntilde', 'Odieresis', 'Udieresis', 'aacute', 'agrave', + 'acircumflex', 'adieresis', 'atilde', 'aring', 'ccedilla', 'eacute', 'egrave', 'ecircumflex', 'edieresis', + 'iacute', 'igrave', 'icircumflex', 'idieresis', 'ntilde', 'oacute', 'ograve', 'ocircumflex', 'odieresis', + 'otilde', 'uacute', 'ugrave', 'ucircumflex', 'udieresis', 'dagger', 'degree', 'cent', 'sterling', 'section', + 'bullet', 'paragraph', 'germandbls', 'registered', 'copyright', 'trademark', 'acute', 'dieresis', 'notequal', + 'AE', 'Oslash', 'infinity', 'plusminus', 'lessequal', 'greaterequal', 'yen', 'mu', 'partialdiff', 'summation', + 'product', 'pi', 'integral', 'ordfeminine', 'ordmasculine', 'Omega', 'ae', 'oslash', 'questiondown', + 'exclamdown', 'logicalnot', 'radical', 'florin', 'approxequal', 'Delta', 'guillemotleft', 'guillemotright', + 'ellipsis', 'nonbreakingspace', 'Agrave', 'Atilde', 'Otilde', 'OE', 'oe', 'endash', 'emdash', 'quotedblleft', + 'quotedblright', 'quoteleft', 'quoteright', 'divide', 'lozenge', 'ydieresis', 'Ydieresis', 'fraction', + 'currency', 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'daggerdbl', 'periodcentered', 'quotesinglbase', + 'quotedblbase', 'perthousand', 'Acircumflex', 'Ecircumflex', 'Aacute', 'Edieresis', 'Egrave', 'Iacute', + 'Icircumflex', 'Idieresis', 'Igrave', 'Oacute', 'Ocircumflex', 'apple', 'Ograve', 'Uacute', 'Ucircumflex', + 'Ugrave', 'dotlessi', 'circumflex', 'tilde', 'macron', 'breve', 'dotaccent', 'ring', 'cedilla', 'hungarumlaut', + 'ogonek', 'caron', 'Lslash', 'lslash', 'Scaron', 'scaron', 'Zcaron', 'zcaron', 'brokenbar', 'Eth', 'eth', + 'Yacute', 'yacute', 'Thorn', 'thorn', 'minus', 'multiply', 'onesuperior', 'twosuperior', 'threesuperior', + 'onehalf', 'onequarter', 'threequarters', 'franc', 'Gbreve', 'gbreve', 'Idotaccent', 'Scedilla', 'scedilla', + 'Cacute', 'cacute', 'Ccaron', 'ccaron', 'dcroat']; + +// This is the encoding used for fonts created from scratch. +// It loops through all glyphs and finds the appropriate unicode value. +// Since it's linear time, other encodings will be faster. +function DefaultEncoding(font) { + this.font = font; +} + +DefaultEncoding.prototype.charToGlyphIndex = function(c) { + var code = c.charCodeAt(0); + var glyphs = this.font.glyphs; + if (glyphs) { + for (var i = 0; i < glyphs.length; i += 1) { + var glyph = glyphs.get(i); + for (var j = 0; j < glyph.unicodes.length; j += 1) { + if (glyph.unicodes[j] === code) { + return i; + } + } + } + } else { + return null; + } +}; + +function CmapEncoding(cmap) { + this.cmap = cmap; +} + +CmapEncoding.prototype.charToGlyphIndex = function(c) { + return this.cmap.glyphIndexMap[c.charCodeAt(0)] || 0; +}; + +function CffEncoding(encoding, charset) { + this.encoding = encoding; + this.charset = charset; +} + +CffEncoding.prototype.charToGlyphIndex = function(s) { + var code = s.charCodeAt(0); + var charName = this.encoding[code]; + return this.charset.indexOf(charName); +}; + +function GlyphNames(post) { + var i; + switch (post.version) { + case 1: + this.names = exports.standardNames.slice(); + break; + case 2: + this.names = new Array(post.numberOfGlyphs); + for (i = 0; i < post.numberOfGlyphs; i++) { + if (post.glyphNameIndex[i] < exports.standardNames.length) { + this.names[i] = exports.standardNames[post.glyphNameIndex[i]]; + } else { + this.names[i] = post.names[post.glyphNameIndex[i] - exports.standardNames.length]; + } + } + + break; + case 2.5: + this.names = new Array(post.numberOfGlyphs); + for (i = 0; i < post.numberOfGlyphs; i++) { + this.names[i] = exports.standardNames[i + post.glyphNameIndex[i]]; + } + + break; + case 3: + this.names = []; + break; + } +} + +GlyphNames.prototype.nameToGlyphIndex = function(name) { + return this.names.indexOf(name); +}; + +GlyphNames.prototype.glyphIndexToName = function(gid) { + return this.names[gid]; +}; + +function addGlyphNames(font) { + var glyph; + var glyphIndexMap = font.tables.cmap.glyphIndexMap; + var charCodes = Object.keys(glyphIndexMap); + + for (var i = 0; i < charCodes.length; i += 1) { + var c = charCodes[i]; + var glyphIndex = glyphIndexMap[c]; + glyph = font.glyphs.get(glyphIndex); + glyph.addUnicode(parseInt(c)); + } + + for (i = 0; i < font.glyphs.length; i += 1) { + glyph = font.glyphs.get(i); + if (font.cffEncoding) { + glyph.name = font.cffEncoding.charset[i]; + } else { + glyph.name = font.glyphNames.glyphIndexToName(i); + } + } +} + +exports.cffStandardStrings = cffStandardStrings; +exports.cffStandardEncoding = cffStandardEncoding; +exports.cffExpertEncoding = cffExpertEncoding; +exports.standardNames = standardNames; +exports.DefaultEncoding = DefaultEncoding; +exports.CmapEncoding = CmapEncoding; +exports.CffEncoding = CffEncoding; +exports.GlyphNames = GlyphNames; +exports.addGlyphNames = addGlyphNames; + +},{}],5:[function(_dereq_,module,exports){ +// The Font object + +'use strict'; + +var path = _dereq_('./path'); +var sfnt = _dereq_('./tables/sfnt'); +var encoding = _dereq_('./encoding'); +var glyphset = _dereq_('./glyphset'); + +// A Font represents a loaded OpenType font file. +// It contains a set of glyphs and methods to draw text on a drawing context, +// or to get a path representing the text. +function Font(options) { + options = options || {}; + + // OS X will complain if the names are empty, so we put a single space everywhere by default. + this.familyName = options.familyName || ' '; + this.styleName = options.styleName || ' '; + this.designer = options.designer || ' '; + this.designerURL = options.designerURL || ' '; + this.manufacturer = options.manufacturer || ' '; + this.manufacturerURL = options.manufacturerURL || ' '; + this.license = options.license || ' '; + this.licenseURL = options.licenseURL || ' '; + this.version = options.version || 'Version 0.1'; + this.description = options.description || ' '; + this.copyright = options.copyright || ' '; + this.trademark = options.trademark || ' '; + this.unitsPerEm = options.unitsPerEm || 1000; + this.ascender = options.ascender; + this.descender = options.descender; + this.supported = true; + this.glyphs = new glyphset.GlyphSet(this, options.glyphs || []); + this.encoding = new encoding.DefaultEncoding(this); + this.tables = {}; +} + +// Check if the font has a glyph for the given character. +Font.prototype.hasChar = function(c) { + return this.encoding.charToGlyphIndex(c) !== null; +}; + +// Convert the given character to a single glyph index. +// Note that this function assumes that there is a one-to-one mapping between +// the given character and a glyph; for complex scripts this might not be the case. +Font.prototype.charToGlyphIndex = function(s) { + return this.encoding.charToGlyphIndex(s); +}; + +// Convert the given character to a single Glyph object. +// Note that this function assumes that there is a one-to-one mapping between +// the given character and a glyph; for complex scripts this might not be the case. +Font.prototype.charToGlyph = function(c) { + var glyphIndex = this.charToGlyphIndex(c); + var glyph = this.glyphs.get(glyphIndex); + if (!glyph) { + // .notdef + glyph = this.glyphs.get(0); + } + + return glyph; +}; + +// Convert the given text to a list of Glyph objects. +// Note that there is no strict one-to-one mapping between characters and +// glyphs, so the list of returned glyphs can be larger or smaller than the +// length of the given string. +Font.prototype.stringToGlyphs = function(s) { + var glyphs = []; + for (var i = 0; i < s.length; i += 1) { + var c = s[i]; + glyphs.push(this.charToGlyph(c)); + } + + return glyphs; +}; + +Font.prototype.nameToGlyphIndex = function(name) { + return this.glyphNames.nameToGlyphIndex(name); +}; + +Font.prototype.nameToGlyph = function(name) { + var glyphIndex = this.nametoGlyphIndex(name); + var glyph = this.glyphs.get(glyphIndex); + if (!glyph) { + // .notdef + glyph = this.glyphs.get(0); + } + + return glyph; +}; + +Font.prototype.glyphIndexToName = function(gid) { + if (!this.glyphNames.glyphIndexToName) { + return ''; + } + + return this.glyphNames.glyphIndexToName(gid); +}; + +// Retrieve the value of the kerning pair between the left glyph (or its index) +// and the right glyph (or its index). If no kerning pair is found, return 0. +// The kerning value gets added to the advance width when calculating the spacing +// between glyphs. +Font.prototype.getKerningValue = function(leftGlyph, rightGlyph) { + leftGlyph = leftGlyph.index || leftGlyph; + rightGlyph = rightGlyph.index || rightGlyph; + var gposKerning = this.getGposKerningValue; + return gposKerning ? gposKerning(leftGlyph, rightGlyph) : + (this.kerningPairs[leftGlyph + ',' + rightGlyph] || 0); +}; + +// Helper function that invokes the given callback for each glyph in the given text. +// The callback gets `(glyph, x, y, fontSize, options)`. +Font.prototype.forEachGlyph = function(text, x, y, fontSize, options, callback) { + if (!this.supported) { + return; + } + + x = x !== undefined ? x : 0; + y = y !== undefined ? y : 0; + fontSize = fontSize !== undefined ? fontSize : 72; + options = options || {}; + var kerning = options.kerning === undefined ? true : options.kerning; + var fontScale = 1 / this.unitsPerEm * fontSize; + var glyphs = this.stringToGlyphs(text); + for (var i = 0; i < glyphs.length; i += 1) { + var glyph = glyphs[i]; + callback(glyph, x, y, fontSize, options); + if (glyph.advanceWidth) { + x += glyph.advanceWidth * fontScale; + } + + if (kerning && i < glyphs.length - 1) { + var kerningValue = this.getKerningValue(glyph, glyphs[i + 1]); + x += kerningValue * fontScale; + } + } +}; + +// Create a Path object that represents the given text. +// +// text - The text to create. +// x - Horizontal position of the beginning of the text. (default: 0) +// y - Vertical position of the *baseline* of the text. (default: 0) +// fontSize - Font size in pixels. We scale the glyph units by `1 / unitsPerEm * fontSize`. (default: 72) +// Options is an optional object that contains: +// - kerning - Whether to take kerning information into account. (default: true) +// +// Returns a Path object. +Font.prototype.getPath = function(text, x, y, fontSize, options) { + var fullPath = new path.Path(); + this.forEachGlyph(text, x, y, fontSize, options, function(glyph, gX, gY, gFontSize) { + var glyphPath = glyph.getPath(gX, gY, gFontSize); + fullPath.extend(glyphPath); + }); + + return fullPath; +}; + +// Draw the text on the given drawing context. +// +// ctx - A 2D drawing context, like Canvas. +// text - The text to create. +// x - Horizontal position of the beginning of the text. (default: 0) +// y - Vertical position of the *baseline* of the text. (default: 0) +// fontSize - Font size in pixels. We scale the glyph units by `1 / unitsPerEm * fontSize`. (default: 72) +// Options is an optional object that contains: +// - kerning - Whether to take kerning information into account. (default: true) +Font.prototype.draw = function(ctx, text, x, y, fontSize, options) { + this.getPath(text, x, y, fontSize, options).draw(ctx); +}; + +// Draw the points of all glyphs in the text. +// On-curve points will be drawn in blue, off-curve points will be drawn in red. +// +// ctx - A 2D drawing context, like Canvas. +// text - The text to create. +// x - Horizontal position of the beginning of the text. (default: 0) +// y - Vertical position of the *baseline* of the text. (default: 0) +// fontSize - Font size in pixels. We scale the glyph units by `1 / unitsPerEm * fontSize`. (default: 72) +// Options is an optional object that contains: +// - kerning - Whether to take kerning information into account. (default: true) +Font.prototype.drawPoints = function(ctx, text, x, y, fontSize, options) { + this.forEachGlyph(text, x, y, fontSize, options, function(glyph, gX, gY, gFontSize) { + glyph.drawPoints(ctx, gX, gY, gFontSize); + }); +}; + +// Draw lines indicating important font measurements for all glyphs in the text. +// Black lines indicate the origin of the coordinate system (point 0,0). +// Blue lines indicate the glyph bounding box. +// Green line indicates the advance width of the glyph. +// +// ctx - A 2D drawing context, like Canvas. +// text - The text to create. +// x - Horizontal position of the beginning of the text. (default: 0) +// y - Vertical position of the *baseline* of the text. (default: 0) +// fontSize - Font size in pixels. We scale the glyph units by `1 / unitsPerEm * fontSize`. (default: 72) +// Options is an optional object that contains: +// - kerning - Whether to take kerning information into account. (default: true) +Font.prototype.drawMetrics = function(ctx, text, x, y, fontSize, options) { + this.forEachGlyph(text, x, y, fontSize, options, function(glyph, gX, gY, gFontSize) { + glyph.drawMetrics(ctx, gX, gY, gFontSize); + }); +}; + +// Validate +Font.prototype.validate = function() { + var warnings = []; + var _this = this; + + function assert(predicate, message) { + if (!predicate) { + warnings.push(message); + } + } + + function assertStringAttribute(attrName) { + assert(_this[attrName] && _this[attrName].trim().length > 0, 'No ' + attrName + ' specified.'); + } + + // Identification information + assertStringAttribute('familyName'); + assertStringAttribute('weightName'); + assertStringAttribute('manufacturer'); + assertStringAttribute('copyright'); + assertStringAttribute('version'); + + // Dimension information + assert(this.unitsPerEm > 0, 'No unitsPerEm specified.'); +}; + +// Convert the font object to a SFNT data structure. +// This structure contains all the necessary tables and metadata to create a binary OTF file. +Font.prototype.toTables = function() { + return sfnt.fontToTable(this); +}; + +Font.prototype.toBuffer = function() { + var sfntTable = this.toTables(); + var bytes = sfntTable.encode(); + var buffer = new ArrayBuffer(bytes.length); + var intArray = new Uint8Array(buffer); + for (var i = 0; i < bytes.length; i++) { + intArray[i] = bytes[i]; + } + + return buffer; +}; + +// Initiate a download of the OpenType font. +Font.prototype.download = function() { + var fileName = this.familyName.replace(/\s/g, '') + '-' + this.styleName + '.otf'; + var buffer = this.toBuffer(); + + window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem; + window.requestFileSystem(window.TEMPORARY, buffer.byteLength, function(fs) { + fs.root.getFile(fileName, {create: true}, function(fileEntry) { + fileEntry.createWriter(function(writer) { + var dataView = new DataView(buffer); + var blob = new Blob([dataView], {type: 'font/opentype'}); + writer.write(blob); + + writer.addEventListener('writeend', function() { + // Navigating to the file will download it. + location.href = fileEntry.toURL(); + }, false); + }); + }); + }, + + function(err) { + throw err; + }); +}; + +exports.Font = Font; + +},{"./encoding":4,"./glyphset":7,"./path":10,"./tables/sfnt":25}],6:[function(_dereq_,module,exports){ +// The Glyph object + +'use strict'; + +var check = _dereq_('./check'); +var draw = _dereq_('./draw'); +var path = _dereq_('./path'); + +function getPathDefinition(glyph, path) { + var _path = path || { commands: [] }; + return { + configurable: true, + + get: function() { + if (typeof _path === 'function') { + _path = _path(); + } + + return _path; + }, + + set: function(p) { + _path = p; + } + }; +} + +// A Glyph is an individual mark that often corresponds to a character. +// Some glyphs, such as ligatures, are a combination of many characters. +// Glyphs are the basic building blocks of a font. +// +// The `Glyph` class contains utility methods for drawing the path and its points. +function Glyph(options) { + // By putting all the code on a prototype function (which is only declared once) + // we reduce the memory requirements for larger fonts by some 2% + this.bindConstructorValues(options); +} + +Glyph.prototype.bindConstructorValues = function(options) { + this.index = options.index || 0; + + // These three values cannnot be deferred for memory optimization: + this.name = options.name || null; + this.unicode = options.unicode || undefined; + this.unicodes = options.unicodes || options.unicode !== undefined ? [options.unicode] : []; + + // But by binding these values only when necessary, we reduce can + // the memory requirements by almost 3% for larger fonts. + if (options.xMin) { + this.xMin = options.xMin; + } + + if (options.yMin) { + this.yMin = options.yMin; + } + + if (options.xMax) { + this.xMax = options.xMax; + } + + if (options.yMax) { + this.yMax = options.yMax; + } + + if (options.advanceWidth) { + this.advanceWidth = options.advanceWidth; + } + + // The path for a glyph is the most memory intensive, and is bound as a value + // with a getter/setter to ensure we actually do path parsing only once the + // path is actually needed by anything. + Object.defineProperty(this, 'path', getPathDefinition(this, options.path)); +}; + +Glyph.prototype.addUnicode = function(unicode) { + if (this.unicodes.length === 0) { + this.unicode = unicode; + } + + this.unicodes.push(unicode); +}; + +// Convert the glyph to a Path we can draw on a drawing context. +// +// x - Horizontal position of the glyph. (default: 0) +// y - Vertical position of the *baseline* of the glyph. (default: 0) +// fontSize - Font size, in pixels (default: 72). +Glyph.prototype.getPath = function(x, y, fontSize) { + x = x !== undefined ? x : 0; + y = y !== undefined ? y : 0; + fontSize = fontSize !== undefined ? fontSize : 72; + var scale = 1 / this.path.unitsPerEm * fontSize; + var p = new path.Path(); + var commands = this.path.commands; + for (var i = 0; i < commands.length; i += 1) { + var cmd = commands[i]; + if (cmd.type === 'M') { + p.moveTo(x + (cmd.x * scale), y + (-cmd.y * scale)); + } else if (cmd.type === 'L') { + p.lineTo(x + (cmd.x * scale), y + (-cmd.y * scale)); + } else if (cmd.type === 'Q') { + p.quadraticCurveTo(x + (cmd.x1 * scale), y + (-cmd.y1 * scale), + x + (cmd.x * scale), y + (-cmd.y * scale)); + } else if (cmd.type === 'C') { + p.curveTo(x + (cmd.x1 * scale), y + (-cmd.y1 * scale), + x + (cmd.x2 * scale), y + (-cmd.y2 * scale), + x + (cmd.x * scale), y + (-cmd.y * scale)); + } else if (cmd.type === 'Z') { + p.closePath(); + } + } + + return p; +}; + +// Split the glyph into contours. +// This function is here for backwards compatibility, and to +// provide raw access to the TrueType glyph outlines. +Glyph.prototype.getContours = function() { + if (this.points === undefined) { + return []; + } + + var contours = []; + var currentContour = []; + for (var i = 0; i < this.points.length; i += 1) { + var pt = this.points[i]; + currentContour.push(pt); + if (pt.lastPointOfContour) { + contours.push(currentContour); + currentContour = []; + } + } + + check.argument(currentContour.length === 0, 'There are still points left in the current contour.'); + return contours; +}; + +// Calculate the xMin/yMin/xMax/yMax/lsb/rsb for a Glyph. +Glyph.prototype.getMetrics = function() { + var commands = this.path.commands; + var xCoords = []; + var yCoords = []; + for (var i = 0; i < commands.length; i += 1) { + var cmd = commands[i]; + if (cmd.type !== 'Z') { + xCoords.push(cmd.x); + yCoords.push(cmd.y); + } + + if (cmd.type === 'Q' || cmd.type === 'C') { + xCoords.push(cmd.x1); + yCoords.push(cmd.y1); + } + + if (cmd.type === 'C') { + xCoords.push(cmd.x2); + yCoords.push(cmd.y2); + } + } + + var metrics = { + xMin: Math.min.apply(null, xCoords), + yMin: Math.min.apply(null, yCoords), + xMax: Math.max.apply(null, xCoords), + yMax: Math.max.apply(null, yCoords), + leftSideBearing: 0 + }; + metrics.rightSideBearing = this.advanceWidth - metrics.leftSideBearing - (metrics.xMax - metrics.xMin); + return metrics; +}; + +// Draw the glyph on the given context. +// +// ctx - The drawing context. +// x - Horizontal position of the glyph. (default: 0) +// y - Vertical position of the *baseline* of the glyph. (default: 0) +// fontSize - Font size, in pixels (default: 72). +Glyph.prototype.draw = function(ctx, x, y, fontSize) { + this.getPath(x, y, fontSize).draw(ctx); +}; + +// Draw the points of the glyph. +// On-curve points will be drawn in blue, off-curve points will be drawn in red. +// +// ctx - The drawing context. +// x - Horizontal position of the glyph. (default: 0) +// y - Vertical position of the *baseline* of the glyph. (default: 0) +// fontSize - Font size, in pixels (default: 72). +Glyph.prototype.drawPoints = function(ctx, x, y, fontSize) { + + function drawCircles(l, x, y, scale) { + var PI_SQ = Math.PI * 2; + ctx.beginPath(); + for (var j = 0; j < l.length; j += 1) { + ctx.moveTo(x + (l[j].x * scale), y + (l[j].y * scale)); + ctx.arc(x + (l[j].x * scale), y + (l[j].y * scale), 2, 0, PI_SQ, false); + } + + ctx.closePath(); + ctx.fill(); + } + + x = x !== undefined ? x : 0; + y = y !== undefined ? y : 0; + fontSize = fontSize !== undefined ? fontSize : 24; + var scale = 1 / this.path.unitsPerEm * fontSize; + + var blueCircles = []; + var redCircles = []; + var path = this.path; + for (var i = 0; i < path.commands.length; i += 1) { + var cmd = path.commands[i]; + if (cmd.x !== undefined) { + blueCircles.push({x: cmd.x, y: -cmd.y}); + } + + if (cmd.x1 !== undefined) { + redCircles.push({x: cmd.x1, y: -cmd.y1}); + } + + if (cmd.x2 !== undefined) { + redCircles.push({x: cmd.x2, y: -cmd.y2}); + } + } + + ctx.fillStyle = 'blue'; + drawCircles(blueCircles, x, y, scale); + ctx.fillStyle = 'red'; + drawCircles(redCircles, x, y, scale); +}; + +// Draw lines indicating important font measurements. +// Black lines indicate the origin of the coordinate system (point 0,0). +// Blue lines indicate the glyph bounding box. +// Green line indicates the advance width of the glyph. +// +// ctx - The drawing context. +// x - Horizontal position of the glyph. (default: 0) +// y - Vertical position of the *baseline* of the glyph. (default: 0) +// fontSize - Font size, in pixels (default: 72). +Glyph.prototype.drawMetrics = function(ctx, x, y, fontSize) { + var scale; + x = x !== undefined ? x : 0; + y = y !== undefined ? y : 0; + fontSize = fontSize !== undefined ? fontSize : 24; + scale = 1 / this.path.unitsPerEm * fontSize; + ctx.lineWidth = 1; + + // Draw the origin + ctx.strokeStyle = 'black'; + draw.line(ctx, x, -10000, x, 10000); + draw.line(ctx, -10000, y, 10000, y); + + // This code is here due to memory optimization: by not using + // defaults in the constructor, we save a notable amount of memory. + var xMin = this.xMin || 0; + var yMin = this.yMin || 0; + var xMax = this.xMax || 0; + var yMax = this.yMax || 0; + var advanceWidth = this.advanceWidth || 0; + + // Draw the glyph box + ctx.strokeStyle = 'blue'; + draw.line(ctx, x + (xMin * scale), -10000, x + (xMin * scale), 10000); + draw.line(ctx, x + (xMax * scale), -10000, x + (xMax * scale), 10000); + draw.line(ctx, -10000, y + (-yMin * scale), 10000, y + (-yMin * scale)); + draw.line(ctx, -10000, y + (-yMax * scale), 10000, y + (-yMax * scale)); + + // Draw the advance width + ctx.strokeStyle = 'green'; + draw.line(ctx, x + (advanceWidth * scale), -10000, x + (advanceWidth * scale), 10000); +}; + +exports.Glyph = Glyph; + +},{"./check":2,"./draw":3,"./path":10}],7:[function(_dereq_,module,exports){ +// The GlyphSet object + +'use strict'; + +var _glyph = _dereq_('./glyph'); + +// A GlyphSet represents all glyphs available in the font, but modelled using +// a deferred glyph loader, for retrieving glyphs only once they are absolutely +// necessary, to keep the memory footprint down. +function GlyphSet(font, glyphs) { + this.font = font; + this.glyphs = {}; + if (Array.isArray(glyphs)) { + for (var i = 0; i < glyphs.length; i++) { + this.glyphs[i] = glyphs[i]; + } + } + + this.length = (glyphs && glyphs.length) || 0; +} + +GlyphSet.prototype.get = function(index) { + if (typeof this.glyphs[index] === 'function') { + this.glyphs[index] = this.glyphs[index](); + } + + return this.glyphs[index]; +}; + +GlyphSet.prototype.push = function(index, loader) { + this.glyphs[index] = loader; + this.length++; +}; + +function glyphLoader(font, index) { + return new _glyph.Glyph({index: index, font: font}); +} + +/** + * Generate a stub glyph that can be filled with all metadata *except* + * the "points" and "path" properties, which must be loaded only once + * the glyph's path is actually requested for text shaping. + */ + +function ttfGlyphLoader(font, index, parseGlyph, data, position, buildPath) { + return function() { + var glyph = new _glyph.Glyph({index: index, font: font}); + + glyph.path = function() { + parseGlyph(glyph, data, position); + var path = buildPath(font.glyphs, glyph); + path.unitsPerEm = font.unitsPerEm; + return path; + }; + + return glyph; + }; +} + +function cffGlyphLoader(font, index, parseCFFCharstring, charstring) { + return function() { + var glyph = new _glyph.Glyph({index: index, font: font}); + + glyph.path = function() { + var path = parseCFFCharstring(font, glyph, charstring); + path.unitsPerEm = font.unitsPerEm; + return path; + }; + + return glyph; + }; +} + +exports.GlyphSet = GlyphSet; +exports.glyphLoader = glyphLoader; +exports.ttfGlyphLoader = ttfGlyphLoader; +exports.cffGlyphLoader = cffGlyphLoader; + +},{"./glyph":6}],8:[function(_dereq_,module,exports){ +// opentype.js +// https://github.com/nodebox/opentype.js +// (c) 2015 Frederik De Bleser +// opentype.js may be freely distributed under the MIT license. + +/* global ArrayBuffer, DataView, Uint8Array, XMLHttpRequest */ + +'use strict'; + +var encoding = _dereq_('./encoding'); +var _font = _dereq_('./font'); +var glyph = _dereq_('./glyph'); +var parse = _dereq_('./parse'); +var path = _dereq_('./path'); + +var cmap = _dereq_('./tables/cmap'); +var cff = _dereq_('./tables/cff'); +var glyf = _dereq_('./tables/glyf'); +var gpos = _dereq_('./tables/gpos'); +var head = _dereq_('./tables/head'); +var hhea = _dereq_('./tables/hhea'); +var hmtx = _dereq_('./tables/hmtx'); +var kern = _dereq_('./tables/kern'); +var loca = _dereq_('./tables/loca'); +var maxp = _dereq_('./tables/maxp'); +var _name = _dereq_('./tables/name'); +var os2 = _dereq_('./tables/os2'); +var post = _dereq_('./tables/post'); + +// File loaders ///////////////////////////////////////////////////////// + +// Convert a Node.js Buffer to an ArrayBuffer +function toArrayBuffer(buffer) { + var arrayBuffer = new ArrayBuffer(buffer.length); + var data = new Uint8Array(arrayBuffer); + for (var i = 0; i < buffer.length; i += 1) { + data[i] = buffer[i]; + } + + return arrayBuffer; +} + +function loadFromFile(path, callback) { + var fs = _dereq_('fs'); + fs.readFile(path, function(err, buffer) { + if (err) { + return callback(err.message); + } + + callback(null, toArrayBuffer(buffer)); + }); +} + +function loadFromUrl(url, callback) { + var request = new XMLHttpRequest(); + request.open('get', url, true); + request.responseType = 'arraybuffer'; + request.onload = function() { + if (request.status !== 200) { + return callback('Font could not be loaded: ' + request.statusText); + } + + return callback(null, request.response); + }; + + request.send(); +} + +// Public API /////////////////////////////////////////////////////////// + +// Parse the OpenType file data (as an ArrayBuffer) and return a Font object. +// If the file could not be parsed (most likely because it contains Postscript outlines) +// we return an empty Font object with the `supported` flag set to `false`. +function parseBuffer(buffer) { + var indexToLocFormat; + var hmtxOffset; + var glyfOffset; + var locaOffset; + var cffOffset; + var kernOffset; + var gposOffset; + + // OpenType fonts use big endian byte ordering. + // We can't rely on typed array view types, because they operate with the endianness of the host computer. + // Instead we use DataViews where we can specify endianness. + + var font = new _font.Font(); + var data = new DataView(buffer, 0); + + var version = parse.getFixed(data, 0); + if (version === 1.0) { + font.outlinesFormat = 'truetype'; + } else { + version = parse.getTag(data, 0); + if (version === 'OTTO') { + font.outlinesFormat = 'cff'; + } else { + throw new Error('Unsupported OpenType version ' + version); + } + } + + var numTables = parse.getUShort(data, 4); + + // Offset into the table records. + var p = 12; + for (var i = 0; i < numTables; i += 1) { + var tag = parse.getTag(data, p); + var offset = parse.getULong(data, p + 8); + switch (tag) { + case 'cmap': + font.tables.cmap = cmap.parse(data, offset); + font.encoding = new encoding.CmapEncoding(font.tables.cmap); + if (!font.encoding) { + font.supported = false; + } + + break; + case 'head': + font.tables.head = head.parse(data, offset); + font.unitsPerEm = font.tables.head.unitsPerEm; + indexToLocFormat = font.tables.head.indexToLocFormat; + break; + case 'hhea': + font.tables.hhea = hhea.parse(data, offset); + font.ascender = font.tables.hhea.ascender; + font.descender = font.tables.hhea.descender; + font.numberOfHMetrics = font.tables.hhea.numberOfHMetrics; + break; + case 'hmtx': + hmtxOffset = offset; + break; + case 'maxp': + font.tables.maxp = maxp.parse(data, offset); + font.numGlyphs = font.tables.maxp.numGlyphs; + break; + case 'name': + font.tables.name = _name.parse(data, offset); + font.familyName = font.tables.name.fontFamily; + font.styleName = font.tables.name.fontSubfamily; + break; + case 'OS/2': + font.tables.os2 = os2.parse(data, offset); + break; + case 'post': + font.tables.post = post.parse(data, offset); + font.glyphNames = new encoding.GlyphNames(font.tables.post); + break; + case 'glyf': + glyfOffset = offset; + break; + case 'loca': + locaOffset = offset; + break; + case 'CFF ': + cffOffset = offset; + break; + case 'kern': + kernOffset = offset; + break; + case 'GPOS': + gposOffset = offset; + break; + } + p += 16; + } + + if (glyfOffset && locaOffset) { + var shortVersion = indexToLocFormat === 0; + var locaTable = loca.parse(data, locaOffset, font.numGlyphs, shortVersion); + font.glyphs = glyf.parse(data, glyfOffset, locaTable, font); + hmtx.parse(data, hmtxOffset, font.numberOfHMetrics, font.numGlyphs, font.glyphs); + encoding.addGlyphNames(font); + } else if (cffOffset) { + cff.parse(data, cffOffset, font); + encoding.addGlyphNames(font); + } else { + font.supported = false; + } + + if (font.supported) { + if (kernOffset) { + font.kerningPairs = kern.parse(data, kernOffset); + } else { + font.kerningPairs = {}; + } + + if (gposOffset) { + gpos.parse(data, gposOffset, font); + } + } + + return font; +} + +// Asynchronously load the font from a URL or a filesystem. When done, call the callback +// with two arguments `(err, font)`. The `err` will be null on success, +// the `font` is a Font object. +// +// We use the node.js callback convention so that +// opentype.js can integrate with frameworks like async.js. +function load(url, callback) { + var isNode = typeof window === 'undefined'; + var loadFn = isNode ? loadFromFile : loadFromUrl; + loadFn(url, function(err, arrayBuffer) { + if (err) { + return callback(err); + } + + var font = parseBuffer(arrayBuffer); + if (!font.supported) { + return callback('Font is not supported (is this a Postscript font?)'); + } + + return callback(null, font); + }); +} + +exports._parse = parse; +exports.Font = _font.Font; +exports.Glyph = glyph.Glyph; +exports.Path = path.Path; +exports.parse = parseBuffer; +exports.load = load; + +},{"./encoding":4,"./font":5,"./glyph":6,"./parse":9,"./path":10,"./tables/cff":12,"./tables/cmap":13,"./tables/glyf":14,"./tables/gpos":15,"./tables/head":16,"./tables/hhea":17,"./tables/hmtx":18,"./tables/kern":19,"./tables/loca":20,"./tables/maxp":21,"./tables/name":22,"./tables/os2":23,"./tables/post":24,"fs":1}],9:[function(_dereq_,module,exports){ +// Parsing utility functions + +'use strict'; + +// Retrieve an unsigned byte from the DataView. +exports.getByte = function getByte(dataView, offset) { + return dataView.getUint8(offset); +}; + +exports.getCard8 = exports.getByte; + +// Retrieve an unsigned 16-bit short from the DataView. +// The value is stored in big endian. +exports.getUShort = function(dataView, offset) { + return dataView.getUint16(offset, false); +}; + +exports.getCard16 = exports.getUShort; + +// Retrieve a signed 16-bit short from the DataView. +// The value is stored in big endian. +exports.getShort = function(dataView, offset) { + return dataView.getInt16(offset, false); +}; + +// Retrieve an unsigned 32-bit long from the DataView. +// The value is stored in big endian. +exports.getULong = function(dataView, offset) { + return dataView.getUint32(offset, false); +}; + +// Retrieve a 32-bit signed fixed-point number (16.16) from the DataView. +// The value is stored in big endian. +exports.getFixed = function(dataView, offset) { + var decimal = dataView.getInt16(offset, false); + var fraction = dataView.getUint16(offset + 2, false); + return decimal + fraction / 65535; +}; + +// Retrieve a 4-character tag from the DataView. +// Tags are used to identify tables. +exports.getTag = function(dataView, offset) { + var tag = ''; + for (var i = offset; i < offset + 4; i += 1) { + tag += String.fromCharCode(dataView.getInt8(i)); + } + + return tag; +}; + +// Retrieve an offset from the DataView. +// Offsets are 1 to 4 bytes in length, depending on the offSize argument. +exports.getOffset = function(dataView, offset, offSize) { + var v = 0; + for (var i = 0; i < offSize; i += 1) { + v <<= 8; + v += dataView.getUint8(offset + i); + } + + return v; +}; + +// Retrieve a number of bytes from start offset to the end offset from the DataView. +exports.getBytes = function(dataView, startOffset, endOffset) { + var bytes = []; + for (var i = startOffset; i < endOffset; i += 1) { + bytes.push(dataView.getUint8(i)); + } + + return bytes; +}; + +// Convert the list of bytes to a string. +exports.bytesToString = function(bytes) { + var s = ''; + for (var i = 0; i < bytes.length; i += 1) { + s += String.fromCharCode(bytes[i]); + } + + return s; +}; + +var typeOffsets = { + byte: 1, + uShort: 2, + short: 2, + uLong: 4, + fixed: 4, + longDateTime: 8, + tag: 4 +}; + +// A stateful parser that changes the offset whenever a value is retrieved. +// The data is a DataView. +function Parser(data, offset) { + this.data = data; + this.offset = offset; + this.relativeOffset = 0; +} + +Parser.prototype.parseByte = function() { + var v = this.data.getUint8(this.offset + this.relativeOffset); + this.relativeOffset += 1; + return v; +}; + +Parser.prototype.parseChar = function() { + var v = this.data.getInt8(this.offset + this.relativeOffset); + this.relativeOffset += 1; + return v; +}; + +Parser.prototype.parseCard8 = Parser.prototype.parseByte; + +Parser.prototype.parseUShort = function() { + var v = this.data.getUint16(this.offset + this.relativeOffset); + this.relativeOffset += 2; + return v; +}; + +Parser.prototype.parseCard16 = Parser.prototype.parseUShort; +Parser.prototype.parseSID = Parser.prototype.parseUShort; +Parser.prototype.parseOffset16 = Parser.prototype.parseUShort; + +Parser.prototype.parseShort = function() { + var v = this.data.getInt16(this.offset + this.relativeOffset); + this.relativeOffset += 2; + return v; +}; + +Parser.prototype.parseF2Dot14 = function() { + var v = this.data.getInt16(this.offset + this.relativeOffset) / 16384; + this.relativeOffset += 2; + return v; +}; + +Parser.prototype.parseULong = function() { + var v = exports.getULong(this.data, this.offset + this.relativeOffset); + this.relativeOffset += 4; + return v; +}; + +Parser.prototype.parseFixed = function() { + var v = exports.getFixed(this.data, this.offset + this.relativeOffset); + this.relativeOffset += 4; + return v; +}; + +Parser.prototype.parseOffset16List = +Parser.prototype.parseUShortList = function(count) { + var offsets = new Array(count); + var dataView = this.data; + var offset = this.offset + this.relativeOffset; + for (var i = 0; i < count; i++) { + offsets[i] = exports.getUShort(dataView, offset); + offset += 2; + } + + this.relativeOffset += count * 2; + return offsets; +}; + +Parser.prototype.parseString = function(length) { + var dataView = this.data; + var offset = this.offset + this.relativeOffset; + var string = ''; + this.relativeOffset += length; + for (var i = 0; i < length; i++) { + string += String.fromCharCode(dataView.getUint8(offset + i)); + } + + return string; +}; + +Parser.prototype.parseTag = function() { + return this.parseString(4); +}; + +// LONGDATETIME is a 64-bit integer. +// JavaScript and unix timestamps traditionally use 32 bits, so we +// only take the last 32 bits. +Parser.prototype.parseLongDateTime = function() { + var v = exports.getULong(this.data, this.offset + this.relativeOffset + 4); + this.relativeOffset += 8; + return v; +}; + +Parser.prototype.parseFixed = function() { + var v = exports.getULong(this.data, this.offset + this.relativeOffset); + this.relativeOffset += 4; + return v / 65536; +}; + +Parser.prototype.parseVersion = function() { + var major = exports.getUShort(this.data, this.offset + this.relativeOffset); + + // How to interpret the minor version is very vague in the spec. 0x5000 is 5, 0x1000 is 1 + // This returns the correct number if minor = 0xN000 where N is 0-9 + var minor = exports.getUShort(this.data, this.offset + this.relativeOffset + 2); + this.relativeOffset += 4; + return major + minor / 0x1000 / 10; +}; + +Parser.prototype.skip = function(type, amount) { + if (amount === undefined) { + amount = 1; + } + + this.relativeOffset += typeOffsets[type] * amount; +}; + +exports.Parser = Parser; + +},{}],10:[function(_dereq_,module,exports){ +// Geometric objects + +'use strict'; + +// A bézier path containing a set of path commands similar to a SVG path. +// Paths can be drawn on a context using `draw`. +function Path() { + this.commands = []; + this.fill = 'black'; + this.stroke = null; + this.strokeWidth = 1; +} + +Path.prototype.moveTo = function(x, y) { + this.commands.push({ + type: 'M', + x: x, + y: y + }); +}; + +Path.prototype.lineTo = function(x, y) { + this.commands.push({ + type: 'L', + x: x, + y: y + }); +}; + +Path.prototype.curveTo = Path.prototype.bezierCurveTo = function(x1, y1, x2, y2, x, y) { + this.commands.push({ + type: 'C', + x1: x1, + y1: y1, + x2: x2, + y2: y2, + x: x, + y: y + }); +}; + +Path.prototype.quadTo = Path.prototype.quadraticCurveTo = function(x1, y1, x, y) { + this.commands.push({ + type: 'Q', + x1: x1, + y1: y1, + x: x, + y: y + }); +}; + +Path.prototype.close = Path.prototype.closePath = function() { + this.commands.push({ + type: 'Z' + }); +}; + +// Add the given path or list of commands to the commands of this path. +Path.prototype.extend = function(pathOrCommands) { + if (pathOrCommands.commands) { + pathOrCommands = pathOrCommands.commands; + } + + Array.prototype.push.apply(this.commands, pathOrCommands); +}; + +// Draw the path to a 2D context. +Path.prototype.draw = function(ctx) { + ctx.beginPath(); + for (var i = 0; i < this.commands.length; i += 1) { + var cmd = this.commands[i]; + if (cmd.type === 'M') { + ctx.moveTo(cmd.x, cmd.y); + } else if (cmd.type === 'L') { + ctx.lineTo(cmd.x, cmd.y); + } else if (cmd.type === 'C') { + ctx.bezierCurveTo(cmd.x1, cmd.y1, cmd.x2, cmd.y2, cmd.x, cmd.y); + } else if (cmd.type === 'Q') { + ctx.quadraticCurveTo(cmd.x1, cmd.y1, cmd.x, cmd.y); + } else if (cmd.type === 'Z') { + ctx.closePath(); + } + } + + if (this.fill) { + ctx.fillStyle = this.fill; + ctx.fill(); + } + + if (this.stroke) { + ctx.strokeStyle = this.stroke; + ctx.lineWidth = this.strokeWidth; + ctx.stroke(); + } +}; + +// Convert the Path to a string of path data instructions +// See http://www.w3.org/TR/SVG/paths.html#PathData +// Parameters: +// - decimalPlaces: The amount of decimal places for floating-point values (default: 2) +Path.prototype.toPathData = function(decimalPlaces) { + decimalPlaces = decimalPlaces !== undefined ? decimalPlaces : 2; + + function floatToString(v) { + if (Math.round(v) === v) { + return '' + Math.round(v); + } else { + return v.toFixed(decimalPlaces); + } + } + + function packValues() { + var s = ''; + for (var i = 0; i < arguments.length; i += 1) { + var v = arguments[i]; + if (v >= 0 && i > 0) { + s += ' '; + } + + s += floatToString(v); + } + + return s; + } + + var d = ''; + for (var i = 0; i < this.commands.length; i += 1) { + var cmd = this.commands[i]; + if (cmd.type === 'M') { + d += 'M' + packValues(cmd.x, cmd.y); + } else if (cmd.type === 'L') { + d += 'L' + packValues(cmd.x, cmd.y); + } else if (cmd.type === 'C') { + d += 'C' + packValues(cmd.x1, cmd.y1, cmd.x2, cmd.y2, cmd.x, cmd.y); + } else if (cmd.type === 'Q') { + d += 'Q' + packValues(cmd.x1, cmd.y1, cmd.x, cmd.y); + } else if (cmd.type === 'Z') { + d += 'Z'; + } + } + + return d; +}; + +// Convert the path to a SVG <path> element, as a string. +// Parameters: +// - decimalPlaces: The amount of decimal places for floating-point values (default: 2) +Path.prototype.toSVG = function(decimalPlaces) { + var svg = '<path d="'; + svg += this.toPathData(decimalPlaces); + svg += '"'; + if (this.fill & this.fill !== 'black') { + if (this.fill === null) { + svg += ' fill="none"'; + } else { + svg += ' fill="' + this.fill + '"'; + } + } + + if (this.stroke) { + svg += ' stroke="' + this.stroke + '" stroke-width="' + this.strokeWidth + '"'; + } + + svg += '/>'; + return svg; +}; + +exports.Path = Path; + +},{}],11:[function(_dereq_,module,exports){ +// Table metadata + +'use strict'; + +var check = _dereq_('./check'); +var encode = _dereq_('./types').encode; +var sizeOf = _dereq_('./types').sizeOf; + +function Table(tableName, fields, options) { + var i; + for (i = 0; i < fields.length; i += 1) { + var field = fields[i]; + this[field.name] = field.value; + } + + this.tableName = tableName; + this.fields = fields; + if (options) { + var optionKeys = Object.keys(options); + for (i = 0; i < optionKeys.length; i += 1) { + var k = optionKeys[i]; + var v = options[k]; + if (this[k] !== undefined) { + this[k] = v; + } + } + } +} + +Table.prototype.sizeOf = function() { + var v = 0; + for (var i = 0; i < this.fields.length; i += 1) { + var field = this.fields[i]; + var value = this[field.name]; + if (value === undefined) { + value = field.value; + } + + if (typeof value.sizeOf === 'function') { + v += value.sizeOf(); + } else { + var sizeOfFunction = sizeOf[field.type]; + check.assert(typeof sizeOfFunction === 'function', 'Could not find sizeOf function for field' + field.name); + v += sizeOfFunction(value); + } + } + + return v; +}; + +Table.prototype.encode = function() { + return encode.TABLE(this); +}; + +exports.Table = Table; + +},{"./check":2,"./types":26}],12:[function(_dereq_,module,exports){ +// The `CFF` table contains the glyph outlines in PostScript format. +// https://www.microsoft.com/typography/OTSPEC/cff.htm +// http://download.microsoft.com/download/8/0/1/801a191c-029d-4af3-9642-555f6fe514ee/cff.pdf +// http://download.microsoft.com/download/8/0/1/801a191c-029d-4af3-9642-555f6fe514ee/type2.pdf + +'use strict'; + +var encoding = _dereq_('../encoding'); +var glyphset = _dereq_('../glyphset'); +var parse = _dereq_('../parse'); +var path = _dereq_('../path'); +var table = _dereq_('../table'); + +// Custom equals function that can also check lists. +function equals(a, b) { + if (a === b) { + return true; + } else if (Array.isArray(a) && Array.isArray(b)) { + if (a.length !== b.length) { + return false; + } + + for (var i = 0; i < a.length; i += 1) { + if (!equals(a[i], b[i])) { + return false; + } + } + + return true; + } else { + return false; + } +} + +// Parse a `CFF` INDEX array. +// An index array consists of a list of offsets, then a list of objects at those offsets. +function parseCFFIndex(data, start, conversionFn) { + //var i, objectOffset, endOffset; + var offsets = []; + var objects = []; + var count = parse.getCard16(data, start); + var i; + var objectOffset; + var endOffset; + if (count !== 0) { + var offsetSize = parse.getByte(data, start + 2); + objectOffset = start + ((count + 1) * offsetSize) + 2; + var pos = start + 3; + for (i = 0; i < count + 1; i += 1) { + offsets.push(parse.getOffset(data, pos, offsetSize)); + pos += offsetSize; + } + + // The total size of the index array is 4 header bytes + the value of the last offset. + endOffset = objectOffset + offsets[count]; + } else { + endOffset = start + 2; + } + + for (i = 0; i < offsets.length - 1; i += 1) { + var value = parse.getBytes(data, objectOffset + offsets[i], objectOffset + offsets[i + 1]); + if (conversionFn) { + value = conversionFn(value); + } + + objects.push(value); + } + + return {objects: objects, startOffset: start, endOffset: endOffset}; +} + +// Parse a `CFF` DICT real value. +function parseFloatOperand(parser) { + var s = ''; + var eof = 15; + var lookup = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', 'E', 'E-', null, '-']; + while (true) { + var b = parser.parseByte(); + var n1 = b >> 4; + var n2 = b & 15; + + if (n1 === eof) { + break; + } + + s += lookup[n1]; + + if (n2 === eof) { + break; + } + + s += lookup[n2]; + } + + return parseFloat(s); +} + +// Parse a `CFF` DICT operand. +function parseOperand(parser, b0) { + var b1; + var b2; + var b3; + var b4; + if (b0 === 28) { + b1 = parser.parseByte(); + b2 = parser.parseByte(); + return b1 << 8 | b2; + } + + if (b0 === 29) { + b1 = parser.parseByte(); + b2 = parser.parseByte(); + b3 = parser.parseByte(); + b4 = parser.parseByte(); + return b1 << 24 | b2 << 16 | b3 << 8 | b4; + } + + if (b0 === 30) { + return parseFloatOperand(parser); + } + + if (b0 >= 32 && b0 <= 246) { + return b0 - 139; + } + + if (b0 >= 247 && b0 <= 250) { + b1 = parser.parseByte(); + return (b0 - 247) * 256 + b1 + 108; + } + + if (b0 >= 251 && b0 <= 254) { + b1 = parser.parseByte(); + return -(b0 - 251) * 256 - b1 - 108; + } + + throw new Error('Invalid b0 ' + b0); +} + +// Convert the entries returned by `parseDict` to a proper dictionary. +// If a value is a list of one, it is unpacked. +function entriesToObject(entries) { + var o = {}; + for (var i = 0; i < entries.length; i += 1) { + var key = entries[i][0]; + var values = entries[i][1]; + var value; + if (values.length === 1) { + value = values[0]; + } else { + value = values; + } + + if (o.hasOwnProperty(key)) { + throw new Error('Object ' + o + ' already has key ' + key); + } + + o[key] = value; + } + + return o; +} + +// Parse a `CFF` DICT object. +// A dictionary contains key-value pairs in a compact tokenized format. +function parseCFFDict(data, start, size) { + start = start !== undefined ? start : 0; + var parser = new parse.Parser(data, start); + var entries = []; + var operands = []; + size = size !== undefined ? size : data.length; + + while (parser.relativeOffset < size) { + var op = parser.parseByte(); + + // The first byte for each dict item distinguishes between operator (key) and operand (value). + // Values <= 21 are operators. + if (op <= 21) { + // Two-byte operators have an initial escape byte of 12. + if (op === 12) { + op = 1200 + parser.parseByte(); + } + + entries.push([op, operands]); + operands = []; + } else { + // Since the operands (values) come before the operators (keys), we store all operands in a list + // until we encounter an operator. + operands.push(parseOperand(parser, op)); + } + } + + return entriesToObject(entries); +} + +// Given a String Index (SID), return the value of the string. +// Strings below index 392 are standard CFF strings and are not encoded in the font. +function getCFFString(strings, index) { + if (index <= 390) { + index = encoding.cffStandardStrings[index]; + } else { + index = strings[index - 391]; + } + + return index; +} + +// Interpret a dictionary and return a new dictionary with readable keys and values for missing entries. +// This function takes `meta` which is a list of objects containing `operand`, `name` and `default`. +function interpretDict(dict, meta, strings) { + var newDict = {}; + + // Because we also want to include missing values, we start out from the meta list + // and lookup values in the dict. + for (var i = 0; i < meta.length; i += 1) { + var m = meta[i]; + var value = dict[m.op]; + if (value === undefined) { + value = m.value !== undefined ? m.value : null; + } + + if (m.type === 'SID') { + value = getCFFString(strings, value); + } + + newDict[m.name] = value; + } + + return newDict; +} + +// Parse the CFF header. +function parseCFFHeader(data, start) { + var header = {}; + header.formatMajor = parse.getCard8(data, start); + header.formatMinor = parse.getCard8(data, start + 1); + header.size = parse.getCard8(data, start + 2); + header.offsetSize = parse.getCard8(data, start + 3); + header.startOffset = start; + header.endOffset = start + 4; + return header; +} + +var TOP_DICT_META = [ + {name: 'version', op: 0, type: 'SID'}, + {name: 'notice', op: 1, type: 'SID'}, + {name: 'copyright', op: 1200, type: 'SID'}, + {name: 'fullName', op: 2, type: 'SID'}, + {name: 'familyName', op: 3, type: 'SID'}, + {name: 'weight', op: 4, type: 'SID'}, + {name: 'isFixedPitch', op: 1201, type: 'number', value: 0}, + {name: 'italicAngle', op: 1202, type: 'number', value: 0}, + {name: 'underlinePosition', op: 1203, type: 'number', value: -100}, + {name: 'underlineThickness', op: 1204, type: 'number', value: 50}, + {name: 'paintType', op: 1205, type: 'number', value: 0}, + {name: 'charstringType', op: 1206, type: 'number', value: 2}, + {name: 'fontMatrix', op: 1207, type: ['real', 'real', 'real', 'real', 'real', 'real'], value: [0.001, 0, 0, 0.001, 0, 0]}, + {name: 'uniqueId', op: 13, type: 'number'}, + {name: 'fontBBox', op: 5, type: ['number', 'number', 'number', 'number'], value: [0, 0, 0, 0]}, + {name: 'strokeWidth', op: 1208, type: 'number', value: 0}, + {name: 'xuid', op: 14, type: [], value: null}, + {name: 'charset', op: 15, type: 'offset', value: 0}, + {name: 'encoding', op: 16, type: 'offset', value: 0}, + {name: 'charStrings', op: 17, type: 'offset', value: 0}, + {name: 'private', op: 18, type: ['number', 'offset'], value: [0, 0]} +]; + +var PRIVATE_DICT_META = [ + {name: 'subrs', op: 19, type: 'offset', value: 0}, + {name: 'defaultWidthX', op: 20, type: 'number', value: 0}, + {name: 'nominalWidthX', op: 21, type: 'number', value: 0} +]; + +// Parse the CFF top dictionary. A CFF table can contain multiple fonts, each with their own top dictionary. +// The top dictionary contains the essential metadata for the font, together with the private dictionary. +function parseCFFTopDict(data, strings) { + var dict = parseCFFDict(data, 0, data.byteLength); + return interpretDict(dict, TOP_DICT_META, strings); +} + +// Parse the CFF private dictionary. We don't fully parse out all the values, only the ones we need. +function parseCFFPrivateDict(data, start, size, strings) { + var dict = parseCFFDict(data, start, size); + return interpretDict(dict, PRIVATE_DICT_META, strings); +} + +// Parse the CFF charset table, which contains internal names for all the glyphs. +// This function will return a list of glyph names. +// See Adobe TN #5176 chapter 13, "Charsets". +function parseCFFCharset(data, start, nGlyphs, strings) { + var i; + var sid; + var count; + var parser = new parse.Parser(data, start); + + // The .notdef glyph is not included, so subtract 1. + nGlyphs -= 1; + var charset = ['.notdef']; + + var format = parser.parseCard8(); + if (format === 0) { + for (i = 0; i < nGlyphs; i += 1) { + sid = parser.parseSID(); + charset.push(getCFFString(strings, sid)); + } + } else if (format === 1) { + while (charset.length <= nGlyphs) { + sid = parser.parseSID(); + count = parser.parseCard8(); + for (i = 0; i <= count; i += 1) { + charset.push(getCFFString(strings, sid)); + sid += 1; + } + } + } else if (format === 2) { + while (charset.length <= nGlyphs) { + sid = parser.parseSID(); + count = parser.parseCard16(); + for (i = 0; i <= count; i += 1) { + charset.push(getCFFString(strings, sid)); + sid += 1; + } + } + } else { + throw new Error('Unknown charset format ' + format); + } + + return charset; +} + +// Parse the CFF encoding data. Only one encoding can be specified per font. +// See Adobe TN #5176 chapter 12, "Encodings". +function parseCFFEncoding(data, start, charset) { + var i; + var code; + var enc = {}; + var parser = new parse.Parser(data, start); + var format = parser.parseCard8(); + if (format === 0) { + var nCodes = parser.parseCard8(); + for (i = 0; i < nCodes; i += 1) { + code = parser.parseCard8(); + enc[code] = i; + } + } else if (format === 1) { + var nRanges = parser.parseCard8(); + code = 1; + for (i = 0; i < nRanges; i += 1) { + var first = parser.parseCard8(); + var nLeft = parser.parseCard8(); + for (var j = first; j <= first + nLeft; j += 1) { + enc[j] = code; + code += 1; + } + } + } else { + throw new Error('Unknown encoding format ' + format); + } + + return new encoding.CffEncoding(enc, charset); +} + +// Take in charstring code and return a Glyph object. +// The encoding is described in the Type 2 Charstring Format +// https://www.microsoft.com/typography/OTSPEC/charstr2.htm +function parseCFFCharstring(font, glyph, code) { + var c1x; + var c1y; + var c2x; + var c2y; + var p = new path.Path(); + var stack = []; + var nStems = 0; + var haveWidth = false; + var width = font.defaultWidthX; + var open = false; + var x = 0; + var y = 0; + + function newContour(x, y) { + if (open) { + p.closePath(); + } + + p.moveTo(x, y); + open = true; + } + + function parseStems() { + var hasWidthArg; + + // The number of stem operators on the stack is always even. + // If the value is uneven, that means a width is specified. + hasWidthArg = stack.length % 2 !== 0; + if (hasWidthArg && !haveWidth) { + width = stack.shift() + font.nominalWidthX; + } + + nStems += stack.length >> 1; + stack.length = 0; + haveWidth = true; + } + + function parse(code) { + var b1; + var b2; + var b3; + var b4; + var codeIndex; + var subrCode; + var jpx; + var jpy; + var c3x; + var c3y; + var c4x; + var c4y; + + var i = 0; + while (i < code.length) { + var v = code[i]; + i += 1; + switch (v) { + case 1: // hstem + parseStems(); + break; + case 3: // vstem + parseStems(); + break; + case 4: // vmoveto + if (stack.length > 1 && !haveWidth) { + width = stack.shift() + font.nominalWidthX; + haveWidth = true; + } + + y += stack.pop(); + newContour(x, y); + break; + case 5: // rlineto + while (stack.length > 0) { + x += stack.shift(); + y += stack.shift(); + p.lineTo(x, y); + } + + break; + case 6: // hlineto + while (stack.length > 0) { + x += stack.shift(); + p.lineTo(x, y); + if (stack.length === 0) { + break; + } + + y += stack.shift(); + p.lineTo(x, y); + } + + break; + case 7: // vlineto + while (stack.length > 0) { + y += stack.shift(); + p.lineTo(x, y); + if (stack.length === 0) { + break; + } + + x += stack.shift(); + p.lineTo(x, y); + } + + break; + case 8: // rrcurveto + while (stack.length > 0) { + c1x = x + stack.shift(); + c1y = y + stack.shift(); + c2x = c1x + stack.shift(); + c2y = c1y + stack.shift(); + x = c2x + stack.shift(); + y = c2y + stack.shift(); + p.curveTo(c1x, c1y, c2x, c2y, x, y); + } + + break; + case 10: // callsubr + codeIndex = stack.pop() + font.subrsBias; + subrCode = font.subrs[codeIndex]; + if (subrCode) { + parse(subrCode); + } + + break; + case 11: // return + return; + case 12: // flex operators + v = code[i]; + i += 1; + switch (v) { + case 35: // flex + // |- dx1 dy1 dx2 dy2 dx3 dy3 dx4 dy4 dx5 dy5 dx6 dy6 fd flex (12 35) |- + c1x = x + stack.shift(); // dx1 + c1y = y + stack.shift(); // dy1 + c2x = c1x + stack.shift(); // dx2 + c2y = c1y + stack.shift(); // dy2 + jpx = c2x + stack.shift(); // dx3 + jpy = c2y + stack.shift(); // dy3 + c3x = jpx + stack.shift(); // dx4 + c3y = jpy + stack.shift(); // dy4 + c4x = c3x + stack.shift(); // dx5 + c4y = c3y + stack.shift(); // dy5 + x = c4x + stack.shift(); // dx6 + y = c4y + stack.shift(); // dy6 + stack.shift(); // flex depth + p.curveTo(c1x, c1y, c2x, c2y, jpx, jpy); + p.curveTo(c3x, c3y, c4x, c4y, x, y); + break; + case 34: // hflex + // |- dx1 dx2 dy2 dx3 dx4 dx5 dx6 hflex (12 34) |- + c1x = x + stack.shift(); // dx1 + c1y = y; // dy1 + c2x = c1x + stack.shift(); // dx2 + c2y = c1y + stack.shift(); // dy2 + jpx = c2x + stack.shift(); // dx3 + jpy = c2y; // dy3 + c3x = jpx + stack.shift(); // dx4 + c3y = c2y; // dy4 + c4x = c3x + stack.shift(); // dx5 + c4y = y; // dy5 + x = c4x + stack.shift(); // dx6 + p.curveTo(c1x, c1y, c2x, c2y, jpx, jpy); + p.curveTo(c3x, c3y, c4x, c4y, x, y); + break; + case 36: // hflex1 + // |- dx1 dy1 dx2 dy2 dx3 dx4 dx5 dy5 dx6 hflex1 (12 36) |- + c1x = x + stack.shift(); // dx1 + c1y = y + stack.shift(); // dy1 + c2x = c1x + stack.shift(); // dx2 + c2y = c1y + stack.shift(); // dy2 + jpx = c2x + stack.shift(); // dx3 + jpy = c2y; // dy3 + c3x = jpx + stack.shift(); // dx4 + c3y = c2y; // dy4 + c4x = c3x + stack.shift(); // dx5 + c4y = c3y + stack.shift(); // dy5 + x = c4x + stack.shift(); // dx6 + p.curveTo(c1x, c1y, c2x, c2y, jpx, jpy); + p.curveTo(c3x, c3y, c4x, c4y, x, y); + break; + case 37: // flex1 + // |- dx1 dy1 dx2 dy2 dx3 dy3 dx4 dy4 dx5 dy5 d6 flex1 (12 37) |- + c1x = x + stack.shift(); // dx1 + c1y = y + stack.shift(); // dy1 + c2x = c1x + stack.shift(); // dx2 + c2y = c1y + stack.shift(); // dy2 + jpx = c2x + stack.shift(); // dx3 + jpy = c2y + stack.shift(); // dy3 + c3x = jpx + stack.shift(); // dx4 + c3y = jpy + stack.shift(); // dy4 + c4x = c3x + stack.shift(); // dx5 + c4y = c3y + stack.shift(); // dy5 + if (Math.abs(c4x - x) > Math.abs(c4y - y)) { + x = c4x + stack.shift(); + } else { + y = c4y + stack.shift(); + } + + p.curveTo(c1x, c1y, c2x, c2y, jpx, jpy); + p.curveTo(c3x, c3y, c4x, c4y, x, y); + break; + default: + console.log('Glyph ' + glyph.index + ': unknown operator ' + 1200 + v); + stack.length = 0; + } + break; + case 14: // endchar + if (stack.length > 0 && !haveWidth) { + width = stack.shift() + font.nominalWidthX; + haveWidth = true; + } + + if (open) { + p.closePath(); + open = false; + } + + break; + case 18: // hstemhm + parseStems(); + break; + case 19: // hintmask + case 20: // cntrmask + parseStems(); + i += (nStems + 7) >> 3; + break; + case 21: // rmoveto + if (stack.length > 2 && !haveWidth) { + width = stack.shift() + font.nominalWidthX; + haveWidth = true; + } + + y += stack.pop(); + x += stack.pop(); + newContour(x, y); + break; + case 22: // hmoveto + if (stack.length > 1 && !haveWidth) { + width = stack.shift() + font.nominalWidthX; + haveWidth = true; + } + + x += stack.pop(); + newContour(x, y); + break; + case 23: // vstemhm + parseStems(); + break; + case 24: // rcurveline + while (stack.length > 2) { + c1x = x + stack.shift(); + c1y = y + stack.shift(); + c2x = c1x + stack.shift(); + c2y = c1y + stack.shift(); + x = c2x + stack.shift(); + y = c2y + stack.shift(); + p.curveTo(c1x, c1y, c2x, c2y, x, y); + } + + x += stack.shift(); + y += stack.shift(); + p.lineTo(x, y); + break; + case 25: // rlinecurve + while (stack.length > 6) { + x += stack.shift(); + y += stack.shift(); + p.lineTo(x, y); + } + + c1x = x + stack.shift(); + c1y = y + stack.shift(); + c2x = c1x + stack.shift(); + c2y = c1y + stack.shift(); + x = c2x + stack.shift(); + y = c2y + stack.shift(); + p.curveTo(c1x, c1y, c2x, c2y, x, y); + break; + case 26: // vvcurveto + if (stack.length % 2) { + x += stack.shift(); + } + + while (stack.length > 0) { + c1x = x; + c1y = y + stack.shift(); + c2x = c1x + stack.shift(); + c2y = c1y + stack.shift(); + x = c2x; + y = c2y + stack.shift(); + p.curveTo(c1x, c1y, c2x, c2y, x, y); + } + + break; + case 27: // hhcurveto + if (stack.length % 2) { + y += stack.shift(); + } + + while (stack.length > 0) { + c1x = x + stack.shift(); + c1y = y; + c2x = c1x + stack.shift(); + c2y = c1y + stack.shift(); + x = c2x + stack.shift(); + y = c2y; + p.curveTo(c1x, c1y, c2x, c2y, x, y); + } + + break; + case 28: // shortint + b1 = code[i]; + b2 = code[i + 1]; + stack.push(((b1 << 24) | (b2 << 16)) >> 16); + i += 2; + break; + case 29: // callgsubr + codeIndex = stack.pop() + font.gsubrsBias; + subrCode = font.gsubrs[codeIndex]; + if (subrCode) { + parse(subrCode); + } + + break; + case 30: // vhcurveto + while (stack.length > 0) { + c1x = x; + c1y = y + stack.shift(); + c2x = c1x + stack.shift(); + c2y = c1y + stack.shift(); + x = c2x + stack.shift(); + y = c2y + (stack.length === 1 ? stack.shift() : 0); + p.curveTo(c1x, c1y, c2x, c2y, x, y); + if (stack.length === 0) { + break; + } + + c1x = x + stack.shift(); + c1y = y; + c2x = c1x + stack.shift(); + c2y = c1y + stack.shift(); + y = c2y + stack.shift(); + x = c2x + (stack.length === 1 ? stack.shift() : 0); + p.curveTo(c1x, c1y, c2x, c2y, x, y); + } + + break; + case 31: // hvcurveto + while (stack.length > 0) { + c1x = x + stack.shift(); + c1y = y; + c2x = c1x + stack.shift(); + c2y = c1y + stack.shift(); + y = c2y + stack.shift(); + x = c2x + (stack.length === 1 ? stack.shift() : 0); + p.curveTo(c1x, c1y, c2x, c2y, x, y); + if (stack.length === 0) { + break; + } + + c1x = x; + c1y = y + stack.shift(); + c2x = c1x + stack.shift(); + c2y = c1y + stack.shift(); + x = c2x + stack.shift(); + y = c2y + (stack.length === 1 ? stack.shift() : 0); + p.curveTo(c1x, c1y, c2x, c2y, x, y); + } + + break; + default: + if (v < 32) { + console.log('Glyph ' + glyph.index + ': unknown operator ' + v); + } else if (v < 247) { + stack.push(v - 139); + } else if (v < 251) { + b1 = code[i]; + i += 1; + stack.push((v - 247) * 256 + b1 + 108); + } else if (v < 255) { + b1 = code[i]; + i += 1; + stack.push(-(v - 251) * 256 - b1 - 108); + } else { + b1 = code[i]; + b2 = code[i + 1]; + b3 = code[i + 2]; + b4 = code[i + 3]; + i += 4; + stack.push(((b1 << 24) | (b2 << 16) | (b3 << 8) | b4) / 65536); + } + } + } + } + + parse(code); + + glyph.advanceWidth = width; + return p; +} + +// Subroutines are encoded using the negative half of the number space. +// See type 2 chapter 4.7 "Subroutine operators". +function calcCFFSubroutineBias(subrs) { + var bias; + if (subrs.length < 1240) { + bias = 107; + } else if (subrs.length < 33900) { + bias = 1131; + } else { + bias = 32768; + } + + return bias; +} + +// Parse the `CFF` table, which contains the glyph outlines in PostScript format. +function parseCFFTable(data, start, font) { + font.tables.cff = {}; + var header = parseCFFHeader(data, start); + var nameIndex = parseCFFIndex(data, header.endOffset, parse.bytesToString); + var topDictIndex = parseCFFIndex(data, nameIndex.endOffset); + var stringIndex = parseCFFIndex(data, topDictIndex.endOffset, parse.bytesToString); + var globalSubrIndex = parseCFFIndex(data, stringIndex.endOffset); + font.gsubrs = globalSubrIndex.objects; + font.gsubrsBias = calcCFFSubroutineBias(font.gsubrs); + + var topDictData = new DataView(new Uint8Array(topDictIndex.objects[0]).buffer); + var topDict = parseCFFTopDict(topDictData, stringIndex.objects); + font.tables.cff.topDict = topDict; + + var privateDictOffset = start + topDict['private'][1]; + var privateDict = parseCFFPrivateDict(data, privateDictOffset, topDict['private'][0], stringIndex.objects); + font.defaultWidthX = privateDict.defaultWidthX; + font.nominalWidthX = privateDict.nominalWidthX; + + if (privateDict.subrs !== 0) { + var subrOffset = privateDictOffset + privateDict.subrs; + var subrIndex = parseCFFIndex(data, subrOffset); + font.subrs = subrIndex.objects; + font.subrsBias = calcCFFSubroutineBias(font.subrs); + } else { + font.subrs = []; + font.subrsBias = 0; + } + + // Offsets in the top dict are relative to the beginning of the CFF data, so add the CFF start offset. + var charStringsIndex = parseCFFIndex(data, start + topDict.charStrings); + font.nGlyphs = charStringsIndex.objects.length; + + var charset = parseCFFCharset(data, start + topDict.charset, font.nGlyphs, stringIndex.objects); + if (topDict.encoding === 0) { // Standard encoding + font.cffEncoding = new encoding.CffEncoding(encoding.cffStandardEncoding, charset); + } else if (topDict.encoding === 1) { // Expert encoding + font.cffEncoding = new encoding.CffEncoding(encoding.cffExpertEncoding, charset); + } else { + font.cffEncoding = parseCFFEncoding(data, start + topDict.encoding, charset); + } + + // Prefer the CMAP encoding to the CFF encoding. + font.encoding = font.encoding || font.cffEncoding; + + font.glyphs = new glyphset.GlyphSet(font); + for (var i = 0; i < font.nGlyphs; i += 1) { + var charString = charStringsIndex.objects[i]; + font.glyphs.push(i, glyphset.cffGlyphLoader(font, i, parseCFFCharstring, charString)); + } +} + +// Convert a string to a String ID (SID). +// The list of strings is modified in place. +function encodeString(s, strings) { + var sid; + + // Is the string in the CFF standard strings? + var i = encoding.cffStandardStrings.indexOf(s); + if (i >= 0) { + sid = i; + } + + // Is the string already in the string index? + i = strings.indexOf(s); + if (i >= 0) { + sid = i + encoding.cffStandardStrings.length; + } else { + sid = encoding.cffStandardStrings.length + strings.length; + strings.push(s); + } + + return sid; +} + +function makeHeader() { + return new table.Table('Header', [ + {name: 'major', type: 'Card8', value: 1}, + {name: 'minor', type: 'Card8', value: 0}, + {name: 'hdrSize', type: 'Card8', value: 4}, + {name: 'major', type: 'Card8', value: 1} + ]); +} + +function makeNameIndex(fontNames) { + var t = new table.Table('Name INDEX', [ + {name: 'names', type: 'INDEX', value: []} + ]); + t.names = []; + for (var i = 0; i < fontNames.length; i += 1) { + t.names.push({name: 'name_' + i, type: 'NAME', value: fontNames[i]}); + } + + return t; +} + +// Given a dictionary's metadata, create a DICT structure. +function makeDict(meta, attrs, strings) { + var m = {}; + for (var i = 0; i < meta.length; i += 1) { + var entry = meta[i]; + var value = attrs[entry.name]; + if (value !== undefined && !equals(value, entry.value)) { + if (entry.type === 'SID') { + value = encodeString(value, strings); + } + + m[entry.op] = {name: entry.name, type: entry.type, value: value}; + } + } + + return m; +} + +// The Top DICT houses the global font attributes. +function makeTopDict(attrs, strings) { + var t = new table.Table('Top DICT', [ + {name: 'dict', type: 'DICT', value: {}} + ]); + t.dict = makeDict(TOP_DICT_META, attrs, strings); + return t; +} + +function makeTopDictIndex(topDict) { + var t = new table.Table('Top DICT INDEX', [ + {name: 'topDicts', type: 'INDEX', value: []} + ]); + t.topDicts = [{name: 'topDict_0', type: 'TABLE', value: topDict}]; + return t; +} + +function makeStringIndex(strings) { + var t = new table.Table('String INDEX', [ + {name: 'strings', type: 'INDEX', value: []} + ]); + t.strings = []; + for (var i = 0; i < strings.length; i += 1) { + t.strings.push({name: 'string_' + i, type: 'STRING', value: strings[i]}); + } + + return t; +} + +function makeGlobalSubrIndex() { + // Currently we don't use subroutines. + return new table.Table('Global Subr INDEX', [ + {name: 'subrs', type: 'INDEX', value: []} + ]); +} + +function makeCharsets(glyphNames, strings) { + var t = new table.Table('Charsets', [ + {name: 'format', type: 'Card8', value: 0} + ]); + for (var i = 0; i < glyphNames.length; i += 1) { + var glyphName = glyphNames[i]; + var glyphSID = encodeString(glyphName, strings); + t.fields.push({name: 'glyph_' + i, type: 'SID', value: glyphSID}); + } + + return t; +} + +function glyphToOps(glyph) { + var ops = []; + var path = glyph.path; + ops.push({name: 'width', type: 'NUMBER', value: glyph.advanceWidth}); + var x = 0; + var y = 0; + for (var i = 0; i < path.commands.length; i += 1) { + var dx; + var dy; + var cmd = path.commands[i]; + if (cmd.type === 'Q') { + // CFF only supports bézier curves, so convert the quad to a bézier. + var _13 = 1 / 3; + var _23 = 2 / 3; + + // We're going to create a new command so we don't change the original path. + cmd = { + type: 'C', + x: cmd.x, + y: cmd.y, + x1: _13 * x + _23 * cmd.x1, + y1: _13 * y + _23 * cmd.y1, + x2: _13 * cmd.x + _23 * cmd.x1, + y2: _13 * cmd.y + _23 * cmd.y1 + }; + } + + if (cmd.type === 'M') { + dx = Math.round(cmd.x - x); + dy = Math.round(cmd.y - y); + ops.push({name: 'dx', type: 'NUMBER', value: dx}); + ops.push({name: 'dy', type: 'NUMBER', value: dy}); + ops.push({name: 'rmoveto', type: 'OP', value: 21}); + x = Math.round(cmd.x); + y = Math.round(cmd.y); + } else if (cmd.type === 'L') { + dx = Math.round(cmd.x - x); + dy = Math.round(cmd.y - y); + ops.push({name: 'dx', type: 'NUMBER', value: dx}); + ops.push({name: 'dy', type: 'NUMBER', value: dy}); + ops.push({name: 'rlineto', type: 'OP', value: 5}); + x = Math.round(cmd.x); + y = Math.round(cmd.y); + } else if (cmd.type === 'C') { + var dx1 = Math.round(cmd.x1 - x); + var dy1 = Math.round(cmd.y1 - y); + var dx2 = Math.round(cmd.x2 - cmd.x1); + var dy2 = Math.round(cmd.y2 - cmd.y1); + dx = Math.round(cmd.x - cmd.x2); + dy = Math.round(cmd.y - cmd.y2); + ops.push({name: 'dx1', type: 'NUMBER', value: dx1}); + ops.push({name: 'dy1', type: 'NUMBER', value: dy1}); + ops.push({name: 'dx2', type: 'NUMBER', value: dx2}); + ops.push({name: 'dy2', type: 'NUMBER', value: dy2}); + ops.push({name: 'dx', type: 'NUMBER', value: dx}); + ops.push({name: 'dy', type: 'NUMBER', value: dy}); + ops.push({name: 'rrcurveto', type: 'OP', value: 8}); + x = Math.round(cmd.x); + y = Math.round(cmd.y); + } + + // Contours are closed automatically. + + } + + ops.push({name: 'endchar', type: 'OP', value: 14}); + return ops; +} + +function makeCharStringsIndex(glyphs) { + var t = new table.Table('CharStrings INDEX', [ + {name: 'charStrings', type: 'INDEX', value: []} + ]); + + for (var i = 0; i < glyphs.length; i += 1) { + var glyph = glyphs.get(i); + var ops = glyphToOps(glyph); + t.charStrings.push({name: glyph.name, type: 'CHARSTRING', value: ops}); + } + + return t; +} + +function makePrivateDict(attrs, strings) { + var t = new table.Table('Private DICT', [ + {name: 'dict', type: 'DICT', value: {}} + ]); + t.dict = makeDict(PRIVATE_DICT_META, attrs, strings); + return t; +} + +function makePrivateDictIndex(privateDict) { + var t = new table.Table('Private DICT INDEX', [ + {name: 'privateDicts', type: 'INDEX', value: []} + ]); + t.privateDicts = [{name: 'privateDict_0', type: 'TABLE', value: privateDict}]; + return t; +} + +function makeCFFTable(glyphs, options) { + var t = new table.Table('CFF ', [ + {name: 'header', type: 'TABLE'}, + {name: 'nameIndex', type: 'TABLE'}, + {name: 'topDictIndex', type: 'TABLE'}, + {name: 'stringIndex', type: 'TABLE'}, + {name: 'globalSubrIndex', type: 'TABLE'}, + {name: 'charsets', type: 'TABLE'}, + {name: 'charStringsIndex', type: 'TABLE'}, + {name: 'privateDictIndex', type: 'TABLE'} + ]); + + var fontScale = 1 / options.unitsPerEm; + // We use non-zero values for the offsets so that the DICT encodes them. + // This is important because the size of the Top DICT plays a role in offset calculation, + // and the size shouldn't change after we've written correct offsets. + var attrs = { + version: options.version, + fullName: options.fullName, + familyName: options.familyName, + weight: options.weightName, + fontMatrix: [fontScale, 0, 0, fontScale, 0, 0], + charset: 999, + encoding: 0, + charStrings: 999, + private: [0, 999] + }; + + var privateAttrs = {}; + + var glyphNames = []; + var glyph; + + // Skip first glyph (.notdef) + for (var i = 1; i < glyphs.length; i += 1) { + glyph = glyphs.get(i); + glyphNames.push(glyph.name); + } + + var strings = []; + + t.header = makeHeader(); + t.nameIndex = makeNameIndex([options.postScriptName]); + var topDict = makeTopDict(attrs, strings); + t.topDictIndex = makeTopDictIndex(topDict); + t.globalSubrIndex = makeGlobalSubrIndex(); + t.charsets = makeCharsets(glyphNames, strings); + t.charStringsIndex = makeCharStringsIndex(glyphs); + var privateDict = makePrivateDict(privateAttrs, strings); + t.privateDictIndex = makePrivateDictIndex(privateDict); + + // Needs to come at the end, to encode all custom strings used in the font. + t.stringIndex = makeStringIndex(strings); + + var startOffset = t.header.sizeOf() + + t.nameIndex.sizeOf() + + t.topDictIndex.sizeOf() + + t.stringIndex.sizeOf() + + t.globalSubrIndex.sizeOf(); + attrs.charset = startOffset; + + // We use the CFF standard encoding; proper encoding will be handled in cmap. + attrs.encoding = 0; + attrs.charStrings = attrs.charset + t.charsets.sizeOf(); + attrs.private[1] = attrs.charStrings + t.charStringsIndex.sizeOf(); + + // Recreate the Top DICT INDEX with the correct offsets. + topDict = makeTopDict(attrs, strings); + t.topDictIndex = makeTopDictIndex(topDict); + + return t; +} + +exports.parse = parseCFFTable; +exports.make = makeCFFTable; + +},{"../encoding":4,"../glyphset":7,"../parse":9,"../path":10,"../table":11}],13:[function(_dereq_,module,exports){ +// The `cmap` table stores the mappings from characters to glyphs. +// https://www.microsoft.com/typography/OTSPEC/cmap.htm + +'use strict'; + +var check = _dereq_('../check'); +var parse = _dereq_('../parse'); +var table = _dereq_('../table'); + +// Parse the `cmap` table. This table stores the mappings from characters to glyphs. +// There are many available formats, but we only support the Windows format 4. +// This function returns a `CmapEncoding` object or null if no supported format could be found. +function parseCmapTable(data, start) { + var i; + var cmap = {}; + cmap.version = parse.getUShort(data, start); + check.argument(cmap.version === 0, 'cmap table version should be 0.'); + + // The cmap table can contain many sub-tables, each with their own format. + // We're only interested in a "platform 3" table. This is a Windows format. + cmap.numTables = parse.getUShort(data, start + 2); + var offset = -1; + for (i = 0; i < cmap.numTables; i += 1) { + var platformId = parse.getUShort(data, start + 4 + (i * 8)); + var encodingId = parse.getUShort(data, start + 4 + (i * 8) + 2); + if (platformId === 3 && (encodingId === 1 || encodingId === 0)) { + offset = parse.getULong(data, start + 4 + (i * 8) + 4); + break; + } + } + + if (offset === -1) { + // There is no cmap table in the font that we support, so return null. + // This font will be marked as unsupported. + return null; + } + + var p = new parse.Parser(data, start + offset); + cmap.format = p.parseUShort(); + check.argument(cmap.format === 4, 'Only format 4 cmap tables are supported.'); + + // Length in bytes of the sub-tables. + cmap.length = p.parseUShort(); + cmap.language = p.parseUShort(); + + // segCount is stored x 2. + var segCount; + cmap.segCount = segCount = p.parseUShort() >> 1; + + // Skip searchRange, entrySelector, rangeShift. + p.skip('uShort', 3); + + // The "unrolled" mapping from character codes to glyph indices. + cmap.glyphIndexMap = {}; + + var endCountParser = new parse.Parser(data, start + offset + 14); + var startCountParser = new parse.Parser(data, start + offset + 16 + segCount * 2); + var idDeltaParser = new parse.Parser(data, start + offset + 16 + segCount * 4); + var idRangeOffsetParser = new parse.Parser(data, start + offset + 16 + segCount * 6); + var glyphIndexOffset = start + offset + 16 + segCount * 8; + for (i = 0; i < segCount - 1; i += 1) { + var glyphIndex; + var endCount = endCountParser.parseUShort(); + var startCount = startCountParser.parseUShort(); + var idDelta = idDeltaParser.parseShort(); + var idRangeOffset = idRangeOffsetParser.parseUShort(); + for (var c = startCount; c <= endCount; c += 1) { + if (idRangeOffset !== 0) { + // The idRangeOffset is relative to the current position in the idRangeOffset array. + // Take the current offset in the idRangeOffset array. + glyphIndexOffset = (idRangeOffsetParser.offset + idRangeOffsetParser.relativeOffset - 2); + + // Add the value of the idRangeOffset, which will move us into the glyphIndex array. + glyphIndexOffset += idRangeOffset; + + // Then add the character index of the current segment, multiplied by 2 for USHORTs. + glyphIndexOffset += (c - startCount) * 2; + glyphIndex = parse.getUShort(data, glyphIndexOffset); + if (glyphIndex !== 0) { + glyphIndex = (glyphIndex + idDelta) & 0xFFFF; + } + } else { + glyphIndex = (c + idDelta) & 0xFFFF; + } + + cmap.glyphIndexMap[c] = glyphIndex; + } + } + + return cmap; +} + +function addSegment(t, code, glyphIndex) { + t.segments.push({ + end: code, + start: code, + delta: -(code - glyphIndex), + offset: 0 + }); +} + +function addTerminatorSegment(t) { + t.segments.push({ + end: 0xFFFF, + start: 0xFFFF, + delta: 1, + offset: 0 + }); +} + +function makeCmapTable(glyphs) { + var i; + var t = new table.Table('cmap', [ + {name: 'version', type: 'USHORT', value: 0}, + {name: 'numTables', type: 'USHORT', value: 1}, + {name: 'platformID', type: 'USHORT', value: 3}, + {name: 'encodingID', type: 'USHORT', value: 1}, + {name: 'offset', type: 'ULONG', value: 12}, + {name: 'format', type: 'USHORT', value: 4}, + {name: 'length', type: 'USHORT', value: 0}, + {name: 'language', type: 'USHORT', value: 0}, + {name: 'segCountX2', type: 'USHORT', value: 0}, + {name: 'searchRange', type: 'USHORT', value: 0}, + {name: 'entrySelector', type: 'USHORT', value: 0}, + {name: 'rangeShift', type: 'USHORT', value: 0} + ]); + + t.segments = []; + for (i = 0; i < glyphs.length; i += 1) { + var glyph = glyphs.get(i); + for (var j = 0; j < glyph.unicodes.length; j += 1) { + addSegment(t, glyph.unicodes[j], i); + } + + t.segments = t.segments.sort(function(a, b) { + return a.start - b.start; + }); + } + + addTerminatorSegment(t); + + var segCount; + segCount = t.segments.length; + t.segCountX2 = segCount * 2; + t.searchRange = Math.pow(2, Math.floor(Math.log(segCount) / Math.log(2))) * 2; + t.entrySelector = Math.log(t.searchRange / 2) / Math.log(2); + t.rangeShift = t.segCountX2 - t.searchRange; + + // Set up parallel segment arrays. + var endCounts = []; + var startCounts = []; + var idDeltas = []; + var idRangeOffsets = []; + var glyphIds = []; + + for (i = 0; i < segCount; i += 1) { + var segment = t.segments[i]; + endCounts = endCounts.concat({name: 'end_' + i, type: 'USHORT', value: segment.end}); + startCounts = startCounts.concat({name: 'start_' + i, type: 'USHORT', value: segment.start}); + idDeltas = idDeltas.concat({name: 'idDelta_' + i, type: 'SHORT', value: segment.delta}); + idRangeOffsets = idRangeOffsets.concat({name: 'idRangeOffset_' + i, type: 'USHORT', value: segment.offset}); + if (segment.glyphId !== undefined) { + glyphIds = glyphIds.concat({name: 'glyph_' + i, type: 'USHORT', value: segment.glyphId}); + } + } + + t.fields = t.fields.concat(endCounts); + t.fields.push({name: 'reservedPad', type: 'USHORT', value: 0}); + t.fields = t.fields.concat(startCounts); + t.fields = t.fields.concat(idDeltas); + t.fields = t.fields.concat(idRangeOffsets); + t.fields = t.fields.concat(glyphIds); + + t.length = 14 + // Subtable header + endCounts.length * 2 + + 2 + // reservedPad + startCounts.length * 2 + + idDeltas.length * 2 + + idRangeOffsets.length * 2 + + glyphIds.length * 2; + + return t; +} + +exports.parse = parseCmapTable; +exports.make = makeCmapTable; + +},{"../check":2,"../parse":9,"../table":11}],14:[function(_dereq_,module,exports){ +// The `glyf` table describes the glyphs in TrueType outline format. +// http://www.microsoft.com/typography/otspec/glyf.htm + +'use strict'; + +var check = _dereq_('../check'); +var glyphset = _dereq_('../glyphset'); +var parse = _dereq_('../parse'); +var path = _dereq_('../path'); + +// Parse the coordinate data for a glyph. +function parseGlyphCoordinate(p, flag, previousValue, shortVectorBitMask, sameBitMask) { + var v; + if ((flag & shortVectorBitMask) > 0) { + // The coordinate is 1 byte long. + v = p.parseByte(); + // The `same` bit is re-used for short values to signify the sign of the value. + if ((flag & sameBitMask) === 0) { + v = -v; + } + + v = previousValue + v; + } else { + // The coordinate is 2 bytes long. + // If the `same` bit is set, the coordinate is the same as the previous coordinate. + if ((flag & sameBitMask) > 0) { + v = previousValue; + } else { + // Parse the coordinate as a signed 16-bit delta value. + v = previousValue + p.parseShort(); + } + } + + return v; +} + +// Parse a TrueType glyph. +function parseGlyph(glyph, data, start) { + var p = new parse.Parser(data, start); + glyph.numberOfContours = p.parseShort(); + glyph.xMin = p.parseShort(); + glyph.yMin = p.parseShort(); + glyph.xMax = p.parseShort(); + glyph.yMax = p.parseShort(); + var flags; + var flag; + if (glyph.numberOfContours > 0) { + var i; + // This glyph is not a composite. + var endPointIndices = glyph.endPointIndices = []; + for (i = 0; i < glyph.numberOfContours; i += 1) { + endPointIndices.push(p.parseUShort()); + } + + glyph.instructionLength = p.parseUShort(); + glyph.instructions = []; + for (i = 0; i < glyph.instructionLength; i += 1) { + glyph.instructions.push(p.parseByte()); + } + + var numberOfCoordinates = endPointIndices[endPointIndices.length - 1] + 1; + flags = []; + for (i = 0; i < numberOfCoordinates; i += 1) { + flag = p.parseByte(); + flags.push(flag); + // If bit 3 is set, we repeat this flag n times, where n is the next byte. + if ((flag & 8) > 0) { + var repeatCount = p.parseByte(); + for (var j = 0; j < repeatCount; j += 1) { + flags.push(flag); + i += 1; + } + } + } + + check.argument(flags.length === numberOfCoordinates, 'Bad flags.'); + + if (endPointIndices.length > 0) { + var points = []; + var point; + // X/Y coordinates are relative to the previous point, except for the first point which is relative to 0,0. + if (numberOfCoordinates > 0) { + for (i = 0; i < numberOfCoordinates; i += 1) { + flag = flags[i]; + point = {}; + point.onCurve = !!(flag & 1); + point.lastPointOfContour = endPointIndices.indexOf(i) >= 0; + points.push(point); + } + + var px = 0; + for (i = 0; i < numberOfCoordinates; i += 1) { + flag = flags[i]; + point = points[i]; + point.x = parseGlyphCoordinate(p, flag, px, 2, 16); + px = point.x; + } + + var py = 0; + for (i = 0; i < numberOfCoordinates; i += 1) { + flag = flags[i]; + point = points[i]; + point.y = parseGlyphCoordinate(p, flag, py, 4, 32); + py = point.y; + } + } + + glyph.points = points; + } else { + glyph.points = []; + } + } else if (glyph.numberOfContours === 0) { + glyph.points = []; + } else { + glyph.isComposite = true; + glyph.points = []; + glyph.components = []; + var moreComponents = true; + while (moreComponents) { + flags = p.parseUShort(); + var component = { + glyphIndex: p.parseUShort(), + xScale: 1, + scale01: 0, + scale10: 0, + yScale: 1, + dx: 0, + dy: 0 + }; + if ((flags & 1) > 0) { + // The arguments are words + component.dx = p.parseShort(); + component.dy = p.parseShort(); + } else { + // The arguments are bytes + component.dx = p.parseChar(); + component.dy = p.parseChar(); + } + + if ((flags & 8) > 0) { + // We have a scale + component.xScale = component.yScale = p.parseF2Dot14(); + } else if ((flags & 64) > 0) { + // We have an X / Y scale + component.xScale = p.parseF2Dot14(); + component.yScale = p.parseF2Dot14(); + } else if ((flags & 128) > 0) { + // We have a 2x2 transformation + component.xScale = p.parseF2Dot14(); + component.scale01 = p.parseF2Dot14(); + component.scale10 = p.parseF2Dot14(); + component.yScale = p.parseF2Dot14(); + } + + glyph.components.push(component); + moreComponents = !!(flags & 32); + } + } +} + +// Transform an array of points and return a new array. +function transformPoints(points, transform) { + var newPoints = []; + for (var i = 0; i < points.length; i += 1) { + var pt = points[i]; + var newPt = { + x: transform.xScale * pt.x + transform.scale01 * pt.y + transform.dx, + y: transform.scale10 * pt.x + transform.yScale * pt.y + transform.dy, + onCurve: pt.onCurve, + lastPointOfContour: pt.lastPointOfContour + }; + newPoints.push(newPt); + } + + return newPoints; +} + +function getContours(points) { + var contours = []; + var currentContour = []; + for (var i = 0; i < points.length; i += 1) { + var pt = points[i]; + currentContour.push(pt); + if (pt.lastPointOfContour) { + contours.push(currentContour); + currentContour = []; + } + } + + check.argument(currentContour.length === 0, 'There are still points left in the current contour.'); + return contours; +} + +// Convert the TrueType glyph outline to a Path. +function getPath(points) { + var p = new path.Path(); + if (!points) { + return p; + } + + var contours = getContours(points); + for (var i = 0; i < contours.length; i += 1) { + var contour = contours[i]; + var firstPt = contour[0]; + var lastPt = contour[contour.length - 1]; + var curvePt; + var realFirstPoint; + if (firstPt.onCurve) { + curvePt = null; + // The first point will be consumed by the moveTo command, + // so skip it in the loop. + realFirstPoint = true; + } else { + if (lastPt.onCurve) { + // If the first point is off-curve and the last point is on-curve, + // start at the last point. + firstPt = lastPt; + } else { + // If both first and last points are off-curve, start at their middle. + firstPt = { x: (firstPt.x + lastPt.x) / 2, y: (firstPt.y + lastPt.y) / 2 }; + } + + curvePt = firstPt; + // The first point is synthesized, so don't skip the real first point. + realFirstPoint = false; + } + + p.moveTo(firstPt.x, firstPt.y); + + for (var j = realFirstPoint ? 1 : 0; j < contour.length; j += 1) { + var pt = contour[j]; + var prevPt = j === 0 ? firstPt : contour[j - 1]; + if (prevPt.onCurve && pt.onCurve) { + // This is a straight line. + p.lineTo(pt.x, pt.y); + } else if (prevPt.onCurve && !pt.onCurve) { + curvePt = pt; + } else if (!prevPt.onCurve && !pt.onCurve) { + var midPt = { x: (prevPt.x + pt.x) / 2, y: (prevPt.y + pt.y) / 2 }; + p.quadraticCurveTo(prevPt.x, prevPt.y, midPt.x, midPt.y); + curvePt = pt; + } else if (!prevPt.onCurve && pt.onCurve) { + // Previous point off-curve, this point on-curve. + p.quadraticCurveTo(curvePt.x, curvePt.y, pt.x, pt.y); + curvePt = null; + } else { + throw new Error('Invalid state.'); + } + } + + if (firstPt !== lastPt) { + // Connect the last and first points + if (curvePt) { + p.quadraticCurveTo(curvePt.x, curvePt.y, firstPt.x, firstPt.y); + } else { + p.lineTo(firstPt.x, firstPt.y); + } + } + } + + p.closePath(); + return p; +} + +function buildPath(glyphs, glyph) { + if (glyph.isComposite) { + for (var j = 0; j < glyph.components.length; j += 1) { + var component = glyph.components[j]; + var componentGlyph = glyphs.get(component.glyphIndex); + if (componentGlyph.points) { + var transformedPoints = transformPoints(componentGlyph.points, component); + glyph.points = glyph.points.concat(transformedPoints); + } + } + } + + return getPath(glyph.points); +} + +// Parse all the glyphs according to the offsets from the `loca` table. +function parseGlyfTable(data, start, loca, font) { + var glyphs = new glyphset.GlyphSet(font); + var i; + + // The last element of the loca table is invalid. + for (i = 0; i < loca.length - 1; i += 1) { + var offset = loca[i]; + var nextOffset = loca[i + 1]; + if (offset !== nextOffset) { + glyphs.push(i, glyphset.ttfGlyphLoader(font, i, parseGlyph, data, start + offset, buildPath)); + } else { + glyphs.push(i, glyphset.glyphLoader(font, i)); + } + } + + return glyphs; +} + +exports.parse = parseGlyfTable; + +},{"../check":2,"../glyphset":7,"../parse":9,"../path":10}],15:[function(_dereq_,module,exports){ +// The `GPOS` table contains kerning pairs, among other things. +// https://www.microsoft.com/typography/OTSPEC/gpos.htm + +'use strict'; + +var check = _dereq_('../check'); +var parse = _dereq_('../parse'); + +// Parse ScriptList and FeatureList tables of GPOS, GSUB, GDEF, BASE, JSTF tables. +// These lists are unused by now, this function is just the basis for a real parsing. +function parseTaggedListTable(data, start) { + var p = new parse.Parser(data, start); + var n = p.parseUShort(); + var list = []; + for (var i = 0; i < n; i++) { + list[p.parseTag()] = { offset: p.parseUShort() }; + } + + return list; +} + +// Parse a coverage table in a GSUB, GPOS or GDEF table. +// Format 1 is a simple list of glyph ids, +// Format 2 is a list of ranges. It is expanded in a list of glyphs, maybe not the best idea. +function parseCoverageTable(data, start) { + var p = new parse.Parser(data, start); + var format = p.parseUShort(); + var count = p.parseUShort(); + if (format === 1) { + return p.parseUShortList(count); + } + else if (format === 2) { + var coverage = []; + for (; count--;) { + var begin = p.parseUShort(); + var end = p.parseUShort(); + var index = p.parseUShort(); + for (var i = begin; i <= end; i++) { + coverage[index++] = i; + } + } + + return coverage; + } +} + +// Parse a Class Definition Table in a GSUB, GPOS or GDEF table. +// Returns a function that gets a class value from a glyph ID. +function parseClassDefTable(data, start) { + var p = new parse.Parser(data, start); + var format = p.parseUShort(); + if (format === 1) { + // Format 1 specifies a range of consecutive glyph indices, one class per glyph ID. + var startGlyph = p.parseUShort(); + var glyphCount = p.parseUShort(); + var classes = p.parseUShortList(glyphCount); + return function(glyphID) { + return classes[glyphID - startGlyph] || 0; + }; + } + else if (format === 2) { + // Format 2 defines multiple groups of glyph indices that belong to the same class. + var rangeCount = p.parseUShort(); + var startGlyphs = []; + var endGlyphs = []; + var classValues = []; + for (var i = 0; i < rangeCount; i++) { + startGlyphs[i] = p.parseUShort(); + endGlyphs[i] = p.parseUShort(); + classValues[i] = p.parseUShort(); + } + + return function(glyphID) { + var l = 0; + var r = startGlyphs.length - 1; + while (l < r) { + var c = (l + r + 1) >> 1; + if (glyphID < startGlyphs[c]) { + r = c - 1; + } else { + l = c; + } + } + + if (startGlyphs[l] <= glyphID && glyphID <= endGlyphs[l]) { + return classValues[l] || 0; + } + + return 0; + }; + } +} + +// Parse a pair adjustment positioning subtable, format 1 or format 2 +// The subtable is returned in the form of a lookup function. +function parsePairPosSubTable(data, start) { + var p = new parse.Parser(data, start); + // This part is common to format 1 and format 2 subtables + var format = p.parseUShort(); + var coverageOffset = p.parseUShort(); + var coverage = parseCoverageTable(data, start + coverageOffset); + // valueFormat 4: XAdvance only, 1: XPlacement only, 0: no ValueRecord for second glyph + // Only valueFormat1=4 and valueFormat2=0 is supported. + var valueFormat1 = p.parseUShort(); + var valueFormat2 = p.parseUShort(); + var value1; + var value2; + if (valueFormat1 !== 4 || valueFormat2 !== 0) return; + var sharedPairSets = {}; + if (format === 1) { + // Pair Positioning Adjustment: Format 1 + var pairSetCount = p.parseUShort(); + var pairSet = []; + // Array of offsets to PairSet tables-from beginning of PairPos subtable-ordered by Coverage Index + var pairSetOffsets = p.parseOffset16List(pairSetCount); + for (var firstGlyph = 0; firstGlyph < pairSetCount; firstGlyph++) { + var pairSetOffset = pairSetOffsets[firstGlyph]; + var sharedPairSet = sharedPairSets[pairSetOffset]; + if (!sharedPairSet) { + // Parse a pairset table in a pair adjustment subtable format 1 + sharedPairSet = {}; + p.relativeOffset = pairSetOffset; + var pairValueCount = p.parseUShort(); + for (; pairValueCount--;) { + var secondGlyph = p.parseUShort(); + if (valueFormat1) value1 = p.parseShort(); + if (valueFormat2) value2 = p.parseShort(); + // We only support valueFormat1 = 4 and valueFormat2 = 0, + // so value1 is the XAdvance and value2 is empty. + sharedPairSet[secondGlyph] = value1; + } + } + + pairSet[coverage[firstGlyph]] = sharedPairSet; + } + + return function(leftGlyph, rightGlyph) { + var pairs = pairSet[leftGlyph]; + if (pairs) return pairs[rightGlyph]; + }; + } + else if (format === 2) { + // Pair Positioning Adjustment: Format 2 + var classDef1Offset = p.parseUShort(); + var classDef2Offset = p.parseUShort(); + var class1Count = p.parseUShort(); + var class2Count = p.parseUShort(); + var getClass1 = parseClassDefTable(data, start + classDef1Offset); + var getClass2 = parseClassDefTable(data, start + classDef2Offset); + + // Parse kerning values by class pair. + var kerningMatrix = []; + for (var i = 0; i < class1Count; i++) { + var kerningRow = kerningMatrix[i] = []; + for (var j = 0; j < class2Count; j++) { + if (valueFormat1) value1 = p.parseShort(); + if (valueFormat2) value2 = p.parseShort(); + // We only support valueFormat1 = 4 and valueFormat2 = 0, + // so value1 is the XAdvance and value2 is empty. + kerningRow[j] = value1; + } + } + + // Convert coverage list to a hash + var covered = {}; + for (i = 0; i < coverage.length; i++) covered[coverage[i]] = 1; + + // Get the kerning value for a specific glyph pair. + return function(leftGlyph, rightGlyph) { + if (!covered[leftGlyph]) return; + var class1 = getClass1(leftGlyph); + var class2 = getClass2(rightGlyph); + var kerningRow = kerningMatrix[class1]; + + if (kerningRow) { + return kerningRow[class2]; + } + }; + } +} + +// Parse a LookupTable (present in of GPOS, GSUB, GDEF, BASE, JSTF tables). +function parseLookupTable(data, start) { + var p = new parse.Parser(data, start); + var lookupType = p.parseUShort(); + var lookupFlag = p.parseUShort(); + var useMarkFilteringSet = lookupFlag & 0x10; + var subTableCount = p.parseUShort(); + var subTableOffsets = p.parseOffset16List(subTableCount); + var table = { + lookupType: lookupType, + lookupFlag: lookupFlag, + markFilteringSet: useMarkFilteringSet ? p.parseUShort() : -1 + }; + // LookupType 2, Pair adjustment + if (lookupType === 2) { + var subtables = []; + for (var i = 0; i < subTableCount; i++) { + subtables.push(parsePairPosSubTable(data, start + subTableOffsets[i])); + } + // Return a function which finds the kerning values in the subtables. + table.getKerningValue = function(leftGlyph, rightGlyph) { + for (var i = subtables.length; i--;) { + var value = subtables[i](leftGlyph, rightGlyph); + if (value !== undefined) return value; + } + + return 0; + }; + } + + return table; +} + +// Parse the `GPOS` table which contains, among other things, kerning pairs. +// https://www.microsoft.com/typography/OTSPEC/gpos.htm +function parseGposTable(data, start, font) { + var p = new parse.Parser(data, start); + var tableVersion = p.parseFixed(); + check.argument(tableVersion === 1, 'Unsupported GPOS table version.'); + + // ScriptList and FeatureList - ignored for now + parseTaggedListTable(data, start + p.parseUShort()); + // 'kern' is the feature we are looking for. + parseTaggedListTable(data, start + p.parseUShort()); + + // LookupList + var lookupListOffset = p.parseUShort(); + p.relativeOffset = lookupListOffset; + var lookupCount = p.parseUShort(); + var lookupTableOffsets = p.parseOffset16List(lookupCount); + var lookupListAbsoluteOffset = start + lookupListOffset; + for (var i = 0; i < lookupCount; i++) { + var table = parseLookupTable(data, lookupListAbsoluteOffset + lookupTableOffsets[i]); + if (table.lookupType === 2 && !font.getGposKerningValue) font.getGposKerningValue = table.getKerningValue; + } +} + +exports.parse = parseGposTable; + +},{"../check":2,"../parse":9}],16:[function(_dereq_,module,exports){ +// The `head` table contains global information about the font. +// https://www.microsoft.com/typography/OTSPEC/head.htm + +'use strict'; + +var check = _dereq_('../check'); +var parse = _dereq_('../parse'); +var table = _dereq_('../table'); + +// Parse the header `head` table +function parseHeadTable(data, start) { + var head = {}; + var p = new parse.Parser(data, start); + head.version = p.parseVersion(); + head.fontRevision = Math.round(p.parseFixed() * 1000) / 1000; + head.checkSumAdjustment = p.parseULong(); + head.magicNumber = p.parseULong(); + check.argument(head.magicNumber === 0x5F0F3CF5, 'Font header has wrong magic number.'); + head.flags = p.parseUShort(); + head.unitsPerEm = p.parseUShort(); + head.created = p.parseLongDateTime(); + head.modified = p.parseLongDateTime(); + head.xMin = p.parseShort(); + head.yMin = p.parseShort(); + head.xMax = p.parseShort(); + head.yMax = p.parseShort(); + head.macStyle = p.parseUShort(); + head.lowestRecPPEM = p.parseUShort(); + head.fontDirectionHint = p.parseShort(); + head.indexToLocFormat = p.parseShort(); // 50 + head.glyphDataFormat = p.parseShort(); + return head; +} + +function makeHeadTable(options) { + return new table.Table('head', [ + {name: 'version', type: 'FIXED', value: 0x00010000}, + {name: 'fontRevision', type: 'FIXED', value: 0x00010000}, + {name: 'checkSumAdjustment', type: 'ULONG', value: 0}, + {name: 'magicNumber', type: 'ULONG', value: 0x5F0F3CF5}, + {name: 'flags', type: 'USHORT', value: 0}, + {name: 'unitsPerEm', type: 'USHORT', value: 1000}, + {name: 'created', type: 'LONGDATETIME', value: 0}, + {name: 'modified', type: 'LONGDATETIME', value: 0}, + {name: 'xMin', type: 'SHORT', value: 0}, + {name: 'yMin', type: 'SHORT', value: 0}, + {name: 'xMax', type: 'SHORT', value: 0}, + {name: 'yMax', type: 'SHORT', value: 0}, + {name: 'macStyle', type: 'USHORT', value: 0}, + {name: 'lowestRecPPEM', type: 'USHORT', value: 0}, + {name: 'fontDirectionHint', type: 'SHORT', value: 2}, + {name: 'indexToLocFormat', type: 'SHORT', value: 0}, + {name: 'glyphDataFormat', type: 'SHORT', value: 0} + ], options); +} + +exports.parse = parseHeadTable; +exports.make = makeHeadTable; + +},{"../check":2,"../parse":9,"../table":11}],17:[function(_dereq_,module,exports){ +// The `hhea` table contains information for horizontal layout. +// https://www.microsoft.com/typography/OTSPEC/hhea.htm + +'use strict'; + +var parse = _dereq_('../parse'); +var table = _dereq_('../table'); + +// Parse the horizontal header `hhea` table +function parseHheaTable(data, start) { + var hhea = {}; + var p = new parse.Parser(data, start); + hhea.version = p.parseVersion(); + hhea.ascender = p.parseShort(); + hhea.descender = p.parseShort(); + hhea.lineGap = p.parseShort(); + hhea.advanceWidthMax = p.parseUShort(); + hhea.minLeftSideBearing = p.parseShort(); + hhea.minRightSideBearing = p.parseShort(); + hhea.xMaxExtent = p.parseShort(); + hhea.caretSlopeRise = p.parseShort(); + hhea.caretSlopeRun = p.parseShort(); + hhea.caretOffset = p.parseShort(); + p.relativeOffset += 8; + hhea.metricDataFormat = p.parseShort(); + hhea.numberOfHMetrics = p.parseUShort(); + return hhea; +} + +function makeHheaTable(options) { + return new table.Table('hhea', [ + {name: 'version', type: 'FIXED', value: 0x00010000}, + {name: 'ascender', type: 'FWORD', value: 0}, + {name: 'descender', type: 'FWORD', value: 0}, + {name: 'lineGap', type: 'FWORD', value: 0}, + {name: 'advanceWidthMax', type: 'UFWORD', value: 0}, + {name: 'minLeftSideBearing', type: 'FWORD', value: 0}, + {name: 'minRightSideBearing', type: 'FWORD', value: 0}, + {name: 'xMaxExtent', type: 'FWORD', value: 0}, + {name: 'caretSlopeRise', type: 'SHORT', value: 1}, + {name: 'caretSlopeRun', type: 'SHORT', value: 0}, + {name: 'caretOffset', type: 'SHORT', value: 0}, + {name: 'reserved1', type: 'SHORT', value: 0}, + {name: 'reserved2', type: 'SHORT', value: 0}, + {name: 'reserved3', type: 'SHORT', value: 0}, + {name: 'reserved4', type: 'SHORT', value: 0}, + {name: 'metricDataFormat', type: 'SHORT', value: 0}, + {name: 'numberOfHMetrics', type: 'USHORT', value: 0} + ], options); +} + +exports.parse = parseHheaTable; +exports.make = makeHheaTable; + +},{"../parse":9,"../table":11}],18:[function(_dereq_,module,exports){ +// The `hmtx` table contains the horizontal metrics for all glyphs. +// https://www.microsoft.com/typography/OTSPEC/hmtx.htm + +'use strict'; + +var parse = _dereq_('../parse'); +var table = _dereq_('../table'); + +// Parse the `hmtx` table, which contains the horizontal metrics for all glyphs. +// This function augments the glyph array, adding the advanceWidth and leftSideBearing to each glyph. +function parseHmtxTable(data, start, numMetrics, numGlyphs, glyphs) { + var advanceWidth; + var leftSideBearing; + var p = new parse.Parser(data, start); + for (var i = 0; i < numGlyphs; i += 1) { + // If the font is monospaced, only one entry is needed. This last entry applies to all subsequent glyphs. + if (i < numMetrics) { + advanceWidth = p.parseUShort(); + leftSideBearing = p.parseShort(); + } + + var glyph = glyphs.get(i); + glyph.advanceWidth = advanceWidth; + glyph.leftSideBearing = leftSideBearing; + } +} + +function makeHmtxTable(glyphs) { + var t = new table.Table('hmtx', []); + for (var i = 0; i < glyphs.length; i += 1) { + var glyph = glyphs.get(i); + var advanceWidth = glyph.advanceWidth || 0; + var leftSideBearing = glyph.leftSideBearing || 0; + t.fields.push({name: 'advanceWidth_' + i, type: 'USHORT', value: advanceWidth}); + t.fields.push({name: 'leftSideBearing_' + i, type: 'SHORT', value: leftSideBearing}); + } + + return t; +} + +exports.parse = parseHmtxTable; +exports.make = makeHmtxTable; + +},{"../parse":9,"../table":11}],19:[function(_dereq_,module,exports){ +// The `kern` table contains kerning pairs. +// Note that some fonts use the GPOS OpenType layout table to specify kerning. +// https://www.microsoft.com/typography/OTSPEC/kern.htm + +'use strict'; + +var check = _dereq_('../check'); +var parse = _dereq_('../parse'); + +// Parse the `kern` table which contains kerning pairs. +function parseKernTable(data, start) { + var pairs = {}; + var p = new parse.Parser(data, start); + var tableVersion = p.parseUShort(); + check.argument(tableVersion === 0, 'Unsupported kern table version.'); + // Skip nTables. + p.skip('uShort', 1); + var subTableVersion = p.parseUShort(); + check.argument(subTableVersion === 0, 'Unsupported kern sub-table version.'); + // Skip subTableLength, subTableCoverage + p.skip('uShort', 2); + var nPairs = p.parseUShort(); + // Skip searchRange, entrySelector, rangeShift. + p.skip('uShort', 3); + for (var i = 0; i < nPairs; i += 1) { + var leftIndex = p.parseUShort(); + var rightIndex = p.parseUShort(); + var value = p.parseShort(); + pairs[leftIndex + ',' + rightIndex] = value; + } + + return pairs; +} + +exports.parse = parseKernTable; + +},{"../check":2,"../parse":9}],20:[function(_dereq_,module,exports){ +// The `loca` table stores the offsets to the locations of the glyphs in the font. +// https://www.microsoft.com/typography/OTSPEC/loca.htm + +'use strict'; + +var parse = _dereq_('../parse'); + +// Parse the `loca` table. This table stores the offsets to the locations of the glyphs in the font, +// relative to the beginning of the glyphData table. +// The number of glyphs stored in the `loca` table is specified in the `maxp` table (under numGlyphs) +// The loca table has two versions: a short version where offsets are stored as uShorts, and a long +// version where offsets are stored as uLongs. The `head` table specifies which version to use +// (under indexToLocFormat). +function parseLocaTable(data, start, numGlyphs, shortVersion) { + var p = new parse.Parser(data, start); + var parseFn = shortVersion ? p.parseUShort : p.parseULong; + // There is an extra entry after the last index element to compute the length of the last glyph. + // That's why we use numGlyphs + 1. + var glyphOffsets = []; + for (var i = 0; i < numGlyphs + 1; i += 1) { + var glyphOffset = parseFn.call(p); + if (shortVersion) { + // The short table version stores the actual offset divided by 2. + glyphOffset *= 2; + } + + glyphOffsets.push(glyphOffset); + } + + return glyphOffsets; +} + +exports.parse = parseLocaTable; + +},{"../parse":9}],21:[function(_dereq_,module,exports){ +// The `maxp` table establishes the memory requirements for the font. +// We need it just to get the number of glyphs in the font. +// https://www.microsoft.com/typography/OTSPEC/maxp.htm + +'use strict'; + +var parse = _dereq_('../parse'); +var table = _dereq_('../table'); + +// Parse the maximum profile `maxp` table. +function parseMaxpTable(data, start) { + var maxp = {}; + var p = new parse.Parser(data, start); + maxp.version = p.parseVersion(); + maxp.numGlyphs = p.parseUShort(); + if (maxp.version === 1.0) { + maxp.maxPoints = p.parseUShort(); + maxp.maxContours = p.parseUShort(); + maxp.maxCompositePoints = p.parseUShort(); + maxp.maxCompositeContours = p.parseUShort(); + maxp.maxZones = p.parseUShort(); + maxp.maxTwilightPoints = p.parseUShort(); + maxp.maxStorage = p.parseUShort(); + maxp.maxFunctionDefs = p.parseUShort(); + maxp.maxInstructionDefs = p.parseUShort(); + maxp.maxStackElements = p.parseUShort(); + maxp.maxSizeOfInstructions = p.parseUShort(); + maxp.maxComponentElements = p.parseUShort(); + maxp.maxComponentDepth = p.parseUShort(); + } + + return maxp; +} + +function makeMaxpTable(numGlyphs) { + return new table.Table('maxp', [ + {name: 'version', type: 'FIXED', value: 0x00005000}, + {name: 'numGlyphs', type: 'USHORT', value: numGlyphs} + ]); +} + +exports.parse = parseMaxpTable; +exports.make = makeMaxpTable; + +},{"../parse":9,"../table":11}],22:[function(_dereq_,module,exports){ +// The `name` naming table. +// https://www.microsoft.com/typography/OTSPEC/name.htm + +'use strict'; + +var encode = _dereq_('../types').encode; +var parse = _dereq_('../parse'); +var table = _dereq_('../table'); + +// NameIDs for the name table. +var nameTableNames = [ + 'copyright', // 0 + 'fontFamily', // 1 + 'fontSubfamily', // 2 + 'uniqueID', // 3 + 'fullName', // 4 + 'version', // 5 + 'postScriptName', // 6 + 'trademark', // 7 + 'manufacturer', // 8 + 'designer', // 9 + 'description', // 10 + 'manufacturerURL', // 11 + 'designerURL', // 12 + 'licence', // 13 + 'licenceURL', // 14 + 'reserved', // 15 + 'preferredFamily', // 16 + 'preferredSubfamily', // 17 + 'compatibleFullName', // 18 + 'sampleText', // 19 + 'postScriptFindFontName', // 20 + 'wwsFamily', // 21 + 'wwsSubfamily' // 22 +]; + +// Parse the naming `name` table +// Only Windows Unicode English names are supported. +// Format 1 additional fields are not supported +function parseNameTable(data, start) { + var name = {}; + var p = new parse.Parser(data, start); + name.format = p.parseUShort(); + var count = p.parseUShort(); + var stringOffset = p.offset + p.parseUShort(); + var unknownCount = 0; + for (var i = 0; i < count; i++) { + var platformID = p.parseUShort(); + var encodingID = p.parseUShort(); + var languageID = p.parseUShort(); + var nameID = p.parseUShort(); + var property = nameTableNames[nameID]; + var byteLength = p.parseUShort(); + var offset = p.parseUShort(); + // platformID - encodingID - languageID standard combinations : + // 1 - 0 - 0 : Macintosh, Roman, English + // 3 - 1 - 0x409 : Windows, Unicode BMP (UCS-2), en-US + if (platformID === 3 && encodingID === 1 && languageID === 0x409) { + var codePoints = []; + var length = byteLength / 2; + for (var j = 0; j < length; j++, offset += 2) { + codePoints[j] = parse.getShort(data, stringOffset + offset); + } + + var str = String.fromCharCode.apply(null, codePoints); + if (property) { + name[property] = str; + } + else { + unknownCount++; + name['unknown' + unknownCount] = str; + } + } + + } + + if (name.format === 1) { + name.langTagCount = p.parseUShort(); + } + + return name; +} + +function makeNameRecord(platformID, encodingID, languageID, nameID, length, offset) { + return new table.Table('NameRecord', [ + {name: 'platformID', type: 'USHORT', value: platformID}, + {name: 'encodingID', type: 'USHORT', value: encodingID}, + {name: 'languageID', type: 'USHORT', value: languageID}, + {name: 'nameID', type: 'USHORT', value: nameID}, + {name: 'length', type: 'USHORT', value: length}, + {name: 'offset', type: 'USHORT', value: offset} + ]); +} + +function addMacintoshNameRecord(t, recordID, s, offset) { + // Macintosh, Roman, English + var stringBytes = encode.STRING(s); + t.records.push(makeNameRecord(1, 0, 0, recordID, stringBytes.length, offset)); + t.strings.push(stringBytes); + offset += stringBytes.length; + return offset; +} + +function addWindowsNameRecord(t, recordID, s, offset) { + // Windows, Unicode BMP (UCS-2), US English + var utf16Bytes = encode.UTF16(s); + t.records.push(makeNameRecord(3, 1, 0x0409, recordID, utf16Bytes.length, offset)); + t.strings.push(utf16Bytes); + offset += utf16Bytes.length; + return offset; +} + +function makeNameTable(options) { + var t = new table.Table('name', [ + {name: 'format', type: 'USHORT', value: 0}, + {name: 'count', type: 'USHORT', value: 0}, + {name: 'stringOffset', type: 'USHORT', value: 0} + ]); + t.records = []; + t.strings = []; + var offset = 0; + var i; + var s; + // Add Macintosh records first + for (i = 0; i < nameTableNames.length; i += 1) { + if (options[nameTableNames[i]] !== undefined) { + s = options[nameTableNames[i]]; + offset = addMacintoshNameRecord(t, i, s, offset); + } + } + // Then add Windows records + for (i = 0; i < nameTableNames.length; i += 1) { + if (options[nameTableNames[i]] !== undefined) { + s = options[nameTableNames[i]]; + offset = addWindowsNameRecord(t, i, s, offset); + } + } + + t.count = t.records.length; + t.stringOffset = 6 + t.count * 12; + for (i = 0; i < t.records.length; i += 1) { + t.fields.push({name: 'record_' + i, type: 'TABLE', value: t.records[i]}); + } + + for (i = 0; i < t.strings.length; i += 1) { + t.fields.push({name: 'string_' + i, type: 'LITERAL', value: t.strings[i]}); + } + + return t; +} + +exports.parse = parseNameTable; +exports.make = makeNameTable; + +},{"../parse":9,"../table":11,"../types":26}],23:[function(_dereq_,module,exports){ +// The `OS/2` table contains metrics required in OpenType fonts. +// https://www.microsoft.com/typography/OTSPEC/os2.htm + +'use strict'; + +var parse = _dereq_('../parse'); +var table = _dereq_('../table'); + +var unicodeRanges = [ + {begin: 0x0000, end: 0x007F}, // Basic Latin + {begin: 0x0080, end: 0x00FF}, // Latin-1 Supplement + {begin: 0x0100, end: 0x017F}, // Latin Extended-A + {begin: 0x0180, end: 0x024F}, // Latin Extended-B + {begin: 0x0250, end: 0x02AF}, // IPA Extensions + {begin: 0x02B0, end: 0x02FF}, // Spacing Modifier Letters + {begin: 0x0300, end: 0x036F}, // Combining Diacritical Marks + {begin: 0x0370, end: 0x03FF}, // Greek and Coptic + {begin: 0x2C80, end: 0x2CFF}, // Coptic + {begin: 0x0400, end: 0x04FF}, // Cyrillic + {begin: 0x0530, end: 0x058F}, // Armenian + {begin: 0x0590, end: 0x05FF}, // Hebrew + {begin: 0xA500, end: 0xA63F}, // Vai + {begin: 0x0600, end: 0x06FF}, // Arabic + {begin: 0x07C0, end: 0x07FF}, // NKo + {begin: 0x0900, end: 0x097F}, // Devanagari + {begin: 0x0980, end: 0x09FF}, // Bengali + {begin: 0x0A00, end: 0x0A7F}, // Gurmukhi + {begin: 0x0A80, end: 0x0AFF}, // Gujarati + {begin: 0x0B00, end: 0x0B7F}, // Oriya + {begin: 0x0B80, end: 0x0BFF}, // Tamil + {begin: 0x0C00, end: 0x0C7F}, // Telugu + {begin: 0x0C80, end: 0x0CFF}, // Kannada + {begin: 0x0D00, end: 0x0D7F}, // Malayalam + {begin: 0x0E00, end: 0x0E7F}, // Thai + {begin: 0x0E80, end: 0x0EFF}, // Lao + {begin: 0x10A0, end: 0x10FF}, // Georgian + {begin: 0x1B00, end: 0x1B7F}, // Balinese + {begin: 0x1100, end: 0x11FF}, // Hangul Jamo + {begin: 0x1E00, end: 0x1EFF}, // Latin Extended Additional + {begin: 0x1F00, end: 0x1FFF}, // Greek Extended + {begin: 0x2000, end: 0x206F}, // General Punctuation + {begin: 0x2070, end: 0x209F}, // Superscripts And Subscripts + {begin: 0x20A0, end: 0x20CF}, // Currency Symbol + {begin: 0x20D0, end: 0x20FF}, // Combining Diacritical Marks For Symbols + {begin: 0x2100, end: 0x214F}, // Letterlike Symbols + {begin: 0x2150, end: 0x218F}, // Number Forms + {begin: 0x2190, end: 0x21FF}, // Arrows + {begin: 0x2200, end: 0x22FF}, // Mathematical Operators + {begin: 0x2300, end: 0x23FF}, // Miscellaneous Technical + {begin: 0x2400, end: 0x243F}, // Control Pictures + {begin: 0x2440, end: 0x245F}, // Optical Character Recognition + {begin: 0x2460, end: 0x24FF}, // Enclosed Alphanumerics + {begin: 0x2500, end: 0x257F}, // Box Drawing + {begin: 0x2580, end: 0x259F}, // Block Elements + {begin: 0x25A0, end: 0x25FF}, // Geometric Shapes + {begin: 0x2600, end: 0x26FF}, // Miscellaneous Symbols + {begin: 0x2700, end: 0x27BF}, // Dingbats + {begin: 0x3000, end: 0x303F}, // CJK Symbols And Punctuation + {begin: 0x3040, end: 0x309F}, // Hiragana + {begin: 0x30A0, end: 0x30FF}, // Katakana + {begin: 0x3100, end: 0x312F}, // Bopomofo + {begin: 0x3130, end: 0x318F}, // Hangul Compatibility Jamo + {begin: 0xA840, end: 0xA87F}, // Phags-pa + {begin: 0x3200, end: 0x32FF}, // Enclosed CJK Letters And Months + {begin: 0x3300, end: 0x33FF}, // CJK Compatibility + {begin: 0xAC00, end: 0xD7AF}, // Hangul Syllables + {begin: 0xD800, end: 0xDFFF}, // Non-Plane 0 * + {begin: 0x10900, end: 0x1091F}, // Phoenicia + {begin: 0x4E00, end: 0x9FFF}, // CJK Unified Ideographs + {begin: 0xE000, end: 0xF8FF}, // Private Use Area (plane 0) + {begin: 0x31C0, end: 0x31EF}, // CJK Strokes + {begin: 0xFB00, end: 0xFB4F}, // Alphabetic Presentation Forms + {begin: 0xFB50, end: 0xFDFF}, // Arabic Presentation Forms-A + {begin: 0xFE20, end: 0xFE2F}, // Combining Half Marks + {begin: 0xFE10, end: 0xFE1F}, // Vertical Forms + {begin: 0xFE50, end: 0xFE6F}, // Small Form Variants + {begin: 0xFE70, end: 0xFEFF}, // Arabic Presentation Forms-B + {begin: 0xFF00, end: 0xFFEF}, // Halfwidth And Fullwidth Forms + {begin: 0xFFF0, end: 0xFFFF}, // Specials + {begin: 0x0F00, end: 0x0FFF}, // Tibetan + {begin: 0x0700, end: 0x074F}, // Syriac + {begin: 0x0780, end: 0x07BF}, // Thaana + {begin: 0x0D80, end: 0x0DFF}, // Sinhala + {begin: 0x1000, end: 0x109F}, // Myanmar + {begin: 0x1200, end: 0x137F}, // Ethiopic + {begin: 0x13A0, end: 0x13FF}, // Cherokee + {begin: 0x1400, end: 0x167F}, // Unified Canadian Aboriginal Syllabics + {begin: 0x1680, end: 0x169F}, // Ogham + {begin: 0x16A0, end: 0x16FF}, // Runic + {begin: 0x1780, end: 0x17FF}, // Khmer + {begin: 0x1800, end: 0x18AF}, // Mongolian + {begin: 0x2800, end: 0x28FF}, // Braille Patterns + {begin: 0xA000, end: 0xA48F}, // Yi Syllables + {begin: 0x1700, end: 0x171F}, // Tagalog + {begin: 0x10300, end: 0x1032F}, // Old Italic + {begin: 0x10330, end: 0x1034F}, // Gothic + {begin: 0x10400, end: 0x1044F}, // Deseret + {begin: 0x1D000, end: 0x1D0FF}, // Byzantine Musical Symbols + {begin: 0x1D400, end: 0x1D7FF}, // Mathematical Alphanumeric Symbols + {begin: 0xFF000, end: 0xFFFFD}, // Private Use (plane 15) + {begin: 0xFE00, end: 0xFE0F}, // Variation Selectors + {begin: 0xE0000, end: 0xE007F}, // Tags + {begin: 0x1900, end: 0x194F}, // Limbu + {begin: 0x1950, end: 0x197F}, // Tai Le + {begin: 0x1980, end: 0x19DF}, // New Tai Lue + {begin: 0x1A00, end: 0x1A1F}, // Buginese + {begin: 0x2C00, end: 0x2C5F}, // Glagolitic + {begin: 0x2D30, end: 0x2D7F}, // Tifinagh + {begin: 0x4DC0, end: 0x4DFF}, // Yijing Hexagram Symbols + {begin: 0xA800, end: 0xA82F}, // Syloti Nagri + {begin: 0x10000, end: 0x1007F}, // Linear B Syllabary + {begin: 0x10140, end: 0x1018F}, // Ancient Greek Numbers + {begin: 0x10380, end: 0x1039F}, // Ugaritic + {begin: 0x103A0, end: 0x103DF}, // Old Persian + {begin: 0x10450, end: 0x1047F}, // Shavian + {begin: 0x10480, end: 0x104AF}, // Osmanya + {begin: 0x10800, end: 0x1083F}, // Cypriot Syllabary + {begin: 0x10A00, end: 0x10A5F}, // Kharoshthi + {begin: 0x1D300, end: 0x1D35F}, // Tai Xuan Jing Symbols + {begin: 0x12000, end: 0x123FF}, // Cuneiform + {begin: 0x1D360, end: 0x1D37F}, // Counting Rod Numerals + {begin: 0x1B80, end: 0x1BBF}, // Sundanese + {begin: 0x1C00, end: 0x1C4F}, // Lepcha + {begin: 0x1C50, end: 0x1C7F}, // Ol Chiki + {begin: 0xA880, end: 0xA8DF}, // Saurashtra + {begin: 0xA900, end: 0xA92F}, // Kayah Li + {begin: 0xA930, end: 0xA95F}, // Rejang + {begin: 0xAA00, end: 0xAA5F}, // Cham + {begin: 0x10190, end: 0x101CF}, // Ancient Symbols + {begin: 0x101D0, end: 0x101FF}, // Phaistos Disc + {begin: 0x102A0, end: 0x102DF}, // Carian + {begin: 0x1F030, end: 0x1F09F} // Domino Tiles +]; + +function getUnicodeRange(unicode) { + for (var i = 0; i < unicodeRanges.length; i += 1) { + var range = unicodeRanges[i]; + if (unicode >= range.begin && unicode < range.end) { + return i; + } + } + + return -1; +} + +// Parse the OS/2 and Windows metrics `OS/2` table +function parseOS2Table(data, start) { + var os2 = {}; + var p = new parse.Parser(data, start); + os2.version = p.parseUShort(); + os2.xAvgCharWidth = p.parseShort(); + os2.usWeightClass = p.parseUShort(); + os2.usWidthClass = p.parseUShort(); + os2.fsType = p.parseUShort(); + os2.ySubscriptXSize = p.parseShort(); + os2.ySubscriptYSize = p.parseShort(); + os2.ySubscriptXOffset = p.parseShort(); + os2.ySubscriptYOffset = p.parseShort(); + os2.ySuperscriptXSize = p.parseShort(); + os2.ySuperscriptYSize = p.parseShort(); + os2.ySuperscriptXOffset = p.parseShort(); + os2.ySuperscriptYOffset = p.parseShort(); + os2.yStrikeoutSize = p.parseShort(); + os2.yStrikeoutPosition = p.parseShort(); + os2.sFamilyClass = p.parseShort(); + os2.panose = []; + for (var i = 0; i < 10; i++) { + os2.panose[i] = p.parseByte(); + } + + os2.ulUnicodeRange1 = p.parseULong(); + os2.ulUnicodeRange2 = p.parseULong(); + os2.ulUnicodeRange3 = p.parseULong(); + os2.ulUnicodeRange4 = p.parseULong(); + os2.achVendID = String.fromCharCode(p.parseByte(), p.parseByte(), p.parseByte(), p.parseByte()); + os2.fsSelection = p.parseUShort(); + os2.usFirstCharIndex = p.parseUShort(); + os2.usLastCharIndex = p.parseUShort(); + os2.sTypoAscender = p.parseShort(); + os2.sTypoDescender = p.parseShort(); + os2.sTypoLineGap = p.parseShort(); + os2.usWinAscent = p.parseUShort(); + os2.usWinDescent = p.parseUShort(); + if (os2.version >= 1) { + os2.ulCodePageRange1 = p.parseULong(); + os2.ulCodePageRange2 = p.parseULong(); + } + + if (os2.version >= 2) { + os2.sxHeight = p.parseShort(); + os2.sCapHeight = p.parseShort(); + os2.usDefaultChar = p.parseUShort(); + os2.usBreakChar = p.parseUShort(); + os2.usMaxContent = p.parseUShort(); + } + + return os2; +} + +function makeOS2Table(options) { + return new table.Table('OS/2', [ + {name: 'version', type: 'USHORT', value: 0x0003}, + {name: 'xAvgCharWidth', type: 'SHORT', value: 0}, + {name: 'usWeightClass', type: 'USHORT', value: 0}, + {name: 'usWidthClass', type: 'USHORT', value: 0}, + {name: 'fsType', type: 'USHORT', value: 0}, + {name: 'ySubscriptXSize', type: 'SHORT', value: 650}, + {name: 'ySubscriptYSize', type: 'SHORT', value: 699}, + {name: 'ySubscriptXOffset', type: 'SHORT', value: 0}, + {name: 'ySubscriptYOffset', type: 'SHORT', value: 140}, + {name: 'ySuperscriptXSize', type: 'SHORT', value: 650}, + {name: 'ySuperscriptYSize', type: 'SHORT', value: 699}, + {name: 'ySuperscriptXOffset', type: 'SHORT', value: 0}, + {name: 'ySuperscriptYOffset', type: 'SHORT', value: 479}, + {name: 'yStrikeoutSize', type: 'SHORT', value: 49}, + {name: 'yStrikeoutPosition', type: 'SHORT', value: 258}, + {name: 'sFamilyClass', type: 'SHORT', value: 0}, + {name: 'bFamilyType', type: 'BYTE', value: 0}, + {name: 'bSerifStyle', type: 'BYTE', value: 0}, + {name: 'bWeight', type: 'BYTE', value: 0}, + {name: 'bProportion', type: 'BYTE', value: 0}, + {name: 'bContrast', type: 'BYTE', value: 0}, + {name: 'bStrokeVariation', type: 'BYTE', value: 0}, + {name: 'bArmStyle', type: 'BYTE', value: 0}, + {name: 'bLetterform', type: 'BYTE', value: 0}, + {name: 'bMidline', type: 'BYTE', value: 0}, + {name: 'bXHeight', type: 'BYTE', value: 0}, + {name: 'ulUnicodeRange1', type: 'ULONG', value: 0}, + {name: 'ulUnicodeRange2', type: 'ULONG', value: 0}, + {name: 'ulUnicodeRange3', type: 'ULONG', value: 0}, + {name: 'ulUnicodeRange4', type: 'ULONG', value: 0}, + {name: 'achVendID', type: 'CHARARRAY', value: 'XXXX'}, + {name: 'fsSelection', type: 'USHORT', value: 0}, + {name: 'usFirstCharIndex', type: 'USHORT', value: 0}, + {name: 'usLastCharIndex', type: 'USHORT', value: 0}, + {name: 'sTypoAscender', type: 'SHORT', value: 0}, + {name: 'sTypoDescender', type: 'SHORT', value: 0}, + {name: 'sTypoLineGap', type: 'SHORT', value: 0}, + {name: 'usWinAscent', type: 'USHORT', value: 0}, + {name: 'usWinDescent', type: 'USHORT', value: 0}, + {name: 'ulCodePageRange1', type: 'ULONG', value: 0}, + {name: 'ulCodePageRange2', type: 'ULONG', value: 0}, + {name: 'sxHeight', type: 'SHORT', value: 0}, + {name: 'sCapHeight', type: 'SHORT', value: 0}, + {name: 'usDefaultChar', type: 'USHORT', value: 0}, + {name: 'usBreakChar', type: 'USHORT', value: 0}, + {name: 'usMaxContext', type: 'USHORT', value: 0} + ], options); +} + +exports.unicodeRanges = unicodeRanges; +exports.getUnicodeRange = getUnicodeRange; +exports.parse = parseOS2Table; +exports.make = makeOS2Table; + +},{"../parse":9,"../table":11}],24:[function(_dereq_,module,exports){ +// The `post` table stores additional PostScript information, such as glyph names. +// https://www.microsoft.com/typography/OTSPEC/post.htm + +'use strict'; + +var encoding = _dereq_('../encoding'); +var parse = _dereq_('../parse'); +var table = _dereq_('../table'); + +// Parse the PostScript `post` table +function parsePostTable(data, start) { + var post = {}; + var p = new parse.Parser(data, start); + var i; + post.version = p.parseVersion(); + post.italicAngle = p.parseFixed(); + post.underlinePosition = p.parseShort(); + post.underlineThickness = p.parseShort(); + post.isFixedPitch = p.parseULong(); + post.minMemType42 = p.parseULong(); + post.maxMemType42 = p.parseULong(); + post.minMemType1 = p.parseULong(); + post.maxMemType1 = p.parseULong(); + switch (post.version) { + case 1: + post.names = encoding.standardNames.slice(); + break; + case 2: + post.numberOfGlyphs = p.parseUShort(); + post.glyphNameIndex = new Array(post.numberOfGlyphs); + for (i = 0; i < post.numberOfGlyphs; i++) { + post.glyphNameIndex[i] = p.parseUShort(); + } + + post.names = []; + for (i = 0; i < post.numberOfGlyphs; i++) { + if (post.glyphNameIndex[i] >= encoding.standardNames.length) { + var nameLength = p.parseChar(); + post.names.push(p.parseString(nameLength)); + } + } + + break; + case 2.5: + post.numberOfGlyphs = p.parseUShort(); + post.offset = new Array(post.numberOfGlyphs); + for (i = 0; i < post.numberOfGlyphs; i++) { + post.offset[i] = p.parseChar(); + } + + break; + } + return post; +} + +function makePostTable() { + return new table.Table('post', [ + {name: 'version', type: 'FIXED', value: 0x00030000}, + {name: 'italicAngle', type: 'FIXED', value: 0}, + {name: 'underlinePosition', type: 'FWORD', value: 0}, + {name: 'underlineThickness', type: 'FWORD', value: 0}, + {name: 'isFixedPitch', type: 'ULONG', value: 0}, + {name: 'minMemType42', type: 'ULONG', value: 0}, + {name: 'maxMemType42', type: 'ULONG', value: 0}, + {name: 'minMemType1', type: 'ULONG', value: 0}, + {name: 'maxMemType1', type: 'ULONG', value: 0} + ]); +} + +exports.parse = parsePostTable; +exports.make = makePostTable; + +},{"../encoding":4,"../parse":9,"../table":11}],25:[function(_dereq_,module,exports){ +// The `sfnt` wrapper provides organization for the tables in the font. +// It is the top-level data structure in a font. +// https://www.microsoft.com/typography/OTSPEC/otff.htm +// Recommendations for creating OpenType Fonts: +// http://www.microsoft.com/typography/otspec140/recom.htm + +'use strict'; + +var check = _dereq_('../check'); +var table = _dereq_('../table'); + +var cmap = _dereq_('./cmap'); +var cff = _dereq_('./cff'); +var head = _dereq_('./head'); +var hhea = _dereq_('./hhea'); +var hmtx = _dereq_('./hmtx'); +var maxp = _dereq_('./maxp'); +var _name = _dereq_('./name'); +var os2 = _dereq_('./os2'); +var post = _dereq_('./post'); + +function log2(v) { + return Math.log(v) / Math.log(2) | 0; +} + +function computeCheckSum(bytes) { + while (bytes.length % 4 !== 0) { + bytes.push(0); + } + + var sum = 0; + for (var i = 0; i < bytes.length; i += 4) { + sum += (bytes[i] << 24) + + (bytes[i + 1] << 16) + + (bytes[i + 2] << 8) + + (bytes[i + 3]); + } + + sum %= Math.pow(2, 32); + return sum; +} + +function makeTableRecord(tag, checkSum, offset, length) { + return new table.Table('Table Record', [ + {name: 'tag', type: 'TAG', value: tag !== undefined ? tag : ''}, + {name: 'checkSum', type: 'ULONG', value: checkSum !== undefined ? checkSum : 0}, + {name: 'offset', type: 'ULONG', value: offset !== undefined ? offset : 0}, + {name: 'length', type: 'ULONG', value: length !== undefined ? length : 0} + ]); +} + +function makeSfntTable(tables) { + var sfnt = new table.Table('sfnt', [ + {name: 'version', type: 'TAG', value: 'OTTO'}, + {name: 'numTables', type: 'USHORT', value: 0}, + {name: 'searchRange', type: 'USHORT', value: 0}, + {name: 'entrySelector', type: 'USHORT', value: 0}, + {name: 'rangeShift', type: 'USHORT', value: 0} + ]); + sfnt.tables = tables; + sfnt.numTables = tables.length; + var highestPowerOf2 = Math.pow(2, log2(sfnt.numTables)); + sfnt.searchRange = 16 * highestPowerOf2; + sfnt.entrySelector = log2(highestPowerOf2); + sfnt.rangeShift = sfnt.numTables * 16 - sfnt.searchRange; + + var recordFields = []; + var tableFields = []; + + var offset = sfnt.sizeOf() + (makeTableRecord().sizeOf() * sfnt.numTables); + while (offset % 4 !== 0) { + offset += 1; + tableFields.push({name: 'padding', type: 'BYTE', value: 0}); + } + + for (var i = 0; i < tables.length; i += 1) { + var t = tables[i]; + check.argument(t.tableName.length === 4, 'Table name' + t.tableName + ' is invalid.'); + var tableLength = t.sizeOf(); + var tableRecord = makeTableRecord(t.tableName, computeCheckSum(t.encode()), offset, tableLength); + recordFields.push({name: tableRecord.tag + ' Table Record', type: 'TABLE', value: tableRecord}); + tableFields.push({name: t.tableName + ' table', type: 'TABLE', value: t}); + offset += tableLength; + check.argument(!isNaN(offset), 'Something went wrong calculating the offset.'); + while (offset % 4 !== 0) { + offset += 1; + tableFields.push({name: 'padding', type: 'BYTE', value: 0}); + } + } + + // Table records need to be sorted alphabetically. + recordFields.sort(function(r1, r2) { + if (r1.value.tag > r2.value.tag) { + return 1; + } else { + return -1; + } + }); + + sfnt.fields = sfnt.fields.concat(recordFields); + sfnt.fields = sfnt.fields.concat(tableFields); + return sfnt; +} + +// Get the metrics for a character. If the string has more than one character +// this function returns metrics for the first available character. +// You can provide optional fallback metrics if no characters are available. +function metricsForChar(font, chars, notFoundMetrics) { + for (var i = 0; i < chars.length; i += 1) { + var glyphIndex = font.charToGlyphIndex(chars[i]); + if (glyphIndex > 0) { + var glyph = font.glyphs.get(glyphIndex); + return glyph.getMetrics(); + } + } + + return notFoundMetrics; +} + +function average(vs) { + var sum = 0; + for (var i = 0; i < vs.length; i += 1) { + sum += vs[i]; + } + + return sum / vs.length; +} + +// Convert the font object to a SFNT data structure. +// This structure contains all the necessary tables and metadata to create a binary OTF file. +function fontToSfntTable(font) { + var xMins = []; + var yMins = []; + var xMaxs = []; + var yMaxs = []; + var advanceWidths = []; + var leftSideBearings = []; + var rightSideBearings = []; + var firstCharIndex; + var lastCharIndex = 0; + var ulUnicodeRange1 = 0; + var ulUnicodeRange2 = 0; + var ulUnicodeRange3 = 0; + var ulUnicodeRange4 = 0; + + for (var i = 0; i < font.glyphs.length; i += 1) { + var glyph = font.glyphs.get(i); + var unicode = glyph.unicode | 0; + if (firstCharIndex > unicode || firstCharIndex === null) { + firstCharIndex = unicode; + } + + if (lastCharIndex < unicode) { + lastCharIndex = unicode; + } + + var position = os2.getUnicodeRange(unicode); + if (position < 32) { + ulUnicodeRange1 |= 1 << position; + } else if (position < 64) { + ulUnicodeRange2 |= 1 << position - 32; + } else if (position < 96) { + ulUnicodeRange3 |= 1 << position - 64; + } else if (position < 123) { + ulUnicodeRange4 |= 1 << position - 96; + } else { + throw new Error('Unicode ranges bits > 123 are reserved for internal usage'); + } + // Skip non-important characters. + if (glyph.name === '.notdef') continue; + var metrics = glyph.getMetrics(); + xMins.push(metrics.xMin); + yMins.push(metrics.yMin); + xMaxs.push(metrics.xMax); + yMaxs.push(metrics.yMax); + leftSideBearings.push(metrics.leftSideBearing); + rightSideBearings.push(metrics.rightSideBearing); + advanceWidths.push(glyph.advanceWidth); + } + + var globals = { + xMin: Math.min.apply(null, xMins), + yMin: Math.min.apply(null, yMins), + xMax: Math.max.apply(null, xMaxs), + yMax: Math.max.apply(null, yMaxs), + advanceWidthMax: Math.max.apply(null, advanceWidths), + advanceWidthAvg: average(advanceWidths), + minLeftSideBearing: Math.min.apply(null, leftSideBearings), + maxLeftSideBearing: Math.max.apply(null, leftSideBearings), + minRightSideBearing: Math.min.apply(null, rightSideBearings) + }; + globals.ascender = font.ascender !== undefined ? font.ascender : globals.yMax; + globals.descender = font.descender !== undefined ? font.descender : globals.yMin; + + var headTable = head.make({ + unitsPerEm: font.unitsPerEm, + xMin: globals.xMin, + yMin: globals.yMin, + xMax: globals.xMax, + yMax: globals.yMax + }); + + var hheaTable = hhea.make({ + ascender: globals.ascender, + descender: globals.descender, + advanceWidthMax: globals.advanceWidthMax, + minLeftSideBearing: globals.minLeftSideBearing, + minRightSideBearing: globals.minRightSideBearing, + xMaxExtent: globals.maxLeftSideBearing + (globals.xMax - globals.xMin), + numberOfHMetrics: font.glyphs.length + }); + + var maxpTable = maxp.make(font.glyphs.length); + + var os2Table = os2.make({ + xAvgCharWidth: Math.round(globals.advanceWidthAvg), + usWeightClass: 500, // Medium FIXME Make this configurable + usWidthClass: 5, // Medium (normal) FIXME Make this configurable + usFirstCharIndex: firstCharIndex, + usLastCharIndex: lastCharIndex, + ulUnicodeRange1: ulUnicodeRange1, + ulUnicodeRange2: ulUnicodeRange2, + ulUnicodeRange3: ulUnicodeRange3, + ulUnicodeRange4: ulUnicodeRange4, + // See http://typophile.com/node/13081 for more info on vertical metrics. + // We get metrics for typical characters (such as "x" for xHeight). + // We provide some fallback characters if characters are unavailable: their + // ordering was chosen experimentally. + sTypoAscender: globals.ascender, + sTypoDescender: globals.descender, + sTypoLineGap: 0, + usWinAscent: globals.ascender, + usWinDescent: -globals.descender, + sxHeight: metricsForChar(font, 'xyvw', {yMax: 0}).yMax, + sCapHeight: metricsForChar(font, 'HIKLEFJMNTZBDPRAGOQSUVWXY', globals).yMax, + usBreakChar: font.hasChar(' ') ? 32 : 0 // Use space as the break character, if available. + }); + + var hmtxTable = hmtx.make(font.glyphs); + var cmapTable = cmap.make(font.glyphs); + + var fullName = font.familyName + ' ' + font.styleName; + var postScriptName = font.familyName.replace(/\s/g, '') + '-' + font.styleName; + var nameTable = _name.make({ + copyright: font.copyright, + fontFamily: font.familyName, + fontSubfamily: font.styleName, + uniqueID: font.manufacturer + ':' + fullName, + fullName: fullName, + version: font.version, + postScriptName: postScriptName, + trademark: font.trademark, + manufacturer: font.manufacturer, + designer: font.designer, + description: font.description, + manufacturerURL: font.manufacturerURL, + designerURL: font.designerURL, + license: font.license, + licenseURL: font.licenseURL, + preferredFamily: font.familyName, + preferredSubfamily: font.styleName + }); + var postTable = post.make(); + var cffTable = cff.make(font.glyphs, { + version: font.version, + fullName: fullName, + familyName: font.familyName, + weightName: font.styleName, + postScriptName: postScriptName, + unitsPerEm: font.unitsPerEm + }); + // Order the tables according to the the OpenType specification 1.4. + var tables = [headTable, hheaTable, maxpTable, os2Table, nameTable, cmapTable, postTable, cffTable, hmtxTable]; + + var sfntTable = makeSfntTable(tables); + + // Compute the font's checkSum and store it in head.checkSumAdjustment. + var bytes = sfntTable.encode(); + var checkSum = computeCheckSum(bytes); + var tableFields = sfntTable.fields; + var checkSumAdjusted = false; + for (i = 0; i < tableFields.length; i += 1) { + if (tableFields[i].name === 'head table') { + tableFields[i].value.checkSumAdjustment = 0xB1B0AFBA - checkSum; + checkSumAdjusted = true; + break; + } + } + + if (!checkSumAdjusted) { + throw new Error('Could not find head table with checkSum to adjust.'); + } + + return sfntTable; +} + +exports.computeCheckSum = computeCheckSum; +exports.make = makeSfntTable; +exports.fontToTable = fontToSfntTable; + +},{"../check":2,"../table":11,"./cff":12,"./cmap":13,"./head":16,"./hhea":17,"./hmtx":18,"./maxp":21,"./name":22,"./os2":23,"./post":24}],26:[function(_dereq_,module,exports){ +// Data types used in the OpenType font file. +// All OpenType fonts use Motorola-style byte ordering (Big Endian) + +/* global WeakMap */ + +'use strict'; + +var check = _dereq_('./check'); + +var LIMIT16 = 32768; // The limit at which a 16-bit number switches signs == 2^15 +var LIMIT32 = 2147483648; // The limit at which a 32-bit number switches signs == 2 ^ 31 + +var decode = {}; +var encode = {}; +var sizeOf = {}; + +// Return a function that always returns the same value. +function constant(v) { + return function() { + return v; + }; +} + +// OpenType data types ////////////////////////////////////////////////////// + +// Convert an 8-bit unsigned integer to a list of 1 byte. +encode.BYTE = function(v) { + check.argument(v >= 0 && v <= 255, 'Byte value should be between 0 and 255.'); + return [v]; +}; + +sizeOf.BYTE = constant(1); + +// Convert a 8-bit signed integer to a list of 1 byte. +encode.CHAR = function(v) { + return [v.charCodeAt(0)]; +}; + +sizeOf.BYTE = constant(1); + +// Convert an ASCII string to a list of bytes. +encode.CHARARRAY = function(v) { + var b = []; + for (var i = 0; i < v.length; i += 1) { + b.push(v.charCodeAt(i)); + } + + return b; +}; + +sizeOf.CHARARRAY = function(v) { + return v.length; +}; + +// Convert a 16-bit unsigned integer to a list of 2 bytes. +encode.USHORT = function(v) { + return [(v >> 8) & 0xFF, v & 0xFF]; +}; + +sizeOf.USHORT = constant(2); + +// Convert a 16-bit signed integer to a list of 2 bytes. +encode.SHORT = function(v) { + // Two's complement + if (v >= LIMIT16) { + v = -(2 * LIMIT16 - v); + } + + return [(v >> 8) & 0xFF, v & 0xFF]; +}; + +sizeOf.SHORT = constant(2); + +// Convert a 24-bit unsigned integer to a list of 3 bytes. +encode.UINT24 = function(v) { + return [(v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF]; +}; + +sizeOf.UINT24 = constant(3); + +// Convert a 32-bit unsigned integer to a list of 4 bytes. +encode.ULONG = function(v) { + return [(v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF]; +}; + +sizeOf.ULONG = constant(4); + +// Convert a 32-bit unsigned integer to a list of 4 bytes. +encode.LONG = function(v) { + // Two's complement + if (v >= LIMIT32) { + v = -(2 * LIMIT32 - v); + } + + return [(v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF]; +}; + +sizeOf.LONG = constant(4); + +encode.FIXED = encode.ULONG; +sizeOf.FIXED = sizeOf.ULONG; + +encode.FWORD = encode.SHORT; +sizeOf.FWORD = sizeOf.SHORT; + +encode.UFWORD = encode.USHORT; +sizeOf.UFWORD = sizeOf.USHORT; + +// FIXME Implement LONGDATETIME +encode.LONGDATETIME = function() { + return [0, 0, 0, 0, 0, 0, 0, 0]; +}; + +sizeOf.LONGDATETIME = constant(8); + +// Convert a 4-char tag to a list of 4 bytes. +encode.TAG = function(v) { + check.argument(v.length === 4, 'Tag should be exactly 4 ASCII characters.'); + return [v.charCodeAt(0), + v.charCodeAt(1), + v.charCodeAt(2), + v.charCodeAt(3)]; +}; + +sizeOf.TAG = constant(4); + +// CFF data types /////////////////////////////////////////////////////////// + +encode.Card8 = encode.BYTE; +sizeOf.Card8 = sizeOf.BYTE; + +encode.Card16 = encode.USHORT; +sizeOf.Card16 = sizeOf.USHORT; + +encode.OffSize = encode.BYTE; +sizeOf.OffSize = sizeOf.BYTE; + +encode.SID = encode.USHORT; +sizeOf.SID = sizeOf.USHORT; + +// Convert a numeric operand or charstring number to a variable-size list of bytes. +encode.NUMBER = function(v) { + if (v >= -107 && v <= 107) { + return [v + 139]; + } else if (v >= 108 && v <= 1131) { + v = v - 108; + return [(v >> 8) + 247, v & 0xFF]; + } else if (v >= -1131 && v <= -108) { + v = -v - 108; + return [(v >> 8) + 251, v & 0xFF]; + } else if (v >= -32768 && v <= 32767) { + return encode.NUMBER16(v); + } else { + return encode.NUMBER32(v); + } +}; + +sizeOf.NUMBER = function(v) { + return encode.NUMBER(v).length; +}; + +// Convert a signed number between -32768 and +32767 to a three-byte value. +// This ensures we always use three bytes, but is not the most compact format. +encode.NUMBER16 = function(v) { + return [28, (v >> 8) & 0xFF, v & 0xFF]; +}; + +sizeOf.NUMBER16 = constant(2); + +// Convert a signed number between -(2^31) and +(2^31-1) to a four-byte value. +// This is useful if you want to be sure you always use four bytes, +// at the expense of wasting a few bytes for smaller numbers. +encode.NUMBER32 = function(v) { + return [29, (v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF]; +}; + +sizeOf.NUMBER32 = constant(4); + +encode.REAL = function(v) { + var value = v.toString(); + + // Some numbers use an epsilon to encode the value. (e.g. JavaScript will store 0.0000001 as 1e-7) + // This code converts it back to a number without the epsilon. + var m = /\.(\d*?)(?:9{5,20}|0{5,20})\d{0,2}(?:e(.+)|$)/.exec(value); + if (m) { + var epsilon = parseFloat('1e' + ((m[2] ? +m[2] : 0) + m[1].length)); + value = (Math.round(v * epsilon) / epsilon).toString(); + } + + var nibbles = ''; + var i; + var ii; + for (i = 0, ii = value.length; i < ii; i += 1) { + var c = value[i]; + if (c === 'e') { + nibbles += value[++i] === '-' ? 'c' : 'b'; + } else if (c === '.') { + nibbles += 'a'; + } else if (c === '-') { + nibbles += 'e'; + } else { + nibbles += c; + } + } + + nibbles += (nibbles.length & 1) ? 'f' : 'ff'; + var out = [30]; + for (i = 0, ii = nibbles.length; i < ii; i += 2) { + out.push(parseInt(nibbles.substr(i, 2), 16)); + } + + return out; +}; + +sizeOf.REAL = function(v) { + return encode.REAL(v).length; +}; + +encode.NAME = encode.CHARARRAY; +sizeOf.NAME = sizeOf.CHARARRAY; + +encode.STRING = encode.CHARARRAY; +sizeOf.STRING = sizeOf.CHARARRAY; + +// Convert a ASCII string to a list of UTF16 bytes. +encode.UTF16 = function(v) { + var b = []; + for (var i = 0; i < v.length; i += 1) { + b.push(0); + b.push(v.charCodeAt(i)); + } + + return b; +}; + +sizeOf.UTF16 = function(v) { + return v.length * 2; +}; + +// Convert a list of values to a CFF INDEX structure. +// The values should be objects containing name / type / value. +encode.INDEX = function(l) { + var i; + //var offset, offsets, offsetEncoder, encodedOffsets, encodedOffset, data, + // dataSize, i, v; + // Because we have to know which data type to use to encode the offsets, + // we have to go through the values twice: once to encode the data and + // calculate the offets, then again to encode the offsets using the fitting data type. + var offset = 1; // First offset is always 1. + var offsets = [offset]; + var data = []; + var dataSize = 0; + for (i = 0; i < l.length; i += 1) { + var v = encode.OBJECT(l[i]); + Array.prototype.push.apply(data, v); + dataSize += v.length; + offset += v.length; + offsets.push(offset); + } + + if (data.length === 0) { + return [0, 0]; + } + + var encodedOffsets = []; + var offSize = (1 + Math.floor(Math.log(dataSize) / Math.log(2)) / 8) | 0; + var offsetEncoder = [undefined, encode.BYTE, encode.USHORT, encode.UINT24, encode.ULONG][offSize]; + for (i = 0; i < offsets.length; i += 1) { + var encodedOffset = offsetEncoder(offsets[i]); + Array.prototype.push.apply(encodedOffsets, encodedOffset); + } + + return Array.prototype.concat(encode.Card16(l.length), + encode.OffSize(offSize), + encodedOffsets, + data); +}; + +sizeOf.INDEX = function(v) { + return encode.INDEX(v).length; +}; + +// Convert an object to a CFF DICT structure. +// The keys should be numeric. +// The values should be objects containing name / type / value. +encode.DICT = function(m) { + var d = []; + var keys = Object.keys(m); + var length = keys.length; + + for (var i = 0; i < length; i += 1) { + // Object.keys() return string keys, but our keys are always numeric. + var k = parseInt(keys[i], 0); + var v = m[k]; + // Value comes before the key. + d = d.concat(encode.OPERAND(v.value, v.type)); + d = d.concat(encode.OPERATOR(k)); + } + + return d; +}; + +sizeOf.DICT = function(m) { + return encode.DICT(m).length; +}; + +encode.OPERATOR = function(v) { + if (v < 1200) { + return [v]; + } else { + return [12, v - 1200]; + } +}; + +encode.OPERAND = function(v, type) { + var d = []; + if (Array.isArray(type)) { + for (var i = 0; i < type.length; i += 1) { + check.argument(v.length === type.length, 'Not enough arguments given for type' + type); + d = d.concat(encode.OPERAND(v[i], type[i])); + } + } else { + if (type === 'SID') { + d = d.concat(encode.NUMBER(v)); + } else if (type === 'offset') { + // We make it easy for ourselves and always encode offsets as + // 4 bytes. This makes offset calculation for the top dict easier. + d = d.concat(encode.NUMBER32(v)); + } else if (type === 'number') { + d = d.concat(encode.NUMBER(v)); + } else if (type === 'real') { + d = d.concat(encode.REAL(v)); + } else { + throw new Error('Unknown operand type ' + type); + // FIXME Add support for booleans + } + } + + return d; +}; + +encode.OP = encode.BYTE; +sizeOf.OP = sizeOf.BYTE; + +// memoize charstring encoding using WeakMap if available +var wmm = typeof WeakMap === 'function' && new WeakMap(); +// Convert a list of CharString operations to bytes. +encode.CHARSTRING = function(ops) { + if (wmm && wmm.has(ops)) { + return wmm.get(ops); + } + + var d = []; + var length = ops.length; + + for (var i = 0; i < length; i += 1) { + var op = ops[i]; + d = d.concat(encode[op.type](op.value)); + } + + if (wmm) { + wmm.set(ops, d); + } + + return d; +}; + +sizeOf.CHARSTRING = function(ops) { + return encode.CHARSTRING(ops).length; +}; + +// Utility functions //////////////////////////////////////////////////////// + +// Convert an object containing name / type / value to bytes. +encode.OBJECT = function(v) { + var encodingFunction = encode[v.type]; + check.argument(encodingFunction !== undefined, 'No encoding function for type ' + v.type); + return encodingFunction(v.value); +}; + +// Convert a table object to bytes. +// A table contains a list of fields containing the metadata (name, type and default value). +// The table itself has the field values set as attributes. +encode.TABLE = function(table) { + var d = []; + var length = table.fields.length; + + for (var i = 0; i < length; i += 1) { + var field = table.fields[i]; + var encodingFunction = encode[field.type]; + check.argument(encodingFunction !== undefined, 'No encoding function for field type ' + field.type); + var value = table[field.name]; + if (value === undefined) { + value = field.value; + } + + var bytes = encodingFunction(value); + d = d.concat(bytes); + } + + return d; +}; + +// Merge in a list of bytes. +encode.LITERAL = function(v) { + return v; +}; + +sizeOf.LITERAL = function(v) { + return v.length; +}; + +exports.decode = decode; +exports.encode = encode; +exports.sizeOf = sizeOf; + +},{"./check":2}],27:[function(_dereq_,module,exports){ +/*! + * Reqwest! A general purpose XHR connection manager + * license MIT (c) Dustin Diaz 2014 + * https://github.com/ded/reqwest + */ + +!function (name, context, definition) { + if (typeof module != 'undefined' && module.exports) module.exports = definition() + else if (typeof define == 'function' && define.amd) define(definition) + else context[name] = definition() +}('reqwest', this, function () { + + var win = window + , doc = document + , httpsRe = /^http/ + , protocolRe = /(^\w+):\/\// + , twoHundo = /^(20\d|1223)$/ //http://stackoverflow.com/questions/10046972/msie-returns-status-code-of-1223-for-ajax-request + , byTag = 'getElementsByTagName' + , readyState = 'readyState' + , contentType = 'Content-Type' + , requestedWith = 'X-Requested-With' + , head = doc[byTag]('head')[0] + , uniqid = 0 + , callbackPrefix = 'reqwest_' + (+new Date()) + , lastValue // data stored by the most recent JSONP callback + , xmlHttpRequest = 'XMLHttpRequest' + , xDomainRequest = 'XDomainRequest' + , noop = function () {} + + , isArray = typeof Array.isArray == 'function' + ? Array.isArray + : function (a) { + return a instanceof Array + } + + , defaultHeaders = { + 'contentType': 'application/x-www-form-urlencoded' + , 'requestedWith': xmlHttpRequest + , 'accept': { + '*': 'text/javascript, text/html, application/xml, text/xml, */*' + , 'xml': 'application/xml, text/xml' + , 'html': 'text/html' + , 'text': 'text/plain' + , 'json': 'application/json, text/javascript' + , 'js': 'application/javascript, text/javascript' + } + } + + , xhr = function(o) { + // is it x-domain + if (o['crossOrigin'] === true) { + var xhr = win[xmlHttpRequest] ? new XMLHttpRequest() : null + if (xhr && 'withCredentials' in xhr) { + return xhr + } else if (win[xDomainRequest]) { + return new XDomainRequest() + } else { + throw new Error('Browser does not support cross-origin requests') + } + } else if (win[xmlHttpRequest]) { + return new XMLHttpRequest() + } else { + return new ActiveXObject('Microsoft.XMLHTTP') + } + } + , globalSetupOptions = { + dataFilter: function (data) { + return data + } + } + + function succeed(r) { + var protocol = protocolRe.exec(r.url); + protocol = (protocol && protocol[1]) || window.location.protocol; + return httpsRe.test(protocol) ? twoHundo.test(r.request.status) : !!r.request.response; + } + + function handleReadyState(r, success, error) { + return function () { + // use _aborted to mitigate against IE err c00c023f + // (can't read props on aborted request objects) + if (r._aborted) return error(r.request) + if (r._timedOut) return error(r.request, 'Request is aborted: timeout') + if (r.request && r.request[readyState] == 4) { + r.request.onreadystatechange = noop + if (succeed(r)) success(r.request) + else + error(r.request) + } + } + } + + function setHeaders(http, o) { + var headers = o['headers'] || {} + , h + + headers['Accept'] = headers['Accept'] + || defaultHeaders['accept'][o['type']] + || defaultHeaders['accept']['*'] + + var isAFormData = typeof FormData === 'function' && (o['data'] instanceof FormData); + // breaks cross-origin requests with legacy browsers + if (!o['crossOrigin'] && !headers[requestedWith]) headers[requestedWith] = defaultHeaders['requestedWith'] + if (!headers[contentType] && !isAFormData) headers[contentType] = o['contentType'] || defaultHeaders['contentType'] + for (h in headers) + headers.hasOwnProperty(h) && 'setRequestHeader' in http && http.setRequestHeader(h, headers[h]) + } + + function setCredentials(http, o) { + if (typeof o['withCredentials'] !== 'undefined' && typeof http.withCredentials !== 'undefined') { + http.withCredentials = !!o['withCredentials'] + } + } + + function generalCallback(data) { + lastValue = data + } + + function urlappend (url, s) { + return url + (/\?/.test(url) ? '&' : '?') + s + } + + function handleJsonp(o, fn, err, url) { + var reqId = uniqid++ + , cbkey = o['jsonpCallback'] || 'callback' // the 'callback' key + , cbval = o['jsonpCallbackName'] || reqwest.getcallbackPrefix(reqId) + , cbreg = new RegExp('((^|\\?|&)' + cbkey + ')=([^&]+)') + , match = url.match(cbreg) + , script = doc.createElement('script') + , loaded = 0 + , isIE10 = navigator.userAgent.indexOf('MSIE 10.0') !== -1 + + if (match) { + if (match[3] === '?') { + url = url.replace(cbreg, '$1=' + cbval) // wildcard callback func name + } else { + cbval = match[3] // provided callback func name + } + } else { + url = urlappend(url, cbkey + '=' + cbval) // no callback details, add 'em + } + + win[cbval] = generalCallback + + script.type = 'text/javascript' + script.src = url + script.async = true + if (typeof script.onreadystatechange !== 'undefined' && !isIE10) { + // need this for IE due to out-of-order onreadystatechange(), binding script + // execution to an event listener gives us control over when the script + // is executed. See http://jaubourg.net/2010/07/loading-script-as-onclick-handler-of.html + script.htmlFor = script.id = '_reqwest_' + reqId + } + + script.onload = script.onreadystatechange = function () { + if ((script[readyState] && script[readyState] !== 'complete' && script[readyState] !== 'loaded') || loaded) { + return false + } + script.onload = script.onreadystatechange = null + script.onclick && script.onclick() + // Call the user callback with the last value stored and clean up values and scripts. + fn(lastValue) + lastValue = undefined + head.removeChild(script) + loaded = 1 + } + + // Add the script to the DOM head + head.appendChild(script) + + // Enable JSONP timeout + return { + abort: function () { + script.onload = script.onreadystatechange = null + err({}, 'Request is aborted: timeout', {}) + lastValue = undefined + head.removeChild(script) + loaded = 1 + } + } + } + + function getRequest(fn, err) { + var o = this.o + , method = (o['method'] || 'GET').toUpperCase() + , url = typeof o === 'string' ? o : o['url'] + // convert non-string objects to query-string form unless o['processData'] is false + , data = (o['processData'] !== false && o['data'] && typeof o['data'] !== 'string') + ? reqwest.toQueryString(o['data']) + : (o['data'] || null) + , http + , sendWait = false + + // if we're working on a GET request and we have data then we should append + // query string to end of URL and not post data + if ((o['type'] == 'jsonp' || method == 'GET') && data) { + url = urlappend(url, data) + data = null + } + + if (o['type'] == 'jsonp') return handleJsonp(o, fn, err, url) + + // get the xhr from the factory if passed + // if the factory returns null, fall-back to ours + http = (o.xhr && o.xhr(o)) || xhr(o) + + http.open(method, url, o['async'] === false ? false : true) + setHeaders(http, o) + setCredentials(http, o) + if (win[xDomainRequest] && http instanceof win[xDomainRequest]) { + http.onload = fn + http.onerror = err + // NOTE: see + // http://social.msdn.microsoft.com/Forums/en-US/iewebdevelopment/thread/30ef3add-767c-4436-b8a9-f1ca19b4812e + http.onprogress = function() {} + sendWait = true + } else { + http.onreadystatechange = handleReadyState(this, fn, err) + } + o['before'] && o['before'](http) + if (sendWait) { + setTimeout(function () { + http.send(data) + }, 200) + } else { + http.send(data) + } + return http + } + + function Reqwest(o, fn) { + this.o = o + this.fn = fn + + init.apply(this, arguments) + } + + function setType(header) { + // json, javascript, text/plain, text/html, xml + if (header.match('json')) return 'json' + if (header.match('javascript')) return 'js' + if (header.match('text')) return 'html' + if (header.match('xml')) return 'xml' + } + + function init(o, fn) { + + this.url = typeof o == 'string' ? o : o['url'] + this.timeout = null + + // whether request has been fulfilled for purpose + // of tracking the Promises + this._fulfilled = false + // success handlers + this._successHandler = function(){} + this._fulfillmentHandlers = [] + // error handlers + this._errorHandlers = [] + // complete (both success and fail) handlers + this._completeHandlers = [] + this._erred = false + this._responseArgs = {} + + var self = this + + fn = fn || function () {} + + if (o['timeout']) { + this.timeout = setTimeout(function () { + timedOut() + }, o['timeout']) + } + + if (o['success']) { + this._successHandler = function () { + o['success'].apply(o, arguments) + } + } + + if (o['error']) { + this._errorHandlers.push(function () { + o['error'].apply(o, arguments) + }) + } + + if (o['complete']) { + this._completeHandlers.push(function () { + o['complete'].apply(o, arguments) + }) + } + + function complete (resp) { + o['timeout'] && clearTimeout(self.timeout) + self.timeout = null + while (self._completeHandlers.length > 0) { + self._completeHandlers.shift()(resp) + } + } + + function success (resp) { + var type = o['type'] || resp && setType(resp.getResponseHeader('Content-Type')) // resp can be undefined in IE + resp = (type !== 'jsonp') ? self.request : resp + // use global data filter on response text + var filteredResponse = globalSetupOptions.dataFilter(resp.responseText, type) + , r = filteredResponse + try { + resp.responseText = r + } catch (e) { + // can't assign this in IE<=8, just ignore + } + if (r) { + switch (type) { + case 'json': + try { + resp = win.JSON ? win.JSON.parse(r) : eval('(' + r + ')') + } catch (err) { + return error(resp, 'Could not parse JSON in response', err) + } + break + case 'js': + resp = eval(r) + break + case 'html': + resp = r + break + case 'xml': + resp = resp.responseXML + && resp.responseXML.parseError // IE trololo + && resp.responseXML.parseError.errorCode + && resp.responseXML.parseError.reason + ? null + : resp.responseXML + break + } + } + + self._responseArgs.resp = resp + self._fulfilled = true + fn(resp) + self._successHandler(resp) + while (self._fulfillmentHandlers.length > 0) { + resp = self._fulfillmentHandlers.shift()(resp) + } + + complete(resp) + } + + function timedOut() { + self._timedOut = true + self.request.abort() + } + + function error(resp, msg, t) { + resp = self.request + self._responseArgs.resp = resp + self._responseArgs.msg = msg + self._responseArgs.t = t + self._erred = true + while (self._errorHandlers.length > 0) { + self._errorHandlers.shift()(resp, msg, t) + } + complete(resp) + } + + this.request = getRequest.call(this, success, error) + } + + Reqwest.prototype = { + abort: function () { + this._aborted = true + this.request.abort() + } + + , retry: function () { + init.call(this, this.o, this.fn) + } + + /** + * Small deviation from the Promises A CommonJs specification + * http://wiki.commonjs.org/wiki/Promises/A + */ + + /** + * `then` will execute upon successful requests + */ + , then: function (success, fail) { + success = success || function () {} + fail = fail || function () {} + if (this._fulfilled) { + this._responseArgs.resp = success(this._responseArgs.resp) + } else if (this._erred) { + fail(this._responseArgs.resp, this._responseArgs.msg, this._responseArgs.t) + } else { + this._fulfillmentHandlers.push(success) + this._errorHandlers.push(fail) + } + return this + } + + /** + * `always` will execute whether the request succeeds or fails + */ + , always: function (fn) { + if (this._fulfilled || this._erred) { + fn(this._responseArgs.resp) + } else { + this._completeHandlers.push(fn) + } + return this + } + + /** + * `fail` will execute when the request fails + */ + , fail: function (fn) { + if (this._erred) { + fn(this._responseArgs.resp, this._responseArgs.msg, this._responseArgs.t) + } else { + this._errorHandlers.push(fn) + } + return this + } + , 'catch': function (fn) { + return this.fail(fn) + } + } + + function reqwest(o, fn) { + return new Reqwest(o, fn) + } + + // normalize newline variants according to spec -> CRLF + function normalize(s) { + return s ? s.replace(/\r?\n/g, '\r\n') : '' + } + + function serial(el, cb) { + var n = el.name + , t = el.tagName.toLowerCase() + , optCb = function (o) { + // IE gives value="" even where there is no value attribute + // 'specified' ref: http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-862529273 + if (o && !o['disabled']) + cb(n, normalize(o['attributes']['value'] && o['attributes']['value']['specified'] ? o['value'] : o['text'])) + } + , ch, ra, val, i + + // don't serialize elements that are disabled or without a name + if (el.disabled || !n) return + + switch (t) { + case 'input': + if (!/reset|button|image|file/i.test(el.type)) { + ch = /checkbox/i.test(el.type) + ra = /radio/i.test(el.type) + val = el.value + // WebKit gives us "" instead of "on" if a checkbox has no value, so correct it here + ;(!(ch || ra) || el.checked) && cb(n, normalize(ch && val === '' ? 'on' : val)) + } + break + case 'textarea': + cb(n, normalize(el.value)) + break + case 'select': + if (el.type.toLowerCase() === 'select-one') { + optCb(el.selectedIndex >= 0 ? el.options[el.selectedIndex] : null) + } else { + for (i = 0; el.length && i < el.length; i++) { + el.options[i].selected && optCb(el.options[i]) + } + } + break + } + } + + // collect up all form elements found from the passed argument elements all + // the way down to child elements; pass a '<form>' or form fields. + // called with 'this'=callback to use for serial() on each element + function eachFormElement() { + var cb = this + , e, i + , serializeSubtags = function (e, tags) { + var i, j, fa + for (i = 0; i < tags.length; i++) { + fa = e[byTag](tags[i]) + for (j = 0; j < fa.length; j++) serial(fa[j], cb) + } + } + + for (i = 0; i < arguments.length; i++) { + e = arguments[i] + if (/input|select|textarea/i.test(e.tagName)) serial(e, cb) + serializeSubtags(e, [ 'input', 'select', 'textarea' ]) + } + } + + // standard query string style serialization + function serializeQueryString() { + return reqwest.toQueryString(reqwest.serializeArray.apply(null, arguments)) + } + + // { 'name': 'value', ... } style serialization + function serializeHash() { + var hash = {} + eachFormElement.apply(function (name, value) { + if (name in hash) { + hash[name] && !isArray(hash[name]) && (hash[name] = [hash[name]]) + hash[name].push(value) + } else hash[name] = value + }, arguments) + return hash + } + + // [ { name: 'name', value: 'value' }, ... ] style serialization + reqwest.serializeArray = function () { + var arr = [] + eachFormElement.apply(function (name, value) { + arr.push({name: name, value: value}) + }, arguments) + return arr + } + + reqwest.serialize = function () { + if (arguments.length === 0) return '' + var opt, fn + , args = Array.prototype.slice.call(arguments, 0) + + opt = args.pop() + opt && opt.nodeType && args.push(opt) && (opt = null) + opt && (opt = opt.type) + + if (opt == 'map') fn = serializeHash + else if (opt == 'array') fn = reqwest.serializeArray + else fn = serializeQueryString + + return fn.apply(null, args) + } + + reqwest.toQueryString = function (o, trad) { + var prefix, i + , traditional = trad || false + , s = [] + , enc = encodeURIComponent + , add = function (key, value) { + // If value is a function, invoke it and return its value + value = ('function' === typeof value) ? value() : (value == null ? '' : value) + s[s.length] = enc(key) + '=' + enc(value) + } + // If an array was passed in, assume that it is an array of form elements. + if (isArray(o)) { + for (i = 0; o && i < o.length; i++) add(o[i]['name'], o[i]['value']) + } else { + // If traditional, encode the "old" way (the way 1.3.2 or older + // did it), otherwise encode params recursively. + for (prefix in o) { + if (o.hasOwnProperty(prefix)) buildParams(prefix, o[prefix], traditional, add) + } + } + + // spaces should be + according to spec + return s.join('&').replace(/%20/g, '+') + } + + function buildParams(prefix, obj, traditional, add) { + var name, i, v + , rbracket = /\[\]$/ + + if (isArray(obj)) { + // Serialize array item. + for (i = 0; obj && i < obj.length; i++) { + v = obj[i] + if (traditional || rbracket.test(prefix)) { + // Treat each array item as a scalar. + add(prefix, v) + } else { + buildParams(prefix + '[' + (typeof v === 'object' ? i : '') + ']', v, traditional, add) + } + } + } else if (obj && obj.toString() === '[object Object]') { + // Serialize object item. + for (name in obj) { + buildParams(prefix + '[' + name + ']', obj[name], traditional, add) + } + + } else { + // Serialize scalar item. + add(prefix, obj) + } + } + + reqwest.getcallbackPrefix = function () { + return callbackPrefix + } + + // jQuery and Zepto compatibility, differences can be remapped here so you can call + // .ajax.compat(options, callback) + reqwest.compat = function (o, fn) { + if (o) { + o['type'] && (o['method'] = o['type']) && delete o['type'] + o['dataType'] && (o['type'] = o['dataType']) + o['jsonpCallback'] && (o['jsonpCallbackName'] = o['jsonpCallback']) && delete o['jsonpCallback'] + o['jsonp'] && (o['jsonpCallback'] = o['jsonp']) + } + return new Reqwest(o, fn) + } + + reqwest.ajaxSetup = function (options) { + options = options || {} + for (var k in options) { + globalSetupOptions[k] = options[k] + } + } + + return reqwest +}); + +},{}],28:[function(_dereq_,module,exports){ + +'use strict'; + +var p5 = _dereq_('./core/core'); +_dereq_('./color/p5.Color'); +_dereq_('./core/p5.Element'); +_dereq_('./typography/p5.Font'); +_dereq_('./core/p5.Graphics'); +_dereq_('./core/p5.Renderer2D'); + +_dereq_('./image/p5.Image'); +_dereq_('./math/p5.Vector'); +_dereq_('./io/p5.TableRow'); +_dereq_('./io/p5.Table'); +_dereq_('./io/p5.XML'); + +_dereq_('./color/creating_reading'); +_dereq_('./color/setting'); +_dereq_('./core/constants'); +_dereq_('./utilities/conversion'); +_dereq_('./utilities/array_functions'); +_dereq_('./utilities/string_functions'); +_dereq_('./core/environment'); +_dereq_('./image/image'); +_dereq_('./image/loading_displaying'); +_dereq_('./image/pixels'); +_dereq_('./io/files'); +_dereq_('./events/keyboard'); +_dereq_('./events/acceleration'); //john +_dereq_('./events/mouse'); +_dereq_('./utilities/time_date'); +_dereq_('./events/touch'); +_dereq_('./math/math'); +_dereq_('./math/calculation'); +_dereq_('./math/random'); +_dereq_('./math/noise'); +_dereq_('./math/trigonometry'); +_dereq_('./core/rendering'); +_dereq_('./core/2d_primitives'); + +_dereq_('./core/attributes'); +_dereq_('./core/curves'); +_dereq_('./core/vertex'); +_dereq_('./core/structure'); +_dereq_('./core/transform'); +_dereq_('./typography/attributes'); +_dereq_('./typography/loading_displaying'); + +_dereq_('./webgl/p5.RendererGL'); +_dereq_('./webgl/p5.Geometry'); +_dereq_('./webgl/p5.RendererGL.Retained'); +_dereq_('./webgl/p5.RendererGL.Immediate'); +_dereq_('./webgl/primitives'); +_dereq_('./webgl/loading'); +_dereq_('./webgl/p5.Matrix'); +_dereq_('./webgl/material'); +_dereq_('./webgl/light'); +_dereq_('./webgl/shader'); +_dereq_('./webgl/camera'); +_dereq_('./webgl/interaction'); + +/** + * _globalInit + * + * TODO: ??? + * if sketch is on window + * assume "global" mode + * and instantiate p5 automatically + * otherwise do nothing + * + * @return {Undefined} + */ +var _globalInit = function() { + if (!window.PHANTOMJS && !window.mocha) { + // If there is a setup or draw function on the window + // then instantiate p5 in "global" mode + if(((window.setup && typeof window.setup === 'function') || + (window.draw && typeof window.draw === 'function')) && + !p5.instance) { + new p5(); + } + } +}; + +// TODO: ??? +if (document.readyState === 'complete') { + _globalInit(); +} else { + window.addEventListener('load', _globalInit , false); +} + +module.exports = p5; + +},{"./color/creating_reading":30,"./color/p5.Color":31,"./color/setting":32,"./core/2d_primitives":33,"./core/attributes":34,"./core/constants":36,"./core/core":37,"./core/curves":38,"./core/environment":39,"./core/p5.Element":41,"./core/p5.Graphics":42,"./core/p5.Renderer2D":44,"./core/rendering":45,"./core/structure":47,"./core/transform":48,"./core/vertex":49,"./events/acceleration":50,"./events/keyboard":51,"./events/mouse":52,"./events/touch":53,"./image/image":55,"./image/loading_displaying":56,"./image/p5.Image":57,"./image/pixels":58,"./io/files":59,"./io/p5.Table":60,"./io/p5.TableRow":61,"./io/p5.XML":62,"./math/calculation":63,"./math/math":64,"./math/noise":65,"./math/p5.Vector":66,"./math/random":68,"./math/trigonometry":69,"./typography/attributes":70,"./typography/loading_displaying":71,"./typography/p5.Font":72,"./utilities/array_functions":73,"./utilities/conversion":74,"./utilities/string_functions":75,"./utilities/time_date":76,"./webgl/camera":77,"./webgl/interaction":78,"./webgl/light":79,"./webgl/loading":80,"./webgl/material":81,"./webgl/p5.Geometry":82,"./webgl/p5.Matrix":83,"./webgl/p5.RendererGL":86,"./webgl/p5.RendererGL.Immediate":84,"./webgl/p5.RendererGL.Retained":85,"./webgl/primitives":87,"./webgl/shader":88}],29:[function(_dereq_,module,exports){ +/** + * module Conversion + * submodule Color Conversion + * @for p5 + * @requires core + */ + +'use strict'; + +/** + * Conversions adapted from <http://www.easyrgb.com/math.html>. + * + * In these functions, hue is always in the range [0,1); all other components + * are in the range [0,1]. 'Brightness' and 'value' are used interchangeably. + */ + +var p5 = _dereq_('../core/core'); +p5.ColorConversion = {}; + +/** + * Convert an HSBA array to HSLA. + */ +p5.ColorConversion._hsbaToHSLA = function(hsba) { + var hue = hsba[0]; + var sat = hsba[1]; + var val = hsba[2]; + + // Calculate lightness. + var li = (2 - sat) * val / 2; + + // Convert saturation. + if (li !== 0) { + if (li === 1) { + sat = 0; + } else if (li < 0.5) { + sat = sat / (2 - sat); + } else { + sat = sat * val / (2 - li * 2); + } + } + + // Hue and alpha stay the same. + return [hue, sat, li, hsba[3]]; +}; + +/** + * Convert an HSBA array to RGBA. + */ +p5.ColorConversion._hsbaToRGBA = function(hsba) { + var hue = hsba[0] * 6; // We will split hue into 6 sectors. + var sat = hsba[1]; + var val = hsba[2]; + + var RGBA = []; + + if (sat === 0) { + RGBA = [val, val, val, hsba[3]]; // Return early if grayscale. + } else { + var sector = Math.floor(hue); + var tint1 = val * (1 - sat); + var tint2 = val * (1 - sat * (hue - sector)); + var tint3 = val * (1 - sat * (1 + sector - hue)); + var red, green, blue; + if (sector === 1) { // Yellow to green. + red = tint2; + green = val; + blue = tint1; + } else if (sector === 2) { // Green to cyan. + red = tint1; + green = val; + blue = tint3; + } else if (sector === 3) { // Cyan to blue. + red = tint1; + green = tint2; + blue = val; + } else if (sector === 4) { // Blue to magenta. + red = tint3; + green = tint1; + blue = val; + } else if (sector === 5) { // Magenta to red. + red = val; + green = tint1; + blue = tint2; + } else { // Red to yellow (sector could be 0 or 6). + red = val; + green = tint3; + blue = tint1; + } + RGBA = [red, green, blue, hsba[3]]; + } + + return RGBA; +}; + +/** + * Convert an HSLA array to HSBA. + */ +p5.ColorConversion._hslaToHSBA = function(hsla) { + var hue = hsla[0]; + var sat = hsla[1]; + var li = hsla[2]; + + // Calculate brightness. + var val; + if (li < 0.5) { + val = (1 + sat) * li; + } else { + val = li + sat - li * sat; + } + + // Convert saturation. + sat = 2 * (val - li) / val; + + // Hue and alpha stay the same. + return [hue, sat, val, hsla[3]]; +}; + +/** + * Convert an HSLA array to RGBA. + * + * We need to change basis from HSLA to something that can be more easily be + * projected onto RGBA. We will choose hue and brightness as our first two + * components, and pick a convenient third one ('zest') so that we don't need + * to calculate formal HSBA saturation. + */ +p5.ColorConversion._hslaToRGBA = function(hsla){ + var hue = hsla[0] * 6; // We will split hue into 6 sectors. + var sat = hsla[1]; + var li = hsla[2]; + + var RGBA = []; + + if (sat === 0) { + RGBA = [li, li, li, hsla[3]]; // Return early if grayscale. + } else { + + // Calculate brightness. + var val; + if (li < 0.5) { + val = (1 + sat) * li; + } else { + val = li + sat - li * sat; + } + + // Define zest. + var zest = 2 * li - val; + + // Implement projection (project onto green by default). + var hzvToRGB = function(hue, zest, val) { + if (hue < 0) { // Hue must wrap to allow projection onto red and blue. + hue += 6; + } else if (hue >= 6) { + hue -= 6; + } + if (hue < 1) { // Red to yellow (increasing green). + return (zest + (val - zest) * hue); + } else if (hue < 3) { // Yellow to cyan (greatest green). + return val; + } else if (hue < 4) { // Cyan to blue (decreasing green). + return (zest + (val - zest) * (4 - hue)); + } else { // Blue to red (least green). + return zest; + } + }; + + // Perform projections, offsetting hue as necessary. + RGBA = [hzvToRGB(hue + 2, zest, val), + hzvToRGB(hue , zest, val), + hzvToRGB(hue - 2, zest, val), + hsla[3]]; + } + + return RGBA; +}; + +/** + * Convert an RGBA array to HSBA. + */ +p5.ColorConversion._rgbaToHSBA = function(rgba) { + var red = rgba[0]; + var green = rgba[1]; + var blue = rgba[2]; + + var val = Math.max(red, green, blue); + var chroma = val - Math.min(red, green, blue); + + var hue, sat; + if (chroma === 0) { // Return early if grayscale. + hue = 0; + sat = 0; + } + else { + sat = chroma / val; + if (red === val) { // Magenta to yellow. + hue = (green - blue) / chroma; + } else if (green === val) { // Yellow to cyan. + hue = 2 + (blue - red) / chroma; + } else if (blue === val) { // Cyan to magenta. + hue = 4 + (red - green) / chroma; + } + if (hue < 0) { // Confine hue to the interval [0, 1). + hue += 6; + } else if (hue >= 6) { + hue -= 6; + } + } + + return [hue / 6, sat, val, rgba[3]]; +}; + +/** + * Convert an RGBA array to HSLA. + */ +p5.ColorConversion._rgbaToHSLA = function(rgba) { + var red = rgba[0]; + var green = rgba[1]; + var blue = rgba[2]; + + var val = Math.max(red, green, blue); + var min = Math.min(red, green, blue); + var li = val + min; // We will halve this later. + var chroma = val - min; + + var hue, sat; + if (chroma === 0) { // Return early if grayscale. + hue = 0; + sat = 0; + } else { + if (li < 1) { + sat = chroma / li; + } else { + sat = chroma / (2 - chroma); + } + if (red === val) { // Magenta to yellow. + hue = (green - blue) / chroma; + } else if (green === val) { // Yellow to cyan. + hue = 2 + (blue - red) / chroma; + } else if (blue === val) { // Cyan to magenta. + hue = 4 + (red - green) / chroma; + } + if (hue < 0) { // Confine hue to the interval [0, 1). + hue += 6; + } else if (hue >= 6) { + hue -= 6; + } + } + + return [hue / 6, sat, li / 2, rgba[3]]; +}; + +module.exports = p5.ColorConversion; + +},{"../core/core":37}],30:[function(_dereq_,module,exports){ +/** + * @module Color + * @submodule Creating & Reading + * @for p5 + * @requires core + * @requires constants + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); +var constants = _dereq_('../core/constants'); +_dereq_('./p5.Color'); + +/** + * Extracts the alpha value from a color or pixel array. + * + * @method alpha + * @param {Object} obj p5.Color object or pixel array + * @example + * <div> + * <code> + * noStroke(); + * c = color(0, 126, 255, 102); + * fill(c); + * rect(15, 15, 35, 70); + * value = alpha(c); // Sets 'value' to 102 + * fill(value); + * rect(50, 15, 35, 70); + * </code> + * </div> + * + * @alt + * Left half of canvas light blue and right half light charcoal grey. + * Left half of canvas light purple and right half a royal blue. + * Left half of canvas salmon pink and the right half white. + * Yellow rect in middle right of canvas, with 55 pixel width and height. + * Yellow ellipse in top left canvas, black ellipse in bottom right,both 80x80. + * Bright fuschia rect in middle of canvas, 60 pixel width and height. + * Two bright green rects on opposite sides of the canvas, both 45x80. + * Four blue rects in each corner of the canvas, each are 35x35. + * Bright sea green rect on left and darker rect on right of canvas, both 45x80. + * Dark green rect on left and light green rect on right of canvas, both 45x80. + * Dark blue rect on left and light teal rect on right of canvas, both 45x80. + * blue rect on left and green on right, both with black outlines & 35x60. + * salmon pink rect on left and black on right, both 35x60. + * 4 rects, tan, brown, brownish purple and purple, with white outlines & 20x60. + * light pastel green rect on left and dark grey rect on right, both 35x60. + * yellow rect on left and red rect on right, both with black outlines & 35x60. + * grey canvas + * deep pink rect on left and grey rect on right, both 35x60. + */ +p5.prototype.alpha = function(c) { + if (c instanceof p5.Color || c instanceof Array) { + return this.color(c)._getAlpha(); + } else { + throw new Error('Needs p5.Color or pixel array as argument.'); + } +}; + +/** + * Extracts the blue value from a color or pixel array. + * + * @method blue + * @param {Object} obj p5.Color object or pixel array + * @example + * <div> + * <code> + * c = color(175, 100, 220); // Define color 'c' + * fill(c); // Use color variable 'c' as fill color + * rect(15, 20, 35, 60); // Draw left rectangle + * + * blueValue = blue(c); // Get blue in 'c' + * print(blueValue); // Prints "220.0" + * fill(0, 0, blueValue); // Use 'blueValue' in new fill + * rect(50, 20, 35, 60); // Draw right rectangle + * </code> + * </div> + * + * @alt + * Left half of canvas light purple and right half a royal blue. + * + */ +p5.prototype.blue = function(c) { + if (c instanceof p5.Color || c instanceof Array) { + return this.color(c)._getBlue(); + } else { + throw new Error('Needs p5.Color or pixel array as argument.'); + } +}; + +/** + * Extracts the HSB brightness value from a color or pixel array. + * + * @method brightness + * @param {Object} color p5.Color object or pixel array + * @example + * <div> + * <code> + * noStroke(); + * colorMode(HSB, 255); + * c = color(0, 126, 255); + * fill(c); + * rect(15, 20, 35, 60); + * value = brightness(c); // Sets 'value' to 255 + * fill(value); + * rect(50, 20, 35, 60); + * </code> + * </div> + * + * @alt + * Left half of canvas salmon pink and the right half white. + * + */ +p5.prototype.brightness = function(c) { + if (c instanceof p5.Color || c instanceof Array) { + return this.color(c)._getBrightness(); + } else { + throw new Error('Needs p5.Color or pixel array as argument.'); + } +}; + +/** + * Creates colors for storing in variables of the color datatype. The + * parameters are interpreted as RGB or HSB values depending on the + * current colorMode(). The default mode is RGB values from 0 to 255 + * and, therefore, the function call color(255, 204, 0) will return a + * bright yellow color. + * <br><br> + * Note that if only one value is provided to color(), it will be interpreted + * as a grayscale value. Add a second value, and it will be used for alpha + * transparency. When three values are specified, they are interpreted as + * either RGB or HSB values. Adding a fourth value applies alpha + * transparency. If a single string parameter is provided it will be + * interpreted as a CSS-compatible color string. + * + * Colors are stored as Numbers or Arrays. + * + * @method color + * @param {Number|String} gray number specifying value between white + * and black. + * @param {Number} [alpha] alpha value relative to current color range + * (default is 0-100) + * @return {Array} resulting color + * + * @example + * <div> + * <code> + * var c = color(255, 204, 0); // Define color 'c' + * fill(c); // Use color variable 'c' as fill color + * noStroke(); // Don't draw a stroke around shapes + * rect(30, 20, 55, 55); // Draw rectangle + * </code> + * </div> + * + * <div> + * <code> + * var c = color(255, 204, 0); // Define color 'c' + * fill(c); // Use color variable 'c' as fill color + * noStroke(); // Don't draw a stroke around shapes + * ellipse(25, 25, 80, 80); // Draw left circle + * + * // Using only one value with color() + * // generates a grayscale value. + * var c = color(65); // Update 'c' with grayscale value + * fill(c); // Use updated 'c' as fill color + * ellipse(75, 75, 80, 80); // Draw right circle + * </code> + * </div> + * + * <div> + * <code> + * // Named SVG & CSS colors may be used, + * var c = color('magenta'); + * fill(c); // Use 'c' as fill color + * noStroke(); // Don't draw a stroke around shapes + * rect(20, 20, 60, 60); // Draw rectangle + * </code> + * </div> + * + * <div> + * <code> + * // as can hex color codes: + * noStroke(); // Don't draw a stroke around shapes + * var c = color('#0f0'); + * fill(c); // Use 'c' as fill color + * rect(0, 10, 45, 80); // Draw rectangle + * + * c = color('#00ff00'); + * fill(c); // Use updated 'c' as fill color + * rect(55, 10, 45, 80); // Draw rectangle + * </code> + * </div> + * + * <div> + * <code> + * // RGB and RGBA color strings are also supported: + * // these all set to the same color (solid blue) + * var c; + * noStroke(); // Don't draw a stroke around shapes + * c = color('rgb(0,0,255)'); + * fill(c); // Use 'c' as fill color + * rect(10, 10, 35, 35); // Draw rectangle + * + * c = color('rgb(0%, 0%, 100%)'); + * fill(c); // Use updated 'c' as fill color + * rect(55, 10, 35, 35); // Draw rectangle + * + * c = color('rgba(0, 0, 255, 1)'); + * fill(c); // Use updated 'c' as fill color + * rect(10, 55, 35, 35); // Draw rectangle + * + * c = color('rgba(0%, 0%, 100%, 1)'); + * fill(c); // Use updated 'c' as fill color + * rect(55, 55, 35, 35); // Draw rectangle + * </code> + * </div> + * + * <div> + * <code> + * // HSL color is also supported and can be specified + * // by value + * var c; + * noStroke(); // Don't draw a stroke around shapes + * c = color('hsl(160, 100%, 50%)'); + * fill(c); // Use 'c' as fill color + * rect(0, 10, 45, 80); // Draw rectangle + * + * c = color('hsla(160, 100%, 50%, 0.5)'); + * fill(c); // Use updated 'c' as fill color + * rect(55, 10, 45, 80); // Draw rectangle + * </code> + * </div> + * + * <div> + * <code> + * // HSB color is also supported and can be specified + * // by value + * var c; + * noStroke(); // Don't draw a stroke around shapes + * c = color('hsb(160, 100%, 50%)'); + * fill(c); // Use 'c' as fill color + * rect(0, 10, 45, 80); // Draw rectangle + * + * c = color('hsba(160, 100%, 50%, 0.5)'); + * fill(c); // Use updated 'c' as fill color + * rect(55, 10, 45, 80); // Draw rectangle + * </code> + * </div> + * + * <div> + * <code> + * var c; // Declare color 'c' + * noStroke(); // Don't draw a stroke around shapes + * + * // If no colorMode is specified, then the + * // default of RGB with scale of 0-255 is used. + * c = color(50, 55, 100); // Create a color for 'c' + * fill(c); // Use color variable 'c' as fill color + * rect(0, 10, 45, 80); // Draw left rect + * + * colorMode(HSB, 100); // Use HSB with scale of 0-100 + * c = color(50, 55, 100); // Update 'c' with new color + * fill(c); // Use updated 'c' as fill color + * rect(55, 10, 45, 80); // Draw right rect + * </code> + * </div> + * + * @alt + * Yellow rect in middle right of canvas, with 55 pixel width and height. + * Yellow ellipse in top left of canvas, black ellipse in bottom right,both 80x80. + * Bright fuschia rect in middle of canvas, 60 pixel width and height. + * Two bright green rects on opposite sides of the canvas, both 45x80. + * Four blue rects in each corner of the canvas, each are 35x35. + * Bright sea green rect on left and darker rect on right of canvas, both 45x80. + * Dark green rect on left and lighter green rect on right of canvas, both 45x80. + * Dark blue rect on left and light teal rect on right of canvas, both 45x80. + * + */ + +/** + * @method color + * @param {Number|String} v1 red or hue value relative to + * the current color range, or a color string + * @param {Number} v2 green or saturation value + * relative to the current color range + * @param {Number} v3 blue or brightness value + * relative to the current color range + * @param {Number} [alpha] + */ + +p5.prototype.color = function() { + if (arguments[0] instanceof p5.Color) { + return arguments[0]; // Do nothing if argument is already a color object. + } else if (arguments[0] instanceof Array) { + if (this instanceof p5.Renderer) { + return new p5.Color(this, arguments[0]); + } else { + return new p5.Color(this._renderer, arguments[0]); + } + } else { + if (this instanceof p5.Renderer) { + return new p5.Color(this, arguments); + } else { + return new p5.Color(this._renderer, arguments); + } + } +}; + +/** + * Extracts the green value from a color or pixel array. + * + * @method green + * @param {Object} color p5.Color object or pixel array + * @example + * <div> + * <code> + * c = color(20, 75, 200); // Define color 'c' + * fill(c); // Use color variable 'c' as fill color + * rect(15, 20, 35, 60); // Draw left rectangle + * + * greenValue = green(c); // Get green in 'c' + * print(greenValue); // Print "75.0" + * fill(0, greenValue, 0); // Use 'greenValue' in new fill + * rect(50, 20, 35, 60); // Draw right rectangle + * </code> + * </div> + * + * @alt + * blue rect on left and green on right, both with black outlines & 35x60. + * + */ + +p5.prototype.green = function(c) { + if (c instanceof p5.Color || c instanceof Array) { + return this.color(c)._getGreen(); + } else { + throw new Error('Needs p5.Color or pixel array as argument.'); + } +}; + +/** + * Extracts the hue value from a color or pixel array. + * + * Hue exists in both HSB and HSL. This function will return the + * HSB-normalized hue when supplied with an HSB color object (or when supplied + * with a pixel array while the color mode is HSB), but will default to the + * HSL-normalized hue otherwise. (The values will only be different if the + * maximum hue setting for each system is different.) + * + * @method hue + * @param {Object} color p5.Color object or pixel array + * @example + * <div> + * <code> + * noStroke(); + * colorMode(HSB, 255); + * c = color(0, 126, 255); + * fill(c); + * rect(15, 20, 35, 60); + * value = hue(c); // Sets 'value' to "0" + * fill(value); + * rect(50, 20, 35, 60); + * </code> + * </div> + * + * @alt + * salmon pink rect on left and black on right, both 35x60. + * + */ + +p5.prototype.hue = function(c) { + if (c instanceof p5.Color || c instanceof Array) { + return this.color(c)._getHue(); + } else { + throw new Error('Needs p5.Color or pixel array as argument.'); + } +}; + +/** + * Blends two colors to find a third color somewhere between them. The amt + * parameter is the amount to interpolate between the two values where 0.0 + * equal to the first color, 0.1 is very near the first color, 0.5 is halfway + * in between, etc. An amount below 0 will be treated as 0. Likewise, amounts + * above 1 will be capped at 1. This is different from the behavior of lerp(), + * but necessary because otherwise numbers outside the range will produce + * strange and unexpected colors. + * <br><br> + * The way that colours are interpolated depends on the current color mode. + * + * @method lerpColor + * @param {Array/Number} c1 interpolate from this color + * @param {Array/Number} c2 interpolate to this color + * @param {Number} amt number between 0 and 1 + * @return {Array/Number} interpolated color + * @example + * <div> + * <code> + * colorMode(RGB); + * stroke(255); + * background(51); + * from = color(218, 165, 32); + * to = color(72, 61, 139); + * colorMode(RGB); // Try changing to HSB. + * interA = lerpColor(from, to, .33); + * interB = lerpColor(from, to, .66); + * fill(from); + * rect(10, 20, 20, 60); + * fill(interA); + * rect(30, 20, 20, 60); + * fill(interB); + * rect(50, 20, 20, 60); + * fill(to); + * rect(70, 20, 20, 60); + * </code> + * </div> + * + * @alt + * 4 rects one tan, brown, brownish purple, purple, with white outlines & 20x60 + * + */ + +p5.prototype.lerpColor = function(c1, c2, amt) { + var mode = this._renderer._colorMode; + var maxes = this._renderer._colorMaxes; + var l0, l1, l2, l3; + var fromArray, toArray; + + if (mode === constants.RGB) { + fromArray = c1.levels.map(function(level) { + return level / 255; + }); + toArray = c2.levels.map(function(level) { + return level / 255; + }); + } else if (mode === constants.HSB) { + c1._getBrightness(); // Cache hsba so it definitely exists. + c2._getBrightness(); + fromArray = c1.hsba; + toArray = c2.hsba; + } else if (mode === constants.HSL) { + c1._getLightness(); // Cache hsla so it definitely exists. + c2._getLightness(); + fromArray = c1.hsla; + toArray = c2.hsla; + } else { + throw new Error (mode + 'cannot be used for interpolation.'); + } + + // Prevent extrapolation. + amt = Math.max(Math.min(amt, 1), 0); + + // Perform interpolation. + l0 = this.lerp(fromArray[0], toArray[0], amt); + l1 = this.lerp(fromArray[1], toArray[1], amt); + l2 = this.lerp(fromArray[2], toArray[2], amt); + l3 = this.lerp(fromArray[3], toArray[3], amt); + + // Scale components. + l0 *= maxes[mode][0]; + l1 *= maxes[mode][1]; + l2 *= maxes[mode][2]; + l3 *= maxes[mode][3]; + + return this.color(l0, l1, l2, l3); +}; + +/** + * Extracts the HSL lightness value from a color or pixel array. + * + * @method lightness + * @param {Object} color p5.Color object or pixel array + * @example + * <div> + * <code> + * noStroke(); + * colorMode(HSL); + * c = color(156, 100, 50, 1); + * fill(c); + * rect(15, 20, 35, 60); + * value = lightness(c); // Sets 'value' to 50 + * fill(value); + * rect(50, 20, 35, 60); + * </code> + * </div> + * + * @alt + * light pastel green rect on left and dark grey rect on right, both 35x60. + * + */ +p5.prototype.lightness = function(c) { + if (c instanceof p5.Color || c instanceof Array) { + return this.color(c)._getLightness(); + } else { + throw new Error('Needs p5.Color or pixel array as argument.'); + } +}; + +/** + * Extracts the red value from a color or pixel array. + * + * @method red + * @param {Object} obj p5.Color object or pixel array + * @example + * <div> + * <code> + * c = color(255, 204, 0); // Define color 'c' + * fill(c); // Use color variable 'c' as fill color + * rect(15, 20, 35, 60); // Draw left rectangle + * + * redValue = red(c); // Get red in 'c' + * print(redValue); // Print "255.0" + * fill(redValue, 0, 0); // Use 'redValue' in new fill + * rect(50, 20, 35, 60); // Draw right rectangle + * </code> + * </div> + * + * <div> + * <code> + * colorMode(RGB, 255); + * var c = color(127, 255, 0); + * colorMode(RGB, 1); + * var myColor = red(c); + * print(myColor); + * </code> + * </div> + * + * @alt + * yellow rect on left and red rect on right, both with black outlines and 35x60. + * grey canvas + */ +p5.prototype.red = function(c) { + if (c instanceof p5.Color || c instanceof Array) { + return this.color(c)._getRed(); + } else { + throw new Error('Needs p5.Color or pixel array as argument.'); + } +}; + +/** + * Extracts the saturation value from a color or pixel array. + * + * Saturation is scaled differently in HSB and HSL. This function will return + * the HSB saturation when supplied with an HSB color object (or when supplied + * with a pixel array while the color mode is HSB), but will default to the + * HSL saturation otherwise. + * + * @method saturation + * @param {Object} color p5.Color object or pixel array + * @example + * <div> + * <code> + * noStroke(); + * colorMode(HSB, 255); + * c = color(0, 126, 255); + * fill(c); + * rect(15, 20, 35, 60); + * value = saturation(c); // Sets 'value' to 126 + * fill(value); + * rect(50, 20, 35, 60); + * </code> + * </div> + * + * @alt + *deep pink rect on left and grey rect on right, both 35x60. + * + */ + +p5.prototype.saturation = function(c) { + if (c instanceof p5.Color || c instanceof Array) { + return this.color(c)._getSaturation(); + } else { + throw new Error('Needs p5.Color or pixel array as argument.'); + } +}; + +module.exports = p5; + +},{"../core/constants":36,"../core/core":37,"./p5.Color":31}],31:[function(_dereq_,module,exports){ +/** + * @module Color + * @submodule Creating & Reading + * @for p5 + * @requires core + * @requires constants + * @requires color_conversion + */ + +var p5 = _dereq_('../core/core'); +var constants = _dereq_('../core/constants'); +var color_conversion = _dereq_('./color_conversion'); + +/** + * We define colors to be immutable objects. Each color stores the color mode + * and level maxes that applied at the time of its construction. These are + * used to interpret the input arguments and to format the output e.g. when + * saturation() is requested. + * + * Internally we store an array representing the ideal RGBA values in floating + * point form, normalized from 0 to 1. From this we calculate the closest + * screen color (RGBA levels from 0 to 255) and expose this to the renderer. + * + * We also cache normalized, floating point components of the color in various + * representations as they are calculated. This is done to prevent repeating a + * conversion that has already been performed. + * + * @class p5.Color + * @constructor + */ +p5.Color = function(renderer, vals) { + + // Record color mode and maxes at time of construction. + this.mode = renderer._colorMode; + this.maxes = renderer._colorMaxes; + + // Calculate normalized RGBA values. + if (this.mode !== constants.RGB && + this.mode !== constants.HSL && + this.mode !== constants.HSB) { + throw new Error(this.mode + ' is an invalid colorMode.'); + } else { + this._array = p5.Color._parseInputs.apply(renderer, vals); + } + + // Expose closest screen color. + this.levels = this._array.map(function(level) { + return Math.round(level * 255); + }); + + return this; +}; + +p5.Color.prototype.toString = function() { + var a = this.levels; + var alpha = this._array[3]; // String representation uses normalized alpha. + return 'rgba('+a[0]+','+a[1]+','+a[2]+','+ alpha +')'; +}; + +p5.Color.prototype._getAlpha = function() { + return this._array[3] * this.maxes[this.mode][3]; +}; + +p5.Color.prototype._getBlue = function() { + return this._array[2] * this.maxes[constants.RGB][2]; +}; + +p5.Color.prototype._getBrightness = function() { + if (!this.hsba) { + this.hsba = color_conversion._rgbaToHSBA(this._array); + } + return this.hsba[2] * this.maxes[constants.HSB][2]; +}; + +p5.Color.prototype._getGreen = function() { + return this._array[1] * this.maxes[constants.RGB][1]; +}; + +/** + * Hue is the same in HSB and HSL, but the maximum value may be different. + * This function will return the HSB-normalized saturation when supplied with + * an HSB color object, but will default to the HSL-normalized saturation + * otherwise. + */ +p5.Color.prototype._getHue = function() { + if (this.mode === constants.HSB) { + if (!this.hsba) { + this.hsba = color_conversion._rgbaToHSBA(this._array); + } + return this.hsba[0] * this.maxes[constants.HSB][0]; + } else { + if (!this.hsla) { + this.hsla = color_conversion._rgbaToHSLA(this._array); + } + return this.hsla[0] * this.maxes[constants.HSL][0]; + } +}; + +p5.Color.prototype._getLightness = function() { + if (!this.hsla) { + this.hsla = color_conversion._rgbaToHSLA(this._array); + } + return this.hsla[2] * this.maxes[constants.HSL][2]; +}; + +p5.Color.prototype._getRed = function() { + return this._array[0] * this.maxes[constants.RGB][0]; +}; + +/** + * Saturation is scaled differently in HSB and HSL. This function will return + * the HSB saturation when supplied with an HSB color object, but will default + * to the HSL saturation otherwise. + */ +p5.Color.prototype._getSaturation = function() { + if (this.mode === constants.HSB) { + if (!this.hsba) { + this.hsba = color_conversion._rgbaToHSBA(this._array); + } + return this.hsba[1] * this.maxes[constants.HSB][1]; + } else { + if (!this.hsla) { + this.hsla = color_conversion._rgbaToHSLA(this._array); + } + return this.hsla[1] * this.maxes[constants.HSL][1]; + } +}; + +/** + * CSS named colors. + */ +var namedColors = { + aliceblue: '#f0f8ff', + antiquewhite: '#faebd7', + aqua: '#00ffff', + aquamarine: '#7fffd4', + azure: '#f0ffff', + beige: '#f5f5dc', + bisque: '#ffe4c4', + black: '#000000', + blanchedalmond: '#ffebcd', + blue: '#0000ff', + blueviolet: '#8a2be2', + brown: '#a52a2a', + burlywood: '#deb887', + cadetblue: '#5f9ea0', + chartreuse: '#7fff00', + chocolate: '#d2691e', + coral: '#ff7f50', + cornflowerblue: '#6495ed', + cornsilk: '#fff8dc', + crimson: '#dc143c', + cyan: '#00ffff', + darkblue: '#00008b', + darkcyan: '#008b8b', + darkgoldenrod: '#b8860b', + darkgray: '#a9a9a9', + darkgreen: '#006400', + darkgrey: '#a9a9a9', + darkkhaki: '#bdb76b', + darkmagenta: '#8b008b', + darkolivegreen: '#556b2f', + darkorange: '#ff8c00', + darkorchid: '#9932cc', + darkred: '#8b0000', + darksalmon: '#e9967a', + darkseagreen: '#8fbc8f', + darkslateblue: '#483d8b', + darkslategray: '#2f4f4f', + darkslategrey: '#2f4f4f', + darkturquoise: '#00ced1', + darkviolet: '#9400d3', + deeppink: '#ff1493', + deepskyblue: '#00bfff', + dimgray: '#696969', + dimgrey: '#696969', + dodgerblue: '#1e90ff', + firebrick: '#b22222', + floralwhite: '#fffaf0', + forestgreen: '#228b22', + fuchsia: '#ff00ff', + gainsboro: '#dcdcdc', + ghostwhite: '#f8f8ff', + gold: '#ffd700', + goldenrod: '#daa520', + gray: '#808080', + green: '#008000', + greenyellow: '#adff2f', + grey: '#808080', + honeydew: '#f0fff0', + hotpink: '#ff69b4', + indianred: '#cd5c5c', + indigo: '#4b0082', + ivory: '#fffff0', + khaki: '#f0e68c', + lavender: '#e6e6fa', + lavenderblush: '#fff0f5', + lawngreen: '#7cfc00', + lemonchiffon: '#fffacd', + lightblue: '#add8e6', + lightcoral: '#f08080', + lightcyan: '#e0ffff', + lightgoldenrodyellow: '#fafad2', + lightgray: '#d3d3d3', + lightgreen: '#90ee90', + lightgrey: '#d3d3d3', + lightpink: '#ffb6c1', + lightsalmon: '#ffa07a', + lightseagreen: '#20b2aa', + lightskyblue: '#87cefa', + lightslategray: '#778899', + lightslategrey: '#778899', + lightsteelblue: '#b0c4de', + lightyellow: '#ffffe0', + lime: '#00ff00', + limegreen: '#32cd32', + linen: '#faf0e6', + magenta: '#ff00ff', + maroon: '#800000', + mediumaquamarine: '#66cdaa', + mediumblue: '#0000cd', + mediumorchid: '#ba55d3', + mediumpurple: '#9370db', + mediumseagreen: '#3cb371', + mediumslateblue: '#7b68ee', + mediumspringgreen: '#00fa9a', + mediumturquoise: '#48d1cc', + mediumvioletred: '#c71585', + midnightblue: '#191970', + mintcream: '#f5fffa', + mistyrose: '#ffe4e1', + moccasin: '#ffe4b5', + navajowhite: '#ffdead', + navy: '#000080', + oldlace: '#fdf5e6', + olive: '#808000', + olivedrab: '#6b8e23', + orange: '#ffa500', + orangered: '#ff4500', + orchid: '#da70d6', + palegoldenrod: '#eee8aa', + palegreen: '#98fb98', + paleturquoise: '#afeeee', + palevioletred: '#db7093', + papayawhip: '#ffefd5', + peachpuff: '#ffdab9', + peru: '#cd853f', + pink: '#ffc0cb', + plum: '#dda0dd', + powderblue: '#b0e0e6', + purple: '#800080', + red: '#ff0000', + rosybrown: '#bc8f8f', + royalblue: '#4169e1', + saddlebrown: '#8b4513', + salmon: '#fa8072', + sandybrown: '#f4a460', + seagreen: '#2e8b57', + seashell: '#fff5ee', + sienna: '#a0522d', + silver: '#c0c0c0', + skyblue: '#87ceeb', + slateblue: '#6a5acd', + slategray: '#708090', + slategrey: '#708090', + snow: '#fffafa', + springgreen: '#00ff7f', + steelblue: '#4682b4', + tan: '#d2b48c', + teal: '#008080', + thistle: '#d8bfd8', + tomato: '#ff6347', + turquoise: '#40e0d0', + violet: '#ee82ee', + wheat: '#f5deb3', + white: '#ffffff', + whitesmoke: '#f5f5f5', + yellow: '#ffff00', + yellowgreen: '#9acd32' +}; + +/** + * These regular expressions are used to build up the patterns for matching + * viable CSS color strings: fragmenting the regexes in this way increases the + * legibility and comprehensibility of the code. + * + * Note that RGB values of .9 are not parsed by IE, but are supported here for + * color string consistency. + */ +var WHITESPACE = /\s*/; // Match zero or more whitespace characters. +var INTEGER = /(\d{1,3})/; // Match integers: 79, 255, etc. +var DECIMAL = /((?:\d+(?:\.\d+)?)|(?:\.\d+))/; // Match 129.6, 79, .9, etc. +var PERCENT = new RegExp(DECIMAL.source + '%'); // Match 12.9%, 79%, .9%, etc. + +/** + * Full color string patterns. The capture groups are necessary. + */ +var colorPatterns = { + // Match colors in format #XXX, e.g. #416. + HEX3: /^#([a-f0-9])([a-f0-9])([a-f0-9])$/i, + + // Match colors in format #XXXXXX, e.g. #b4d455. + HEX6: /^#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})$/i, + + // Match colors in format rgb(R, G, B), e.g. rgb(255, 0, 128). + RGB: new RegExp([ + '^rgb\\(', + INTEGER.source, + ',', + INTEGER.source, + ',', + INTEGER.source, + '\\)$' + ].join(WHITESPACE.source), 'i'), + + // Match colors in format rgb(R%, G%, B%), e.g. rgb(100%, 0%, 28.9%). + RGB_PERCENT: new RegExp([ + '^rgb\\(', + PERCENT.source, + ',', + PERCENT.source, + ',', + PERCENT.source, + '\\)$' + ].join(WHITESPACE.source), 'i'), + + // Match colors in format rgb(R, G, B, A), e.g. rgb(255, 0, 128, 0.25). + RGBA: new RegExp([ + '^rgba\\(', + INTEGER.source, + ',', + INTEGER.source, + ',', + INTEGER.source, + ',', + DECIMAL.source, + '\\)$' + ].join(WHITESPACE.source), 'i'), + + // Match colors in format rgb(R%, G%, B%, A), e.g. rgb(100%, 0%, 28.9%, 0.5). + RGBA_PERCENT: new RegExp([ + '^rgba\\(', + PERCENT.source, + ',', + PERCENT.source, + ',', + PERCENT.source, + ',', + DECIMAL.source, + '\\)$' + ].join(WHITESPACE.source), 'i'), + + // Match colors in format hsla(H, S%, L%), e.g. hsl(100, 40%, 28.9%). + HSL: new RegExp([ + '^hsl\\(', + INTEGER.source, + ',', + PERCENT.source, + ',', + PERCENT.source, + '\\)$' + ].join(WHITESPACE.source), 'i'), + + // Match colors in format hsla(H, S%, L%, A), e.g. hsla(100, 40%, 28.9%, 0.5). + HSLA: new RegExp([ + '^hsla\\(', + INTEGER.source, + ',', + PERCENT.source, + ',', + PERCENT.source, + ',', + DECIMAL.source, + '\\)$' + ].join(WHITESPACE.source), 'i'), + + // Match colors in format hsb(H, S%, B%), e.g. hsb(100, 40%, 28.9%). + HSB: new RegExp([ + '^hsb\\(', + INTEGER.source, + ',', + PERCENT.source, + ',', + PERCENT.source, + '\\)$' + ].join(WHITESPACE.source), 'i'), + + // Match colors in format hsba(H, S%, B%, A), e.g. hsba(100, 40%, 28.9%, 0.5). + HSBA: new RegExp([ + '^hsba\\(', + INTEGER.source, + ',', + PERCENT.source, + ',', + PERCENT.source, + ',', + DECIMAL.source, + '\\)$' + ].join(WHITESPACE.source), 'i') +}; + +/** + * For a number of different inputs, returns a color formatted as [r, g, b, a] + * arrays, with each component normalized between 0 and 1. + * + * @param {Array-like} args An 'array-like' object that represents a list of + * arguments + * @return {Array} a color formatted as [r, g, b, a] + * Example: + * input ==> output + * g ==> [g, g, g, 255] + * g,a ==> [g, g, g, a] + * r, g, b ==> [r, g, b, 255] + * r, g, b, a ==> [r, g, b, a] + * [g] ==> [g, g, g, 255] + * [g, a] ==> [g, g, g, a] + * [r, g, b] ==> [r, g, b, 255] + * [r, g, b, a] ==> [r, g, b, a] + * @example + * <div> + * <code> + * // todo + * </code> + * </div> + * + * @alt + * //todo + * + */ +p5.Color._parseInputs = function() { + var numArgs = arguments.length; + var mode = this._colorMode; + var maxes = this._colorMaxes; + var results = []; + + if (numArgs >= 3) { // Argument is a list of component values. + + results[0] = arguments[0] / maxes[mode][0]; + results[1] = arguments[1] / maxes[mode][1]; + results[2] = arguments[2] / maxes[mode][2]; + + // Alpha may be undefined, so default it to 100%. + if (typeof arguments[3] === 'number') { + results[3] = arguments[3] / maxes[mode][3]; + } else { + results[3] = 1; + } + + // Constrain components to the range [0,1]. + results = results.map(function(value) { + return Math.max(Math.min(value, 1), 0); + }); + + // Convert to RGBA and return. + if (mode === constants.HSL) { + return color_conversion._hslaToRGBA(results); + } else if (mode === constants.HSB) { + return color_conversion._hsbaToRGBA(results); + } else { + return results; + } + + } else if (numArgs === 1 && typeof arguments[0] === 'string') { + + var str = arguments[0].trim().toLowerCase(); + + // Return if string is a named colour. + if (namedColors[str]) { + return p5.Color._parseInputs.apply(this, [namedColors[str]]); + } + + // Try RGBA pattern matching. + if (colorPatterns.HEX3.test(str)) { // #rgb + results = colorPatterns.HEX3.exec(str).slice(1).map(function(color) { + return parseInt(color + color, 16) / 255; + }); + results[3] = 1; + return results; + } else if (colorPatterns.HEX6.test(str)) { // #rrggbb + results = colorPatterns.HEX6.exec(str).slice(1).map(function(color) { + return parseInt(color, 16) / 255; + }); + results[3] = 1; + return results; + } else if (colorPatterns.RGB.test(str)) { // rgb(R,G,B) + results = colorPatterns.RGB.exec(str).slice(1).map(function(color) { + return color / 255; + }); + results[3] = 1; + return results; + } else if (colorPatterns.RGB_PERCENT.test(str)) { // rgb(R%,G%,B%) + results = colorPatterns.RGB_PERCENT.exec(str).slice(1) + .map(function(color) { + return parseFloat(color) / 100; + }); + results[3] = 1; + return results; + } else if (colorPatterns.RGBA.test(str)) { // rgba(R,G,B,A) + results = colorPatterns.RGBA.exec(str).slice(1) + .map(function(color, idx) { + if (idx === 3) { + return parseFloat(color); + } + return color / 255; + }); + return results; + } else if (colorPatterns.RGBA_PERCENT.test(str)) { // rgba(R%,G%,B%,A%) + results = colorPatterns.RGBA_PERCENT.exec(str).slice(1) + .map(function(color, idx) { + if (idx === 3) { + return parseFloat(color); + } + return parseFloat(color) / 100; + }); + return results; + } + + // Try HSLA pattern matching. + if (colorPatterns.HSL.test(str)) { // hsl(H,S,L) + results = colorPatterns.HSL.exec(str).slice(1) + .map(function(color, idx) { + if (idx === 0) { + return parseInt(color, 10) / 360; + } + return parseInt(color, 10) / 100; + }); + results[3] = 1; + } else if (colorPatterns.HSLA.test(str)) { // hsla(H,S,L,A) + results = colorPatterns.HSLA.exec(str).slice(1) + .map(function(color, idx) { + if (idx === 0) { + return parseInt(color, 10) / 360; + } + else if (idx === 3) { + return parseFloat(color); + } + return parseInt(color, 10) / 100; + }); + } + if (results.length) { + return color_conversion._hslaToRGBA(results); + } + + // Try HSBA pattern matching. + if (colorPatterns.HSB.test(str)) { // hsb(H,S,B) + results = colorPatterns.HSB.exec(str).slice(1) + .map(function(color, idx) { + if (idx === 0) { + return parseInt(color, 10) / 360; + } + return parseInt(color, 10) / 100; + }); + results[3] = 1; + } else if (colorPatterns.HSBA.test(str)) { // hsba(H,S,B,A) + results = colorPatterns.HSBA.exec(str).slice(1) + .map(function(color, idx) { + if (idx === 0) { + return parseInt(color, 10) / 360; + } + else if (idx === 3) { + return parseFloat(color); + } + return parseInt(color, 10) / 100; + }); + } + if (results.length) { + return color_conversion._hsbaToRGBA(results); + } + + // Input did not match any CSS color pattern: default to white. + results = [1, 1, 1, 1]; + + } else if ((numArgs === 1 || numArgs === 2) && + typeof arguments[0] === 'number') { // 'Grayscale' mode. + + /** + * For HSB and HSL, interpret the gray level as a brightness/lightness + * value (they are equivalent when chroma is zero). For RGB, normalize the + * gray level according to the blue maximum. + */ + results[0] = arguments[0] / maxes[mode][2]; + results[1] = arguments[0] / maxes[mode][2]; + results[2] = arguments[0] / maxes[mode][2]; + + // Alpha may be undefined, so default it to 100%. + if (typeof arguments[1] === 'number') { + results[3] = arguments[1] / maxes[mode][3]; + } else { + results[3] = 1; + } + + // Constrain components to the range [0,1]. + results = results.map(function(value) { + return Math.max(Math.min(value, 1), 0); + }); + + } else { + throw new Error (arguments + 'is not a valid color representation.'); + } + + return results; +}; + +module.exports = p5.Color; + +},{"../core/constants":36,"../core/core":37,"./color_conversion":29}],32:[function(_dereq_,module,exports){ +/** + * @module Color + * @submodule Setting + * @for p5 + * @requires core + * @requires constants + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); +var constants = _dereq_('../core/constants'); +_dereq_('./p5.Color'); + +/** + * The background() function sets the color used for the background of the + * p5.js canvas. The default background is light gray. This function is + * typically used within draw() to clear the display window at the beginning + * of each frame, but it can be used inside setup() to set the background on + * the first frame of animation or if the background need only be set once. + * + * @method background + * @param {p5.Color} color any value created by the color() function + * @param {Number} [a] opacity of the background relative to current + * color range (default is 0-100) + * + * @example + * <div> + * <code> + * // Grayscale integer value + * background(51); + * </code> + * </div> + * + * <div> + * <code> + * // R, G & B integer values + * background(255, 204, 0); + * </code> + * </div> + * + * <div> + * <code> + * // H, S & B integer values + * colorMode(HSB); + * background(255, 204, 100); + * </code> + * </div> + * + * <div> + * <code> + * // Named SVG/CSS color string + * background('red'); + * </code> + * </div> + * + * <div> + * <code> + * // three-digit hexadecimal RGB notation + * background('#fae'); + * </code> + * </div> + * + * <div> + * <code> + * // six-digit hexadecimal RGB notation + * background('#222222'); + * </code> + * </div> + * + * <div> + * <code> + * // integer RGB notation + * background('rgb(0,255,0)'); + * </code> + * </div> + * + * <div> + * <code> + * // integer RGBA notation + * background('rgba(0,255,0, 0.25)'); + * </code> + * </div> + * + * <div> + * <code> + * // percentage RGB notation + * background('rgb(100%,0%,10%)'); + * </code> + * </div> + * + * <div> + * <code> + * // percentage RGBA notation + * background('rgba(100%,0%,100%,0.5)'); + * </code> + * </div> + * + * <div> + * <code> + * // p5 Color object + * background(color(0, 0, 255)); + * </code> + * </div> + * + * @alt + * canvas with darkest charcoal grey background. + * canvas with yellow background. + * canvas with royal blue background. + * canvas with red background. + * canvas with pink background. + * canvas with black background. + * canvas with bright green background. + * canvas with soft green background. + * canvas with red background. + * canvas with light purple background. + * canvas with blue background. + */ + +/** + * @method background + * @param {String} colorstring color string, possible formats include: integer + * rgb() or rgba(), percentage rgb() or rgba(), + * 3-digit hex, 6-digit hex + * @param {Number} [a] + */ + +/** + * @method background + * @param {Number} gray specifies a value between white and black + * @param {Number} [a] + */ + +/** + * @method background + * @param {Number} v1 red or hue value (depending on the current color + * mode) + * @param {Number} v2 green or saturation value (depending on the current + * color mode) + * @param {Number} v3 blue or brightness value (depending on the current + * color mode) + * @param {Number} [a] + */ + +/** + * @method background + * @param {p5.Image} image image created with loadImage() or createImage(), + * to set as background + * (must be same size as the sketch window) + * @param {Number} [a] + */ +p5.prototype.background = function() { + if (arguments[0] instanceof p5.Image) { + this.image(arguments[0], 0, 0, this.width, this.height); + } else { + this._renderer.background.apply(this._renderer, arguments); + } + return this; +}; + +/** + * Clears the pixels within a buffer. This function only works on p5.Canvas + * objects created with the createCanvas() function; it won't work with the + * main display window. Unlike the main graphics context, pixels in + * additional graphics areas created with createGraphics() can be entirely + * or partially transparent. This function clears everything to make all of + * the pixels 100% transparent. + * + * @method clear + * @example + * <div> + * <code> + * // Clear the screen on mouse press. + * function setup() { + * createCanvas(100, 100); + * } + * + * function draw() { + * ellipse(mouseX, mouseY, 20, 20); + * } + * + * function mousePressed() { + * clear(); + * } + * </code> + * </div> + * + * @alt + * 20x20 white ellipses are continually drawn at mouse x and y coordinates. + * + */ + +p5.prototype.clear = function() { + this._renderer.clear(); + return this; +}; + +/** + * colorMode() changes the way p5.js interprets color data. By default, the + * parameters for fill(), stroke(), background(), and color() are defined by + * values between 0 and 255 using the RGB color model. This is equivalent to + * setting colorMode(RGB, 255). Setting colorMode(HSB) lets you use the HSB + * system instead. By default, this is colorMode(HSB, 360, 100, 100, 1). You + * can also use HSL. + * <br><br> + * Note: existing color objects remember the mode that they were created in, + * so you can change modes as you like without affecting their appearance. + * + * @method colorMode + * @param {Constant} mode either RGB or HSB, corresponding to + * Red/Green/Blue and Hue/Saturation/Brightness + * (or Lightness) + * @param {Number} [max1] range for the red or hue depending on the + * current color mode, or range for all values + * @param {Number} [max2] range for the green or saturation depending + * on the current color mode + * @param {Number} [max3] range for the blue or brightness/lighntess + * depending on the current color mode + * @param {Number} [maxA] range for the alpha + * @example + * <div> + * <code> + * noStroke(); + * colorMode(RGB, 100); + * for (i = 0; i < 100; i++) { + * for (j = 0; j < 100; j++) { + * stroke(i, j, 0); + * point(i, j); + * } + * } + * </code> + * </div> + * + * <div> + * <code> + * noStroke(); + * colorMode(HSB, 100); + * for (i = 0; i < 100; i++) { + * for (j = 0; j < 100; j++) { + * stroke(i, j, 100); + * point(i, j); + * } + * } + * </code> + * </div> + * + * <div> + * <code> + * colorMode(RGB, 255); + * var c = color(127, 255, 0); + * + * colorMode(RGB, 1); + * var myColor = c._getRed(); + * text(myColor, 10, 10, 80, 80); + * </code> + * </div> + * + * <div> + * <code> + * noFill(); + * colorMode(RGB, 255, 255, 255, 1); + * background(255); + * + * strokeWeight(4); + * stroke(255, 0 , 10, 0.3); + * ellipse(40, 40, 50, 50); + * ellipse(50, 50, 40, 40); + * </code> + * </div> + * + * @alt + *Green to red gradient from bottom L to top R. shading originates from top left. + *Rainbow gradient from left to right. Brightness increasing to white at top. + *unknown image. + *50x50 ellipse at middle L & 40x40 ellipse at center. Transluscent pink outlines. + * + */ +p5.prototype.colorMode = function() { + if (arguments[0] === constants.RGB || + arguments[0] === constants.HSB || + arguments[0] === constants.HSL) { + + // Set color mode. + this._renderer._colorMode = arguments[0]; + + // Set color maxes. + var maxes = this._renderer._colorMaxes[this._renderer._colorMode]; + if (arguments.length === 2) { + maxes[0] = arguments[1]; // Red + maxes[1] = arguments[1]; // Green + maxes[2] = arguments[1]; // Blue + maxes[3] = arguments[1]; // Alpha + } else if (arguments.length === 4) { + maxes[0] = arguments[1]; // Red + maxes[1] = arguments[2]; // Green + maxes[2] = arguments[3]; // Blue + } else if (arguments.length === 5) { + maxes[0] = arguments[1]; // Red + maxes[1] = arguments[2]; // Green + maxes[2] = arguments[3]; // Blue + maxes[3] = arguments[4]; // Alpha + } + } + + return this; +}; + +/** + * Sets the color used to fill shapes. For example, if you run + * fill(204, 102, 0), all subsequent shapes will be filled with orange. This + * color is either specified in terms of the RGB or HSB color depending on + * the current colorMode(). (The default color space is RGB, with each value + * in the range from 0 to 255). + * <br><br> + * If a single string argument is provided, RGB, RGBA and Hex CSS color strings + * and all named color strings are supported. A p5 Color object can also be + * provided to set the fill color. + * + * @method fill + * @param {Number|Array|String|p5.Color} v1 gray value, red or hue value + * (depending on the current color + * mode), or color Array, or CSS + * color string + * @param {Number} [v2] green or saturation value + * (depending on the current + * color mode) + * @param {Number} [v3] blue or brightness value + * (depending on the current + * color mode) + * @param {Number} [a] opacity of the background + * + * @example + * <div> + * <code> + * // Grayscale integer value + * fill(51); + * rect(20, 20, 60, 60); + * </code> + * </div> + * + * <div> + * <code> + * // R, G & B integer values + * fill(255, 204, 0); + * rect(20, 20, 60, 60); + * </code> + * </div> + * + * <div> + * <code> + * // H, S & B integer values + * colorMode(HSB); + * fill(255, 204, 100); + * rect(20, 20, 60, 60); + * </code> + * </div> + * + * <div> + * <code> + * // Named SVG/CSS color string + * fill('red'); + * rect(20, 20, 60, 60); + * </code> + * </div> + * + * <div> + * <code> + * // three-digit hexadecimal RGB notation + * fill('#fae'); + * rect(20, 20, 60, 60); + * </code> + * </div> + * + * <div> + * <code> + * // six-digit hexadecimal RGB notation + * fill('#222222'); + * rect(20, 20, 60, 60); + * </code> + * </div> + * + * <div> + * <code> + * // integer RGB notation + * fill('rgb(0,255,0)'); + * rect(20, 20, 60, 60); + * </code> + * </div> + * + * <div> + * <code> + * // integer RGBA notation + * fill('rgba(0,255,0, 0.25)'); + * rect(20, 20, 60, 60); + * </code> + * </div> + * + * <div> + * <code> + * // percentage RGB notation + * fill('rgb(100%,0%,10%)'); + * rect(20, 20, 60, 60); + * </code> + * </div> + * + * <div> + * <code> + * // percentage RGBA notation + * fill('rgba(100%,0%,100%,0.5)'); + * rect(20, 20, 60, 60); + * </code> + * </div> + * + * <div> + * <code> + * // p5 Color object + * fill(color(0, 0, 255)); + * rect(20, 20, 60, 60); + * </code> + * </div> + * @alt + * 60x60 dark charcoal grey rect with black outline in center of canvas. + * 60x60 yellow rect with black outline in center of canvas. + * 60x60 royal blue rect with black outline in center of canvas. + * 60x60 red rect with black outline in center of canvas. + * 60x60 pink rect with black outline in center of canvas. + * 60x60 black rect with black outline in center of canvas. + * 60x60 light green rect with black outline in center of canvas. + * 60x60 soft green rect with black outline in center of canvas. + * 60x60 red rect with black outline in center of canvas. + * 60x60 dark fushcia rect with black outline in center of canvas. + * 60x60 blue rect with black outline in center of canvas. + */ + +p5.prototype.fill = function() { + this._renderer._setProperty('_fillSet', true); + this._renderer._setProperty('_doFill', true); + this._renderer.fill.apply(this._renderer, arguments); + return this; +}; + +/** + * Disables filling geometry. If both noStroke() and noFill() are called, + * nothing will be drawn to the screen. + * + * @method noFill + * @example + * <div> + * <code> + * rect(15, 10, 55, 55); + * noFill(); + * rect(20, 20, 60, 60); + * </code> + * </div> + * @alt + * white rect top middle and noFill rect center. Both 60x60 with black outlines. + */ +p5.prototype.noFill = function() { + this._renderer._setProperty('_doFill', false); + return this; +}; + +/** + * Disables drawing the stroke (outline). If both noStroke() and noFill() + * are called, nothing will be drawn to the screen. + * + * @method noStroke + * @example + * <div> + * <code> + * noStroke(); + * rect(20, 20, 60, 60); + * </code> + * </div> + * + * + * @alt + *60x60 white rect at center. no outline. + * + */ + +p5.prototype.noStroke = function() { + this._renderer._setProperty('_doStroke', false); + return this; +}; + +/** + * Sets the color used to draw lines and borders around shapes. This color + * is either specified in terms of the RGB or HSB color depending on the + * current colorMode() (the default color space is RGB, with each value in + * the range from 0 to 255). + * <br><br> + * If a single string argument is provided, RGB, RGBA and Hex CSS color + * strings and all named color strings are supported. A p5 Color object + * can also be provided to set the stroke color. + * + * @method stroke + * @param {Number|Array|String|p5.Color} v1 gray value, red or hue value + * (depending on the current color + * mode), or color Array, or CSS + * color string + * @param {Number} [v2] green or saturation value + * (depending on the current + * color mode) + * @param {Number} [v3] blue or brightness value + * (depending on the current + * color mode) + * @param {Number} [a] opacity of the background + * + * @example + * <div> + * <code> + * // Grayscale integer value + * strokeWeight(4); + * stroke(51); + * rect(20, 20, 60, 60); + * </code> + * </div> + * + * <div> + * <code> + * // R, G & B integer values + * stroke(255, 204, 0); + * strokeWeight(4); + * rect(20, 20, 60, 60); + * </code> + * </div> + * + * <div> + * <code> + * // H, S & B integer values + * colorMode(HSB); + * strokeWeight(4); + * stroke(255, 204, 100); + * rect(20, 20, 60, 60); + * </code> + * </div> + * + * <div> + * <code> + * // Named SVG/CSS color string + * stroke('red'); + * strokeWeight(4); + * rect(20, 20, 60, 60); + * </code> + * </div> + * + * <div> + * <code> + * // three-digit hexadecimal RGB notation + * stroke('#fae'); + * strokeWeight(4); + * rect(20, 20, 60, 60); + * </code> + * </div> + * + * <div> + * <code> + * // six-digit hexadecimal RGB notation + * stroke('#222222'); + * strokeWeight(4); + * rect(20, 20, 60, 60); + * </code> + * </div> + * + * <div> + * <code> + * // integer RGB notation + * stroke('rgb(0,255,0)'); + * strokeWeight(4); + * rect(20, 20, 60, 60); + * </code> + * </div> + * + * <div> + * <code> + * // integer RGBA notation + * stroke('rgba(0,255,0,0.25)'); + * strokeWeight(4); + * rect(20, 20, 60, 60); + * </code> + * </div> + * + * <div> + * <code> + * // percentage RGB notation + * stroke('rgb(100%,0%,10%)'); + * strokeWeight(4); + * rect(20, 20, 60, 60); + * </code> + * </div> + * + * <div> + * <code> + * // percentage RGBA notation + * stroke('rgba(100%,0%,100%,0.5)'); + * strokeWeight(4); + * rect(20, 20, 60, 60); + * </code> + * </div> + * + * <div> + * <code> + * // p5 Color object + * stroke(color(0, 0, 255)); + * strokeWeight(4); + * rect(20, 20, 60, 60); + * </code> + * </div> + * + * @alt + * 60x60 white rect at center. Dark charcoal grey outline. + * 60x60 white rect at center. Yellow outline. + * 60x60 white rect at center. Royal blue outline. + * 60x60 white rect at center. Red outline. + * 60x60 white rect at center. Pink outline. + * 60x60 white rect at center. Black outline. + * 60x60 white rect at center. Bright green outline. + * 60x60 white rect at center. Soft green outline. + * 60x60 white rect at center. Red outline. + * 60x60 white rect at center. Dark fushcia outline. + * 60x60 white rect at center. Blue outline. + */ + +p5.prototype.stroke = function() { + this._renderer._setProperty('_strokeSet', true); + this._renderer._setProperty('_doStroke', true); + this._renderer.stroke.apply(this._renderer, arguments); + return this; +}; + +module.exports = p5; + +},{"../core/constants":36,"../core/core":37,"./p5.Color":31}],33:[function(_dereq_,module,exports){ +/** + * @module Shape + * @submodule 2D Primitives + * @for p5 + * @requires core + * @requires constants + */ + +'use strict'; + +var p5 = _dereq_('./core'); +var constants = _dereq_('./constants'); +var canvas = _dereq_('./canvas'); +_dereq_('./error_helpers'); + +/** + * Draw an arc to the screen. If called with only a, b, c, d, start, and + * stop, the arc will be drawn as an open pie. If mode is provided, the arc + * will be drawn either open, as a chord, or as a pie as specified. The + * origin may be changed with the ellipseMode() function.<br><br> + * Note that drawing a full circle (ex: 0 to TWO_PI) will appear blank + * because 0 and TWO_PI are the same position on the unit circle. The + * best way to handle this is by using the ellipse() function instead + * to create a closed ellipse, and to use the arc() function + * only to draw parts of an ellipse. + * + * @method arc + * @param {Number} a x-coordinate of the arc's ellipse + * @param {Number} b y-coordinate of the arc's ellipse + * @param {Number} c width of the arc's ellipse by default + * @param {Number} d height of the arc's ellipse by default + * @param {Number} start angle to start the arc, specified in radians + * @param {Number} stop angle to stop the arc, specified in radians + * @param {Constant} [mode] optional parameter to determine the way of drawing + * the arc + * @return {Object} the p5 object + * @example + * <div> + * <code> + * arc(50, 55, 50, 50, 0, HALF_PI); + * noFill(); + * arc(50, 55, 60, 60, HALF_PI, PI); + * arc(50, 55, 70, 70, PI, PI+QUARTER_PI); + * arc(50, 55, 80, 80, PI+QUARTER_PI, TWO_PI); + * </code> + * </div> + * + * <div> + * <code> + * arc(50, 50, 80, 80, 0, PI+QUARTER_PI, OPEN); + * </code> + * </div> + * + * <div> + * <code> + * arc(50, 50, 80, 80, 0, PI+QUARTER_PI, CHORD); + * </code> + * </div> + * + * <div> + * <code> + * arc(50, 50, 80, 80, 0, PI+QUARTER_PI, PIE); + * </code> + * </div> + * + * @alt + *shattered outline of an ellipse with a quarter of a white circle bottom-right. + *white ellipse with black outline with top right missing. + *white ellipse with top right missing with black outline around shape. + *white ellipse with top right quarter missing with black outline around the shape. + * + */ +p5.prototype.arc = function(x, y, w, h, start, stop, mode) { + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + if (!this._renderer._doStroke && !this._renderer._doFill) { + return this; + } + if (this._angleMode === constants.DEGREES) { + start = this.radians(start); + stop = this.radians(stop); + } + + // Make all angles positive... + while (start < 0) { + start += constants.TWO_PI; + } + while (stop < 0) { + stop += constants.TWO_PI; + } + // ...and confine them to the interval [0,TWO_PI). + start %= constants.TWO_PI; + stop %= constants.TWO_PI; + + // account for full circle + if (stop === start) { + stop += constants.TWO_PI; + } + + // Adjust angles to counter linear scaling. + if (start <= constants.HALF_PI) { + start = Math.atan(w / h * Math.tan(start)); + } else if (start > constants.HALF_PI && start <= 3 * constants.HALF_PI) { + start = Math.atan(w / h * Math.tan(start)) + constants.PI; + } else { + start = Math.atan(w / h * Math.tan(start)) + constants.TWO_PI; + } + if (stop <= constants.HALF_PI) { + stop = Math.atan(w / h * Math.tan(stop)); + } else if (stop > constants.HALF_PI && stop <= 3 * constants.HALF_PI) { + stop = Math.atan(w / h * Math.tan(stop)) + constants.PI; + } else { + stop = Math.atan(w / h * Math.tan(stop)) + constants.TWO_PI; + } + + // Exceed the interval if necessary in order to preserve the size and + // orientation of the arc. + if (start > stop) { + stop += constants.TWO_PI; + } + // p5 supports negative width and heights for ellipses + w = Math.abs(w); + h = Math.abs(h); + this._renderer.arc(x, y, w, h, start, stop, mode); + return this; +}; + +/** + * Draws an ellipse (oval) to the screen. An ellipse with equal width and + * height is a circle. By default, the first two parameters set the location, + * and the third and fourth parameters set the shape's width and height. If + * no height is specified, the value of width is used for both the width and + * height. The origin may be changed with the ellipseMode() function. + * + * @method ellipse + * @param {Number} x x-coordinate of the ellipse. + * @param {Number} y y-coordinate of the ellipse. + * @param {Number} w width of the ellipse. + * @param {Number} [h] height of the ellipse. + * @return {p5} the p5 object + * @example + * <div> + * <code> + * ellipse(56, 46, 55, 55); + * </code> + * </div> + * + * @alt + *white ellipse with black outline in middle-right of canvas that is 55x55. + * + */ +/** + * @method ellipse + * @param {Number} x + * @param {Number} y + * @param {Number} w + * @param {Number} [h] + * @return {p5} + */ +p5.prototype.ellipse = function() { + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + // Duplicate 3rd argument if only 3 given. + if (args.length === 3) { + args.push(args[2]); + } + // p5 supports negative width and heights for rects + if (args[2] < 0){args[2] = Math.abs(args[2]);} + if (args[3] < 0){args[3] = Math.abs(args[3]);} + if (!this._renderer._doStroke && !this._renderer._doFill) { + return this; + } + var vals = canvas.modeAdjust( + args[0], + args[1], + args[2], + args[3], + this._renderer._ellipseMode); + args[0] = vals.x; + args[1] = vals.y; + args[2] = vals.w; + args[3] = vals.h; + this._renderer.ellipse(args); + return this; +}; +/** + * Draws a line (a direct path between two points) to the screen. The version + * of line() with four parameters draws the line in 2D. To color a line, use + * the stroke() function. A line cannot be filled, therefore the fill() + * function will not affect the color of a line. 2D lines are drawn with a + * width of one pixel by default, but this can be changed with the + * strokeWeight() function. + * + * @method line + * @param {Number} x1 the x-coordinate of the first point + * @param {Number} y1 the y-coordinate of the first point + * @param {Number} x2 the x-coordinate of the second point + * @param {Number} y2 the y-coordinate of the second point + * @return {p5} the p5 object + * @example + * <div> + * <code> + * line(30, 20, 85, 75); + * </code> + * </div> + * + * <div> + * <code> + * line(30, 20, 85, 20); + * stroke(126); + * line(85, 20, 85, 75); + * stroke(255); + * line(85, 75, 30, 75); + * </code> + * </div> + * + * @alt + *line 78 pixels long running from mid-top to bottom-right of canvas. + *3 lines of various stroke sizes. Form top, bottom and right sides of a square. + * + */ +////commented out original +// p5.prototype.line = function(x1, y1, x2, y2) { +// if (!this._renderer._doStroke) { +// return this; +// } +// if(this._renderer.isP3D){ +// } else { +// this._renderer.line(x1, y1, x2, y2); +// } +// }; +p5.prototype.line = function() { + if (!this._renderer._doStroke) { + return this; + } + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + //check whether we should draw a 3d line or 2d + if(this._renderer.isP3D){ + this._renderer.line( + args[0], + args[1], + args[2], + args[3], + args[4], + args[5]); + } else { + this._renderer.line( + args[0], + args[1], + args[2], + args[3]); + } + return this; +}; + +/** + * Draws a point, a coordinate in space at the dimension of one pixel. + * The first parameter is the horizontal value for the point, the second + * value is the vertical value for the point. The color of the point is + * determined by the current stroke. + * + * @method point + * @param {Number} x the x-coordinate + * @param {Number} y the y-coordinate + * @return {p5} the p5 object + * @example + * <div> + * <code> + * point(30, 20); + * point(85, 20); + * point(85, 75); + * point(30, 75); + * </code> + * </div> + * + * @alt + *4 points centered in the middle-right of the canvas. + * + */ +p5.prototype.point = function() { + if (!this._renderer._doStroke) { + return this; + } + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + //check whether we should draw a 3d line or 2d + if(this._renderer.isP3D){ + this._renderer.point( + args[0], + args[1], + args[2] + ); + } else { + this._renderer.point( + args[0], + args[1] + ); + } + return this; +}; + + +/** + * Draw a quad. A quad is a quadrilateral, a four sided polygon. It is + * similar to a rectangle, but the angles between its edges are not + * constrained to ninety degrees. The first pair of parameters (x1,y1) + * sets the first vertex and the subsequent pairs should proceed + * clockwise or counter-clockwise around the defined shape. + * + * @method quad + * @param {Number} x1 the x-coordinate of the first point + * @param {Number} y1 the y-coordinate of the first point + * @param {Number} x2 the x-coordinate of the second point + * @param {Number} y2 the y-coordinate of the second point + * @param {Number} x3 the x-coordinate of the third point + * @param {Number} y3 the y-coordinate of the third point + * @param {Number} x4 the x-coordinate of the fourth point + * @param {Number} y4 the y-coordinate of the fourth point + * @return {p5} the p5 object + * @example + * <div> + * <code> + * quad(38, 31, 86, 20, 69, 63, 30, 76); + * </code> + * </div> + * + * @alt + *irregular white quadrilateral shape with black outline mid-right of canvas. + * + */ +/** + * @method quad + * @param {Number} x1 + * @param {Number} y1 + * @param {Number} x2 + * @param {Number} y2 + * @param {Number} x3 + * @param {Number} y3 + * @param {Number} x4 + * @param {Number} y4 + * @return {p5} the p5 object + */ +p5.prototype.quad = function() { + if (!this._renderer._doStroke && !this._renderer._doFill) { + return this; + } + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + if(this._renderer.isP3D){ + this._renderer.quad( + args[0], + args[1], + args[2], + args[3], + args[4], + args[5], + args[6], + args[7], + args[8], + args[9], + args[10], + args[11] + ); + } else { + this._renderer.quad( + args[0], + args[1], + args[2], + args[3], + args[4], + args[5], + args[6], + args[7] + ); + } + return this; +}; + +/** +* Draws a rectangle to the screen. A rectangle is a four-sided shape with +* every angle at ninety degrees. By default, the first two parameters set +* the location of the upper-left corner, the third sets the width, and the +* fourth sets the height. The way these parameters are interpreted, however, +* may be changed with the rectMode() function. +* <br><br> +* The fifth, sixth, seventh and eighth parameters, if specified, +* determine corner radius for the top-right, top-left, lower-right and +* lower-left corners, respectively. An omitted corner radius parameter is set +* to the value of the previously specified radius value in the parameter list. +* +* @method rect +* @param {Number} x x-coordinate of the rectangle. +* @param {Number} y y-coordinate of the rectangle. +* @param {Number} w width of the rectangle. +* @param {Number} h height of the rectangle. +* @param {Number} [tl] optional radius of top-left corner. +* @param {Number} [tr] optional radius of top-right corner. +* @param {Number} [br] optional radius of bottom-right corner. +* @param {Number} [bl] optional radius of bottom-left corner. +* @return {p5} the p5 object. +* @example +* <div> +* <code> +* // Draw a rectangle at location (30, 20) with a width and height of 55. +* rect(30, 20, 55, 55); +* </code> +* </div> +* +* <div> +* <code> +* // Draw a rectangle with rounded corners, each having a radius of 20. +* rect(30, 20, 55, 55, 20); +* </code> +* </div> +* +* <div> +* <code> +* // Draw a rectangle with rounded corners having the following radii: +* // top-left = 20, top-right = 15, bottom-right = 10, bottom-left = 5. +* rect(30, 20, 55, 55, 20, 15, 10, 5); +* </code> +* </div> +* +* @alt +* 55x55 white rect with black outline in mid-right of canvas. +* 55x55 white rect with black outline and rounded edges in mid-right of canvas. +* 55x55 white rect with black outline and rounded edges of different radii. +*/ +/** +* @method rect +* @param {Number} x +* @param {Number} y +* @param {Number} w +* @param {Number} h +* @param {Number} [detailX] +* @param {Number} [detailY] +* @return {p5} the p5 object. +*/ +p5.prototype.rect = function () { + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + if (!this._renderer._doStroke && !this._renderer._doFill) { + return; + } + var vals = canvas.modeAdjust( + args[0], + args[1], + args[2], + args[3], + this._renderer._rectMode); + args[0] = vals.x; + args[1] = vals.y; + args[2] = vals.w; + args[3] = vals.h; + this._renderer.rect(args); + return this; +}; + +/** +* A triangle is a plane created by connecting three points. The first two +* arguments specify the first point, the middle two arguments specify the +* second point, and the last two arguments specify the third point. +* +* @method triangle +* @param {Number} x1 x-coordinate of the first point +* @param {Number} y1 y-coordinate of the first point +* @param {Number} x2 x-coordinate of the second point +* @param {Number} y2 y-coordinate of the second point +* @param {Number} x3 x-coordinate of the third point +* @param {Number} y3 y-coordinate of the third point +* @return {p5} the p5 object +* @example +* <div> +* <code> +* triangle(30, 75, 58, 20, 86, 75); +* </code> +* </div> +* +*@alt +* white triangle with black outline in mid-right of canvas. +* +*/ +p5.prototype.triangle = function() { + + if (!this._renderer._doStroke && !this._renderer._doFill) { + return this; + } + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + this._renderer.triangle(args); + return this; +}; + +module.exports = p5; + +},{"./canvas":35,"./constants":36,"./core":37,"./error_helpers":40}],34:[function(_dereq_,module,exports){ +/** + * @module Shape + * @submodule Attributes + * @for p5 + * @requires core + * @requires constants + */ + +'use strict'; + +var p5 = _dereq_('./core'); +var constants = _dereq_('./constants'); + +/** + * Modifies the location from which ellipses are drawn by changing the way + * in which parameters given to ellipse() are interpreted. + * <br><br> + * The default mode is ellipseMode(CENTER), which interprets the first two + * parameters of ellipse() as the shape's center point, while the third and + * fourth parameters are its width and height. + * <br><br> + * ellipseMode(RADIUS) also uses the first two parameters of ellipse() as + * the shape's center point, but uses the third and fourth parameters to + * specify half of the shapes's width and height. + * <br><br> + * ellipseMode(CORNER) interprets the first two parameters of ellipse() as + * the upper-left corner of the shape, while the third and fourth parameters + * are its width and height. + * <br><br> + * ellipseMode(CORNERS) interprets the first two parameters of ellipse() as + * the location of one corner of the ellipse's bounding box, and the third + * and fourth parameters as the location of the opposite corner. + * <br><br> + * The parameter must be written in ALL CAPS because Javascript is a + * case-sensitive language. + * + * @method ellipseMode + * @param {Constant} mode either CENTER, RADIUS, CORNER, or CORNERS + * @return {p5} the p5 object + * @example + * <div> + * <code> + * ellipseMode(RADIUS); // Set ellipseMode to RADIUS + * fill(255); // Set fill to white + * ellipse(50, 50, 30, 30); // Draw white ellipse using RADIUS mode + * + * ellipseMode(CENTER); // Set ellipseMode to CENTER + * fill(100); // Set fill to gray + * ellipse(50, 50, 30, 30); // Draw gray ellipse using CENTER mode + * </code> + * </div> + * + * <div> + * <code> + * ellipseMode(CORNER); // Set ellipseMode is CORNER + * fill(255); // Set fill to white + * ellipse(25, 25, 50, 50); // Draw white ellipse using CORNER mode + * + * ellipseMode(CORNERS); // Set ellipseMode to CORNERS + * fill(100); // Set fill to gray + * ellipse(25, 25, 50, 50); // Draw gray ellipse using CORNERS mode + * </code> + * </div> + * + * @alt + * 60x60 white ellipse and 30x30 grey ellipse with black outlines at center. + * 60x60 white ellipse @center and 30x30 grey ellipse top-right, black outlines. + * + */ +p5.prototype.ellipseMode = function(m) { + if (m === constants.CORNER || + m === constants.CORNERS || + m === constants.RADIUS || + m === constants.CENTER) { + this._renderer._ellipseMode = m; + } + return this; +}; + +/** + * Draws all geometry with jagged (aliased) edges. Note that smooth() is + * active by default, so it is necessary to call noSmooth() to disable + * smoothing of geometry, images, and fonts. + * + * @method noSmooth + * @return {p5} the p5 object + * @example + * <div> + * <code> + * background(0); + * noStroke(); + * smooth(); + * ellipse(30, 48, 36, 36); + * noSmooth(); + * ellipse(70, 48, 36, 36); + * </code> + * </div> + * + * @alt + * 2 pixelated 36x36 white ellipses to left & right of center, black background + * + */ +p5.prototype.noSmooth = function() { + this._renderer.noSmooth(); + return this; +}; + +/** + * Modifies the location from which rectangles are drawn by changing the way + * in which parameters given to rect() are interpreted. + * <br><br> + * The default mode is rectMode(CORNER), which interprets the first two + * parameters of rect() as the upper-left corner of the shape, while the + * third and fourth parameters are its width and height. + * <br><br> + * rectMode(CORNERS) interprets the first two parameters of rect() as the + * location of one corner, and the third and fourth parameters as the + * location of the opposite corner. + * <br><br> + * rectMode(CENTER) interprets the first two parameters of rect() as the + * shape's center point, while the third and fourth parameters are its + * width and height. + * <br><br> + * rectMode(RADIUS) also uses the first two parameters of rect() as the + * shape's center point, but uses the third and fourth parameters to specify + * half of the shapes's width and height. + * <br><br> + * The parameter must be written in ALL CAPS because Javascript is a + * case-sensitive language. + * + * @method rectMode + * @param {Constant} mode either CORNER, CORNERS, CENTER, or RADIUS + * @return {p5} the p5 object + * @example + * <div> + * <code> + * rectMode(CORNER); // Default rectMode is CORNER + * fill(255); // Set fill to white + * rect(25, 25, 50, 50); // Draw white rect using CORNER mode + * + * rectMode(CORNERS); // Set rectMode to CORNERS + * fill(100); // Set fill to gray + * rect(25, 25, 50, 50); // Draw gray rect using CORNERS mode + * </code> + * </div> + * + * <div> + * <code> + * rectMode(RADIUS); // Set rectMode to RADIUS + * fill(255); // Set fill to white + * rect(50, 50, 30, 30); // Draw white rect using RADIUS mode + * + * rectMode(CENTER); // Set rectMode to CENTER + * fill(100); // Set fill to gray + * rect(50, 50, 30, 30); // Draw gray rect using CENTER mode + * </code> + * </div> + * + * @alt + * 50x50 white rect at center and 25x25 grey rect in the top left of the other. + * 50x50 white rect at center and 25x25 grey rect in the center of the other. + * + */ +p5.prototype.rectMode = function(m) { + if (m === constants.CORNER || + m === constants.CORNERS || + m === constants.RADIUS || + m === constants.CENTER) { + this._renderer._rectMode = m; + } + return this; +}; + +/** + * Draws all geometry with smooth (anti-aliased) edges. smooth() will also + * improve image quality of resized images. Note that smooth() is active by + * default; noSmooth() can be used to disable smoothing of geometry, + * images, and fonts. + * + * @method smooth + * @return {p5} the p5 object + * @example + * <div> + * <code> + * background(0); + * noStroke(); + * smooth(); + * ellipse(30, 48, 36, 36); + * noSmooth(); + * ellipse(70, 48, 36, 36); + * </code> + * </div> + * + * @alt + * 2 pixelated 36x36 white ellipses one left one right of center. On black. + * + */ +p5.prototype.smooth = function() { + this._renderer.smooth(); + return this; +}; + +/** + * Sets the style for rendering line endings. These ends are either squared, + * extended, or rounded, each of which specified with the corresponding + * parameters: SQUARE, PROJECT, and ROUND. The default cap is ROUND. + * + * @method strokeCap + * @param {Number/Constant} cap either SQUARE, PROJECT, or ROUND + * @return {p5} the p5 object + * @example + * <div> + * <code> + * strokeWeight(12.0); + * strokeCap(ROUND); + * line(20, 30, 80, 30); + * strokeCap(SQUARE); + * line(20, 50, 80, 50); + * strokeCap(PROJECT); + * line(20, 70, 80, 70); + * </code> + * </div> + * + * @alt + * 3 lines. Top line: rounded ends, mid: squared, bottom:longer squared ends. + * + */ +p5.prototype.strokeCap = function(cap) { + if (cap === constants.ROUND || + cap === constants.SQUARE || + cap === constants.PROJECT) { + this._renderer.strokeCap(cap); + } + return this; +}; + +/** + * Sets the style of the joints which connect line segments. These joints + * are either mitered, beveled, or rounded and specified with the + * corresponding parameters MITER, BEVEL, and ROUND. The default joint is + * MITER. + * + * @method strokeJoin + * @param {Number/Constant} join either MITER, BEVEL, ROUND + * @return {p5} the p5 object + * @example + * <div> + * <code> + * noFill(); + * strokeWeight(10.0); + * strokeJoin(MITER); + * beginShape(); + * vertex(35, 20); + * vertex(65, 50); + * vertex(35, 80); + * endShape(); + * </code> + * </div> + * + * <div> + * <code> + * noFill(); + * strokeWeight(10.0); + * strokeJoin(BEVEL); + * beginShape(); + * vertex(35, 20); + * vertex(65, 50); + * vertex(35, 80); + * endShape(); + * </code> + * </div> + * + * <div> + * <code> + * noFill(); + * strokeWeight(10.0); + * strokeJoin(ROUND); + * beginShape(); + * vertex(35, 20); + * vertex(65, 50); + * vertex(35, 80); + * endShape(); + * </code> + * </div> + * + * @alt + * Right-facing arrowhead shape with pointed tip in center of canvas. + * Right-facing arrowhead shape with flat tip in center of canvas. + * Right-facing arrowhead shape with rounded tip in center of canvas. + * + */ +p5.prototype.strokeJoin = function(join) { + if (join === constants.ROUND || + join === constants.BEVEL || + join === constants.MITER) { + this._renderer.strokeJoin(join); + } + return this; +}; + +/** + * Sets the width of the stroke used for lines, points, and the border + * around shapes. All widths are set in units of pixels. + * + * @method strokeWeight + * @param {Number} weight the weight (in pixels) of the stroke + * @return {p5} the p5 object + * @example + * <div> + * <code> + * strokeWeight(1); // Default + * line(20, 20, 80, 20); + * strokeWeight(4); // Thicker + * line(20, 40, 80, 40); + * strokeWeight(10); // Beastly + * line(20, 70, 80, 70); + * </code> + * </div> + * + * @alt + * 3 horizontal black lines. Top line: thin, mid: medium, bottom:thick. + * + */ +p5.prototype.strokeWeight = function(w) { + this._renderer.strokeWeight(w); + return this; +}; + +module.exports = p5; + +},{"./constants":36,"./core":37}],35:[function(_dereq_,module,exports){ +/** + * @requires constants + */ + +var constants = _dereq_('./constants'); + +module.exports = { + + modeAdjust: function(a, b, c, d, mode) { + if (mode === constants.CORNER) { + return { x: a, y: b, w: c, h: d }; + } else if (mode === constants.CORNERS) { + return { x: a, y: b, w: c-a, h: d-b }; + } else if (mode === constants.RADIUS) { + return { x: a-c, y: b-d, w: 2*c, h: 2*d }; + } else if (mode === constants.CENTER) { + return { x: a-c*0.5, y: b-d*0.5, w: c, h: d }; + } + }, + + arcModeAdjust: function(a, b, c, d, mode) { + if (mode === constants.CORNER) { + return { x: a+c*0.5, y: b+d*0.5, w: c, h: d }; + } else if (mode === constants.CORNERS) { + return { x: a, y: b, w: c+a, h: d+b }; + } else if (mode === constants.RADIUS) { + return { x: a, y: b, w: 2*c, h: 2*d }; + } else if (mode === constants.CENTER) { + return { x: a, y: b, w: c, h: d }; + } + } + +}; + + +},{"./constants":36}],36:[function(_dereq_,module,exports){ +/** + * @module Constants + * @submodule Constants + * @for p5 + */ + +var PI = Math.PI; + +module.exports = { + + // GRAPHICS RENDERER + P2D: 'p2d', + WEBGL: 'webgl', + + // ENVIRONMENT + ARROW: 'default', + CROSS: 'crosshair', + HAND: 'pointer', + MOVE: 'move', + TEXT: 'text', + WAIT: 'wait', + + // TRIGONOMETRY + + /** + * HALF_PI is a mathematical constant with the value + * 1.57079632679489661923. It is half the ratio of the + * circumference of a circle to its diameter. It is useful in + * combination with the trigonometric functions sin() and cos(). + * + * @property HALF_PI + * + * @example + * <div><code> + * arc(50, 50, 80, 80, 0, HALF_PI); + * </code></div> + * + * @alt + * 80x80 white quarter-circle with curve toward bottom right of canvas. + * + */ + HALF_PI: PI / 2, + /** + * PI is a mathematical constant with the value + * 3.14159265358979323846. It is the ratio of the circumference + * of a circle to its diameter. It is useful in combination with + * the trigonometric functions sin() and cos(). + * + * @property PI + * + * @example + * <div><code> + * arc(50, 50, 80, 80, 0, PI); + * </code></div> + * + * @alt + * white half-circle with curve toward bottom of canvas. + * + */ + PI: PI, + /** + * QUARTER_PI is a mathematical constant with the value 0.7853982. + * It is one quarter the ratio of the circumference of a circle to + * its diameter. It is useful in combination with the trigonometric + * functions sin() and cos(). + * + * @property QUARTER_PI + * + * @example + * <div><code> + * arc(50, 50, 80, 80, 0, QUARTER_PI); + * </code></div> + * + * @alt + * white eighth-circle rotated about 40 degrees with curve bottom right canvas. + * + */ + QUARTER_PI: PI / 4, + /** + * TAU is an alias for TWO_PI, a mathematical constant with the + * value 6.28318530717958647693. It is twice the ratio of the + * circumference of a circle to its diameter. It is useful in + * combination with the trigonometric functions sin() and cos(). + * + * @property TAU + * + * @example + * <div><code> + * arc(50, 50, 80, 80, 0, TAU); + * </code></div> + * + * @alt + * 80x80 white ellipse shape in center of canvas. + * + */ + TAU: PI * 2, + /** + * TWO_PI is a mathematical constant with the value + * 6.28318530717958647693. It is twice the ratio of the + * circumference of a circle to its diameter. It is useful in + * combination with the trigonometric functions sin() and cos(). + * + * @property TWO_PI + * + * @example + * <div><code> + * arc(50, 50, 80, 80, 0, TWO_PI); + * </code></div> + * + * @alt + * 80x80 white ellipse shape in center of canvas. + * + */ + TWO_PI: PI * 2, + DEGREES: 'degrees', + RADIANS: 'radians', + + // SHAPE + CORNER: 'corner', + CORNERS: 'corners', + RADIUS: 'radius', + RIGHT: 'right', + LEFT: 'left', + CENTER: 'center', + TOP: 'top', + BOTTOM: 'bottom', + BASELINE: 'alphabetic', + POINTS: 0x0000, + LINES: 0x0001, + LINE_STRIP: 0x0003, + LINE_LOOP: 0x0002, + TRIANGLES: 0x0004, + TRIANGLE_FAN: 0x0006, + TRIANGLE_STRIP: 0x0005, + QUADS: 'quads', + QUAD_STRIP: 'quad_strip', + CLOSE: 'close', + OPEN: 'open', + CHORD: 'chord', + PIE: 'pie', + PROJECT: 'square', // PEND: careful this is counterintuitive + SQUARE: 'butt', + ROUND: 'round', + BEVEL: 'bevel', + MITER: 'miter', + + // COLOR + RGB: 'rgb', + HSB: 'hsb', + HSL: 'hsl', + + // DOM EXTENSION + AUTO: 'auto', + + // INPUT + ALT: 18, + BACKSPACE: 8, + CONTROL: 17, + DELETE: 46, + DOWN_ARROW: 40, + ENTER: 13, + ESCAPE: 27, + LEFT_ARROW: 37, + OPTION: 18, + RETURN: 13, + RIGHT_ARROW: 39, + SHIFT: 16, + TAB: 9, + UP_ARROW: 38, + + // RENDERING + BLEND: 'normal', + ADD: 'lighter', + //ADD: 'add', // + //SUBTRACT: 'subtract', // + DARKEST: 'darken', + LIGHTEST: 'lighten', + DIFFERENCE: 'difference', + EXCLUSION: 'exclusion', + MULTIPLY: 'multiply', + SCREEN: 'screen', + REPLACE: 'source-over', + OVERLAY: 'overlay', + HARD_LIGHT: 'hard-light', + SOFT_LIGHT: 'soft-light', + DODGE: 'color-dodge', + BURN: 'color-burn', + + // FILTERS + THRESHOLD: 'threshold', + GRAY: 'gray', + OPAQUE: 'opaque', + INVERT: 'invert', + POSTERIZE: 'posterize', + DILATE: 'dilate', + ERODE: 'erode', + BLUR: 'blur', + + // TYPOGRAPHY + NORMAL: 'normal', + ITALIC: 'italic', + BOLD: 'bold', + + // TYPOGRAPHY-INTERNAL + _DEFAULT_TEXT_FILL: '#000000', + _DEFAULT_LEADMULT: 1.25, + _CTX_MIDDLE: 'middle', + + // VERTICES + LINEAR: 'linear', + QUADRATIC: 'quadratic', + BEZIER: 'bezier', + CURVE: 'curve', + + // DEFAULTS + _DEFAULT_STROKE: '#000000', + _DEFAULT_FILL: '#FFFFFF' + +}; + +},{}],37:[function(_dereq_,module,exports){ +/** + * @module Structure + * @submodule Structure + * @for p5 + * @requires constants + */ + +'use strict'; + +_dereq_('./shim'); + +// Core needs the PVariables object +var constants = _dereq_('./constants'); + +/** + * This is the p5 instance constructor. + * + * A p5 instance holds all the properties and methods related to + * a p5 sketch. It expects an incoming sketch closure and it can also + * take an optional node parameter for attaching the generated p5 canvas + * to a node. The sketch closure takes the newly created p5 instance as + * its sole argument and may optionally set preload(), setup(), and/or + * draw() properties on it for running a sketch. + * + * A p5 sketch can run in "global" or "instance" mode: + * "global" - all properties and methods are attached to the window + * "instance" - all properties and methods are bound to this p5 object + * + * @param {Function} sketch a closure that can set optional preload(), + * setup(), and/or draw() properties on the + * given p5 instance + * @param {HTMLElement|boolean} [node] element to attach canvas to, if a + * boolean is passed in use it as sync + * @param {boolean} [sync] start synchronously (optional) + * @return {p5} a p5 instance + */ +var p5 = function(sketch, node, sync) { + + if (arguments.length === 2 && typeof node === 'boolean') { + sync = node; + node = undefined; + } + + ////////////////////////////////////////////// + // PUBLIC p5 PROPERTIES AND METHODS + ////////////////////////////////////////////// + + + /** + * Called directly before setup(), the preload() function is used to handle + * asynchronous loading of external files. If a preload function is + * defined, setup() will wait until any load calls within have finished. + * Nothing besides load calls should be inside preload (loadImage, + * loadJSON, loadFont, loadStrings, etc). + * + * @method preload + * @example + * <div><code> + * var img; + * var c; + * function preload() { // preload() runs once + * img = loadImage('assets/laDefense.jpg'); + * } + * + * function setup() { // setup() waits until preload() is done + * img.loadPixels(); + * // get color of middle pixel + * c = img.get(img.width/2, img.height/2); + * } + * + * function draw() { + * background(c); + * image(img, 25, 25, 50, 50); + * } + * </code></div> + * + * @alt + * nothing displayed + * + */ + + /** + * The setup() function is called once when the program starts. It's used to + * define initial environment properties such as screen size and background + * color and to load media such as images and fonts as the program starts. + * There can only be one setup() function for each program and it shouldn't + * be called again after its initial execution. + * <br><br> + * Note: Variables declared within setup() are not accessible within other + * functions, including draw(). + * + * @method setup + * @example + * <div><code> + * var a = 0; + * + * function setup() { + * background(0); + * noStroke(); + * fill(102); + * } + * + * function draw() { + * rect(a++%width, 10, 2, 80); + * } + * </code></div> + * + * @alt + * nothing displayed + * + */ + + /** + * Called directly after setup(), the draw() function continuously executes + * the lines of code contained inside its block until the program is stopped + * or noLoop() is called. Note if noLoop() is called in setup(), draw() will + * still be executed once before stopping. draw() is called automatically and + * should never be called explicitly. + * <br><br> + * It should always be controlled with noLoop(), redraw() and loop(). After + * noLoop() stops the code in draw() from executing, redraw() causes the + * code inside draw() to execute once, and loop() will cause the code + * inside draw() to resume executing continuously. + * <br><br> + * The number of times draw() executes in each second may be controlled with + * the frameRate() function. + * <br><br> + * There can only be one draw() function for each sketch, and draw() must + * exist if you want the code to run continuously, or to process events such + * as mousePressed(). Sometimes, you might have an empty call to draw() in + * your program, as shown in the above example. + * <br><br> + * It is important to note that the drawing coordinate system will be reset + * at the beginning of each draw() call. If any transformations are performed + * within draw() (ex: scale, rotate, translate, their effects will be + * undone at the beginning of draw(), so transformations will not accumulate + * over time. On the other hand, styling applied (ex: fill, stroke, etc) will + * remain in effect. + * + * @method draw + * @example + * <div><code> + * var yPos = 0; + * function setup() { // setup() runs once + * frameRate(30); + * } + * function draw() { // draw() loops forever, until stopped + * background(204); + * yPos = yPos - 1; + * if (yPos < 0) { + * yPos = height; + * } + * line(0, yPos, width, yPos); + * } + * </code></div> + * + * @alt + * nothing displayed + * + */ + + + ////////////////////////////////////////////// + // PRIVATE p5 PROPERTIES AND METHODS + ////////////////////////////////////////////// + + this._setupDone = false; + // for handling hidpi + this._pixelDensity = Math.ceil(window.devicePixelRatio) || 1; + this._userNode = node; + this._curElement = null; + this._elements = []; + this._requestAnimId = 0; + this._preloadCount = 0; + this._isGlobal = false; + this._loop = true; + this._styles = []; + this._defaultCanvasSize = { + width: 100, + height: 100 + }; + this._events = { // keep track of user-events for unregistering later + 'mousemove': null, + 'mousedown': null, + 'mouseup': null, + 'dragend': null, + 'dragover': null, + 'click': null, + 'mouseover': null, + 'mouseout': null, + 'keydown': null, + 'keyup': null, + 'keypress': null, + 'touchstart': null, + 'touchmove': null, + 'touchend': null, + 'resize': null, + 'blur': null + }; + + this._events.wheel = null; + this._loadingScreenId = 'p5_loading'; + + if (window.DeviceOrientationEvent) { + this._events.deviceorientation = null; + } + if (window.DeviceMotionEvent && !window._isNodeWebkit) { + this._events.devicemotion = null; + } + + this._start = function () { + // Find node if id given + if (this._userNode) { + if (typeof this._userNode === 'string') { + this._userNode = document.getElementById(this._userNode); + } + } + + // Always create a default canvas. + // Later on if the user calls createCanvas, this default one + // will be replaced + this.createCanvas( + this._defaultCanvasSize.width, + this._defaultCanvasSize.height, + 'p2d', + true + ); + + var userPreload = this.preload || window.preload; // look for "preload" + if (userPreload) { + + // Setup loading screen + // Set loading scfeen into dom if not present + // Otherwise displays and removes user provided loading screen + var loadingScreen = document.getElementById(this._loadingScreenId); + if(!loadingScreen){ + loadingScreen = document.createElement('div'); + loadingScreen.innerHTML = 'Loading...'; + loadingScreen.style.position = 'absolute'; + loadingScreen.id = this._loadingScreenId; + var node = this._userNode || document.body; + node.appendChild(loadingScreen); + } + // var methods = this._preloadMethods; + for (var method in this._preloadMethods){ + // default to p5 if no object defined + this._preloadMethods[method] = this._preloadMethods[method] || p5; + var obj = this._preloadMethods[method]; + //it's p5, check if it's global or instance + if (obj === p5.prototype || obj === p5){ + obj = this._isGlobal ? window : this; + } + this._registeredPreloadMethods[method] = obj[method]; + obj[method] = this._wrapPreload(obj, method); + } + + userPreload(); + this._runIfPreloadsAreDone(); + } else { + this._setup(); + this._runFrames(); + this._draw(); + } + }.bind(this); + + this._runIfPreloadsAreDone = function(){ + var context = this._isGlobal ? window : this; + if (context._preloadCount === 0) { + var loadingScreen = document.getElementById(context._loadingScreenId); + if (loadingScreen) { + loadingScreen.parentNode.removeChild(loadingScreen); + } + context._setup(); + context._runFrames(); + context._draw(); + } + }; + + this._decrementPreload = function(){ + var context = this._isGlobal ? window : this; + context._setProperty('_preloadCount', context._preloadCount - 1); + context._runIfPreloadsAreDone(); + }; + + this._wrapPreload = function(obj, fnName){ + return function(){ + //increment counter + this._incrementPreload(); + //call original function + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + args.push(this._decrementPreload.bind(this)); + return this._registeredPreloadMethods[fnName].apply(obj, args); + }.bind(this); + }; + + this._incrementPreload = function(){ + var context = this._isGlobal ? window : this; + context._setProperty('_preloadCount', context._preloadCount + 1); + }; + + this._setup = function() { + + // return preload functions to their normal vals if switched by preload + var context = this._isGlobal ? window : this; + if (typeof context.preload === 'function') { + for (var f in this._preloadMethods) { + context[f] = this._preloadMethods[f][f]; + if (context[f] && this) { + context[f] = context[f].bind(this); + } + } + } + + // Short-circuit on this, in case someone used the library in "global" + // mode earlier + if (typeof context.setup === 'function') { + context.setup(); + } + + // unhide any hidden canvases that were created + var canvases = document.getElementsByTagName('canvas'); + for (var i = 0; i < canvases.length; i++) { + var k = canvases[i]; + if (k.dataset.hidden === 'true') { + k.style.visibility = ''; + delete(k.dataset.hidden); + } + } + this._setupDone = true; + + }.bind(this); + + this._draw = function () { + var now = window.performance.now(); + var time_since_last = now - this._lastFrameTime; + var target_time_between_frames = 1000 / this._targetFrameRate; + + // only draw if we really need to; don't overextend the browser. + // draw if we're within 5ms of when our next frame should paint + // (this will prevent us from giving up opportunities to draw + // again when it's really about time for us to do so). fixes an + // issue where the frameRate is too low if our refresh loop isn't + // in sync with the browser. note that we have to draw once even + // if looping is off, so we bypass the time delay if that + // is the case. + var epsilon = 5; + if (!this._loop || + time_since_last >= target_time_between_frames - epsilon) { + + //mandatory update values(matrixs and stack) + + this._setProperty('frameCount', this.frameCount + 1); + this.redraw(); + this._updateMouseCoords(); + this._updateTouchCoords(); + this._frameRate = 1000.0/(now - this._lastFrameTime); + this._lastFrameTime = now; + } + + // get notified the next time the browser gives us + // an opportunity to draw. + if (this._loop) { + this._requestAnimId = window.requestAnimationFrame(this._draw); + } + }.bind(this); + + this._runFrames = function() { + if (this._updateInterval) { + clearInterval(this._updateInterval); + } + }.bind(this); + + this._setProperty = function(prop, value) { + this[prop] = value; + if (this._isGlobal) { + window[prop] = value; + } + }.bind(this); + + /** + * Removes the entire p5 sketch. This will remove the canvas and any + * elements created by p5.js. It will also stop the draw loop and unbind + * any properties or methods from the window global scope. It will + * leave a variable p5 in case you wanted to create a new p5 sketch. + * If you like, you can set p5 = null to erase it. + * @method remove + * @example + * <div class='norender'><code> + * function draw() { + * ellipse(50, 50, 10, 10); + * } + * + * function mousePressed() { + * remove(); // remove whole sketch on mouse press + * } + * </code></div> + * + * @alt + * nothing displayed + * + */ + this.remove = function() { + if (this._curElement) { + + // stop draw + this._loop = false; + if (this._requestAnimId) { + window.cancelAnimationFrame(this._requestAnimId); + } + + // unregister events sketch-wide + for (var ev in this._events) { + window.removeEventListener(ev, this._events[ev]); + } + + // remove DOM elements created by p5, and listeners + for (var i=0; i<this._elements.length; i++) { + var e = this._elements[i]; + if (e.elt.parentNode) { + e.elt.parentNode.removeChild(e.elt); + } + for (var elt_ev in e._events) { + e.elt.removeEventListener(elt_ev, e._events[elt_ev]); + } + } + + // call any registered remove functions + var self = this; + this._registeredMethods.remove.forEach(function (f) { + if (typeof(f) !== 'undefined') { + f.call(self); + } + }); + + // remove window bound properties and methods + if (this._isGlobal) { + for (var p in p5.prototype) { + try { + delete window[p]; + } catch (x) { + window[p] = undefined; + } + } + for (var p2 in this) { + if (this.hasOwnProperty(p2)) { + try { + delete window[p2]; + } catch (x) { + window[p2] = undefined; + } + } + } + } + } + // window.p5 = undefined; + }.bind(this); + + // call any registered init functions + this._registeredMethods.init.forEach(function (f) { + if (typeof(f) !== 'undefined') { + f.call(this); + } + }, this); + + var friendlyBindGlobal = this._createFriendlyGlobalFunctionBinder(); + + // If the user has created a global setup or draw function, + // assume "global" mode and make everything global (i.e. on the window) + if (!sketch) { + this._isGlobal = true; + p5.instance = this; + // Loop through methods on the prototype and attach them to the window + for (var p in p5.prototype) { + if(typeof p5.prototype[p] === 'function') { + var ev = p.substring(2); + if (!this._events.hasOwnProperty(ev)) { + if (Math.hasOwnProperty(p) && (Math[p] === p5.prototype[p])) { + // Multiple p5 methods are just native Math functions. These can be + // called without any binding. + friendlyBindGlobal(p, p5.prototype[p]); + } else { + friendlyBindGlobal(p, p5.prototype[p].bind(this)); + } + } + } else { + friendlyBindGlobal(p, p5.prototype[p]); + } + } + // Attach its properties to the window + for (var p2 in this) { + if (this.hasOwnProperty(p2)) { + friendlyBindGlobal(p2, this[p2]); + } + } + + } else { + // Else, the user has passed in a sketch closure that may set + // user-provided 'setup', 'draw', etc. properties on this instance of p5 + sketch(this); + } + + // Bind events to window (not using container div bc key events don't work) + + for (var e in this._events) { + var f = this['_on'+e]; + if (f) { + var m = f.bind(this); + window.addEventListener(e, m); + this._events[e] = m; + } + } + + var focusHandler = function() { + this._setProperty('focused', true); + }.bind(this); + var blurHandler = function() { + this._setProperty('focused', false); + }.bind(this); + window.addEventListener('focus', focusHandler); + window.addEventListener('blur', blurHandler); + this.registerMethod('remove', function() { + window.removeEventListener('focus', focusHandler); + window.removeEventListener('blur', blurHandler); + }); + + if (sync) { + this._start(); + } else { + if (document.readyState === 'complete') { + this._start(); + } else { + window.addEventListener('load', this._start.bind(this), false); + } + } +}; + +// This is a pointer to our global mode p5 instance, if we're in +// global mode. +p5.instance = null; + +// Allows for the friendly error system to be turned off when creating a sketch, +// which can give a significant boost to performance when needed. +p5.disableFriendlyErrors = false; + +// attach constants to p5 prototype +for (var k in constants) { + p5.prototype[k] = constants[k]; +} + +// functions that cause preload to wait +// more can be added by using registerPreloadMethod(func) +p5.prototype._preloadMethods = { + loadJSON: p5.prototype, + loadImage: p5.prototype, + loadStrings: p5.prototype, + loadXML: p5.prototype, + loadShape: p5.prototype, + loadTable: p5.prototype, + loadFont: p5.prototype, + loadModel: p5.prototype +}; + +p5.prototype._registeredMethods = { init: [], pre: [], post: [], remove: [] }; + +p5.prototype._registeredPreloadMethods = {}; + +p5.prototype.registerPreloadMethod = function(fnString, obj) { + // obj = obj || p5.prototype; + if (!p5.prototype._preloadMethods.hasOwnProperty(fnString)) { + p5.prototype._preloadMethods[fnString] = obj; + } +}; + +p5.prototype.registerMethod = function(name, m) { + if (!p5.prototype._registeredMethods.hasOwnProperty(name)) { + p5.prototype._registeredMethods[name] = []; + } + p5.prototype._registeredMethods[name].push(m); +}; + +p5.prototype._createFriendlyGlobalFunctionBinder = function(options) { + options = options || {}; + + var globalObject = options.globalObject || window; + var log = options.log || console.log.bind(console); + var propsToForciblyOverwrite = { + // p5.print actually always overwrites an existing global function, + // albeit one that is very unlikely to be used: + // + // https://developer.mozilla.org/en-US/docs/Web/API/Window/print + 'print': true + }; + + return function(prop, value) { + if (!p5.disableFriendlyErrors && + typeof(IS_MINIFIED) === 'undefined' && + typeof(value) === 'function' && + !(prop in p5.prototype._preloadMethods)) { + try { + // Because p5 has so many common function names, it's likely + // that users may accidentally overwrite global p5 functions with + // their own variables. Let's allow this but log a warning to + // help users who may be doing this unintentionally. + // + // For more information, see: + // + // https://github.com/processing/p5.js/issues/1317 + + if (prop in globalObject && !(prop in propsToForciblyOverwrite)) { + throw new Error('global "' + prop + '" already exists'); + } + + // It's possible that this might throw an error because there + // are a lot of edge-cases in which `Object.defineProperty` might + // not succeed; since this functionality is only intended to + // help beginners anyways, we'll just catch such an exception + // if it occurs, and fall back to legacy behavior. + Object.defineProperty(globalObject, prop, { + configurable: true, + enumerable: true, + get: function() { + return value; + }, + set: function(newValue) { + Object.defineProperty(globalObject, prop, { + configurable: true, + enumerable: true, + value: newValue, + writable: true + }); + log( + 'You just changed the value of "' + prop + '", which was ' + + 'a p5 function. This could cause problems later if you\'re ' + + 'not careful.' + ); + } + }); + } catch (e) { + log( + 'p5 had problems creating the global function "' + prop + '", ' + + 'possibly because your code is already using that name as ' + + 'a variable. You may want to rename your variable to something ' + + 'else.' + ); + globalObject[prop] = value; + } + } else { + globalObject[prop] = value; + } + }; +}; + +module.exports = p5; + +},{"./constants":36,"./shim":46}],38:[function(_dereq_,module,exports){ +/** + * @module Shape + * @submodule Curves + * @for p5 + * @requires core + */ + +'use strict'; + +var p5 = _dereq_('./core'); + +_dereq_('./error_helpers'); + +var bezierDetail = 20; +var curveDetail = 20; + +/** + * Draws a cubic Bezier curve on the screen. These curves are defined by a + * series of anchor and control points. The first two parameters specify + * the first anchor point and the last two parameters specify the other + * anchor point, which become the first and last points on the curve. The + * middle parameters specify the two control points which define the shape + * of the curve. Approximately speaking, control points "pull" the curve + * towards them.<br /><br />Bezier curves were developed by French + * automotive engineer Pierre Bezier, and are commonly used in computer + * graphics to define gently sloping curves. See also curve(). + * + * @method bezier + * @param {Number} x1 x-coordinate for the first anchor point + * @param {Number} y1 y-coordinate for the first anchor point + * @param {Number} x2 x-coordinate for the first control point + * @param {Number} y2 y-coordinate for the first control point + * @param {Number} x3 x-coordinate for the second control point + * @param {Number} y3 y-coordinate for the second control point + * @param {Number} x4 x-coordinate for the second anchor point + * @param {Number} y4 y-coordinate for the second anchor point + * @return {Object} the p5 object + * @example + * <div> + * <code> + * noFill(); + * stroke(255, 102, 0); + * line(85, 20, 10, 10); + * line(90, 90, 15, 80); + * stroke(0, 0, 0); + * bezier(85, 20, 10, 10, 90, 90, 15, 80); + * </code> + * </div> + * @alt + * stretched black s-shape in center with orange lines extending from end points. + * stretched black s-shape with 10 5x5 white ellipses along the shape. + * stretched black s-shape with 7 5x5 ellipses and orange lines along the shape. + * stretched black s-shape with 17 small orange lines extending from under shape. + * horseshoe shape with orange ends facing left and black curved center. + * horseshoe shape with orange ends facing left and black curved center. + * Line shaped like right-facing arrow,points move with mouse-x and warp shape. + * horizontal line that hooks downward on the right and 13 5x5 ellipses along it. + * right curving line mid-right of canvas with 7 short lines radiating from it. + */ +/** + * @method bezier + * @param {Number} z1 z-coordinate for the first anchor point + * @param {Number} z2 z-coordinate for the first control point + * @param {Number} z3 z-coordinate for the first anchor point + * @param {Number} z4 z-coordinate for the first control point + * @return {p5.Renderer3D} [description] + * @example + * <div> + * <code> + *background(0, 0, 0); + *noFill(); + *stroke(255); + *bezier(250,250,0, 100,100,0, 100,0,0, 0,100,0); + * </code> + * </div> +*/ +p5.prototype.bezier = function() { + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + if(this._renderer.isP3D){ + this._validateParameters( + 'bezier', + args, + ['Number', 'Number', 'Number', + 'Number', 'Number', 'Number', + 'Number', 'Number', 'Number', + 'Number', 'Number', 'Number' + ] + ); + } else{ + this._validateParameters( + 'bezier', + args, + [ 'Number', 'Number', 'Number', 'Number', + 'Number', 'Number', 'Number', 'Number' ] + ); + } + if (!this._renderer._doStroke) { + return this; + } + if (this._renderer.isP3D){ + args.push(bezierDetail);//adding value of bezier detail to the args array + this._renderer.bezier(args); + } else{ + this._renderer.bezier(args[0],args[1], + args[2],args[3], + args[4],args[5], + args[6],args[7]); + } + + return this; +}; + +/** + * Sets the resolution at which Beziers display. + * + * The default value is 20. + * + * @param {Number} detail resolution of the curves + * @return {Object} the p5 object + * @example + * <div> + * <code> + * background(204); + * bezierDetail(50); + * bezier(85, 20, 10, 10, 90, 90, 15, 80); + * </code> + * </div> + * + * @alt + * stretched black s-shape with 7 5x5 ellipses and orange lines along the shape. + * + */ +p5.prototype.bezierDetail = function(d) { + bezierDetail = d; + return this; +}; + +/** + * Evaluates the Bezier at position t for points a, b, c, d. + * The parameters a and d are the first and last points + * on the curve, and b and c are the control points. + * The final parameter t varies between 0 and 1. + * This can be done once with the x coordinates and a second time + * with the y coordinates to get the location of a bezier curve at t. + * + * @method bezierPoint + * @param {Number} a coordinate of first point on the curve + * @param {Number} b coordinate of first control point + * @param {Number} c coordinate of second control point + * @param {Number} d coordinate of second point on the curve + * @param {Number} t value between 0 and 1 + * @return {Number} the value of the Bezier at position t + * @example + * <div> + * <code> + * noFill(); + * x1 = 85, x2 = 10, x3 = 90, x4 = 15; + * y1 = 20, y2 = 10, y3 = 90, y4 = 80; + * bezier(x1, y1, x2, y2, x3, y3, x4, y4); + * fill(255); + * steps = 10; + * for (i = 0; i <= steps; i++) { + * t = i / steps; + * x = bezierPoint(x1, x2, x3, x4, t); + * y = bezierPoint(y1, y2, y3, y4, t); + * ellipse(x, y, 5, 5); + * } + * </code> + * </div> + * + * @alt + * stretched black s-shape with 17 small orange lines extending from under shape. + * + */ +p5.prototype.bezierPoint = function(a, b, c, d, t) { + var adjustedT = 1-t; + return Math.pow(adjustedT,3)*a + + 3*(Math.pow(adjustedT,2))*t*b + + 3*adjustedT*Math.pow(t,2)*c + + Math.pow(t,3)*d; +}; + +/** + * Evaluates the tangent to the Bezier at position t for points a, b, c, d. + * The parameters a and d are the first and last points + * on the curve, and b and c are the control points. + * The final parameter t varies between 0 and 1. + * + * @method bezierTangent + * @param {Number} a coordinate of first point on the curve + * @param {Number} b coordinate of first control point + * @param {Number} c coordinate of second control point + * @param {Number} d coordinate of second point on the curve + * @param {Number} t value between 0 and 1 + * @return {Number} the tangent at position t + * @example + * <div> + * <code> + * noFill(); + * bezier(85, 20, 10, 10, 90, 90, 15, 80); + * steps = 6; + * fill(255); + * for (i = 0; i <= steps; i++) { + * t = i / steps; + * // Get the location of the point + * x = bezierPoint(85, 10, 90, 15, t); + * y = bezierPoint(20, 10, 90, 80, t); + * // Get the tangent points + * tx = bezierTangent(85, 10, 90, 15, t); + * ty = bezierTangent(20, 10, 90, 80, t); + * // Calculate an angle from the tangent points + * a = atan2(ty, tx); + * a += PI; + * stroke(255, 102, 0); + * line(x, y, cos(a)*30 + x, sin(a)*30 + y); + * // The following line of code makes a line + * // inverse of the above line + * //line(x, y, cos(a)*-30 + x, sin(a)*-30 + y); + * stroke(0); + * ellipse(x, y, 5, 5); + * } + * </code> + * </div> + * + * <div> + * <code> + * noFill(); + * bezier(85, 20, 10, 10, 90, 90, 15, 80); + * stroke(255, 102, 0); + * steps = 16; + * for (i = 0; i <= steps; i++) { + * t = i / steps; + * x = bezierPoint(85, 10, 90, 15, t); + * y = bezierPoint(20, 10, 90, 80, t); + * tx = bezierTangent(85, 10, 90, 15, t); + * ty = bezierTangent(20, 10, 90, 80, t); + * a = atan2(ty, tx); + * a -= HALF_PI; + * line(x, y, cos(a)*8 + x, sin(a)*8 + y); + * } + * </code> + * </div> + * + * @alt + * s-shaped line with 17 short orange lines extending from underside of shape + * + */ +p5.prototype.bezierTangent = function(a, b, c, d, t) { + var adjustedT = 1-t; + return 3*d*Math.pow(t,2) - + 3*c*Math.pow(t,2) + + 6*c*adjustedT*t - + 6*b*adjustedT*t + + 3*b*Math.pow(adjustedT,2) - + 3*a*Math.pow(adjustedT,2); +}; + +/** + * Draws a curved line on the screen between two points, given as the + * middle four parameters. The first two parameters are a control point, as + * if the curve came from this point even though it's not drawn. The last + * two parameters similarly describe the other control point. <br /><br /> + * Longer curves can be created by putting a series of curve() functions + * together or using curveVertex(). An additional function called + * curveTightness() provides control for the visual quality of the curve. + * The curve() function is an implementation of Catmull-Rom splines. + * + * @method curve + * @param {Number} x1 x-coordinate for the beginning control point + * @param {Number} y1 y-coordinate for the beginning control point + * @param {Number} x2 x-coordinate for the first point + * @param {Number} y2 y-coordinate for the first point + * @param {Number} x3 x-coordinate for the second point + * @param {Number} y3 y-coordinate for the second point + * @param {Number} x4 x-coordinate for the ending control point + * @param {Number} y4 y-coordinate for the ending control point + * @return {Object} the p5 object + * @example + * <div> + * <code> + * noFill(); + * stroke(255, 102, 0); + * curve(5, 26, 5, 26, 73, 24, 73, 61); + * stroke(0); + * curve(5, 26, 73, 24, 73, 61, 15, 65); + * stroke(255, 102, 0); + * curve(73, 24, 73, 61, 15, 65, 15, 65); + * </code> + * </div> + * <div> + * <code> + * // Define the curve points as JavaScript objects + * p1 = {x: 5, y: 26}, p2 = {x: 73, y: 24} + * p3 = {x: 73, y: 61}, p4 = {x: 15, y: 65} + * noFill(); + * stroke(255, 102, 0); + * curve(p1.x, p1.y, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y) + * stroke(0); + * curve(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y) + * stroke(255, 102, 0); + * curve(p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, p4.x, p4.y) + * </code> + * </div> + * + * @alt + * horseshoe shape with orange ends facing left and black curved center. + * horseshoe shape with orange ends facing left and black curved center. + * + */ +/** + * @method curve + * @param {Number} z1 z-coordinate for the beginning control point + * @param {Number} z2 z-coordinate for the first point + * @param {Number} z3 z-coordinate for the second point + * @param {Number} z4 z-coordinate for the ending control point + * @return {Object} the p5 object + * @example + * <div> + * <code> + * noFill(); + * stroke(255, 102, 0); + * curve(5,26,0, 5,26,0, 73,24,0, 73,61,0); + * stroke(0); + * curve(5,26,0, 73,24,0, 73,61,0, 15,65,0); + * stroke(255, 102, 0); + * curve(73,24,0, 73,61,0, 15,65,0, 15,65,0); + * </code> + * </div> + * + * @alt + * curving black and orange lines. + */ +p5.prototype.curve = function() { + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + if(this._renderer.isP3D){ + this._validateParameters( + 'curve', + args, + ['Number', 'Number', 'Number', + 'Number', 'Number', 'Number', + 'Number', 'Number', 'Number', + 'Number', 'Number', 'Number' + ] + ); + } else{ + this._validateParameters( + 'curve', + args, + [ 'Number', 'Number', 'Number', 'Number', + 'Number', 'Number', 'Number', 'Number' ] + ); + } + if (!this._renderer._doStroke) { + return this; + } + if (this._renderer.isP3D){ + args.push(curveDetail); + this._renderer.curve(args); + } else{ + this._renderer.curve(args[0],args[1], + args[2],args[3], + args[4],args[5], + args[6],args[7]); + } + return this; +}; + +/** + * Sets the resolution at which curves display. + * + * The default value is 20. + * + * @param {Number} resolution of the curves + * @return {Object} the p5 object + * @example + * <div> + * <code> + * background(204); + * curveDetail(20); + * curve(5, 26, 5, 26, 73, 24, 73, 61); + * </code> + * </div> + * + * @alt + * white arch shape in top-mid canvas. + * + */ +p5.prototype.curveDetail = function(d) { + curveDetail = d; + return this; +}; + +/** + * Modifies the quality of forms created with curve() and curveVertex(). + * The parameter tightness determines how the curve fits to the vertex + * points. The value 0.0 is the default value for tightness (this value + * defines the curves to be Catmull-Rom splines) and the value 1.0 connects + * all the points with straight lines. Values within the range -5.0 and 5.0 + * will deform the curves but will leave them recognizable and as values + * increase in magnitude, they will continue to deform. + * + * @method curveTightness + * @param {Number} amount of deformation from the original vertices + * @return {Object} the p5 object + * @example + * <div> + * <code> + * // Move the mouse left and right to see the curve change + * + * function setup() { + * createCanvas(100, 100); + * noFill(); + * } + * + * function draw() { + * background(204); + * var t = map(mouseX, 0, width, -5, 5); + * curveTightness(t); + * beginShape(); + * curveVertex(10, 26); + * curveVertex(10, 26); + * curveVertex(83, 24); + * curveVertex(83, 61); + * curveVertex(25, 65); + * curveVertex(25, 65); + * endShape(); + * } + * </code> + * </div> + * + * @alt + * Line shaped like right-facing arrow,points move with mouse-x and warp shape. + */ +p5.prototype.curveTightness = function (t) { + this._renderer._curveTightness = t; +}; + +/** + * Evaluates the curve at position t for points a, b, c, d. + * The parameter t varies between 0 and 1, a and d are points + * on the curve, and b and c are the control points. + * This can be done once with the x coordinates and a second time + * with the y coordinates to get the location of a curve at t. + * + * @method curvePoint + * @param {Number} a coordinate of first point on the curve + * @param {Number} b coordinate of first control point + * @param {Number} c coordinate of second control point + * @param {Number} d coordinate of second point on the curve + * @param {Number} t value between 0 and 1 + * @return {Number} bezier value at position t + * @example + * <div> + * <code> + * noFill(); + * curve(5, 26, 5, 26, 73, 24, 73, 61); + * curve(5, 26, 73, 24, 73, 61, 15, 65); + * fill(255); + * ellipseMode(CENTER); + * steps = 6; + * for (i = 0; i <= steps; i++) { + * t = i / steps; + * x = curvePoint(5, 5, 73, 73, t); + * y = curvePoint(26, 26, 24, 61, t); + * ellipse(x, y, 5, 5); + * x = curvePoint(5, 73, 73, 15, t); + * y = curvePoint(26, 24, 61, 65, t); + * ellipse(x, y, 5, 5); + * } + * </code> + * </div> + * + *line hooking down to right-bottom with 13 5x5 white ellipse points + */ +p5.prototype.curvePoint = function(a, b, c, d, t) { + var t3 = t*t*t, + t2 = t*t, + f1 = -0.5 * t3 + t2 - 0.5 * t, + f2 = 1.5 * t3 - 2.5 * t2 + 1.0, + f3 = -1.5 * t3 + 2.0 * t2 + 0.5 * t, + f4 = 0.5 * t3 - 0.5 * t2; + return a*f1 + b*f2 + c*f3 + d*f4; +}; + +/** + * Evaluates the tangent to the curve at position t for points a, b, c, d. + * The parameter t varies between 0 and 1, a and d are points on the curve, + * and b and c are the control points. + * + * @method curveTangent + * @param {Number} a coordinate of first point on the curve + * @param {Number} b coordinate of first control point + * @param {Number} c coordinate of second control point + * @param {Number} d coordinate of second point on the curve + * @param {Number} t value between 0 and 1 + * @return {Number} the tangent at position t + * @example + * <div> + * <code> + * noFill(); + * curve(5, 26, 73, 24, 73, 61, 15, 65); + * steps = 6; + * for (i = 0; i <= steps; i++) { + * t = i / steps; + * x = curvePoint(5, 73, 73, 15, t); + * y = curvePoint(26, 24, 61, 65, t); + * //ellipse(x, y, 5, 5); + * tx = curveTangent(5, 73, 73, 15, t); + * ty = curveTangent(26, 24, 61, 65, t); + * a = atan2(ty, tx); + * a -= PI/2.0; + * line(x, y, cos(a)*8 + x, sin(a)*8 + y); + * } + * </code> + * </div> + * + * @alt + *right curving line mid-right of canvas with 7 short lines radiating from it. + */ +p5.prototype.curveTangent = function(a, b,c, d, t) { + var t2 = t*t, + f1 = (-3*t2)/2 + 2*t - 0.5, + f2 = (9*t2)/2 - 5*t, + f3 = (-9*t2)/2 + 4*t + 0.5, + f4 = (3*t2)/2 - t; + return a*f1 + b*f2 + c*f3 + d*f4; +}; + +module.exports = p5; + +},{"./core":37,"./error_helpers":40}],39:[function(_dereq_,module,exports){ +/** + * @module Environment + * @submodule Environment + * @for p5 + * @requires core + * @requires constants + */ + +'use strict'; + +var p5 = _dereq_('./core'); +var C = _dereq_('./constants'); + +var standardCursors = [C.ARROW, C.CROSS, C.HAND, C.MOVE, C.TEXT, C.WAIT]; + +p5.prototype._frameRate = 0; +p5.prototype._lastFrameTime = window.performance.now(); +p5.prototype._targetFrameRate = 60; + +var _windowPrint = window.print; + + +if (window.console && console.log) { + /** + * The print() function writes to the console area of your browser. + * This function is often helpful for looking at the data a program is + * producing. This function creates a new line of text for each call to + * the function. Individual elements can be + * separated with quotes ("") and joined with the addition operator (+). + * <br><br> + * While print() is similar to console.log(), it does not directly map to + * it in order to simulate easier to understand behavior than + * console.log(). Due to this, it is slower. For fastest results, use + * console.log(). + * + * @method print + * @param {Any} contents any combination of Number, String, Object, Boolean, + * Array to print + * @example + * <div><code class='norender'> + * var x = 10; + * print("The value of x is " + x); + * // prints "The value of x is 10" + * </code></div> + * @alt + * default grey canvas + */ + // Converts passed args into a string and then parses that string to + // simulate synchronous behavior. This is a hack and is gross. + // Since this will not work on all objects, particularly circular + // structures, simply console.log() on error. + p5.prototype.print = function(args) { + try { + if (arguments.length === 0) { + _windowPrint(); + } + else if (arguments.length > 1) { + console.log.apply(console, arguments); + } else { + var newArgs = JSON.parse(JSON.stringify(args)); + console.log(newArgs); + } + } catch(err) { + console.log(args); + } + }; +} else { + p5.prototype.print = function() {}; +} + + +/** + * The system variable frameCount contains the number of frames that have + * been displayed since the program started. Inside setup() the value is 0, + * after the first iteration of draw it is 1, etc. + * + * @property frameCount + * @example + * <div><code> + * function setup() { + * frameRate(30); + * textSize(20); + * textSize(30); + * textAlign(CENTER); + * } + * + * function draw() { + * background(200); + * text(frameCount, width/2, height/2); + * } + * </code></div> + * + * @alt + * numbers rapidly counting upward with frame count set to 30. + * + */ +p5.prototype.frameCount = 0; + +/** + * Confirms if the window a p5.js program is in is "focused," meaning that + * the sketch will accept mouse or keyboard input. This variable is + * "true" if the window is focused and "false" if not. + * + * @property focused + * @example + * <div><code> + * // To demonstrate, put two windows side by side. + * // Click on the window that the p5 sketch isn't in! + * function draw() { + * background(200); + * noStroke(); + * fill(0, 200, 0); + * ellipse(25, 25, 50, 50); + * + * if (!focused) { // or "if (focused === false)" + * stroke(200,0,0); + * line(0, 0, 100, 100); + * line(100, 0, 0, 100); + * } + * } + * </code></div> + * + * @alt + * green 50x50 ellipse at top left. Red X covers canvas when page focus changes + * + */ +p5.prototype.focused = (document.hasFocus()); + +/** + * Sets the cursor to a predefined symbol or an image, or makes it visible + * if already hidden. If you are trying to set an image as the cursor, the + * recommended size is 16x16 or 32x32 pixels. It is not possible to load an + * image as the cursor if you are exporting your program for the Web, and not + * all MODES work with all browsers. The values for parameters x and y must + * be less than the dimensions of the image. + * + * @method cursor + * @param {Number/Constant} type either ARROW, CROSS, HAND, MOVE, TEXT, or + * WAIT, or path for image + * @param {Number} [x] the horizontal active spot of the cursor + * @param {Number} [y] the vertical active spot of the cursor + * @example + * <div><code> + * // Move the mouse left and right across the image + * // to see the cursor change from a cross to a hand + * function draw() { + * line(width/2, 0, width/2, height); + * if (mouseX < 50) { + * cursor(CROSS); + * } else { + * cursor(HAND); + * } + * } + * </code></div> + * + * @alt + * horizontal line divides canvas. cursor on left is a cross, right is hand. + * + */ +p5.prototype.cursor = function(type, x, y) { + var cursor = 'auto'; + var canvas = this._curElement.elt; + if (standardCursors.indexOf(type) > -1) { + // Standard css cursor + cursor = type; + } else if (typeof type === 'string') { + var coords = ''; + if (x && y && (typeof x === 'number' && typeof y === 'number')) { + // Note that x and y values must be unit-less positive integers < 32 + // https://developer.mozilla.org/en-US/docs/Web/CSS/cursor + coords = x + ' ' + y; + } + if (type.substring(0, 6) !== 'http://') { + // Image (absolute url) + cursor = 'url(' + type + ') ' + coords + ', auto'; + } else if (/\.(cur|jpg|jpeg|gif|png|CUR|JPG|JPEG|GIF|PNG)$/.test(type)) { + // Image file (relative path) - Separated for performance reasons + cursor = 'url(' + type + ') ' + coords + ', auto'; + } else { + // Any valid string for the css cursor property + cursor = type; + } + } + canvas.style.cursor = cursor; +}; + +/** + * Specifies the number of frames to be displayed every second. For example, + * the function call frameRate(30) will attempt to refresh 30 times a second. + * If the processor is not fast enough to maintain the specified rate, the + * frame rate will not be achieved. Setting the frame rate within setup() is + * recommended. The default rate is 60 frames per second. This is the same as + * setFrameRate(val). + * <br><br> + * Calling frameRate() with no arguments returns the current framerate. This + * is the same as getFrameRate(). + * <br><br> + * Calling frameRate() with arguments that are not of the type numbers + * or are non positive also returns current framerate. + * + * @method frameRate + * @param {Number} [fps] number of frames to be displayed every second + * @return {Number} current frameRate + * @example + * + * <div><code> + * var rectX = 0; + * var fr = 30; //starting FPS + * var clr; + * + * function setup() { + * background(200); + * frameRate(fr); // Attempt to refresh at starting FPS + * clr = color(255,0,0); + * } + * + * function draw() { + * background(200); + * rectX = rectX += 1; // Move Rectangle + * + * if (rectX >= width) { // If you go off screen. + * if (fr == 30) { + * clr = color(0,0,255); + * fr = 10; + * frameRate(fr); // make frameRate 10 FPS + * } else { + * clr = color(255,0,0); + * fr = 30; + * frameRate(fr); // make frameRate 30 FPS + * } + * rectX = 0; + * } + * fill(clr); + * rect(rectX, 40, 20,20); + * } + * </div></code> + * + * @alt + * blue rect moves left to right, followed by red rect moving faster. Loops. + * + */ +p5.prototype.frameRate = function(fps) { + if (typeof fps !== 'number' || fps <= 0) { + return this._frameRate; + } else { + this._setProperty('_targetFrameRate', fps); + this._runFrames(); + return this; + } +}; +/** + * Returns the current framerate. + * + * @return {Number} current frameRate + */ +p5.prototype.getFrameRate = function() { + return this.frameRate(); +}; + +/** + * Specifies the number of frames to be displayed every second. For example, + * the function call frameRate(30) will attempt to refresh 30 times a second. + * If the processor is not fast enough to maintain the specified rate, the + * frame rate will not be achieved. Setting the frame rate within setup() is + * recommended. The default rate is 60 frames per second. + * + * Calling frameRate() with no arguments returns the current framerate. + * + * @param {Number} [fps] number of frames to be displayed every second + */ +p5.prototype.setFrameRate = function(fps) { + return this.frameRate(fps); +}; + +/** + * Hides the cursor from view. + * + * @method noCursor + * @example + * <div><code> + * function setup() { + * noCursor(); + * } + * + * function draw() { + * background(200); + * ellipse(mouseX, mouseY, 10, 10); + * } + * </code></div> + * + * + * @alt + * cursor becomes 10x 10 white ellipse the moves with mouse x and y. + * + */ +p5.prototype.noCursor = function() { + this._curElement.elt.style.cursor = 'none'; +}; + + +/** + * System variable that stores the width of the entire screen display. This + * is used to run a full-screen program on any display size. + * + * @property displayWidth + * @example + * <div class="norender"><code> + * createCanvas(displayWidth, displayHeight); + * </code></div> + * + * @alt + * cursor becomes 10x 10 white ellipse the moves with mouse x and y. + * + */ +p5.prototype.displayWidth = screen.width; + +/** + * System variable that stores the height of the entire screen display. This + * is used to run a full-screen program on any display size. + * + * @property displayHeight + * @example + * <div class="norender"><code> + * createCanvas(displayWidth, displayHeight); + * </code></div> + * + * @alt + * no display. + * + */ +p5.prototype.displayHeight = screen.height; + +/** + * System variable that stores the width of the inner window, it maps to + * window.innerWidth. + * + * @property windowWidth + * @example + * <div class="norender"><code> + * createCanvas(windowWidth, windowHeight); + * </code></div> + * + * @alt + * no display. + * + */ +p5.prototype.windowWidth = getWindowWidth(); +/** + * System variable that stores the height of the inner window, it maps to + * window.innerHeight. + * + * @property windowHeight + * @example + * <div class="norender"><code> + * createCanvas(windowWidth, windowHeight); + * </code></div> +*@alt + * no display. + * +*/ +p5.prototype.windowHeight = getWindowHeight(); + +/** + * The windowResized() function is called once every time the browser window + * is resized. This is a good place to resize the canvas or do any other + * adjustements to accomodate the new window size. + * + * @method windowResized + * @example + * <div class="norender"><code> + * function setup() { + * createCanvas(windowWidth, windowHeight); + * } + * + * function draw() { + * background(0, 100, 200); + * } + * + * function windowResized() { + * resizeCanvas(windowWidth, windowHeight); + * } + * </code></div> + * @alt + * no display. + */ +p5.prototype._onresize = function(e){ + this._setProperty('windowWidth', getWindowWidth()); + this._setProperty('windowHeight', getWindowHeight()); + var context = this._isGlobal ? window : this; + var executeDefault; + if (typeof context.windowResized === 'function') { + executeDefault = context.windowResized(e); + if (executeDefault !== undefined && !executeDefault) { + e.preventDefault(); + } + } +}; + +function getWindowWidth() { + return window.innerWidth || + document.documentElement && document.documentElement.clientWidth || + document.body && document.body.clientWidth || + 0; +} + +function getWindowHeight() { + return window.innerHeight || + document.documentElement && document.documentElement.clientHeight || + document.body && document.body.clientHeight || + 0; +} + +/** + * System variable that stores the width of the drawing canvas. This value + * is set by the first parameter of the createCanvas() function. + * For example, the function call createCanvas(320, 240) sets the width + * variable to the value 320. The value of width defaults to 100 if + * createCanvas() is not used in a program. + * + * @property width + */ +p5.prototype.width = 0; + +/** + * System variable that stores the height of the drawing canvas. This value + * is set by the second parameter of the createCanvas() function. For + * example, the function call createCanvas(320, 240) sets the height + * variable to the value 240. The value of height defaults to 100 if + * createCanvas() is not used in a program. + * + * @property height + */ +p5.prototype.height = 0; + +/** + * If argument is given, sets the sketch to fullscreen or not based on the + * value of the argument. If no argument is given, returns the current + * fullscreen state. Note that due to browser restrictions this can only + * be called on user input, for example, on mouse press like the example + * below. + * + * @method fullscreen + * @param {Boolean} [val] whether the sketch should be in fullscreen mode + * or not + * @return {Boolean} current fullscreen state + * @example + * <div> + * <code> + * // Clicking in the box toggles fullscreen on and off. + * function setup() { + * background(200); + * } + * function mousePressed() { + * if (mouseX > 0 && mouseX < 100 && mouseY > 0 && mouseY < 100) { + * var fs = fullscreen(); + * fullscreen(!fs); + * } + * } + * </code> + * </div> + * + * @alt + * no display. + * + */ +p5.prototype.fullscreen = function(val) { + // no arguments, return fullscreen or not + if (typeof val === 'undefined') { + return document.fullscreenElement || + document.webkitFullscreenElement || + document.mozFullScreenElement || + document.msFullscreenElement; + } else { // otherwise set to fullscreen or not + if (val) { + launchFullscreen(document.documentElement); + } else { + exitFullscreen(); + } + } +}; + +/** + * Sets the pixel scaling for high pixel density displays. By default + * pixel density is set to match display density, call pixelDensity(1) + * to turn this off. Calling pixelDensity() with no arguments returns + * the current pixel density of the sketch. + * + * + * @method pixelDensity + * @param {Number} [val] whether or how much the sketch should scale + * @returns {Number} current pixel density of the sketch + * @example + * <div> + * <code> + * function setup() { + * pixelDensity(1); + * createCanvas(100, 100); + * background(200); + * ellipse(width/2, height/2, 50, 50); + * } + * </code> + * </div> + * <div> + * <code> + * function setup() { + * pixelDensity(3.0); + * createCanvas(100, 100); + * background(200); + * ellipse(width/2, height/2, 50, 50); + * } + * </code> + * </div> + * + * @alt + * fuzzy 50x50 white ellipse with black outline in center of canvas. + * sharp 50x50 white ellipse with black outline in center of canvas. + */ +p5.prototype.pixelDensity = function(val) { + if (typeof val === 'number') { + this._pixelDensity = val; + } else { + return this._pixelDensity; + } + this.resizeCanvas(this.width, this.height, true); +}; + +/** + * Returns the pixel density of the current display the sketch is running on. + * + * @method displayDensity + * @returns {Number} current pixel density of the display + * @example + * <div> + * <code> + * function setup() { + * var density = displayDensity(); + * pixelDensity(density); + * createCanvas(100, 100); + * background(200); + * ellipse(width/2, height/2, 50, 50); + * } + * </code> + * </div> + * + * @alt + * 50x50 white ellipse with black outline in center of canvas. + */ +p5.prototype.displayDensity = function() { + return window.devicePixelRatio; +}; + +function launchFullscreen(element) { + var enabled = document.fullscreenEnabled || + document.webkitFullscreenEnabled || + document.mozFullScreenEnabled || + document.msFullscreenEnabled; + if (!enabled) { + throw new Error('Fullscreen not enabled in this browser.'); + } + if(element.requestFullscreen) { + element.requestFullscreen(); + } else if(element.mozRequestFullScreen) { + element.mozRequestFullScreen(); + } else if(element.webkitRequestFullscreen) { + element.webkitRequestFullscreen(); + } else if(element.msRequestFullscreen) { + element.msRequestFullscreen(); + } +} + +function exitFullscreen() { + if(document.exitFullscreen) { + document.exitFullscreen(); + } else if(document.mozCancelFullScreen) { + document.mozCancelFullScreen(); + } else if(document.webkitExitFullscreen) { + document.webkitExitFullscreen(); + } else if (document.msExitFullscreen) { + document.msExitFullscreen(); + } +} + + +/** + * Gets the current URL. + * @method getURL + * @return {String} url + * @example + * <div> + * <code> + * var url; + * var x = 100; + * + * function setup() { + * fill(0); + * noStroke(); + * url = getURL(); + * } + * + * function draw() { + * background(200); + * text(url, x, height/2); + * x--; + * } + * </code> + * </div> + * + * @alt + * current url (http://p5js.org/reference/#/p5/getURL) moves right to left. + * + */ +p5.prototype.getURL = function() { + return location.href; +}; +/** + * Gets the current URL path as an array. + * @method getURLPath + * @return {Array} path components + * @example + * <div class='norender'><code> + * function setup() { + * var urlPath = getURLPath(); + * for (var i=0; i<urlPath.length; i++) { + * text(urlPath[i], 10, i*20+20); + * } + * } + * </code></div> + * + * @alt + *no display + * + */ +p5.prototype.getURLPath = function() { + return location.pathname.split('/').filter(function(v){return v!=='';}); +}; +/** + * Gets the current URL params as an Object. + * @method getURLParams + * @return {Object} URL params + * @example + * <div class='norender'> + * <code> + * // Example: http://p5js.org?year=2014&month=May&day=15 + * + * function setup() { + * var params = getURLParams(); + * text(params.day, 10, 20); + * text(params.month, 10, 40); + * text(params.year, 10, 60); + * } + * </code> + * </div> + * @alt + * no display. + * + */ +p5.prototype.getURLParams = function() { + var re = /[?&]([^&=]+)(?:[&=])([^&=]+)/gim; + var m; + var v={}; + while ((m = re.exec(location.search)) != null) { + if (m.index === re.lastIndex) { + re.lastIndex++; + } + v[m[1]]=m[2]; + } + return v; +}; + +module.exports = p5; + +},{"./constants":36,"./core":37}],40:[function(_dereq_,module,exports){ +/** + * @for p5 + * @requires core + */ + +'use strict'; + +var p5 = _dereq_('./core'); +var doFriendlyWelcome = false; // TEMP until we get it all working LM + +// -- Borrowed from jQuery 1.11.3 -- +var class2type = {}; +var toString = class2type.toString; +var names = ['Boolean', 'Number', 'String', 'Function', + 'Array', 'Date', 'RegExp', 'Object', 'Error']; +for (var n=0; n<names.length; n++) { + class2type[ '[object ' + names[n] + ']' ] = names[n].toLowerCase(); +} +var getType = function( obj ) { + if ( obj == null ) { + return obj + ''; + } + return typeof obj === 'object' || typeof obj === 'function' ? + class2type[ toString.call(obj) ] || 'object' : + typeof obj; +}; +var isArray = Array.isArray || function( obj ) { + return getType(obj) === 'array'; +}; +var isNumeric =function( obj ) { + // parseFloat NaNs numeric-cast false positives (null|true|false|"") + // ...but misinterprets leading-number strings, particularly hex literals + // subtraction forces infinities to NaN + // adding 1 corrects loss of precision from parseFloat (#15100) + return !isArray( obj ) && (obj - parseFloat( obj ) + 1) >= 0; +}; +// -- End borrow -- + +/** + * Checks the definition type against the argument type + * If any of these passes (in order), it matches: + * + * - p5.* definitions are checked with instanceof + * - Booleans are let through (everything is truthy or falsey) + * - Lowercase of the definition is checked against the js type + * - Number types are checked to see if they are numerically castable + */ +var numberTypes = ['Number', 'Integer', 'Number/Constant']; +function typeMatches(defType, argType, arg) { + if(defType.match(/^p5\./)) { + var parts = defType.split('.'); + return arg instanceof p5[parts[1]]; + } + return defType === 'Boolean' || // Anything is truthy, cover in Debug Guide + (defType.toLowerCase() === argType) || + (numberTypes.indexOf(defType) > -1 && isNumeric(arg)); +} + +/** + * Prints out a fancy, colorful message to the console log + * + * @param {String} message the words to be said + * @param {String} func the name of the function to link + * @param {Integer/Color String} color CSS color string or error type + * + * @return console logs + */ +// Wrong number of params, undefined param, wrong type +var PARAM_COUNT = 0; +var EMPTY_VAR = 1; +var WRONG_TYPE = 2; +var FILE_LOAD = 3; +// p5.js blue, p5.js orange, auto dark green; fallback p5.js darkened magenta +// See testColors below for all the color codes and names +var typeColors = ['#2D7BB6', '#EE9900', '#4DB200', '#C83C00']; +function report(message, func, color) { + if(doFriendlyWelcome){ + friendlyWelcome(); + doFriendlyWelcome =false; + } + if ('undefined' === getType(color)) { + color = '#B40033'; // dark magenta + } else if (getType(color) === 'number') { // Type to color + color = typeColors[color]; + } + // LM TEMP commenting this out until we get the whole system working + // if (func.substring(0,4) === 'load'){ + // console.log( + // '%c> p5.js says: '+message+'%c'+ + // '[https://github.com/processing/p5.js/wiki/Local-server]', + // 'background-color:' + color + ';color:#FFF;', + // 'background-color:transparent;color:' + color +';', + // 'background-color:' + color + ';color:#FFF;', + // 'background-color:transparent;color:' + color +';' + // ); + // } + // else{ + // console.log( + // '%c> p5.js says: '+message+'%c [http://p5js.org/reference/#p5/'+func+ + // ']', 'background-color:' + color + ';color:#FFF;', + // 'background-color:transparent;color:' + color +';' + // ); + // } +} + +/** + * Validate all the parameters of a function for number and type + * NOTE THIS FUNCTION IS TEMPORARILY DISABLED UNTIL FURTHER WORK + * AND UPDATES ARE IMPLEMENTED. -LMCCART + * + * @param {String} func name of function we're checking + * @param {Array} args pass of the JS default arguments array + * @param {Array} types List of types accepted ['Number', 'String, ...] OR + * a list of lists for each format: [ + * ['String', 'Number', 'Number'], + * ['String', 'Number', 'Number', 'Number', 'Number' + * ] + * + * @return console logs + */ +p5.prototype._validateParameters = function(func, args, types) { + if (!isArray(types[0])) { + types = [types]; + } + // Check number of parameters + // Example: "You wrote ellipse(X,X,X). ellipse was expecting 4 + // parameters. Try ellipse(X,X,X,X)." + var diff = Math.abs(args.length-types[0].length); + var message, tindex = 0; + for (var i=1, len=types.length; i<len; i++) { + var d = Math.abs(args.length-types[i].length); + if (d <= diff) { + tindex = i; + diff = d; + } + } + var symbol = 'X'; // Parameter placeholder + if(diff > 0) { + message = 'You wrote ' + func + '('; + // Concat an appropriate number of placeholders for call + if (args.length > 0) { + message += symbol + Array(args.length).join(',' + symbol); + } + message += '). ' + func + ' was expecting ' + types[tindex].length + + ' parameters. Try ' + func + '('; + // Concat an appropriate number of placeholders for definition + if (types[tindex].length > 0) { + message += symbol + Array(types[tindex].length).join(',' + symbol); + } + message += ').'; + // If multiple definitions + if (types.length > 1) { + message += ' ' + func + ' takes different numbers of parameters ' + + 'depending on what you want to do. Click this link to learn more: '; + } + report(message, func, PARAM_COUNT); + } + // Type checking + // Example: "It looks like ellipse received an empty variable in spot #2." + // Example: "ellipse was expecting a number for parameter #1, + // received "foo" instead." + for (var format=0; format<types.length; format++) { + for (var p=0; p < types[format].length && p < args.length; p++) { + var defType = types[format][p]; + var argType = getType(args[p]); + if ('undefined' === argType || null === argType) { + report('It looks like ' + func + + ' received an empty variable in spot #' + (p+1) + + '. If not intentional, this is often a problem with scope: ' + + '[link to scope].', func, EMPTY_VAR); + } else if (defType !== '*' && !typeMatches(defType, argType, args[p])) { + message = func + ' was expecting a ' + defType.toLowerCase() + + ' for parameter #' + (p+1) + ', received '; + // Wrap strings in quotes + message += 'string' === argType ? '"' + args[p] + '"' : args[p]; + message += ' instead.'; + // If multiple definitions + if (types.length > 1) { + message += ' ' + func + ' takes different numbers of parameters ' + + 'depending on what you want to do. ' + + 'Click this link to learn more:'; + } + report(message, func, WRONG_TYPE); + } + } + } +}; +/* + * NOTE THIS FUNCTION IS TEMPORARILY DISABLED UNTIL FURTHER WORK + * AND UPDATES ARE IMPLEMENTED. -LMCCART + */ +p5.prototype._validateParameters = function() { + return true; +}; + +var errorCases = { + '0': { + fileType: 'image', + method: 'loadImage', + message: ' hosting the image online,' + }, + '1': { + fileType: 'XML file', + method: 'loadXML' + }, + '2': { + fileType: 'table file', + method: 'loadTable' + }, + '3': { + fileType: 'text file', + method: 'loadStrings' + }, + '4': { + fileType: 'font', + method: 'loadFont', + message: ' hosting the font online,' + }, +}; +p5._friendlyFileLoadError = function (errorType, filePath) { + var errorInfo = errorCases[ errorType ]; + var message = 'It looks like there was a problem' + + ' loading your ' + errorInfo.fileType + '.' + + ' Try checking if the file path%c [' + filePath + '] %cis correct,' + + (errorInfo.message || '') + ' or running a local server.'; + report(message, errorInfo.method, FILE_LOAD); +}; + +function friendlyWelcome() { + // p5.js brand - magenta: #ED225D + var astrixBgColor = 'transparent'; + var astrixTxtColor = '#ED225D'; + var welcomeBgColor = '#ED225D'; + var welcomeTextColor = 'white'; + console.log( + '%c _ \n'+ + ' /\\| |/\\ \n'+ + ' \\ ` \' / \n'+ + ' / , . \\ \n'+ + ' \\/|_|\\/ '+ + '\n\n%c> p5.js says: Welcome! '+ + 'This is your friendly debugger. ' + + 'To turn me off switch to using “p5.min.js”.', + 'background-color:'+astrixBgColor+';color:' + astrixTxtColor +';', + 'background-color:'+welcomeBgColor+';color:' + welcomeTextColor +';' + ); +} + +/** + * Prints out all the colors in the color pallete with white text. + * For color blindness testing. + */ +/* function testColors() { + var str = 'A box of biscuits, a box of mixed biscuits and a biscuit mixer'; + report(str, 'print', '#ED225D'); // p5.js magenta + report(str, 'print', '#2D7BB6'); // p5.js blue + report(str, 'print', '#EE9900'); // p5.js orange + report(str, 'print', '#A67F59'); // p5.js light brown + report(str, 'print', '#704F21'); // p5.js gold + report(str, 'print', '#1CC581'); // auto cyan + report(str, 'print', '#FF6625'); // auto orange + report(str, 'print', '#79EB22'); // auto green + report(str, 'print', '#B40033'); // p5.js darkened magenta + report(str, 'print', '#084B7F'); // p5.js darkened blue + report(str, 'print', '#945F00'); // p5.js darkened orange + report(str, 'print', '#6B441D'); // p5.js darkened brown + report(str, 'print', '#2E1B00'); // p5.js darkened gold + report(str, 'print', '#008851'); // auto dark cyan + report(str, 'print', '#C83C00'); // auto dark orange + report(str, 'print', '#4DB200'); // auto dark green +} */ + +// This is a lazily-defined list of p5 symbols that may be +// misused by beginners at top-level code, outside of setup/draw. We'd like +// to detect these errors and help the user by suggesting they move them +// into setup/draw. +// +// For more details, see https://github.com/processing/p5.js/issues/1121. +var misusedAtTopLevelCode = null; +var FAQ_URL = 'https://github.com/processing/p5.js/wiki/' + + 'Frequently-Asked-Questions' + + '#why-cant-i-assign-variables-using-p5-functions-and-' + + 'variables-before-setup'; + +function defineMisusedAtTopLevelCode() { + var uniqueNamesFound = {}; + + var getSymbols = function(obj) { + return Object.getOwnPropertyNames(obj).filter(function(name) { + if (name[0] === '_') { + return false; + } + if (name in uniqueNamesFound) { + return false; + } + + uniqueNamesFound[name] = true; + + return true; + }).map(function(name) { + var type; + + if (typeof(obj[name]) === 'function') { + type = 'function'; + } else if (name === name.toUpperCase()) { + type = 'constant'; + } else { + type = 'variable'; + } + + return {name: name, type: type}; + }); + }; + + misusedAtTopLevelCode = [].concat( + getSymbols(p5.prototype), + // At present, p5 only adds its constants to p5.prototype during + // construction, which may not have happened at the time a + // ReferenceError is thrown, so we'll manually add them to our list. + getSymbols(_dereq_('./constants')) + ); + + // This will ultimately ensure that we report the most specific error + // possible to the user, e.g. advising them about HALF_PI instead of PI + // when their code misuses the former. + misusedAtTopLevelCode.sort(function(a, b) { + return b.name.length - a.name.length; + }); +} + +function helpForMisusedAtTopLevelCode(e, log) { + if (!log) { + log = console.log.bind(console); + } + + if (!misusedAtTopLevelCode) { + defineMisusedAtTopLevelCode(); + } + + // If we find that we're logging lots of false positives, we can + // uncomment the following code to avoid displaying anything if the + // user's code isn't likely to be using p5's global mode. (Note that + // setup/draw are more likely to be defined due to JS function hoisting.) + // + //if (!('setup' in window || 'draw' in window)) { + // return; + //} + + misusedAtTopLevelCode.some(function(symbol) { + // Note that while just checking for the occurrence of the + // symbol name in the error message could result in false positives, + // a more rigorous test is difficult because different browsers + // log different messages, and the format of those messages may + // change over time. + // + // For example, if the user uses 'PI' in their code, it may result + // in any one of the following messages: + // + // * 'PI' is undefined (Microsoft Edge) + // * ReferenceError: PI is undefined (Firefox) + // * Uncaught ReferenceError: PI is not defined (Chrome) + + if (e.message && e.message.indexOf(symbol.name) !== -1) { + log('%cDid you just try to use p5.js\'s ' + symbol.name + + (symbol.type === 'function' ? '() ' : ' ') + symbol.type + + '? If so, you may want to ' + + 'move it into your sketch\'s setup() function.\n\n' + + 'For more details, see: ' + FAQ_URL, + 'color: #B40033' /* Dark magenta */); + return true; + } + }); +} + +// Exposing this primarily for unit testing. +p5.prototype._helpForMisusedAtTopLevelCode = helpForMisusedAtTopLevelCode; + +if (document.readyState !== 'complete') { + window.addEventListener('error', helpForMisusedAtTopLevelCode, false); + + // Our job is only to catch ReferenceErrors that are thrown when + // global (non-instance mode) p5 APIs are used at the top-level + // scope of a file, so we'll unbind our error listener now to make + // sure we don't log false positives later. + window.addEventListener('load', function() { + window.removeEventListener('error', helpForMisusedAtTopLevelCode, false); + }); +} + +module.exports = p5; + +},{"./constants":36,"./core":37}],41:[function(_dereq_,module,exports){ +/** + * @module DOM + * @submodule DOM + * @for p5.Element + */ + +var p5 = _dereq_('./core'); + +/** + * Base class for all elements added to a sketch, including canvas, + * graphics buffers, and other HTML elements. Methods in blue are + * included in the core functionality, methods in brown are added + * with the <a href="http://p5js.org/reference/#/libraries/p5.dom">p5.dom + * library</a>. + * It is not called directly, but p5.Element + * objects are created by calling createCanvas, createGraphics, + * or in the p5.dom library, createDiv, createImg, createInput, etc. + * + * @class p5.Element + * @constructor + * @param {String} elt DOM node that is wrapped + * @param {Object} [pInst] pointer to p5 instance + */ +p5.Element = function(elt, pInst) { + /** + * Underlying HTML element. All normal HTML methods can be called on this. + * + * @property elt + */ + this.elt = elt; + this._pInst = pInst; + this._events = {}; + this.width = this.elt.offsetWidth; + this.height = this.elt.offsetHeight; +}; + +/** + * + * Attaches the element to the parent specified. A way of setting + * the container for the element. Accepts either a string ID, DOM + * node, or p5.Element. If no arguments given, parent node is returned. + * For more ways to position the canvas, see the + * <a href='https://github.com/processing/p5.js/wiki/Positioning-your-canvas'> + * positioning the canvas</a> wiki page. + * + * @method parent + * @param {String|Object} parent the ID, DOM node, or p5.Element + * of desired parent element + * @return {p5.Element} + * @example + * <div class="norender"><code> + * // in the html file: + * <div id="myContainer"></div> + * // in the js file: + * var cnv = createCanvas(100, 100); + * cnv.parent("myContainer"); + * </code></div> + * <div class='norender'><code> + * var div0 = createDiv('this is the parent'); + * var div1 = createDiv('this is the child'); + * div1.parent(div0); // use p5.Element + * </code></div> + * <div class='norender'><code> + * var div0 = createDiv('this is the parent'); + * div0.id('apples'); + * var div1 = createDiv('this is the child'); + * div1.parent('apples'); // use id + * </code></div> + * <div class='norender'><code> + * var elt = document.getElementById('myParentDiv'); + * var div1 = createDiv('this is the child'); + * div1.parent(elt); // use element from page + * </code></div> + * + * @alt + * no display. + * + */ +p5.Element.prototype.parent = function(p) { + if (arguments.length === 0){ + return this.elt.parentNode; + } else { + if (typeof p === 'string') { + if (p[0] === '#') { + p = p.substring(1); + } + p = document.getElementById(p); + } else if (p instanceof p5.Element) { + p = p.elt; + } + p.appendChild(this.elt); + return this; + } +}; + +/** + * + * Sets the ID of the element. If no ID argument is passed in, it instead + * returns the current ID of the element. + * + * @method id + * @param {String} [id] ID of the element + * @return {p5.Element|String} + * @example + * <div><code class='norender'> + * function setup() { + * var cnv = createCanvas(100, 100); + * // Assigns a CSS selector ID to + * // the canvas element. + * cnv.id("mycanvas"); + * } + * </code></div> + * + * @alt + * no display. + * + */ +p5.Element.prototype.id = function(id) { + if (arguments.length === 0) { + return this.elt.id; + } else { + this.elt.id = id; + this.width = this.elt.offsetWidth; + this.height = this.elt.offsetHeight; + return this; + } +}; + +/** + * + * Adds given class to the element. If no class argument is passed in, it + * instead returns a string containing the current class(es) of the element. + * + * @method class + * @param {String} [class] class to add + * @return {p5.Element|String} + */ +p5.Element.prototype.class = function(c) { + if (arguments.length === 0) { + return this.elt.className; + } else { + this.elt.className = c; + return this; + } +}; + +/** + * The .mousePressed() function is called once after every time a + * mouse button is pressed over the element. This can be used to + * attach element specific event listeners. + * + * @method mousePressed + * @param {Function} fxn function to be fired when mouse is + * pressed over the element. + * @return {p5.Element} + * @example + * <div class='norender'><code> + * var cnv; + * var d; + * var g; + * function setup() { + * cnv = createCanvas(100, 100); + * cnv.mousePressed(changeGray); // attach listener for + * // canvas click only + * d = 10; + * g = 100; + * } + * + * function draw() { + * background(g); + * ellipse(width/2, height/2, d, d); + * } + * + * // this function fires with any click anywhere + * function mousePressed() { + * d = d + 10; + * } + * + * // this function fires only when cnv is clicked + * function changeGray() { + * g = random(0, 255); + * } + * </code></div> + * + * @alt + * no display. + * + */ +p5.Element.prototype.mousePressed = function (fxn) { + attachListener('mousedown', fxn, this); + attachListener('touchstart', fxn, this); + return this; +}; + +/** + * The .mouseWheel() function is called once after every time a + * mouse wheel is scrolled over the element. This can be used to + * attach element specific event listeners. + * <br><br> + * The function accepts a callback function as argument which will be executed + * when the `wheel` event is triggered on the element, the callabck function is + * passed one argument `event`. The `event.deltaY` property returns negative + * values if the mouse wheel is rotated up or away from the user and positive + * in the other direction. The `event.deltaX` does the same as `event.deltaY` + * except it reads the horizontal wheel scroll of the mouse wheel. + * <br><br> + * On OS X with "natural" scrolling enabled, the `event.deltaY` values are + * reversed. + * + * @method mouseWheel + * @param {Function} fxn function to be fired when mouse wheel is + * scrolled over the element. + * @return {p5.Element} + * @example + * <div class='norender'><code> + * var cnv; + * var d; + * var g; + * function setup() { + * cnv = createCanvas(100, 100); + * cnv.mouseWheel(changeSize); // attach listener for + * // activity on canvas only + * d = 10; + * g = 100; + * } + * + * function draw() { + * background(g); + * ellipse(width/2, height/2, d, d); + * } + * + * // this function fires with mousewheel movement + * // anywhere on screen + * function mouseWheel() { + * g = g + 10; + * } + * + * // this function fires with mousewheel movement + * // over canvas only + * function changeSize(event) { + * if (event.deltaY > 0) { + * d = d + 10; + * } else { + * d = d - 10; + * } + * } + * </code></div> + * + * + * @alt + * no display. + * + */ +p5.Element.prototype.mouseWheel = function (fxn) { + attachListener('wheel', fxn, this); + return this; +}; + +/** + * The .mouseReleased() function is called once after every time a + * mouse button is released over the element. This can be used to + * attach element specific event listeners. + * + * @method mouseReleased + * @param {Function} fxn function to be fired when mouse is + * released over the element. + * @return {p5.Element} + * @example + * <div class='norender'><code> + * var cnv; + * var d; + * var g; + * function setup() { + * cnv = createCanvas(100, 100); + * cnv.mouseReleased(changeGray); // attach listener for + * // activity on canvas only + * d = 10; + * g = 100; + * } + * + * function draw() { + * background(g); + * ellipse(width/2, height/2, d, d); + * } + * + * // this function fires after the mouse has been + * // released + * function mouseReleased() { + * d = d + 10; + * } + * + * // this function fires after the mouse has been + * // released while on canvas + * function changeGray() { + * g = random(0, 255); + * } + * </code></div> + * + * + * @alt + * no display. + * + */ +p5.Element.prototype.mouseReleased = function (fxn) { + attachListener('mouseup', fxn, this); + attachListener('touchend', fxn, this); + return this; +}; + + +/** + * The .mouseClicked() function is called once after a mouse button is + * pressed and released over the element. This can be used to + * attach element specific event listeners. + * + * @method mouseClicked + * @param {Function} fxn function to be fired when mouse is + * clicked over the element. + * @return {p5.Element} + * @example + * var cnv; + * var d; + * var g; + * function setup() { + * cnv = createCanvas(100, 100); + * cnv.mouseClicked(changeGray); // attach listener for + * // activity on canvas only + * d = 10; + * g = 100; + * } + * + * function draw() { + * background(g); + * ellipse(width/2, height/2, d, d); + * } + * + * // this function fires after the mouse has been + * // clicked anywhere + * function mouseClicked() { + * d = d + 10; + * } + * + * // this function fires after the mouse has been + * // clicked on canvas + * function changeGray() { + * g = random(0, 255); + * } + * </code></div> + * + * + * @alt + * no display. + * + */ +p5.Element.prototype.mouseClicked = function (fxn) { + attachListener('click', fxn, this); + return this; +}; + +/** + * The .mouseMoved() function is called once every time a + * mouse moves over the element. This can be used to attach an + * element specific event listener. + * + * @method mouseMoved + * @param {Function} fxn function to be fired when mouse is + * moved over the element. + * @return {p5.Element} + * @example + * <div class='norender'><code> + * var cnv; + * var d = 30; + * var g; + * function setup() { + * cnv = createCanvas(100, 100); + * cnv.mouseMoved(changeSize); // attach listener for + * // activity on canvas only + * d = 10; + * g = 100; + * } + * + * function draw() { + * background(g); + * fill(200); + * ellipse(width/2, height/2, d, d); + * } + * + * // this function fires when mouse moves anywhere on + * // page + * function mouseMoved() { + * g = g + 5; + * if (g > 255) { + * g = 0; + * } + * } + * + * // this function fires when mouse moves over canvas + * function changeSize() { + * d = d + 2; + * if (d > 100) { + * d = 0; + * } + * } + * </code></div> + * + * + * @alt + * no display. + * + */ +p5.Element.prototype.mouseMoved = function (fxn) { + attachListener('mousemove', fxn, this); + attachListener('touchmove', fxn, this); + return this; +}; + +/** + * The .mouseOver() function is called once after every time a + * mouse moves onto the element. This can be used to attach an + * element specific event listener. + * + * @method mouseOver + * @param {Function} fxn function to be fired when mouse is + * moved over the element. + * @return {p5.Element} + * @example + * <div class='norender'><code> + * var cnv; + * var d; + * var g; + * function setup() { + * cnv = createCanvas(100, 100); + * cnv.mouseOver(changeGray); + * d = 10; + * } + * + * function draw() { + * ellipse(width/2, height/2, d, d); + * } + * + * function changeGray() { + * d = d + 10; + * if (d > 100) { + * d = 0; + * } + * } + * </code></div> + * + * + * @alt + * no display. + * + */ +p5.Element.prototype.mouseOver = function (fxn) { + attachListener('mouseover', fxn, this); + return this; +}; + + +/** + * The .changed() function is called when the value of an + * element is changed. + * This can be used to attach an element specific event listener. + * + * @method changed + * @param {Function} fxn function to be fired when the value of an + * element changes. + * @return {p5.Element} + * @example + * <div><code> + * var sel; + * + * function setup() { + * textAlign(CENTER); + * background(200); + * sel = createSelect(); + * sel.position(10, 10); + * sel.option('pear'); + * sel.option('kiwi'); + * sel.option('grape'); + * sel.changed(mySelectEvent); + * } + * + * function mySelectEvent() { + * var item = sel.value(); + * background(200); + * text("it's a "+item+"!", 50, 50); + * } + * </code></div> + * <div><code> + * var checkbox; + * var cnv; + * + * function setup() { + * checkbox = createCheckbox(" fill"); + * checkbox.changed(changeFill); + * cnv = createCanvas(100, 100); + * cnv.position(0, 30); + * noFill(); + * } + * + * function draw() { + * background(200); + * ellipse(50, 50, 50, 50); + * } + * + * function changeFill() { + * if (checkbox.checked()) { + * fill(0); + * } else { + * noFill(); + * } + * } + * </code></div> + * + * @alt + * dropdown: pear, kiwi, grape. When selected text "its a" + selection shown. + * + */ +p5.Element.prototype.changed = function (fxn) { + attachListener('change', fxn, this); + return this; +}; + +/** + * The .input() function is called when any user input is + * detected with an element. The input event is often used + * to detect keystrokes in a input element, or changes on a + * slider element. This can be used to attach an element specific + * event listener. + * + * @method input + * @param {Function} fxn function to be fired on user input. + * @return {p5.Element} + * @example + * <div class='norender'><code> + * // Open your console to see the output + * function setup() { + * var inp = createInput(''); + * inp.input(myInputEvent); + * } + * + * function myInputEvent() { + * console.log('you are typing: ', this.value()); + * } + * </code></div> + * + * @alt + * no display. + * + */ +p5.Element.prototype.input = function (fxn) { + attachListener('input', fxn, this); + return this; +}; + +/** + * The .mouseOut() function is called once after every time a + * mouse moves off the element. This can be used to attach an + * element specific event listener. + * + * @method mouseOut + * @param {Function} fxn function to be fired when mouse is + * moved off the element. + * @return {p5.Element} + * @example + * <div class='norender'><code> + * var cnv; + * var d; + * var g; + * function setup() { + * cnv = createCanvas(100, 100); + * cnv.mouseOut(changeGray); + * d = 10; + * } + * + * function draw() { + * ellipse(width/2, height/2, d, d); + * } + * + * function changeGray() { + * d = d + 10; + * if (d > 100) { + * d = 0; + * } + * } + * </code></div> + * + * @alt + * no display. + * + */ +p5.Element.prototype.mouseOut = function (fxn) { + attachListener('mouseout', fxn, this); + return this; +}; + +/** + * The .touchStarted() function is called once after every time a touch is + * registered. This can be used to attach element specific event listeners. + * + * @method touchStarted + * @param {Function} fxn function to be fired when touch is + * started over the element. + * @return {p5.Element} + * @example + * <div class='norender'><code> + * var cnv; + * var d; + * var g; + * function setup() { + * cnv = createCanvas(100, 100); + * cnv.touchStarted(changeGray); // attach listener for + * // canvas click only + * d = 10; + * g = 100; + * } + * + * function draw() { + * background(g); + * ellipse(width/2, height/2, d, d); + * } + * + * // this function fires with any touch anywhere + * function touchStarted() { + * d = d + 10; + * } + * + * // this function fires only when cnv is clicked + * function changeGray() { + * g = random(0, 255); + * } + * </code></div> + * + * @alt + * no display. + * + */ +p5.Element.prototype.touchStarted = function (fxn) { + attachListener('touchstart', fxn, this); + attachListener('mousedown', fxn, this); + return this; +}; + +/** + * The .touchMoved() function is called once after every time a touch move is + * registered. This can be used to attach element specific event listeners. + * + * @method touchMoved + * @param {Function} fxn function to be fired when touch is moved + * over the element. + * @return {p5.Element} + * @example + * <div class='norender'><code> + * var cnv; + * var g; + * function setup() { + * cnv = createCanvas(100, 100); + * cnv.touchMoved(changeGray); // attach listener for + * // canvas click only + * g = 100; + * } + * + * function draw() { + * background(g); + * } + * + * // this function fires only when cnv is clicked + * function changeGray() { + * g = random(0, 255); + * } + * </code></div> + * + * @alt + * no display. + * + */ +p5.Element.prototype.touchMoved = function (fxn) { + attachListener('touchmove', fxn, this); + attachListener('mousemove', fxn, this); + return this; +}; + +/** + * The .touchEnded() function is called once after every time a touch is + * registered. This can be used to attach element specific event listeners. + * + * @method touchEnded + * @param {Function} fxn function to be fired when touch is + * ended over the element. + * @return {p5.Element} + * @example + * <div class='norender'><code> + * var cnv; + * var d; + * var g; + * function setup() { + * cnv = createCanvas(100, 100); + * cnv.touchEnded(changeGray); // attach listener for + * // canvas click only + * d = 10; + * g = 100; + * } + * + * function draw() { + * background(g); + * ellipse(width/2, height/2, d, d); + * } + * + * // this function fires with any touch anywhere + * function touchEnded() { + * d = d + 10; + * } + * + * // this function fires only when cnv is clicked + * function changeGray() { + * g = random(0, 255); + * } + * </code></div> + * + * + * @alt + * no display. + * + */ +p5.Element.prototype.touchEnded = function (fxn) { + attachListener('touchend', fxn, this); + attachListener('mouseup', fxn, this); + return this; +}; + + + +/** + * The .dragOver() function is called once after every time a + * file is dragged over the element. This can be used to attach an + * element specific event listener. + * + * @method dragOver + * @param {Function} fxn function to be fired when mouse is + * dragged over the element. + * @return {p5.Element} + */ +p5.Element.prototype.dragOver = function (fxn) { + attachListener('dragover', fxn, this); + return this; +}; + +/** + * The .dragLeave() function is called once after every time a + * dragged file leaves the element area. This can be used to attach an + * element specific event listener. + * + * @method dragLeave + * @param {Function} fxn function to be fired when mouse is + * dragged over the element. + * @return {p5.Element} + */ +p5.Element.prototype.dragLeave = function (fxn) { + attachListener('dragleave', fxn, this); + return this; +}; + +/** + * The .drop() function is called for each file dropped on the element. + * It requires a callback that is passed a p5.File object. You can + * optionally pass two callbacks, the first one (required) is triggered + * for each file dropped when the file is loaded. The second (optional) + * is triggered just once when a file (or files) are dropped. + * + * @method drop + * @param {Function} callback triggered when files are dropped. + * @param {Function} callback to receive loaded file. + * @return {p5.Element} + * @example + * <div><code> + * function setup() { + * var c = createCanvas(100, 100); + * background(200); + * textAlign(CENTER); + * text('drop image', width/2, height/2); + * c.drop(gotFile); + * } + * + * function gotFile(file) { + * var img = createImg(file.data).hide(); + * // Draw the image onto the canvas + * image(img, 0, 0, width, height); + * } + * </code></div> + * + * @alt + * Canvas turns into whatever image is dragged/dropped onto it. + * + */ +p5.Element.prototype.drop = function (callback, fxn) { + // Make a file loader callback and trigger user's callback + function makeLoader(theFile) { + // Making a p5.File object + var p5file = new p5.File(theFile); + return function(e) { + p5file.data = e.target.result; + callback(p5file); + }; + } + + // Is the file stuff supported? + if (window.File && window.FileReader && window.FileList && window.Blob) { + + // If you want to be able to drop you've got to turn off + // a lot of default behavior + attachListener('dragover',function(evt) { + evt.stopPropagation(); + evt.preventDefault(); + },this); + + // If this is a drag area we need to turn off the default behavior + attachListener('dragleave',function(evt) { + evt.stopPropagation(); + evt.preventDefault(); + },this); + + // If just one argument it's the callback for the files + if (arguments.length > 1) { + attachListener('drop', fxn, this); + } + + // Deal with the files + attachListener('drop', function(evt) { + + evt.stopPropagation(); + evt.preventDefault(); + + // A FileList + var files = evt.dataTransfer.files; + + // Load each one and trigger the callback + for (var i = 0; i < files.length; i++) { + var f = files[i]; + var reader = new FileReader(); + reader.onload = makeLoader(f); + + + // Text or data? + // This should likely be improved + if (f.type.indexOf('text') > -1) { + reader.readAsText(f); + } else { + reader.readAsDataURL(f); + } + } + }, this); + } else { + console.log('The File APIs are not fully supported in this browser.'); + } + + return this; +}; + + + + +function attachListener(ev, fxn, ctx) { + // LM removing, not sure why we had this? + // var _this = ctx; + // var f = function (e) { fxn(e, _this); }; + var f = fxn.bind(ctx); + ctx.elt.addEventListener(ev, f, false); + ctx._events[ev] = f; +} + +/** + * Helper fxn for sharing pixel methods + * + */ +p5.Element.prototype._setProperty = function (prop, value) { + this[prop] = value; +}; + + +module.exports = p5.Element; + +},{"./core":37}],42:[function(_dereq_,module,exports){ +/** + * @module Rendering + * @submodule Rendering + * @for p5 + */ + +var p5 = _dereq_('./core'); +var constants = _dereq_('./constants'); + +/** + * Thin wrapper around a renderer, to be used for creating a + * graphics buffer object. Use this class if you need + * to draw into an off-screen graphics buffer. The two parameters define the + * width and height in pixels. The fields and methods for this class are + * extensive, but mirror the normal drawing API for p5. + * + * @class p5.Graphics + * @constructor + * @extends p5.Element + * @param {String} elt DOM node that is wrapped + * @param {Object} [pInst] pointer to p5 instance + * @param {Boolean} whether we're using it as main canvas + */ +p5.Graphics = function(w, h, renderer, pInst) { + + var r = renderer || constants.P2D; + + var c = document.createElement('canvas'); + var node = this._userNode || document.body; + node.appendChild(c); + + p5.Element.call(this, c, pInst, false); + this._styles = []; + this.width = w; + this.height = h; + this._pixelDensity = pInst._pixelDensity; + + if (r === constants.WEBGL) { + this._renderer = new p5.RendererGL(c, this, false); + } else { + this._renderer = new p5.Renderer2D(c, this, false); + } + + this._renderer.resize(w, h); + this._renderer._applyDefaults(); + + pInst._elements.push(this); + + // bind methods and props of p5 to the new object + for (var p in p5.prototype) { + if (!this[p]) { + if (typeof p5.prototype[p] === 'function') { + this[p] = p5.prototype[p].bind(this); + } else { + this[p] = p5.prototype[p]; + } + } + } + + return this; +}; + +p5.Graphics.prototype = Object.create(p5.Element.prototype); + +module.exports = p5.Graphics; + +},{"./constants":36,"./core":37}],43:[function(_dereq_,module,exports){ +/** + * @module Rendering + * @submodule Rendering + * @for p5 + */ + +var p5 = _dereq_('./core'); +var constants = _dereq_('../core/constants'); + +/** + * Main graphics and rendering context, as well as the base API + * implementation for p5.js "core". To be used as the superclass for + * Renderer2D and Renderer3D classes, respecitvely. + * + * @class p5.Renderer + * @constructor + * @extends p5.Element + * @param {String} elt DOM node that is wrapped + * @param {Object} [pInst] pointer to p5 instance + * @param {Boolean} whether we're using it as main canvas + */ +p5.Renderer = function(elt, pInst, isMainCanvas) { + p5.Element.call(this, elt, pInst); + this.canvas = elt; + this._pInst = pInst; + if (isMainCanvas) { + this._isMainCanvas = true; + // for pixel method sharing with pimage + this._pInst._setProperty('_curElement', this); + this._pInst._setProperty('canvas', this.canvas); + this._pInst._setProperty('width', this.width); + this._pInst._setProperty('height', this.height); + } else { // hide if offscreen buffer by default + this.canvas.style.display = 'none'; + this._styles = []; // non-main elt styles stored in p5.Renderer + } + + + this._textSize = 12; + this._textLeading = 15; + this._textFont = 'sans-serif'; + this._textStyle = constants.NORMAL; + this._textAscent = null; + this._textDescent = null; + + + this._rectMode = constants.CORNER; + this._ellipseMode = constants.CENTER; + this._curveTightness = 0; + this._imageMode = constants.CORNER; + + this._tint = null; + this._doStroke = true; + this._doFill = true; + this._strokeSet = false; + this._fillSet = false; + this._colorMode = constants.RGB; + this._colorMaxes = { + rgb: [255, 255, 255, 255], + hsb: [360, 100, 100, 1], + hsl: [360, 100, 100, 1] + }; + +}; + +p5.Renderer.prototype = Object.create(p5.Element.prototype); + + + + +/** + * Resize our canvas element. + */ +p5.Renderer.prototype.resize = function(w, h) { + this.width = w; + this.height = h; + this.elt.width = w * this._pInst._pixelDensity; + this.elt.height = h * this._pInst._pixelDensity; + this.elt.style.width = w +'px'; + this.elt.style.height = h + 'px'; + if (this._isMainCanvas) { + this._pInst._setProperty('width', this.width); + this._pInst._setProperty('height', this.height); + } +}; + +p5.Renderer.prototype.textLeading = function(l) { + + if (arguments.length && arguments[0]) { + + this._setProperty('_textLeading', l); + return this; + } + + return this._textLeading; +}; + +p5.Renderer.prototype.textSize = function(s) { + + if (arguments.length && arguments[0]) { + + this._setProperty('_textSize', s); + this._setProperty('_textLeading', s * constants._DEFAULT_LEADMULT); + return this._applyTextProperties(); + } + + return this._textSize; +}; + +p5.Renderer.prototype.textStyle = function(s) { + + if (arguments.length && arguments[0]) { + + if (s === constants.NORMAL || + s === constants.ITALIC || + s === constants.BOLD) { + this._setProperty('_textStyle', s); + } + + return this._applyTextProperties(); + } + + return this._textStyle; +}; + +p5.Renderer.prototype.textAscent = function() { + if (this._textAscent === null) { + this._updateTextMetrics(); + } + return this._textAscent; +}; + +p5.Renderer.prototype.textDescent = function() { + + if (this._textDescent === null) { + this._updateTextMetrics(); + } + return this._textDescent; +}; + +p5.Renderer.prototype._applyDefaults = function(){ + return this; +}; + +/** + * Helper fxn to check font type (system or otf) + */ +p5.Renderer.prototype._isOpenType = function(f) { + + f = f || this._textFont; + return (typeof f === 'object' && f.font && f.font.supported); +}; + +p5.Renderer.prototype._updateTextMetrics = function() { + + if (this._isOpenType()) { + + this._setProperty('_textAscent', this._textFont._textAscent()); + this._setProperty('_textDescent', this._textFont._textDescent()); + return this; + } + + // Adapted from http://stackoverflow.com/a/25355178 + var text = document.createElement('span'); + text.style.fontFamily = this._textFont; + text.style.fontSize = this._textSize + 'px'; + text.innerHTML = 'ABCjgq|'; + + var block = document.createElement('div'); + block.style.display = 'inline-block'; + block.style.width = '1px'; + block.style.height = '0px'; + + var container = document.createElement('div'); + container.appendChild(text); + container.appendChild(block); + + container.style.height = '0px'; + container.style.overflow = 'hidden'; + document.body.appendChild(container); + + block.style.verticalAlign = 'baseline'; + var blockOffset = calculateOffset(block); + var textOffset = calculateOffset(text); + var ascent = blockOffset[1] - textOffset[1]; + + block.style.verticalAlign = 'bottom'; + blockOffset = calculateOffset(block); + textOffset = calculateOffset(text); + var height = blockOffset[1] - textOffset[1]; + var descent = height - ascent; + + document.body.removeChild(container); + + this._setProperty('_textAscent', ascent); + this._setProperty('_textDescent', descent); + + return this; +}; + +/** + * Helper fxn to measure ascent and descent. + * Adapted from http://stackoverflow.com/a/25355178 + */ +function calculateOffset(object) { + var currentLeft = 0, + currentTop = 0; + if (object.offsetParent) { + do { + currentLeft += object.offsetLeft; + currentTop += object.offsetTop; + } while (object = object.offsetParent); + } else { + currentLeft += object.offsetLeft; + currentTop += object.offsetTop; + } + return [currentLeft, currentTop]; +} + +module.exports = p5.Renderer; + +},{"../core/constants":36,"./core":37}],44:[function(_dereq_,module,exports){ + +var p5 = _dereq_('./core'); +var canvas = _dereq_('./canvas'); +var constants = _dereq_('./constants'); +var filters = _dereq_('../image/filters'); + +_dereq_('./p5.Renderer'); + +/** + * p5.Renderer2D + * The 2D graphics canvas renderer class. + * extends p5.Renderer + */ +var styleEmpty = 'rgba(0,0,0,0)'; +// var alphaThreshold = 0.00125; // minimum visible + +p5.Renderer2D = function(elt, pInst, isMainCanvas){ + p5.Renderer.call(this, elt, pInst, isMainCanvas); + this.drawingContext = this.canvas.getContext('2d'); + this._pInst._setProperty('drawingContext', this.drawingContext); + return this; +}; + +p5.Renderer2D.prototype = Object.create(p5.Renderer.prototype); + +p5.Renderer2D.prototype._applyDefaults = function() { + this.drawingContext.fillStyle = constants._DEFAULT_FILL; + this.drawingContext.strokeStyle = constants._DEFAULT_STROKE; + this.drawingContext.lineCap = constants.ROUND; + this.drawingContext.font = 'normal 12px sans-serif'; +}; + +p5.Renderer2D.prototype.resize = function(w,h) { + p5.Renderer.prototype.resize.call(this, w,h); + this.drawingContext.scale(this._pInst._pixelDensity, + this._pInst._pixelDensity); +}; + +////////////////////////////////////////////// +// COLOR | Setting +////////////////////////////////////////////// + +p5.Renderer2D.prototype.background = function() { + this.drawingContext.save(); + this.drawingContext.setTransform(1, 0, 0, 1, 0, 0); + this.drawingContext.scale(this._pInst._pixelDensity, + this._pInst._pixelDensity); + + if (arguments[0] instanceof p5.Image) { + this._pInst.image(arguments[0], 0, 0, this.width, this.height); + } else { + var curFill = this.drawingContext.fillStyle; + // create background rect + var color = this._pInst.color.apply(this, arguments); + var newFill = color.toString(); + this.drawingContext.fillStyle = newFill; + this.drawingContext.fillRect(0, 0, this.width, this.height); + // reset fill + this.drawingContext.fillStyle = curFill; + } + this.drawingContext.restore(); +}; + +p5.Renderer2D.prototype.clear = function() { + this.drawingContext.clearRect(0, 0, this.width, this.height); +}; + +p5.Renderer2D.prototype.fill = function() { + + var ctx = this.drawingContext; + var color = this._pInst.color.apply(this, arguments); + ctx.fillStyle = color.toString(); +}; + +p5.Renderer2D.prototype.stroke = function() { + var ctx = this.drawingContext; + var color = this._pInst.color.apply(this, arguments); + ctx.strokeStyle = color.toString(); +}; + +////////////////////////////////////////////// +// IMAGE | Loading & Displaying +////////////////////////////////////////////// + +p5.Renderer2D.prototype.image = + function (img, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight) { + var cnv; + try { + if (this._tint) { + if (p5.MediaElement && img instanceof p5.MediaElement) { + img.loadPixels(); + } + if (img.canvas) { + cnv = this._getTintedImageCanvas(img); + } + } + if (!cnv) { + cnv = img.canvas || img.elt; + } + this.drawingContext.drawImage(cnv, sx, sy, sWidth, sHeight, dx, dy, + dWidth, dHeight); + } catch (e) { + if (e.name !== 'NS_ERROR_NOT_AVAILABLE') { + throw e; + } + } +}; + +p5.Renderer2D.prototype._getTintedImageCanvas = function (img) { + if (!img.canvas) { + return img; + } + var pixels = filters._toPixels(img.canvas); + var tmpCanvas = document.createElement('canvas'); + tmpCanvas.width = img.canvas.width; + tmpCanvas.height = img.canvas.height; + var tmpCtx = tmpCanvas.getContext('2d'); + var id = tmpCtx.createImageData(img.canvas.width, img.canvas.height); + var newPixels = id.data; + for (var i = 0; i < pixels.length; i += 4) { + var r = pixels[i]; + var g = pixels[i + 1]; + var b = pixels[i + 2]; + var a = pixels[i + 3]; + newPixels[i] = r * this._tint[0] / 255; + newPixels[i + 1] = g * this._tint[1] / 255; + newPixels[i + 2] = b * this._tint[2] / 255; + newPixels[i + 3] = a * this._tint[3] / 255; + } + tmpCtx.putImageData(id, 0, 0); + return tmpCanvas; +}; + + +////////////////////////////////////////////// +// IMAGE | Pixels +////////////////////////////////////////////// + +p5.Renderer2D.prototype.blendMode = function(mode) { + this.drawingContext.globalCompositeOperation = mode; +}; +p5.Renderer2D.prototype.blend = function() { + var currBlend = this.drawingContext.globalCompositeOperation; + var blendMode = arguments[arguments.length - 1]; + + var copyArgs = Array.prototype.slice.call( + arguments, + 0, + arguments.length - 1 + ); + + this.drawingContext.globalCompositeOperation = blendMode; + if (this._pInst) { + this._pInst.copy.apply(this._pInst, copyArgs); + } else { + this.copy.apply(this, copyArgs); + } + this.drawingContext.globalCompositeOperation = currBlend; +}; + +p5.Renderer2D.prototype.copy = function () { + var srcImage, sx, sy, sw, sh, dx, dy, dw, dh; + if (arguments.length === 9) { + srcImage = arguments[0]; + sx = arguments[1]; + sy = arguments[2]; + sw = arguments[3]; + sh = arguments[4]; + dx = arguments[5]; + dy = arguments[6]; + dw = arguments[7]; + dh = arguments[8]; + } else if (arguments.length === 8) { + srcImage = this._pInst; + sx = arguments[0]; + sy = arguments[1]; + sw = arguments[2]; + sh = arguments[3]; + dx = arguments[4]; + dy = arguments[5]; + dw = arguments[6]; + dh = arguments[7]; + } else { + throw new Error('Signature not supported'); + } + p5.Renderer2D._copyHelper(srcImage, sx, sy, sw, sh, dx, dy, dw, dh); +}; + +p5.Renderer2D._copyHelper = +function (srcImage, sx, sy, sw, sh, dx, dy, dw, dh) { + if (!srcImage.canvas) { + srcImage.loadPixels(); + } + var s = srcImage.canvas.width / srcImage.width; + this.drawingContext.drawImage(srcImage.canvas, + s * sx, s * sy, s * sw, s * sh, dx, dy, dw, dh); +}; + +p5.Renderer2D.prototype.get = function(x, y, w, h) { + if (x === undefined && y === undefined && + w === undefined && h === undefined){ + x = 0; + y = 0; + w = this.width; + h = this.height; + } else if (w === undefined && h === undefined) { + w = 1; + h = 1; + } + + // if the section does not overlap the canvas + if(x + w < 0 || y + h < 0 || x > this.width || y > this.height){ + return [0, 0, 0, 255]; + } + + var ctx = this._pInst || this; + + var pd = ctx._pixelDensity; + + // round down to get integer numbers + x = Math.floor(x); + y = Math.floor(y); + + var sx = x * pd; + var sy = y * pd; + if (w === 1 && h === 1){ + var imageData = this.drawingContext.getImageData(sx, sy, 1, 1).data; + //imageData = [0,0,0,0]; + return [ + imageData[0], + imageData[1], + imageData[2], + imageData[3] + ]; + } else { + //auto constrain the width and height to + //dimensions of the source image + var dw = Math.min(w, ctx.width); + var dh = Math.min(h, ctx.height); + var sw = dw * pd; + var sh = dh * pd; + + var region = new p5.Image(dw, dh); + region.canvas.getContext('2d').drawImage(this.canvas, sx, sy, sw, sh, + 0, 0, dw, dh); + + return region; + } +}; + +p5.Renderer2D.prototype.loadPixels = function () { + var pd = this._pixelDensity || this._pInst._pixelDensity; + var w = this.width * pd; + var h = this.height * pd; + var imageData = this.drawingContext.getImageData(0, 0, w, h); + // @todo this should actually set pixels per object, so diff buffers can + // have diff pixel arrays. + if (this._pInst) { + this._pInst._setProperty('imageData', imageData); + this._pInst._setProperty('pixels', imageData.data); + } else { // if called by p5.Image + this._setProperty('imageData', imageData); + this._setProperty('pixels', imageData.data); + } +}; + +p5.Renderer2D.prototype.set = function (x, y, imgOrCol) { + // round down to get integer numbers + x = Math.floor(x); + y = Math.floor(y); + if (imgOrCol instanceof p5.Image) { + this.drawingContext.save(); + this.drawingContext.setTransform(1, 0, 0, 1, 0, 0); + this.drawingContext.scale(this._pInst._pixelDensity, + this._pInst._pixelDensity); + this.drawingContext.drawImage(imgOrCol.canvas, x, y); + this.loadPixels.call(this._pInst); + this.drawingContext.restore(); + } else { + var ctx = this._pInst || this; + var r = 0, g = 0, b = 0, a = 0; + var idx = 4*((y * ctx._pixelDensity) * + (this.width * ctx._pixelDensity) + (x * ctx._pixelDensity)); + if (!ctx.imageData) { + ctx.loadPixels.call(ctx); + } + if (typeof imgOrCol === 'number') { + if (idx < ctx.pixels.length) { + r = imgOrCol; + g = imgOrCol; + b = imgOrCol; + a = 255; + //this.updatePixels.call(this); + } + } + else if (imgOrCol instanceof Array) { + if (imgOrCol.length < 4) { + throw new Error('pixel array must be of the form [R, G, B, A]'); + } + if (idx < ctx.pixels.length) { + r = imgOrCol[0]; + g = imgOrCol[1]; + b = imgOrCol[2]; + a = imgOrCol[3]; + //this.updatePixels.call(this); + } + } else if (imgOrCol instanceof p5.Color) { + if (idx < ctx.pixels.length) { + r = imgOrCol.levels[0]; + g = imgOrCol.levels[1]; + b = imgOrCol.levels[2]; + a = imgOrCol.levels[3]; + //this.updatePixels.call(this); + } + } + // loop over pixelDensity * pixelDensity + for (var i = 0; i < ctx._pixelDensity; i++) { + for (var j = 0; j < ctx._pixelDensity; j++) { + // loop over + idx = 4*((y * ctx._pixelDensity + j) * this.width * + ctx._pixelDensity + (x * ctx._pixelDensity + i)); + ctx.pixels[idx] = r; + ctx.pixels[idx+1] = g; + ctx.pixels[idx+2] = b; + ctx.pixels[idx+3] = a; + } + } + } +}; + +p5.Renderer2D.prototype.updatePixels = function (x, y, w, h) { + var pd = this._pixelDensity || this._pInst._pixelDensity; + if (x === undefined && + y === undefined && + w === undefined && + h === undefined) { + x = 0; + y = 0; + w = this.width; + h = this.height; + } + w *= pd; + h *= pd; + + if (this._pInst) { + this.drawingContext.putImageData(this._pInst.imageData, x, y, 0, 0, w, h); + } else { + this.drawingContext.putImageData(this.imageData, x, y, 0, 0, w, h); + } +}; + +////////////////////////////////////////////// +// SHAPE | 2D Primitives +////////////////////////////////////////////// + +/** + * Generate a cubic Bezier representing an arc on the unit circle of total + * angle `size` radians, beginning `start` radians above the x-axis. Up to + * four of these curves are combined to make a full arc. + * + * See www.joecridge.me/bezier.pdf for an explanation of the method. + */ +p5.Renderer2D.prototype._acuteArcToBezier = + function _acuteArcToBezier(start, size) { + // Evauate constants. + var alpha = size / 2.0, + cos_alpha = Math.cos(alpha), + sin_alpha = Math.sin(alpha), + cot_alpha = 1.0 / Math.tan(alpha), + phi = start + alpha, // This is how far the arc needs to be rotated. + cos_phi = Math.cos(phi), + sin_phi = Math.sin(phi), + lambda = (4.0 - cos_alpha) / 3.0, + mu = sin_alpha + (cos_alpha - lambda) * cot_alpha; + + // Return rotated waypoints. + return { + ax: Math.cos(start), + ay: Math.sin(start), + bx: lambda * cos_phi + mu * sin_phi, + by: lambda * sin_phi - mu * cos_phi, + cx: lambda * cos_phi - mu * sin_phi, + cy: lambda * sin_phi + mu * cos_phi, + dx: Math.cos(start + size), + dy: Math.sin(start + size) + }; +}; + +p5.Renderer2D.prototype.arc = + function(x, y, w, h, start, stop, mode) { + var ctx = this.drawingContext; + var vals = canvas.arcModeAdjust(x, y, w, h, this._ellipseMode); + var rx = vals.w / 2.0; + var ry = vals.h / 2.0; + var epsilon = 0.00001; // Smallest visible angle on displays up to 4K. + var arcToDraw = 0; + var curves = []; + + // Create curves + while(stop - start > epsilon) { + arcToDraw = Math.min(stop - start, constants.HALF_PI); + curves.push(this._acuteArcToBezier(start, arcToDraw)); + start += arcToDraw; + } + + // Fill curves + if (this._doFill) { + ctx.beginPath(); + curves.forEach(function (curve, index) { + if (index === 0) { + ctx.moveTo(vals.x + curve.ax * rx, vals.y + curve.ay * ry); + } + ctx.bezierCurveTo(vals.x + curve.bx * rx, vals.y + curve.by * ry, + vals.x + curve.cx * rx, vals.y + curve.cy * ry, + vals.x + curve.dx * rx, vals.y + curve.dy * ry); + }); + if (mode === constants.PIE || mode == null) { + ctx.lineTo(vals.x, vals.y); + } + ctx.closePath(); + ctx.fill(); + } + + // Stroke curves + if (this._doStroke) { + ctx.beginPath(); + curves.forEach(function (curve, index) { + if (index === 0) { + ctx.moveTo(vals.x + curve.ax * rx, vals.y + curve.ay * ry); + } + ctx.bezierCurveTo(vals.x + curve.bx * rx, vals.y + curve.by * ry, + vals.x + curve.cx * rx, vals.y + curve.cy * ry, + vals.x + curve.dx * rx, vals.y + curve.dy * ry); + }); + if (mode === constants.PIE) { + ctx.lineTo(vals.x, vals.y); + ctx.closePath(); + } else if (mode === constants.CHORD) { + ctx.closePath(); + } + ctx.stroke(); + } + return this; +}; + +p5.Renderer2D.prototype.ellipse = function(args) { + var ctx = this.drawingContext; + var doFill = this._doFill, doStroke = this._doStroke; + var x = args[0], + y = args[1], + w = args[2], + h = args[3]; + if (doFill && !doStroke) { + if(ctx.fillStyle === styleEmpty) { + return this; + } + } else if (!doFill && doStroke) { + if(ctx.strokeStyle === styleEmpty) { + return this; + } + } + var kappa = 0.5522847498, + ox = (w / 2) * kappa, // control point offset horizontal + oy = (h / 2) * kappa, // control point offset vertical + xe = x + w, // x-end + ye = y + h, // y-end + xm = x + w / 2, // x-middle + ym = y + h / 2; // y-middle + ctx.beginPath(); + ctx.moveTo(x, ym); + ctx.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); + ctx.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); + ctx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); + ctx.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); + ctx.closePath(); + if (doFill) { + ctx.fill(); + } + if (doStroke) { + ctx.stroke(); + } +}; + +p5.Renderer2D.prototype.line = function(x1, y1, x2, y2) { + var ctx = this.drawingContext; + if (!this._doStroke) { + return this; + } else if(ctx.strokeStyle === styleEmpty){ + return this; + } + // Translate the line by (0.5, 0.5) to draw it crisp + if (ctx.lineWidth % 2 === 1) { + ctx.translate(0.5, 0.5); + } + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.stroke(); + if (ctx.lineWidth % 2 === 1) { + ctx.translate(-0.5, -0.5); + } + return this; +}; + +p5.Renderer2D.prototype.point = function(x, y) { + var ctx = this.drawingContext; + var s = ctx.strokeStyle; + var f = ctx.fillStyle; + if (!this._doStroke) { + return this; + } else if(ctx.strokeStyle === styleEmpty){ + return this; + } + x = Math.round(x); + y = Math.round(y); + ctx.fillStyle = s; + if (ctx.lineWidth > 1) { + ctx.beginPath(); + ctx.arc( + x, + y, + ctx.lineWidth / 2, + 0, + constants.TWO_PI, + false + ); + ctx.fill(); + } else { + ctx.fillRect(x, y, 1, 1); + } + ctx.fillStyle = f; +}; + +p5.Renderer2D.prototype.quad = + function(x1, y1, x2, y2, x3, y3, x4, y4) { + var ctx = this.drawingContext; + var doFill = this._doFill, doStroke = this._doStroke; + if (doFill && !doStroke) { + if(ctx.fillStyle === styleEmpty) { + return this; + } + } else if (!doFill && doStroke) { + if(ctx.strokeStyle === styleEmpty) { + return this; + } + } + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.lineTo(x3, y3); + ctx.lineTo(x4, y4); + ctx.closePath(); + if (doFill) { + ctx.fill(); + } + if (doStroke) { + ctx.stroke(); + } + return this; +}; + +p5.Renderer2D.prototype.rect = function(args) { + var x = args[0], + y = args[1], + w = args[2], + h = args[3], + tl = args[4], + tr = args[5], + br = args[6], + bl = args[7]; + var ctx = this.drawingContext; + var doFill = this._doFill, doStroke = this._doStroke; + if (doFill && !doStroke) { + if(ctx.fillStyle === styleEmpty) { + return this; + } + } else if (!doFill && doStroke) { + if(ctx.strokeStyle === styleEmpty) { + return this; + } + } + // Translate the line by (0.5, 0.5) to draw a crisp rectangle border + if (this._doStroke && ctx.lineWidth % 2 === 1) { + ctx.translate(0.5, 0.5); + } + ctx.beginPath(); + + if (typeof tl === 'undefined') { + // No rounded corners + ctx.rect(x, y, w, h); + } else { + // At least one rounded corner + // Set defaults when not specified + if (typeof tr === 'undefined') { tr = tl; } + if (typeof br === 'undefined') { br = tr; } + if (typeof bl === 'undefined') { bl = br; } + + var hw = w / 2; + var hh = h / 2; + + // Clip radii + if (w < 2 * tl) { tl = hw; } + if (h < 2 * tl) { tl = hh; } + if (w < 2 * tr) { tr = hw; } + if (h < 2 * tr) { tr = hh; } + if (w < 2 * br) { br = hw; } + if (h < 2 * br) { br = hh; } + if (w < 2 * bl) { bl = hw; } + if (h < 2 * bl) { bl = hh; } + + // Draw shape + ctx.beginPath(); + ctx.moveTo(x + tl, y); + ctx.arcTo(x + w, y, x + w, y + h, tr); + ctx.arcTo(x + w, y + h, x, y + h, br); + ctx.arcTo(x, y + h, x, y, bl); + ctx.arcTo(x, y, x + w, y, tl); + ctx.closePath(); + } + if (this._doFill) { + ctx.fill(); + } + if (this._doStroke) { + ctx.stroke(); + } + if (this._doStroke && ctx.lineWidth % 2 === 1) { + ctx.translate(-0.5, -0.5); + } + return this; +}; + +p5.Renderer2D.prototype.triangle = function(args) { + var ctx = this.drawingContext; + var doFill = this._doFill, doStroke = this._doStroke; + var x1=args[0], y1=args[1]; + var x2=args[2], y2=args[3]; + var x3=args[4], y3=args[5]; + if (doFill && !doStroke) { + if(ctx.fillStyle === styleEmpty) { + return this; + } + } else if (!doFill && doStroke) { + if(ctx.strokeStyle === styleEmpty) { + return this; + } + } + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.lineTo(x3, y3); + ctx.closePath(); + if (doFill) { + ctx.fill(); + } + if (doStroke) { + ctx.stroke(); + } +}; + +p5.Renderer2D.prototype.endShape = +function (mode, vertices, isCurve, isBezier, + isQuadratic, isContour, shapeKind) { + if (vertices.length === 0) { + return this; + } + if (!this._doStroke && !this._doFill) { + return this; + } + var closeShape = mode === constants.CLOSE; + var v; + if (closeShape && !isContour) { + vertices.push(vertices[0]); + } + var i, j; + var numVerts = vertices.length; + if (isCurve && (shapeKind === constants.POLYGON || shapeKind === null)) { + if (numVerts > 3) { + var b = [], s = 1 - this._curveTightness; + this.drawingContext.beginPath(); + this.drawingContext.moveTo(vertices[1][0], vertices[1][1]); + for (i = 1; i + 2 < numVerts; i++) { + v = vertices[i]; + b[0] = [ + v[0], + v[1] + ]; + b[1] = [ + v[0] + (s * vertices[i + 1][0] - s * vertices[i - 1][0]) / 6, + v[1] + (s * vertices[i + 1][1] - s * vertices[i - 1][1]) / 6 + ]; + b[2] = [ + vertices[i + 1][0] + + (s * vertices[i][0]-s * vertices[i + 2][0]) / 6, + vertices[i + 1][1]+(s * vertices[i][1] - s*vertices[i + 2][1]) / 6 + ]; + b[3] = [ + vertices[i + 1][0], + vertices[i + 1][1] + ]; + this.drawingContext.bezierCurveTo(b[1][0],b[1][1], + b[2][0],b[2][1],b[3][0],b[3][1]); + } + if (closeShape) { + this.drawingContext.lineTo(vertices[i + 1][0], vertices[i + 1][1]); + } + this._doFillStrokeClose(); + } + } else if (isBezier&&(shapeKind===constants.POLYGON ||shapeKind === null)) { + this.drawingContext.beginPath(); + for (i = 0; i < numVerts; i++) { + if (vertices[i].isVert) { + if (vertices[i].moveTo) { + this.drawingContext.moveTo(vertices[i][0], vertices[i][1]); + } else { + this.drawingContext.lineTo(vertices[i][0], vertices[i][1]); + } + } else { + this.drawingContext.bezierCurveTo(vertices[i][0], vertices[i][1], + vertices[i][2], vertices[i][3], vertices[i][4], vertices[i][5]); + } + } + this._doFillStrokeClose(); + } else if (isQuadratic && + (shapeKind === constants.POLYGON || shapeKind === null)) { + this.drawingContext.beginPath(); + for (i = 0; i < numVerts; i++) { + if (vertices[i].isVert) { + if (vertices[i].moveTo) { + this.drawingContext.moveTo([0], vertices[i][1]); + } else { + this.drawingContext.lineTo(vertices[i][0], vertices[i][1]); + } + } else { + this.drawingContext.quadraticCurveTo(vertices[i][0], vertices[i][1], + vertices[i][2], vertices[i][3]); + } + } + this._doFillStrokeClose(); + } else { + if (shapeKind === constants.POINTS) { + for (i = 0; i < numVerts; i++) { + v = vertices[i]; + if (this._doStroke) { + this._pInst.stroke(v[6]); + } + this._pInst.point(v[0], v[1]); + } + } else if (shapeKind === constants.LINES) { + for (i = 0; i + 1 < numVerts; i += 2) { + v = vertices[i]; + if (this._doStroke) { + this._pInst.stroke(vertices[i + 1][6]); + } + this._pInst.line(v[0], v[1], vertices[i + 1][0], vertices[i + 1][1]); + } + } else if (shapeKind === constants.TRIANGLES) { + for (i = 0; i + 2 < numVerts; i += 3) { + v = vertices[i]; + this.drawingContext.beginPath(); + this.drawingContext.moveTo(v[0], v[1]); + this.drawingContext.lineTo(vertices[i + 1][0], vertices[i + 1][1]); + this.drawingContext.lineTo(vertices[i + 2][0], vertices[i + 2][1]); + this.drawingContext.lineTo(v[0], v[1]); + if (this._doFill) { + this._pInst.fill(vertices[i + 2][5]); + this.drawingContext.fill(); + } + if (this._doStroke) { + this._pInst.stroke(vertices[i + 2][6]); + this.drawingContext.stroke(); + } + this.drawingContext.closePath(); + } + } else if (shapeKind === constants.TRIANGLE_STRIP) { + for (i = 0; i + 1 < numVerts; i++) { + v = vertices[i]; + this.drawingContext.beginPath(); + this.drawingContext.moveTo(vertices[i + 1][0], vertices[i + 1][1]); + this.drawingContext.lineTo(v[0], v[1]); + if (this._doStroke) { + this._pInst.stroke(vertices[i + 1][6]); + } + if (this._doFill) { + this._pInst.fill(vertices[i + 1][5]); + } + if (i + 2 < numVerts) { + this.drawingContext.lineTo(vertices[i + 2][0], vertices[i + 2][1]); + if (this._doStroke) { + this._pInst.stroke(vertices[i + 2][6]); + } + if (this._doFill) { + this._pInst.fill(vertices[i + 2][5]); + } + } + this._doFillStrokeClose(); + } + } else if (shapeKind === constants.TRIANGLE_FAN) { + if (numVerts > 2) { + this.drawingContext.beginPath(); + this.drawingContext.moveTo(vertices[0][0], vertices[0][1]); + this.drawingContext.lineTo(vertices[1][0], vertices[1][1]); + this.drawingContext.lineTo(vertices[2][0], vertices[2][1]); + if (this._doFill) { + this._pInst.fill(vertices[2][5]); + } + if (this._doStroke) { + this._pInst.stroke(vertices[2][6]); + } + this._doFillStrokeClose(); + for (i = 3; i < numVerts; i++) { + v = vertices[i]; + this.drawingContext.beginPath(); + this.drawingContext.moveTo(vertices[0][0], vertices[0][1]); + this.drawingContext.lineTo(vertices[i - 1][0], vertices[i - 1][1]); + this.drawingContext.lineTo(v[0], v[1]); + if (this._doFill) { + this._pInst.fill(v[5]); + } + if (this._doStroke) { + this._pInst.stroke(v[6]); + } + this._doFillStrokeClose(); + } + } + } else if (shapeKind === constants.QUADS) { + for (i = 0; i + 3 < numVerts; i += 4) { + v = vertices[i]; + this.drawingContext.beginPath(); + this.drawingContext.moveTo(v[0], v[1]); + for (j = 1; j < 4; j++) { + this.drawingContext.lineTo(vertices[i + j][0], vertices[i + j][1]); + } + this.drawingContext.lineTo(v[0], v[1]); + if (this._doFill) { + this._pInst.fill(vertices[i + 3][5]); + } + if (this._doStroke) { + this._pInst.stroke(vertices[i + 3][6]); + } + this._doFillStrokeClose(); + } + } else if (shapeKind === constants.QUAD_STRIP) { + if (numVerts > 3) { + for (i = 0; i + 1 < numVerts; i += 2) { + v = vertices[i]; + this.drawingContext.beginPath(); + if (i + 3 < numVerts) { + this.drawingContext.moveTo(vertices[i + 2][0], vertices[i+2][1]); + this.drawingContext.lineTo(v[0], v[1]); + this.drawingContext.lineTo(vertices[i + 1][0], vertices[i+1][1]); + this.drawingContext.lineTo(vertices[i + 3][0], vertices[i+3][1]); + if (this._doFill) { + this._pInst.fill(vertices[i + 3][5]); + } + if (this._doStroke) { + this._pInst.stroke(vertices[i + 3][6]); + } + } else { + this.drawingContext.moveTo(v[0], v[1]); + this.drawingContext.lineTo(vertices[i + 1][0], vertices[i+1][1]); + } + this._doFillStrokeClose(); + } + } + } else { + this.drawingContext.beginPath(); + this.drawingContext.moveTo(vertices[0][0], vertices[0][1]); + for (i = 1; i < numVerts; i++) { + v = vertices[i]; + if (v.isVert) { + if (v.moveTo) { + this.drawingContext.moveTo(v[0], v[1]); + } else { + this.drawingContext.lineTo(v[0], v[1]); + } + } + } + this._doFillStrokeClose(); + } + } + isCurve = false; + isBezier = false; + isQuadratic = false; + isContour = false; + if (closeShape) { + vertices.pop(); + } + return this; +}; +////////////////////////////////////////////// +// SHAPE | Attributes +////////////////////////////////////////////// + +p5.Renderer2D.prototype.noSmooth = function() { + if ('imageSmoothingEnabled' in this.drawingContext) { + this.drawingContext.imageSmoothingEnabled = false; + } + else if ('mozImageSmoothingEnabled' in this.drawingContext) { + this.drawingContext.mozImageSmoothingEnabled = false; + } + else if ('webkitImageSmoothingEnabled' in this.drawingContext) { + this.drawingContext.webkitImageSmoothingEnabled = false; + } + else if ('msImageSmoothingEnabled' in this.drawingContext) { + this.drawingContext.msImageSmoothingEnabled = false; + } + return this; +}; + +p5.Renderer2D.prototype.smooth = function() { + if ('imageSmoothingEnabled' in this.drawingContext) { + this.drawingContext.imageSmoothingEnabled = true; + } + else if ('mozImageSmoothingEnabled' in this.drawingContext) { + this.drawingContext.mozImageSmoothingEnabled = true; + } + else if ('webkitImageSmoothingEnabled' in this.drawingContext) { + this.drawingContext.webkitImageSmoothingEnabled = true; + } + else if ('msImageSmoothingEnabled' in this.drawingContext) { + this.drawingContext.msImageSmoothingEnabled = true; + } + return this; +}; + +p5.Renderer2D.prototype.strokeCap = function(cap) { + if (cap === constants.ROUND || + cap === constants.SQUARE || + cap === constants.PROJECT) { + this.drawingContext.lineCap = cap; + } + return this; +}; + +p5.Renderer2D.prototype.strokeJoin = function(join) { + if (join === constants.ROUND || + join === constants.BEVEL || + join === constants.MITER) { + this.drawingContext.lineJoin = join; + } + return this; +}; + +p5.Renderer2D.prototype.strokeWeight = function(w) { + if (typeof w === 'undefined' || w === 0) { + // hack because lineWidth 0 doesn't work + this.drawingContext.lineWidth = 0.0001; + } else { + this.drawingContext.lineWidth = w; + } + return this; +}; + +p5.Renderer2D.prototype._getFill = function(){ + return this.drawingContext.fillStyle; +}; + +p5.Renderer2D.prototype._getStroke = function(){ + return this.drawingContext.strokeStyle; +}; + +////////////////////////////////////////////// +// SHAPE | Curves +////////////////////////////////////////////// +p5.Renderer2D.prototype.bezier = function (x1, y1, x2, y2, x3, y3, x4, y4) { + this._pInst.beginShape(); + this._pInst.vertex(x1, y1); + this._pInst.bezierVertex(x2, y2, x3, y3, x4, y4); + this._pInst.endShape(); + return this; +}; + +p5.Renderer2D.prototype.curve = function (x1, y1, x2, y2, x3, y3, x4, y4) { + this._pInst.beginShape(); + this._pInst.curveVertex(x1, y1); + this._pInst.curveVertex(x2, y2); + this._pInst.curveVertex(x3, y3); + this._pInst.curveVertex(x4, y4); + this._pInst.endShape(); + return this; +}; + +////////////////////////////////////////////// +// SHAPE | Vertex +////////////////////////////////////////////// + +p5.Renderer2D.prototype._doFillStrokeClose = function () { + if (this._doFill) { + this.drawingContext.fill(); + } + if (this._doStroke) { + this.drawingContext.stroke(); + } + this.drawingContext.closePath(); +}; + +////////////////////////////////////////////// +// TRANSFORM +////////////////////////////////////////////// + +p5.Renderer2D.prototype.applyMatrix = +function(n00, n01, n02, n10, n11, n12) { + this.drawingContext.transform(n00, n01, n02, n10, n11, n12); +}; + +p5.Renderer2D.prototype.resetMatrix = function() { + this.drawingContext.setTransform(1, 0, 0, 1, 0, 0); + this.drawingContext.scale(this._pInst._pixelDensity, + this._pInst._pixelDensity); + return this; +}; + +p5.Renderer2D.prototype.rotate = function(r) { + this.drawingContext.rotate(r); +}; + +p5.Renderer2D.prototype.scale = function(x,y) { + this.drawingContext.scale(x, y); + return this; +}; + +p5.Renderer2D.prototype.shearX = function(angle) { + if (this._pInst._angleMode === constants.DEGREES) { + // undoing here, because it gets redone in tan() + angle = this._pInst.degrees(angle); + } + this.drawingContext.transform(1, 0, this._pInst.tan(angle), 1, 0, 0); + return this; +}; + +p5.Renderer2D.prototype.shearY = function(angle) { + if (this._pInst._angleMode === constants.DEGREES) { + // undoing here, because it gets redone in tan() + angle = this._pInst.degrees(angle); + } + this.drawingContext.transform(1, this._pInst.tan(angle), 0, 1, 0, 0); + return this; +}; + +p5.Renderer2D.prototype.translate = function(x, y) { + this.drawingContext.translate(x, y); + return this; +}; + +////////////////////////////////////////////// +// TYPOGRAPHY +// +////////////////////////////////////////////// + +p5.Renderer2D.prototype.text = function (str, x, y, maxWidth, maxHeight) { + + var p = this._pInst, cars, n, ii, jj, line, testLine, + testWidth, words, totalHeight, baselineHacked, + finalMaxHeight = Number.MAX_VALUE; + + // baselineHacked: (HACK) + // A temporary fix to conform to Processing's implementation + // of BASELINE vertical alignment in a bounding box + + if (!(this._doFill || this._doStroke)) { + return; + } + + if (typeof str !== 'string') { + str = str.toString(); + } + + str = str.replace(/(\t)/g, ' '); + cars = str.split('\n'); + + if (typeof maxWidth !== 'undefined') { + + totalHeight = 0; + for (ii = 0; ii < cars.length; ii++) { + line = ''; + words = cars[ii].split(' '); + for (n = 0; n < words.length; n++) { + testLine = line + words[n] + ' '; + testWidth = this.textWidth(testLine); + if (testWidth > maxWidth) { + line = words[n] + ' '; + totalHeight += p.textLeading(); + } else { + line = testLine; + } + } + } + + if (this._rectMode === constants.CENTER) { + + x -= maxWidth / 2; + y -= maxHeight / 2; + } + + switch (this.drawingContext.textAlign) { + + case constants.CENTER: + x += maxWidth / 2; + break; + case constants.RIGHT: + x += maxWidth; + break; + } + + if (typeof maxHeight !== 'undefined') { + + switch (this.drawingContext.textBaseline) { + case constants.BOTTOM: + y += (maxHeight - totalHeight); + break; + case constants._CTX_MIDDLE: // CENTER? + y += (maxHeight - totalHeight) / 2; + break; + case constants.BASELINE: + baselineHacked = true; + this.drawingContext.textBaseline = constants.TOP; + break; + } + + // remember the max-allowed y-position for any line (fix to #928) + finalMaxHeight = (y + maxHeight) - p.textAscent(); + } + + for (ii = 0; ii < cars.length; ii++) { + + line = ''; + words = cars[ii].split(' '); + for (n = 0; n < words.length; n++) { + testLine = line + words[n] + ' '; + testWidth = this.textWidth(testLine); + if (testWidth > maxWidth && line.length > 0) { + this._renderText(p, line, x, y, finalMaxHeight); + line = words[n] + ' '; + y += p.textLeading(); + } else { + line = testLine; + } + } + + this._renderText(p, line, x, y, finalMaxHeight); + y += p.textLeading(); + } + } + else { + // Offset to account for vertically centering multiple lines of text - no + // need to adjust anything for vertical align top or baseline + var offset = 0, + vAlign = p.textAlign().vertical; + if (vAlign === constants.CENTER) { + offset = ((cars.length - 1) * p.textLeading()) / 2; + } else if (vAlign === constants.BOTTOM) { + offset = (cars.length - 1) * p.textLeading(); + } + + for (jj = 0; jj < cars.length; jj++) { + + this._renderText(p, cars[jj], x, y-offset, finalMaxHeight); + y += p.textLeading(); + } + } + + if (baselineHacked) { + this.drawingContext.textBaseline = constants.BASELINE; + } + + return p; +}; + +p5.Renderer2D.prototype._renderText = function(p, line, x, y, maxY) { + + if (y >= maxY) { + return; // don't render lines beyond our maxY position + } + + p.push(); // fix to #803 + + if (!this._isOpenType()) { // a system/browser font + + // no stroke unless specified by user + if (this._doStroke && this._strokeSet) { + + this.drawingContext.strokeText(line, x, y); + } + + if (this._doFill) { + + // if fill hasn't been set by user, use default text fill + this.drawingContext.fillStyle = this._fillSet ? + this.drawingContext.fillStyle : constants._DEFAULT_TEXT_FILL; + + this.drawingContext.fillText(line, x, y); + } + } + else { // an opentype font, let it handle the rendering + + this._textFont._renderPath(line, x, y, { renderer: this }); + } + + p.pop(); + + return p; +}; + +p5.Renderer2D.prototype.textWidth = function(s) { + + if (this._isOpenType()) { + + return this._textFont._textWidth(s, this._textSize); + } + + return this.drawingContext.measureText(s).width; +}; + +p5.Renderer2D.prototype.textAlign = function(h, v) { + + if (arguments.length) { + + if (h === constants.LEFT || + h === constants.RIGHT || + h === constants.CENTER) { + + this.drawingContext.textAlign = h; + } + + if (v === constants.TOP || + v === constants.BOTTOM || + v === constants.CENTER || + v === constants.BASELINE) { + + if (v === constants.CENTER) { + this.drawingContext.textBaseline = constants._CTX_MIDDLE; + } else { + this.drawingContext.textBaseline = v; + } + } + + return this._pInst; + + } else { + + var valign = this.drawingContext.textBaseline; + + if (valign === constants._CTX_MIDDLE) { + + valign = constants.CENTER; + } + + return { + + horizontal: this.drawingContext.textAlign, + vertical: valign + }; + } +}; + +p5.Renderer2D.prototype._applyTextProperties = function() { + + var font, p = this._pInst; + + this._setProperty('_textAscent', null); + this._setProperty('_textDescent', null); + + font = this._textFont; + + if (this._isOpenType()) { + + font = this._textFont.font.familyName; + this._setProperty('_textStyle', this._textFont.font.styleName); + } + + this.drawingContext.font = this._textStyle + ' ' + + this._textSize + 'px ' + font; + + return p; +}; + + +////////////////////////////////////////////// +// STRUCTURE +////////////////////////////////////////////// + +p5.Renderer2D.prototype.push = function() { + this.drawingContext.save(); +}; + +p5.Renderer2D.prototype.pop = function() { + this.drawingContext.restore(); +}; + +module.exports = p5.Renderer2D; + +},{"../image/filters":54,"./canvas":35,"./constants":36,"./core":37,"./p5.Renderer":43}],45:[function(_dereq_,module,exports){ +/** + * @module Rendering + * @submodule Rendering + * @for p5 + */ + +var p5 = _dereq_('./core'); +var constants = _dereq_('./constants'); +_dereq_('./p5.Graphics'); +_dereq_('./p5.Renderer2D'); +_dereq_('../webgl/p5.RendererGL'); +var defaultId = 'defaultCanvas0'; // this gets set again in createCanvas + +/** + * Creates a canvas element in the document, and sets the dimensions of it + * in pixels. This method should be called only once at the start of setup. + * Calling createCanvas more than once in a sketch will result in very + * unpredicable behavior. If you want more than one drawing canvas + * you could use createGraphics (hidden by default but it can be shown). + * <br><br> + * The system variables width and height are set by the parameters passed + * to this function. If createCanvas() is not used, the window will be + * given a default size of 100x100 pixels. + * <br><br> + * For more ways to position the canvas, see the + * <a href='https://github.com/processing/p5.js/wiki/Positioning-your-canvas'> + * positioning the canvas</a> wiki page. + * + * @method createCanvas + * @param {Number} w width of the canvas + * @param {Number} h height of the canvas + * @param {Constant} [renderer] P2D or WEBGL + * @return {Object} canvas generated + * @example + * <div> + * <code> + * function setup() { + * createCanvas(100, 50); + * background(153); + * line(0, 0, width, height); + * } + * </code> + * </div> + * + * @alt + * Black line extending from top-left of canvas to bottom right. + * + */ + +p5.prototype.createCanvas = function(w, h, renderer) { + //optional: renderer, otherwise defaults to p2d + var r = renderer || constants.P2D; + var isDefault, c; + + //4th arg (isDefault) used when called onLoad, + //otherwise hidden to the public api + if(arguments[3]){ + isDefault = + (typeof arguments[3] === 'boolean') ? arguments[3] : false; + } + + if(r === constants.WEBGL){ + c = document.getElementById(defaultId); + if(c){ //if defaultCanvas already exists + c.parentNode.removeChild(c); //replace the existing defaultCanvas + } + c = document.createElement('canvas'); + c.id = defaultId; + } + else { + if (isDefault) { + c = document.createElement('canvas'); + var i = 0; + while (document.getElementById('defaultCanvas'+i)) { + i++; + } + defaultId = 'defaultCanvas'+i; + c.id = defaultId; + } else { // resize the default canvas if new one is created + c = this.canvas; + } + } + + // set to invisible if still in setup (to prevent flashing with manipulate) + if (!this._setupDone) { + c.dataset.hidden = true; // tag to show later + c.style.visibility='hidden'; + } + + if (this._userNode) { // user input node case + this._userNode.appendChild(c); + } else { + document.body.appendChild(c); + } + + + + // Init our graphics renderer + //webgl mode + if (r === constants.WEBGL) { + this._setProperty('_renderer', new p5.RendererGL(c, this, true)); + this._isdefaultGraphics = true; + } + //P2D mode + else { + if (!this._isdefaultGraphics) { + this._setProperty('_renderer', new p5.Renderer2D(c, this, true)); + this._isdefaultGraphics = true; + } + } + this._renderer.resize(w, h); + this._renderer._applyDefaults(); + if (isDefault) { // only push once + this._elements.push(this._renderer); + } + return this._renderer; +}; + +/** + * Resizes the canvas to given width and height. The canvas will be cleared + * and draw will be called immediately, allowing the sketch to re-render itself + * in the resized canvas. + * @method resizeCanvas + * @example + * <div class="norender"><code> + * function setup() { + * createCanvas(windowWidth, windowHeight); + * } + * + * function draw() { + * background(0, 100, 200); + * } + * + * function windowResized() { + * resizeCanvas(windowWidth, windowHeight); + * } + * </code></div> + * + * @alt + * No image displayed. + * + */ +p5.prototype.resizeCanvas = function (w, h, noRedraw) { + if (this._renderer) { + + // save canvas properties + var props = {}; + for (var key in this.drawingContext) { + var val = this.drawingContext[key]; + if (typeof val !== 'object' && typeof val !== 'function') { + props[key] = val; + } + } + this._renderer.resize(w, h); + // reset canvas properties + for (var savedKey in props) { + this.drawingContext[savedKey] = props[savedKey]; + } + if (!noRedraw) { + this.redraw(); + } + } +}; + + +/** + * Removes the default canvas for a p5 sketch that doesn't + * require a canvas + * @method noCanvas + * @example + * <div> + * <code> + * function setup() { + * noCanvas(); + * } + * </code> + * </div> + * + * @alt + * no image displayed + * + */ +p5.prototype.noCanvas = function() { + if (this.canvas) { + this.canvas.parentNode.removeChild(this.canvas); + } +}; + +/** + * Creates and returns a new p5.Renderer object. Use this class if you need + * to draw into an off-screen graphics buffer. The two parameters define the + * width and height in pixels. + * + * @method createGraphics + * @param {Number} w width of the offscreen graphics buffer + * @param {Number} h height of the offscreen graphics buffer + * @param {Constant} [renderer] P2D or WEBGL + * undefined defaults to p2d + * @return {Object} offscreen graphics buffer + * @example + * <div> + * <code> + * var pg; + * function setup() { + * createCanvas(100, 100); + * pg = createGraphics(100, 100); + * } + * function draw() { + * background(200); + * pg.background(100); + * pg.noStroke(); + * pg.ellipse(pg.width/2, pg.height/2, 50, 50); + * image(pg, 50, 50); + * image(pg, 0, 0, 50, 50); + * } + * </code> + * </div> + * + * @alt + * 4 grey squares alternating light and dark grey. White quarter circle mid-left. + * + */ +p5.prototype.createGraphics = function(w, h, renderer){ + return new p5.Graphics(w, h, renderer, this); +}; + +/** + * Blends the pixels in the display window according to the defined mode. + * There is a choice of the following modes to blend the source pixels (A) + * with the ones of pixels already in the display window (B): + * <ul> + * <li><code>BLEND</code> - linear interpolation of colours: C = + * A*factor + B. This is the default blending mode.</li> + * <li><code>ADD</code> - sum of A and B</li> + * <li><code>DARKEST</code> - only the darkest colour succeeds: C = + * min(A*factor, B).</li> + * <li><code>LIGHTEST</code> - only the lightest colour succeeds: C = + * max(A*factor, B).</li> + * <li><code>DIFFERENCE</code> - subtract colors from underlying image.</li> + * <li><code>EXCLUSION</code> - similar to <code>DIFFERENCE</code>, but less + * extreme.</li> + * <li><code>MULTIPLY</code> - multiply the colors, result will always be + * darker.</li> + * <li><code>SCREEN</code> - opposite multiply, uses inverse values of the + * colors.</li> + * <li><code>REPLACE</code> - the pixels entirely replace the others and + * don't utilize alpha (transparency) values.</li> + * <li><code>OVERLAY</code> - mix of <code>MULTIPLY</code> and <code>SCREEN + * </code>. Multiplies dark values, and screens light values.</li> + * <li><code>HARD_LIGHT</code> - <code>SCREEN</code> when greater than 50% + * gray, <code>MULTIPLY</code> when lower.</li> + * <li><code>SOFT_LIGHT</code> - mix of <code>DARKEST</code> and + * <code>LIGHTEST</code>. Works like <code>OVERLAY</code>, but not as harsh. + * </li> + * <li><code>DODGE</code> - lightens light tones and increases contrast, + * ignores darks.</li> + * <li><code>BURN</code> - darker areas are applied, increasing contrast, + * ignores lights.</li> + * </ul> + * + * @method blendMode + * @param {Constant} mode blend mode to set for canvas + * @example + * <div> + * <code> + * blendMode(LIGHTEST); + * strokeWeight(30); + * stroke(80, 150, 255); + * line(25, 25, 75, 75); + * stroke(255, 50, 50); + * line(75, 25, 25, 75); + * </code> + * </div> + * <div> + * <code> + * blendMode(MULTIPLY); + * strokeWeight(30); + * stroke(80, 150, 255); + * line(25, 25, 75, 75); + * stroke(255, 50, 50); + * line(75, 25, 25, 75); + * </code> + * </div> + * @alt + * translucent image thick red & blue diagonal rounded lines intersecting center + * Thick red & blue diagonal rounded lines intersecting center. dark at overlap + * + */ +p5.prototype.blendMode = function(mode) { + if (mode === constants.BLEND || mode === constants.DARKEST || + mode === constants.LIGHTEST || mode === constants.DIFFERENCE || + mode === constants.MULTIPLY || mode === constants.EXCLUSION || + mode === constants.SCREEN || mode === constants.REPLACE || + mode === constants.OVERLAY || mode === constants.HARD_LIGHT || + mode === constants.SOFT_LIGHT || mode === constants.DODGE || + mode === constants.BURN || mode === constants.ADD || + mode === constants.NORMAL) { + this._renderer.blendMode(mode); + } else { + throw new Error('Mode '+mode+' not recognized.'); + } +}; + +module.exports = p5; + +},{"../webgl/p5.RendererGL":86,"./constants":36,"./core":37,"./p5.Graphics":42,"./p5.Renderer2D":44}],46:[function(_dereq_,module,exports){ + +// requestAnim shim layer by Paul Irish +window.requestAnimationFrame = (function(){ + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function(callback, element){ + // should '60' here be framerate? + window.setTimeout(callback, 1000 / 60); + }; +})(); + +// use window.performance() to get max fast and accurate time in milliseconds +window.performance = window.performance || {}; +window.performance.now = (function(){ + var load_date = Date.now(); + return window.performance.now || + window.performance.mozNow || + window.performance.msNow || + window.performance.oNow || + window.performance.webkitNow || + function () { + return Date.now() - load_date; + }; +})(); + +/* +// http://paulirish.com/2011/requestanimationframe-for-smart-animating/ +// http://my.opera.com/emoller/blog/2011/12/20/ +// requestanimationframe-for-smart-er-animating +// requestAnimationFrame polyfill by Erik Möller +// fixes from Paul Irish and Tino Zijdel +(function() { + var lastTime = 0; + var vendors = ['ms', 'moz', 'webkit', 'o']; + for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { + window.requestAnimationFrame = + window[vendors[x]+'RequestAnimationFrame']; + window.cancelAnimationFrame = + window[vendors[x]+'CancelAnimationFrame'] || + window[vendors[x]+'CancelRequestAnimationFrame']; + } + + if (!window.requestAnimationFrame) { + window.requestAnimationFrame = function(callback, element) { + var currTime = new Date().getTime(); + var timeToCall = Math.max(0, 16 - (currTime - lastTime)); + var id = window.setTimeout(function() + { callback(currTime + timeToCall); }, timeToCall); + lastTime = currTime + timeToCall; + return id; + }; + } + + if (!window.cancelAnimationFrame) { + window.cancelAnimationFrame = function(id) { + clearTimeout(id); + }; + } +}()); +*/ + +/** + * shim for Uint8ClampedArray.slice + * (allows arrayCopy to work with pixels[]) + * with thanks to http://halfpapstudios.com/blog/tag/html5-canvas/ + * Enumerable set to false to protect for...in from + * Uint8ClampedArray.prototype pollution. + */ +(function () { + 'use strict'; + if (typeof Uint8ClampedArray !== 'undefined' && + !Uint8ClampedArray.prototype.slice) { + Object.defineProperty(Uint8ClampedArray.prototype, 'slice', { + value: Array.prototype.slice, + writable: true, configurable: true, enumerable: false + }); + } +}()); + +},{}],47:[function(_dereq_,module,exports){ +/** + * @module Structure + * @submodule Structure + * @for p5 + * @requires core + */ + +'use strict'; + +var p5 = _dereq_('./core'); + +p5.prototype.exit = function() { + throw 'exit() not implemented, see remove()'; +}; +/** + * Stops p5.js from continuously executing the code within draw(). + * If loop() is called, the code in draw() begins to run continuously again. + * If using noLoop() in setup(), it should be the last line inside the block. + * <br><br> + * When noLoop() is used, it's not possible to manipulate or access the + * screen inside event handling functions such as mousePressed() or + * keyPressed(). Instead, use those functions to call redraw() or loop(), + * which will run draw(), which can update the screen properly. This means + * that when noLoop() has been called, no drawing can happen, and functions + * like saveFrame() or loadPixels() may not be used. + * <br><br> + * Note that if the sketch is resized, redraw() will be called to update + * the sketch, even after noLoop() has been specified. Otherwise, the sketch + * would enter an odd state until loop() was called. + * + * @method noLoop + * @example + * <div><code> + * function setup() { + * createCanvas(100, 100); + * background(200); + * noLoop(); + * } + + * function draw() { + * line(10, 10, 90, 90); + * } + * </code></div> + * + * <div><code> + * var x = 0; + * function setup() { + * createCanvas(100, 100); + * } + * + * function draw() { + * background(204); + * x = x + 0.1; + * if (x > width) { + * x = 0; + * } + * line(x, 0, x, height); + * } + * + * function mousePressed() { + * noLoop(); + * } + * + * function mouseReleased() { + * loop(); + * } + * </code></div> + * + * @alt + * 113 pixel long line extending from top-left to bottom right of canvas. + * horizontal line moves slowly from left. Loops but stops on mouse press. + * + */ +p5.prototype.noLoop = function() { + this._loop = false; +}; +/** + * By default, p5.js loops through draw() continuously, executing the code + * within it. However, the draw() loop may be stopped by calling noLoop(). + * In that case, the draw() loop can be resumed with loop(). + * + * @method loop + * @example + * <div><code> + * var x = 0; + * function setup() { + * createCanvas(100, 100); + * noLoop(); + * } + * + * function draw() { + * background(204); + * x = x + 0.1; + * if (x > width) { + * x = 0; + * } + * line(x, 0, x, height); + * } + * + * function mousePressed() { + * loop(); + * } + * + * function mouseReleased() { + * noLoop(); + * } + * </code></div> + * + * @alt + * horizontal line moves slowly from left. Loops but stops on mouse press. + * + */ + +p5.prototype.loop = function() { + this._loop = true; + this._draw(); +}; + +/** + * The push() function saves the current drawing style settings and + * transformations, while pop() restores these settings. Note that these + * functions are always used together. They allow you to change the style + * and transformation settings and later return to what you had. When a new + * state is started with push(), it builds on the current style and transform + * information. The push() and pop() functions can be embedded to provide + * more control. (See the second example for a demonstration.) + * <br><br> + * push() stores information related to the current transformation state + * and style settings controlled by the following functions: fill(), + * stroke(), tint(), strokeWeight(), strokeCap(), strokeJoin(), + * imageMode(), rectMode(), ellipseMode(), colorMode(), textAlign(), + * textFont(), textMode(), textSize(), textLeading(). + * + * @method push + * @example + * <div> + * <code> + * ellipse(0, 50, 33, 33); // Left circle + * + * push(); // Start a new drawing state + * strokeWeight(10); + * fill(204, 153, 0); + * translate(50, 0); + * ellipse(0, 50, 33, 33); // Middle circle + * pop(); // Restore original state + * + * ellipse(100, 50, 33, 33); // Right circle + * </code> + * </div> + * <div> + * <code> + * ellipse(0, 50, 33, 33); // Left circle + * + * push(); // Start a new drawing state + * strokeWeight(10); + * fill(204, 153, 0); + * ellipse(33, 50, 33, 33); // Left-middle circle + * + * push(); // Start another new drawing state + * stroke(0, 102, 153); + * ellipse(66, 50, 33, 33); // Right-middle circle + * pop(); // Restore previous state + * + * pop(); // Restore original state + * + * ellipse(100, 50, 33, 33); // Right circle + * </code> + * </div> + * + * @alt + * Gold ellipse + thick black outline @center 2 white ellipses on left and right. + * 2 Gold ellipses left black right blue stroke. 2 white ellipses on left+right. + * + */ +p5.prototype.push = function () { + this._renderer.push(); + this._styles.push({ + _doStroke: this._renderer._doStroke, + _strokeSet: this._renderer._strokeSet, + _doFill: this._renderer._doFill, + _fillSet: this._renderer._fillSet, + _tint: this._renderer._tint, + _imageMode: this._renderer._imageMode, + _rectMode: this._renderer._rectMode, + _ellipseMode: this._renderer._ellipseMode, + _colorMode: this._renderer._colorMode, + _textFont: this._renderer._textFont, + _textLeading: this._renderer._textLeading, + _textSize: this._renderer._textSize, + _textStyle: this._renderer._textStyle + }); +}; + +/** + * The push() function saves the current drawing style settings and + * transformations, while pop() restores these settings. Note that these + * functions are always used together. They allow you to change the style + * and transformation settings and later return to what you had. When a new + * state is started with push(), it builds on the current style and transform + * information. The push() and pop() functions can be embedded to provide + * more control. (See the second example for a demonstration.) + * <br><br> + * push() stores information related to the current transformation state + * and style settings controlled by the following functions: fill(), + * stroke(), tint(), strokeWeight(), strokeCap(), strokeJoin(), + * imageMode(), rectMode(), ellipseMode(), colorMode(), textAlign(), + * textFont(), textMode(), textSize(), textLeading(). + * + * @method pop + * @example + * <div> + * <code> + * ellipse(0, 50, 33, 33); // Left circle + * + * push(); // Start a new drawing state + * translate(50, 0); + * strokeWeight(10); + * fill(204, 153, 0); + * ellipse(0, 50, 33, 33); // Middle circle + * pop(); // Restore original state + * + * ellipse(100, 50, 33, 33); // Right circle + * </code> + * </div> + * <div> + * <code> + * ellipse(0, 50, 33, 33); // Left circle + * + * push(); // Start a new drawing state + * strokeWeight(10); + * fill(204, 153, 0); + * ellipse(33, 50, 33, 33); // Left-middle circle + * + * push(); // Start another new drawing state + * stroke(0, 102, 153); + * ellipse(66, 50, 33, 33); // Right-middle circle + * pop(); // Restore previous state + * + * pop(); // Restore original state + * + * ellipse(100, 50, 33, 33); // Right circle + * </code> + * </div> + * + * @alt + * Gold ellipse + thick black outline @center 2 white ellipses on left and right. + * 2 Gold ellipses left black right blue stroke. 2 white ellipses on left+right. + * + */ +p5.prototype.pop = function () { + this._renderer.pop(); + var lastS = this._styles.pop(); + for(var prop in lastS){ + this._renderer[prop] = lastS[prop]; + } +}; + +p5.prototype.pushStyle = function() { + throw new Error('pushStyle() not used, see push()'); +}; + +p5.prototype.popStyle = function() { + throw new Error('popStyle() not used, see pop()'); +}; + +/** + * + * Executes the code within draw() one time. This functions allows the + * program to update the display window only when necessary, for example + * when an event registered by mousePressed() or keyPressed() occurs. + * <br><br> + * In structuring a program, it only makes sense to call redraw() within + * events such as mousePressed(). This is because redraw() does not run + * draw() immediately (it only sets a flag that indicates an update is + * needed). + * <br><br> + * The redraw() function does not work properly when called inside draw(). + * To enable/disable animations, use loop() and noLoop(). + * <br><br> + * In addition you can set the number of redraws per method call. Just + * add an integer as single parameter for the number of redraws. + * + * @method redraw + * @param {Integer} [n] Redraw for n-times. The default value is 1. + * @example + * <div><code> + * var x = 0; + * + * function setup() { + * createCanvas(100, 100); + * noLoop(); + * } + * + * function draw() { + * background(204); + * line(x, 0, x, height); + * } + * + * function mousePressed() { + * x += 1; + * redraw(); + * } + * </code></div> + * + * <div class='norender'><code> + * var x = 0; + * + * function setup() { + * createCanvas(100, 100); + * noLoop(); + * } + * + * function draw() { + * background(204); + * x += 1; + * line(x, 0, x, height); + * } + * + * function mousePressed() { + * redraw(5); + * } + * </code></div> + * + * @alt + * black line on far left of canvas + * black line on far left of canvas + * + */ +p5.prototype.redraw = function () { + this.resetMatrix(); + if(this._renderer.isP3D){ + this._renderer._update(); + } + + var numberOfRedraws = 1; + if (arguments.length === 1) { + try { + if (parseInt(arguments[0]) > 1) { + numberOfRedraws = parseInt(arguments[0]); + } + } catch (error) { + // Do nothing, because the default value didn't be changed. + } + } + var userSetup = this.setup || window.setup; + var userDraw = this.draw || window.draw; + if (typeof userDraw === 'function') { + if (typeof userSetup === 'undefined') { + this.scale(this._pixelDensity, this._pixelDensity); + } + var self = this; + var callMethod = function (f) { + f.call(self); + }; + for (var idxRedraw = 0; idxRedraw < numberOfRedraws; idxRedraw++) { + this._registeredMethods.pre.forEach(callMethod); + userDraw(); + this._registeredMethods.post.forEach(callMethod); + } + } +}; + +p5.prototype.size = function() { + var s = 'size() is not a valid p5 function, to set the size of the '; + s += 'drawing canvas, please use createCanvas() instead'; + throw s; +}; + + +module.exports = p5; + +},{"./core":37}],48:[function(_dereq_,module,exports){ +/** + * @module Transform + * @submodule Transform + * @for p5 + * @requires core + * @requires constants + */ + + +'use strict'; + +var p5 = _dereq_('./core'); +var constants = _dereq_('./constants'); + +/** + * Multiplies the current matrix by the one specified through the parameters. + * This is very slow because it will try to calculate the inverse of the + * transform, so avoid it whenever possible. + * + * @method applyMatrix + * @param {Number} n00 numbers which define the 3x2 matrix to be multiplied + * @param {Number} n01 numbers which define the 3x2 matrix to be multiplied + * @param {Number} n02 numbers which define the 3x2 matrix to be multiplied + * @param {Number} n10 numbers which define the 3x2 matrix to be multiplied + * @param {Number} n11 numbers which define the 3x2 matrix to be multiplied + * @param {Number} n12 numbers which define the 3x2 matrix to be multiplied + * @return {p5} the p5 object + * @example + * <div> + * <code> + * // Example in the works. + * </code> + * </div> + * + * @alt + * no image diplayed + * + */ +p5.prototype.applyMatrix = function(n00, n01, n02, n10, n11, n12) { + this._renderer.applyMatrix(n00, n01, n02, n10, n11, n12); + return this; +}; + +p5.prototype.popMatrix = function() { + throw new Error('popMatrix() not used, see pop()'); +}; + +p5.prototype.printMatrix = function() { + throw new Error('printMatrix() not implemented'); +}; + +p5.prototype.pushMatrix = function() { + throw new Error('pushMatrix() not used, see push()'); +}; + +/** + * Replaces the current matrix with the identity matrix. + * + * @method resetMatrix + * @return {p5} the p5 object + * @example + * <div> + * <code> + * // Example in the works. + * </code> + * </div> + * + * @alt + * no image diplayed + * + */ +p5.prototype.resetMatrix = function() { + this._renderer.resetMatrix(); + return this; +}; + +/** + * Rotates a shape the amount specified by the angle parameter. This + * function accounts for angleMode, so angles can be entered in either + * RADIANS or DEGREES. + * <br><br> + * Objects are always rotated around their relative position to the + * origin and positive numbers rotate objects in a clockwise direction. + * Transformations apply to everything that happens after and subsequent + * calls to the function accumulates the effect. For example, calling + * rotate(HALF_PI) and then rotate(HALF_PI) is the same as rotate(PI). + * All tranformations are reset when draw() begins again. + * <br><br> + * Technically, rotate() multiplies the current transformation matrix + * by a rotation matrix. This function can be further controlled by + * the push() and pop(). + * + * @method rotate + * @param {Number} angle the angle of rotation, specified in radians + * or degrees, depending on current angleMode + * @return {p5} the p5 object + * @example + * <div> + * <code> + * translate(width/2, height/2); + * rotate(PI/3.0); + * rect(-26, -26, 52, 52); + * </code> + * </div> + * + * @alt + * white 52x52 rect with black outline at center rotated counter 45 degrees + * + */ +/** + * @method rotate + * @param {Number} rad angle in radians + * @param {p5.Vector | Array} axis axis to rotate around + * @return {p5.RendererGL} [description] + */ +p5.prototype.rotate = function() { + var args = new Array(arguments.length); + var r; + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + if (this._angleMode === constants.DEGREES) { + r = this.radians(args[0]); + } else if (this._angleMode === constants.RADIANS){ + r = args[0]; + } + //in webgl mode + if(args.length > 1){ + this._renderer.rotate(r, args[1]); + } + else { + this._renderer.rotate(r); + } + return this; +}; + +/** + * Rotates around X axis. + * @method rotateX + * @param {Number} rad angles in radians + * @return {[type]} [description] + */ +p5.prototype.rotateX = function(rad) { + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + if (this._renderer.isP3D) { + this._validateParameters( + 'rotateX', + args, + [ + ['Number'] + ] + ); + this._renderer.rotateX(rad); + } else { + throw 'not supported in p2d. Please use webgl mode'; + } + return this; +}; + +/** + * Rotates around Y axis. + * @method rotateY + * @param {Number} rad angles in radians + * @return {[type]} [description] + */ +p5.prototype.rotateY = function(rad) { + if (this._renderer.isP3D) { + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + this._validateParameters( + 'rotateY', + args, + [ + ['Number'] + ] + ); + this._renderer.rotateY(rad); + } else { + throw 'not supported in p2d. Please use webgl mode'; + } + return this; +}; + +/** + * Rotates around Z axis. Webgl mode only. + * @method rotateZ + * @param {Number} rad angles in radians + * @return {[type]} [description] + */ +p5.prototype.rotateZ = function(rad) { + if (this._renderer.isP3D) { + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + this._validateParameters( + 'rotateZ', + args, + [ + ['Number'] + ] + ); + this._renderer.rotateZ(rad); + } else { + throw 'not supported in p2d. Please use webgl mode'; + } + return this; +}; + +/** + * Increases or decreases the size of a shape by expanding and contracting + * vertices. Objects always scale from their relative origin to the + * coordinate system. Scale values are specified as decimal percentages. + * For example, the function call scale(2.0) increases the dimension of a + * shape by 200%. + * <br><br> + * Transformations apply to everything that happens after and subsequent + * calls to the function multiply the effect. For example, calling scale(2.0) + * and then scale(1.5) is the same as scale(3.0). If scale() is called + * within draw(), the transformation is reset when the loop begins again. + * <br><br> + * Using this function with the z parameter is only available in WEBGL mode. + * This function can be further controlled with push() and pop(). + * + * @method scale + * @param {Number | p5.Vector | Array} s + * percent to scale the object, or percentage to + * scale the object in the x-axis if multiple arguments + * are given + * @param {Number} [y] percent to scale the object in the y-axis + * @param {Number} [z] percent to scale the object in the z-axis (webgl only) + * @return {p5} the p5 object + * @example + * <div> + * <code> + * translate(width/2, height/2); + * rotate(PI/3.0); + * rect(-26, -26, 52, 52); + * </code> + * </div> + * + * <div> + * <code> + * rect(30, 20, 50, 50); + * scale(0.5, 1.3); + * rect(30, 20, 50, 50); + * </code> + * </div> + * + * @alt + * white 52x52 rect with black outline at center rotated counter 45 degrees + * 2 white rects with black outline- 1 50x50 at center. other 25x65 bottom left + * + */ +p5.prototype.scale = function() { + var x,y,z; + var args = new Array(arguments.length); + for(var i = 0; i < args.length; i++) { + args[i] = arguments[i]; + } + if(args[0] instanceof p5.Vector){ + x = args[0].x; + y = args[0].y; + z = args[0].z; + } + else if(args[0] instanceof Array){ + x = args[0][0]; + y = args[0][1]; + z = args[0][2] || 1; + } + else { + if(args.length === 1){ + x = y = z = args[0]; + } + else { + x = args[0]; + y = args[1]; + z = args[2] || 1; + } + } + + if(this._renderer.isP3D){ + this._renderer.scale.call(this._renderer, x,y,z); + } + else { + this._renderer.scale.call(this._renderer, x,y); + } + return this; +}; + +/** + * Shears a shape around the x-axis the amount specified by the angle + * parameter. Angles should be specified in the current angleMode. + * Objects are always sheared around their relative position to the origin + * and positive numbers shear objects in a clockwise direction. + * <br><br> + * Transformations apply to everything that happens after and subsequent + * calls to the function accumulates the effect. For example, calling + * shearX(PI/2) and then shearX(PI/2) is the same as shearX(PI). + * If shearX() is called within the draw(), the transformation is reset when + * the loop begins again. + * <br><br> + * Technically, shearX() multiplies the current transformation matrix by a + * rotation matrix. This function can be further controlled by the + * push() and pop() functions. + * + * @method shearX + * @param {Number} angle angle of shear specified in radians or degrees, + * depending on current angleMode + * @return {p5} the p5 object + * @example + * <div> + * <code> + * translate(width/4, height/4); + * shearX(PI/4.0); + * rect(0, 0, 30, 30); + * </code> + * </div> + * + * @alt + * white irregular quadrilateral with black outline at top middle. + * + */ +p5.prototype.shearX = function(angle) { + if (this._angleMode === constants.DEGREES) { + angle = this.radians(angle); + } + this._renderer.shearX(angle); + return this; +}; + +/** + * Shears a shape around the y-axis the amount specified by the angle + * parameter. Angles should be specified in the current angleMode. Objects + * are always sheared around their relative position to the origin and + * positive numbers shear objects in a clockwise direction. + * <br><br> + * Transformations apply to everything that happens after and subsequent + * calls to the function accumulates the effect. For example, calling + * shearY(PI/2) and then shearY(PI/2) is the same as shearY(PI). If + * shearY() is called within the draw(), the transformation is reset when + * the loop begins again. + * <br><br> + * Technically, shearY() multiplies the current transformation matrix by a + * rotation matrix. This function can be further controlled by the + * push() and pop() functions. + * + * @method shearY + * @param {Number} angle angle of shear specified in radians or degrees, + * depending on current angleMode + * @return {p5} the p5 object + * @example + * <div> + * <code> + * translate(width/4, height/4); + * shearY(PI/4.0); + * rect(0, 0, 30, 30); + * </code> + * </div> + * + * @alt + * white irregular quadrilateral with black outline at middle bottom. + * + */ +p5.prototype.shearY = function(angle) { + if (this._angleMode === constants.DEGREES) { + angle = this.radians(angle); + } + this._renderer.shearY(angle); + return this; +}; + +/** + * Specifies an amount to displace objects within the display window. + * The x parameter specifies left/right translation, the y parameter + * specifies up/down translation. + * <br><br> + * Transformations are cumulative and apply to everything that happens after + * and subsequent calls to the function accumulates the effect. For example, + * calling translate(50, 0) and then translate(20, 0) is the same as + * translate(70, 0). If translate() is called within draw(), the + * transformation is reset when the loop begins again. This function can be + * further controlled by using push() and pop(). + * + * @method translate + * @param {Number} x left/right translation + * @param {Number} y up/down translation + * @param {Number} [z] forward/backward translation (webgl only) + * @return {p5} the p5 object + * @example + * <div> + * <code> + * translate(30, 20); + * rect(0, 0, 55, 55); + * </code> + * </div> + * + * <div> + * <code> + * rect(0, 0, 55, 55); // Draw rect at original 0,0 + * translate(30, 20); + * rect(0, 0, 55, 55); // Draw rect at new 0,0 + * translate(14, 14); + * rect(0, 0, 55, 55); // Draw rect at new 0,0 + * </code> + * </div> + * + * @alt + * white 55x55 rect with black outline at center right. + * 3 white 55x55 rects with black outlines at top-l, center-r and bottom-r. + * + */ +p5.prototype.translate = function(x, y, z) { + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + + if (this._renderer.isP3D) { + this._validateParameters( + 'translate', + args, + [ + //p3d + ['Number', 'Number', 'Number'] + ] + ); + this._renderer.translate(x, y, z); + } else { + this._validateParameters( + 'translate', + args, + [ + //p2d + ['Number', 'Number'] + ] + ); + this._renderer.translate(x, y); + } + return this; +}; + +module.exports = p5; + +},{"./constants":36,"./core":37}],49:[function(_dereq_,module,exports){ +/** + * @module Shape + * @submodule Vertex + * @for p5 + * @requires core + * @requires constants + */ + +'use strict'; + +var p5 = _dereq_('./core'); +var constants = _dereq_('./constants'); +var shapeKind = null; +var vertices = []; +var contourVertices = []; +var isBezier = false; +var isCurve = false; +var isQuadratic = false; +var isContour = false; +var isFirstContour = true; + +/** + * Use the beginContour() and endContour() functions to create negative + * shapes within shapes such as the center of the letter 'O'. beginContour() + * begins recording vertices for the shape and endContour() stops recording. + * The vertices that define a negative shape must "wind" in the opposite + * direction from the exterior shape. First draw vertices for the exterior + * clockwise order, then for internal shapes, draw vertices + * shape in counter-clockwise. + * <br><br> + * These functions can only be used within a beginShape()/endShape() pair and + * transformations such as translate(), rotate(), and scale() do not work + * within a beginContour()/endContour() pair. It is also not possible to use + * other shapes, such as ellipse() or rect() within. + * + * @method beginContour + * @return {Object} the p5 object + * @example + * <div> + * <code> + * translate(50, 50); + * stroke(255, 0, 0); + * beginShape(); + * // Exterior part of shape, clockwise winding + * vertex(-40, -40); + * vertex(40, -40); + * vertex(40, 40); + * vertex(-40, 40); + * // Interior part of shape, counter-clockwise winding + * beginContour(); + * vertex(-20, -20); + * vertex(-20, 20); + * vertex(20, 20); + * vertex(20, -20); + * endContour(); + * endShape(CLOSE); + * </code> + * </div> + * + * @alt + * white rect and smaller grey rect with red outlines in center of canvas. + * + */ +p5.prototype.beginContour = function() { + contourVertices = []; + isContour = true; + return this; +}; + +/** + * Using the beginShape() and endShape() functions allow creating more + * complex forms. beginShape() begins recording vertices for a shape and + * endShape() stops recording. The value of the kind parameter tells it which + * types of shapes to create from the provided vertices. With no mode + * specified, the shape can be any irregular polygon. + * <br><br> + * The parameters available for beginShape() are POINTS, LINES, TRIANGLES, + * TRIANGLE_FAN, TRIANGLE_STRIP, QUADS, and QUAD_STRIP. After calling the + * beginShape() function, a series of vertex() commands must follow. To stop + * drawing the shape, call endShape(). Each shape will be outlined with the + * current stroke color and filled with the fill color. + * <br><br> + * Transformations such as translate(), rotate(), and scale() do not work + * within beginShape(). It is also not possible to use other shapes, such as + * ellipse() or rect() within beginShape(). + * + * @method beginShape + * @param {Constant} kind either POINTS, LINES, TRIANGLES, TRIANGLE_FAN + * TRIANGLE_STRIP, QUADS, or QUAD_STRIP + * @return {Object} the p5 object + * @example + * <div> + * <code> + * beginShape(); + * vertex(30, 20); + * vertex(85, 20); + * vertex(85, 75); + * vertex(30, 75); + * endShape(CLOSE); + * </code> + * </div> + * + * <div> + * <code> + * // currently not working + * beginShape(POINTS); + * vertex(30, 20); + * vertex(85, 20); + * vertex(85, 75); + * vertex(30, 75); + * endShape(); + * </code> + * </div> + * + * <div> + * <code> + * beginShape(LINES); + * vertex(30, 20); + * vertex(85, 20); + * vertex(85, 75); + * vertex(30, 75); + * endShape(); + * </code> + * </div> + * + * <div> + * <code> + * noFill(); + * beginShape(); + * vertex(30, 20); + * vertex(85, 20); + * vertex(85, 75); + * vertex(30, 75); + * endShape(); + * </code> + * </div> + * + * <div> + * <code> + * noFill(); + * beginShape(); + * vertex(30, 20); + * vertex(85, 20); + * vertex(85, 75); + * vertex(30, 75); + * endShape(CLOSE); + * </code> + * </div> + * + * <div> + * <code> + * beginShape(TRIANGLES); + * vertex(30, 75); + * vertex(40, 20); + * vertex(50, 75); + * vertex(60, 20); + * vertex(70, 75); + * vertex(80, 20); + * endShape(); + * </code> + * </div> + * + * <div> + * <code> + * beginShape(TRIANGLE_STRIP); + * vertex(30, 75); + * vertex(40, 20); + * vertex(50, 75); + * vertex(60, 20); + * vertex(70, 75); + * vertex(80, 20); + * vertex(90, 75); + * endShape(); + * </code> + * </div> + * + * <div> + * <code> + * beginShape(TRIANGLE_FAN); + * vertex(57.5, 50); + * vertex(57.5, 15); + * vertex(92, 50); + * vertex(57.5, 85); + * vertex(22, 50); + * vertex(57.5, 15); + * endShape(); + * </code> + * </div> + * + * <div> + * <code> + * beginShape(QUADS); + * vertex(30, 20); + * vertex(30, 75); + * vertex(50, 75); + * vertex(50, 20); + * vertex(65, 20); + * vertex(65, 75); + * vertex(85, 75); + * vertex(85, 20); + * endShape(); + * </code> + * </div> + * + * <div> + * <code> + * beginShape(QUAD_STRIP); + * vertex(30, 20); + * vertex(30, 75); + * vertex(50, 20); + * vertex(50, 75); + * vertex(65, 20); + * vertex(65, 75); + * vertex(85, 20); + * vertex(85, 75); + * endShape(); + * </code> + * </div> + * + * <div> + * <code> + * beginShape(); + * vertex(20, 20); + * vertex(40, 20); + * vertex(40, 40); + * vertex(60, 40); + * vertex(60, 60); + * vertex(20, 60); + * endShape(CLOSE); + * </code> + * </div> + * @alt + * white square-shape with black outline in middle-right of canvas. + * 4 black points in a square shape in middle-right of canvas. + * 2 horizontal black lines. In the top-right and bottom-right of canvas. + * 3 line shape with horizontal on top, vertical in middle and horizontal bottom. + * square line shape in middle-right of canvas. + * 2 white triangle shapes mid-right canvas. left one pointing up and right down. + * 5 horizontal interlocking and alternating white triangles in mid-right canvas. + * 4 interlocking white triangles in 45 degree rotated square-shape. + * 2 white rectangle shapes in mid-right canvas. Both 20x55. + * 3 side-by-side white rectangles center rect is smaller in mid-right canvas. + * Thick white l-shape with black outline mid-top-left of canvas. + * + */ +p5.prototype.beginShape = function(kind) { + if (kind === constants.POINTS || + kind === constants.LINES || + kind === constants.TRIANGLES || + kind === constants.TRIANGLE_FAN || + kind === constants.TRIANGLE_STRIP || + kind === constants.QUADS || + kind === constants.QUAD_STRIP) { + shapeKind = kind; + } else { + shapeKind = null; + } + if(this._renderer.isP3D){ + this._renderer.beginShape(kind); + } else { + vertices = []; + contourVertices = []; + } + return this; +}; + +/** + * Specifies vertex coordinates for Bezier curves. Each call to + * bezierVertex() defines the position of two control points and + * one anchor point of a Bezier curve, adding a new segment to a + * line or shape. + * <br><br> + * The first time bezierVertex() is used within a + * beginShape() call, it must be prefaced with a call to vertex() + * to set the first anchor point. This function must be used between + * beginShape() and endShape() and only when there is no MODE + * parameter specified to beginShape(). + * + * @method bezierVertex + * @param {Number} x2 x-coordinate for the first control point + * @param {Number} y2 y-coordinate for the first control point + * @param {Number} x3 x-coordinate for the second control point + * @param {Number} y3 y-coordinate for the second control point + * @param {Number} x4 x-coordinate for the anchor point + * @param {Number} y4 y-coordinate for the anchor point + * @return {Object} the p5 object + * @example + * <div> + * <code> + * noFill(); + * beginShape(); + * vertex(30, 20); + * bezierVertex(80, 0, 80, 75, 30, 75); + * endShape(); + * </code> + * </div> + * + * <div> + * <code> + * beginShape(); + * vertex(30, 20); + * bezierVertex(80, 0, 80, 75, 30, 75); + * bezierVertex(50, 80, 60, 25, 30, 20); + * endShape(); + * </code> + * </div> + * + * @alt + * crescent-shaped line in middle of canvas. Points facing left. + * white crescent shape in middle of canvas. Points facing left. + * + */ +p5.prototype.bezierVertex = function(x2, y2, x3, y3, x4, y4) { + if (vertices.length === 0) { + throw 'vertex() must be used once before calling bezierVertex()'; + } else { + isBezier = true; + var vert = []; + for (var i = 0; i < arguments.length; i++) { + vert[i] = arguments[i]; + } + vert.isVert = false; + if (isContour) { + contourVertices.push(vert); + } else { + vertices.push(vert); + } + } + return this; +}; + +/** + * Specifies vertex coordinates for curves. This function may only + * be used between beginShape() and endShape() and only when there + * is no MODE parameter specified to beginShape(). + * <br><br> + * The first and last points in a series of curveVertex() lines will be used to + * guide the beginning and end of a the curve. A minimum of four + * points is required to draw a tiny curve between the second and + * third points. Adding a fifth point with curveVertex() will draw + * the curve between the second, third, and fourth points. The + * curveVertex() function is an implementation of Catmull-Rom + * splines. + * + * @method curveVertex + * @param {Number} x x-coordinate of the vertex + * @param {Number} y y-coordinate of the vertex + * @return {Object} the p5 object + * @example + * <div> + * <code> + * noFill(); + * beginShape(); + * curveVertex(84, 91); + * curveVertex(84, 91); + * curveVertex(68, 19); + * curveVertex(21, 17); + * curveVertex(32, 100); + * curveVertex(32, 100); + * endShape(); + * </code> + * </div> + * + * @alt + * Upside-down u-shape line, mid canvas. left point extends beyond canvas view. + * + */ +p5.prototype.curveVertex = function(x,y) { + isCurve = true; + this.vertex(x, y); + return this; +}; + +/** + * Use the beginContour() and endContour() functions to create negative + * shapes within shapes such as the center of the letter 'O'. beginContour() + * begins recording vertices for the shape and endContour() stops recording. + * The vertices that define a negative shape must "wind" in the opposite + * direction from the exterior shape. First draw vertices for the exterior + * clockwise order, then for internal shapes, draw vertices + * shape in counter-clockwise. + * <br><br> + * These functions can only be used within a beginShape()/endShape() pair and + * transformations such as translate(), rotate(), and scale() do not work + * within a beginContour()/endContour() pair. It is also not possible to use + * other shapes, such as ellipse() or rect() within. + * + * @method endContour + * @return {Object} the p5 object + * @example + * <div> + * <code> + * translate(50, 50); + * stroke(255, 0, 0); + * beginShape(); + * // Exterior part of shape, clockwise winding + * vertex(-40, -40); + * vertex(40, -40); + * vertex(40, 40); + * vertex(-40, 40); + * // Interior part of shape, counter-clockwise winding + * beginContour(); + * vertex(-20, -20); + * vertex(-20, 20); + * vertex(20, 20); + * vertex(20, -20); + * endContour(); + * endShape(CLOSE); + * </code> + * </div> + * + * @alt + * white rect and smaller grey rect with red outlines in center of canvas. + * + */ +p5.prototype.endContour = function() { + var vert = contourVertices[0].slice(); // copy all data + vert.isVert = contourVertices[0].isVert; + vert.moveTo = false; + contourVertices.push(vert); + + // prevent stray lines with multiple contours + if (isFirstContour) { + vertices.push(vertices[0]); + isFirstContour = false; + } + + for (var i = 0; i < contourVertices.length; i++) { + vertices.push(contourVertices[i]); + } + return this; +}; + +/** + * The endShape() function is the companion to beginShape() and may only be + * called after beginShape(). When endshape() is called, all of image data + * defined since the previous call to beginShape() is written into the image + * buffer. The constant CLOSE as the value for the MODE parameter to close + * the shape (to connect the beginning and the end). + * + * @method endShape + * @param {Constant} mode use CLOSE to close the shape + * @return {Object} the p5 object + * @example + * <div> + * <code> + * noFill(); + * + * beginShape(); + * vertex(20, 20); + * vertex(45, 20); + * vertex(45, 80); + * endShape(CLOSE); + * + * beginShape(); + * vertex(50, 20); + * vertex(75, 20); + * vertex(75, 80); + * endShape(); + * </code> + * </div> + * + * @alt + * Triangle line shape with smallest interior angle on bottom and upside-down L. + * + */ +p5.prototype.endShape = function(mode) { + if(this._renderer.isP3D){ + this._renderer.endShape(mode, isCurve, isBezier, + isQuadratic, isContour, shapeKind); + }else{ + if (vertices.length === 0) { return this; } + if (!this._renderer._doStroke && !this._renderer._doFill) { return this; } + + var closeShape = mode === constants.CLOSE; + + // if the shape is closed, the first element is also the last element + if (closeShape && !isContour) { + vertices.push(vertices[0]); + } + + this._renderer.endShape(mode, vertices, isCurve, isBezier, + isQuadratic, isContour, shapeKind); + + // Reset some settings + isCurve = false; + isBezier = false; + isQuadratic = false; + isContour = false; + isFirstContour = true; + + // If the shape is closed, the first element was added as last element. + // We must remove it again to prevent the list of vertices from growing + // over successive calls to endShape(CLOSE) + if (closeShape) { + vertices.pop(); + } + } + return this; +}; + +/** + * Specifies vertex coordinates for quadratic Bezier curves. Each call to + * quadraticVertex() defines the position of one control points and one + * anchor point of a Bezier curve, adding a new segment to a line or shape. + * The first time quadraticVertex() is used within a beginShape() call, it + * must be prefaced with a call to vertex() to set the first anchor point. + * This function must be used between beginShape() and endShape() and only + * when there is no MODE parameter specified to beginShape(). + * + * @method quadraticVertex + * @param {Number} cx x-coordinate for the control point + * @param {Number} cy y-coordinate for the control point + * @param {Number} x3 x-coordinate for the anchor point + * @param {Number} y3 y-coordinate for the anchor point + * @return {Object} the p5 object + * @example + * <div> + * <code> + * noFill(); + * strokeWeight(4); + * beginShape(); + * vertex(20, 20); + * quadraticVertex(80, 20, 50, 50); + * endShape(); + * </code> + * </div> + * + * <div> + * <code> + * noFill(); + * strokeWeight(4); + * beginShape(); + * vertex(20, 20); + * quadraticVertex(80, 20, 50, 50); + * quadraticVertex(20, 80, 80, 80); + * vertex(80, 60); + * endShape(); + * </code> + * </div> + * + * @alt + * arched-shaped black line with 4 pixel thick stroke weight. + * backwards s-shaped black line with 4 pixel thick stroke weight. + * + */ +p5.prototype.quadraticVertex = function(cx, cy, x3, y3) { + //if we're drawing a contour, put the points into an + // array for inside drawing + if(this._contourInited) { + var pt = {}; + pt.x = cx; + pt.y = cy; + pt.x3 = x3; + pt.y3 = y3; + pt.type = constants.QUADRATIC; + this._contourVertices.push(pt); + + return this; + } + if (vertices.length > 0) { + isQuadratic = true; + var vert = []; + for (var i = 0; i < arguments.length; i++) { + vert[i] = arguments[i]; + } + vert.isVert = false; + if (isContour) { + contourVertices.push(vert); + } else { + vertices.push(vert); + } + } else { + throw 'vertex() must be used once before calling quadraticVertex()'; + } + return this; +}; + +/** + * All shapes are constructed by connecting a series of vertices. vertex() + * is used to specify the vertex coordinates for points, lines, triangles, + * quads, and polygons. It is used exclusively within the beginShape() and + * endShape() functions. + * + * @method vertex + * @param {Number} x x-coordinate of the vertex + * @param {Number} y y-coordinate of the vertex + * @return {Object} the p5 object + * @example + * <div> + * <code> + * beginShape(POINTS); + * vertex(30, 20); + * vertex(85, 20); + * vertex(85, 75); + * vertex(30, 75); + * endShape(); + * </code> + * </div> + * + * @alt + * 4 black points in a square shape in middle-right of canvas. + * + */ +p5.prototype.vertex = function(x, y, moveTo) { + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + if(this._renderer.isP3D){ + this._validateParameters( + 'vertex', + args, + [ + ['Number', 'Number', 'Number'] + ] + ); + this._renderer.vertex + (arguments[0], arguments[1], arguments[2]); + }else{ + this._validateParameters( + 'vertex', + args, + [ + ['Number', 'Number'], + ['Number', 'Number', 'Number'] + ] + ); + var vert = []; + vert.isVert = true; + vert[0] = x; + vert[1] = y; + vert[2] = 0; + vert[3] = 0; + vert[4] = 0; + vert[5] = this._renderer._getFill(); + vert[6] = this._renderer._getStroke(); + + if (moveTo) { + vert.moveTo = moveTo; + } + if (isContour) { + if (contourVertices.length === 0) { + vert.moveTo = true; + } + contourVertices.push(vert); + } else { + vertices.push(vert); + } + } + return this; +}; + +module.exports = p5; +},{"./constants":36,"./core":37}],50:[function(_dereq_,module,exports){ +/** + * @module Events + * @submodule Acceleration + * @for p5 + * @requires core + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); + +/** + * The system variable deviceOrientation always contains the orientation of + * the device. The value of this variable will either be set 'landscape' + * or 'portrait'. If no data is available it will be set to 'undefined'. + * + * @property deviceOrientation + */ +p5.prototype.deviceOrientation = undefined; + +/** + * The system variable accelerationX always contains the acceleration of the + * device along the x axis. Value is represented as meters per second squared. + * + * @property accelerationX + */ +p5.prototype.accelerationX = 0; + +/** + * The system variable accelerationY always contains the acceleration of the + * device along the y axis. Value is represented as meters per second squared. + * + * @property accelerationY + */ +p5.prototype.accelerationY = 0; + +/** + * The system variable accelerationZ always contains the acceleration of the + * device along the z axis. Value is represented as meters per second squared. + * + * @property accelerationZ + */ +p5.prototype.accelerationZ = 0; + +/** + * The system variable pAccelerationX always contains the acceleration of the + * device along the x axis in the frame previous to the current frame. Value + * is represented as meters per second squared. + * + * @property pAccelerationX + */ +p5.prototype.pAccelerationX = 0; + +/** + * The system variable pAccelerationY always contains the acceleration of the + * device along the y axis in the frame previous to the current frame. Value + * is represented as meters per second squared. + * + * @property pAccelerationY + */ +p5.prototype.pAccelerationY = 0; + +/** + * The system variable pAccelerationZ always contains the acceleration of the + * device along the z axis in the frame previous to the current frame. Value + * is represented as meters per second squared. + * + * @property pAccelerationZ + */ +p5.prototype.pAccelerationZ = 0; + +/** + * _updatePAccelerations updates the pAcceleration values + * + * @private + */ +p5.prototype._updatePAccelerations = function(){ + this._setProperty('pAccelerationX', this.accelerationX); + this._setProperty('pAccelerationY', this.accelerationY); + this._setProperty('pAccelerationZ', this.accelerationZ); +}; + +/** + * The system variable rotationX always contains the rotation of the + * device along the x axis. Value is represented as 0 to +/-180 degrees. + * <br><br> + * Note: The order the rotations are called is important, ie. if used + * together, it must be called in the order Z-X-Y or there might be + * unexpected behaviour. + * + * @example + * <div> + * <code> + * function setup(){ + * createCanvas(100, 100, WEBGL); + * } + * + * function draw(){ + * background(200); + * //rotateZ(radians(rotationZ)); + * rotateX(radians(rotationX)); + * //rotateY(radians(rotationY)); + * box(200, 200, 200); + * } + * </code> + * </div> + * + * @property rotationX + * + * @alt + * red horizontal line right, green vertical line bottom. black background. + * + */ +p5.prototype.rotationX = 0; + +/** + * The system variable rotationY always contains the rotation of the + * device along the y axis. Value is represented as 0 to +/-90 degrees. + * <br><br> + * Note: The order the rotations are called is important, ie. if used + * together, it must be called in the order Z-X-Y or there might be + * unexpected behaviour. + * + * @example + * <div> + * <code> + * function setup(){ + * createCanvas(100, 100, WEBGL); + * } + * + * function draw(){ + * background(200); + * //rotateZ(radians(rotationZ)); + * //rotateX(radians(rotationX)); + * rotateY(radians(rotationY)); + * box(200, 200, 200); + * } + * </code> + * </div> + * + * @property rotationY + * + * @alt + * red horizontal line right, green vertical line bottom. black background. + */ +p5.prototype.rotationY = 0; + +/** + * The system variable rotationZ always contains the rotation of the + * device along the z axis. Value is represented as 0 to 359 degrees. + * <br><br> + * Unlike rotationX and rotationY, this variable is available for devices + * with a built-in compass only. + * <br><br> + * Note: The order the rotations are called is important, ie. if used + * together, it must be called in the order Z-X-Y or there might be + * unexpected behaviour. + * + * @example + * <div> + * <code> + * function setup(){ + * createCanvas(100, 100, WEBGL); + * } + * + * function draw(){ + * background(200); + * rotateZ(radians(rotationZ)); + * //rotateX(radians(rotationX)); + * //rotateY(radians(rotationY)); + * box(200, 200, 200); + * } + * </code> + * </div> + * + * @property rotationZ + * + * @alt + * red horizontal line right, green vertical line bottom. black background. + */ +p5.prototype.rotationZ = 0; + +/** + * The system variable pRotationX always contains the rotation of the + * device along the x axis in the frame previous to the current frame. Value + * is represented as 0 to +/-180 degrees. + * <br><br> + * pRotationX can also be used with rotationX to determine the rotate + * direction of the device along the X-axis. + * @example + * <div class='norender'> + * <code> + * // A simple if statement looking at whether + * // rotationX - pRotationX < 0 is true or not will be + * // sufficient for determining the rotate direction + * // in most cases. + * + * // Some extra logic is needed to account for cases where + * // the angles wrap around. + * var rotateDirection = 'clockwise'; + * + * // Simple range conversion to make things simpler. + * // This is not absolutely neccessary but the logic + * // will be different in that case. + * + * var rX = rotationX + 180; + * var pRX = pRotationX + 180; + * + * if ((rX - pRX > 0 && rX - pRX < 270)|| rX - pRX < -270){ + * rotateDirection = 'clockwise'; + * } else if (rX - pRX < 0 || rX - pRX > 270){ + * rotateDirection = 'counter-clockwise'; + * } + * </code> + * </div> + * + * @alt + * no image to display. + * + * + * @property pRotationX + */ +p5.prototype.pRotationX = 0; + +/** + * The system variable pRotationY always contains the rotation of the + * device along the y axis in the frame previous to the current frame. Value + * is represented as 0 to +/-90 degrees. + * <br><br> + * pRotationY can also be used with rotationY to determine the rotate + * direction of the device along the Y-axis. + * @example + * <div class='norender'> + * <code> + * // A simple if statement looking at whether + * // rotationY - pRotationY < 0 is true or not will be + * // sufficient for determining the rotate direction + * // in most cases. + * + * // Some extra logic is needed to account for cases where + * // the angles wrap around. + * var rotateDirection = 'clockwise'; + * + * // Simple range conversion to make things simpler. + * // This is not absolutely neccessary but the logic + * // will be different in that case. + * + * var rY = rotationY + 180; + * var pRY = pRotationY + 180; + * + * if ((rY - pRY > 0 && rY - pRY < 270)|| rY - pRY < -270){ + * rotateDirection = 'clockwise'; + * } else if (rY - pRY < 0 || rY - pRY > 270){ + * rotateDirection = 'counter-clockwise'; + * } + * </code> + * </div> + * + * @alt + * no image to display. + * + * + * @property pRotationY + */ +p5.prototype.pRotationY = 0; + +/** + * The system variable pRotationZ always contains the rotation of the + * device along the z axis in the frame previous to the current frame. Value + * is represented as 0 to 359 degrees. + * <br><br> + * pRotationZ can also be used with rotationZ to determine the rotate + * direction of the device along the Z-axis. + * @example + * <div class='norender'> + * <code> + * // A simple if statement looking at whether + * // rotationZ - pRotationZ < 0 is true or not will be + * // sufficient for determining the rotate direction + * // in most cases. + * + * // Some extra logic is needed to account for cases where + * // the angles wrap around. + * var rotateDirection = 'clockwise'; + * + * if ((rotationZ - pRotationZ > 0 && + * rotationZ - pRotationZ < 270)|| + * rotationZ - pRotationZ < -270){ + * + * rotateDirection = 'clockwise'; + * + * } else if (rotationZ - pRotationZ < 0 || + * rotationZ - pRotationZ > 270){ + * + * rotateDirection = 'counter-clockwise'; + * + * } + * </code> + * </div> + * + * @alt + * no image to display. + * + * + * @property pRotationZ + */ +p5.prototype.pRotationZ = 0; + +var startAngleX = 0; +var startAngleY = 0; +var startAngleZ = 0; + +var rotateDirectionX = 'clockwise'; +var rotateDirectionY = 'clockwise'; +var rotateDirectionZ = 'clockwise'; + +var pRotateDirectionX; +var pRotateDirectionY; +var pRotateDirectionZ; + +p5.prototype._updatePRotations = function(){ + this._setProperty('pRotationX', this.rotationX); + this._setProperty('pRotationY', this.rotationY); + this._setProperty('pRotationZ', this.rotationZ); +}; + +p5.prototype.turnAxis = undefined; + +var move_threshold = 0.5; +var shake_threshold = 30; + +/** + * The setMoveThreshold() function is used to set the movement threshold for + * the deviceMoved() function. The default threshold is set to 0.5. + * + * @method setMoveThreshold + * @param {number} value The threshold value + */ +p5.prototype.setMoveThreshold = function(val){ + if(typeof val === 'number'){ + move_threshold = val; + } +}; + +/** + * The setShakeThreshold() function is used to set the movement threshold for + * the deviceShaken() function. The default threshold is set to 30. + * + * @method setShakeThreshold + * @param {number} value The threshold value + */ +p5.prototype.setShakeThreshold = function(val){ + if(typeof val === 'number'){ + shake_threshold = val; + } +}; + +/** + * The deviceMoved() function is called when the device is moved by more than + * the threshold value along X, Y or Z axis. The default threshold is set to + * 0.5. + * @method deviceMoved + * @example + * <div> + * <code> + * // Run this example on a mobile device + * // Move the device around + * // to change the value. + * + * var value = 0; + * function draw() { + * fill(value); + * rect(25, 25, 50, 50); + * } + * function deviceMoved() { + * value = value + 5; + * if (value > 255) { + * value = 0; + * } + * } + * </code> + * </div> + * + * @alt + * 50x50 black rect in center of canvas. turns white on mobile when device moves + * + */ + +/** + * The deviceTurned() function is called when the device rotates by + * more than 90 degrees continuously. + * <br><br> + * The axis that triggers the deviceTurned() method is stored in the turnAxis + * variable. The deviceTurned() method can be locked to trigger on any axis: + * X, Y or Z by comparing the turnAxis variable to 'X', 'Y' or 'Z'. + * + * @method deviceTurned + * @example + * <div> + * <code> + * // Run this example on a mobile device + * // Rotate the device by 90 degrees + * // to change the value. + * + * var value = 0; + * function draw() { + * fill(value); + * rect(25, 25, 50, 50); + * } + * function deviceTurned() { + * if (value == 0){ + * value = 255 + * } else if (value == 255) { + * value = 0; + * } + * } + * </code> + * </div> + * <div> + * <code> + * // Run this example on a mobile device + * // Rotate the device by 90 degrees in the + * // X-axis to change the value. + * + * var value = 0; + * function draw() { + * fill(value); + * rect(25, 25, 50, 50); + * } + * function deviceTurned() { + * if (turnAxis == 'X'){ + * if (value == 0){ + * value = 255 + * } else if (value == 255) { + * value = 0; + * } + * } + * } + * </code> + * </div> + * + * @alt + * 50x50 black rect in center of canvas. turns white on mobile when device turns + * 50x50 black rect in center of canvas. turns white on mobile when x-axis turns + * + */ + +/** + * The deviceShaken() function is called when the device total acceleration + * changes of accelerationX and accelerationY values is more than + * the threshold value. The default threshold is set to 30. + * @method deviceShaken + * @example + * <div> + * <code> + * // Run this example on a mobile device + * // Shake the device to change the value. + * + * var value = 0; + * function draw() { + * fill(value); + * rect(25, 25, 50, 50); + * } + * function deviceShaken() { + * value = value + 5; + * if (value > 255) { + * value = 0; + * } + * } + * </code> + * </div> + * + * @alt + * 50x50 black rect in center of canvas. turns white on mobile when device shakes + * + */ + +p5.prototype._ondeviceorientation = function (e) { + this._updatePRotations(); + this._setProperty('rotationX', e.beta); + this._setProperty('rotationY', e.gamma); + this._setProperty('rotationZ', e.alpha); + this._handleMotion(); +}; +p5.prototype._ondevicemotion = function (e) { + this._updatePAccelerations(); + this._setProperty('accelerationX', e.acceleration.x * 2); + this._setProperty('accelerationY', e.acceleration.y * 2); + this._setProperty('accelerationZ', e.acceleration.z * 2); + this._handleMotion(); +}; +p5.prototype._handleMotion = function() { + if (window.orientation === 90 || window.orientation === -90) { + this._setProperty('deviceOrientation', 'landscape'); + } else if (window.orientation === 0) { + this._setProperty('deviceOrientation', 'portrait'); + } else if (window.orientation === undefined) { + this._setProperty('deviceOrientation', 'undefined'); + } + var deviceMoved = this.deviceMoved || window.deviceMoved; + if (typeof deviceMoved === 'function') { + if (Math.abs(this.accelerationX - this.pAccelerationX) > move_threshold || + Math.abs(this.accelerationY - this.pAccelerationY) > move_threshold || + Math.abs(this.accelerationZ - this.pAccelerationZ) > move_threshold) { + deviceMoved(); + } + } + var deviceTurned = this.deviceTurned || window.deviceTurned; + if (typeof deviceTurned === 'function') { + // The angles given by rotationX etc is from range -180 to 180. + // The following will convert them to 0 to 360 for ease of calculation + // of cases when the angles wrapped around. + // _startAngleX will be converted back at the end and updated. + var wRX = this.rotationX + 180; + var wPRX = this.pRotationX + 180; + var wSAX = startAngleX + 180; + if ((wRX - wPRX > 0 && wRX - wPRX < 270)|| wRX - wPRX < -270){ + rotateDirectionX = 'clockwise'; + } else if (wRX - wPRX < 0 || wRX - wPRX > 270){ + rotateDirectionX = 'counter-clockwise'; + } + if (rotateDirectionX !== pRotateDirectionX){ + wSAX = wRX; + } + if (Math.abs(wRX - wSAX) > 90 && Math.abs(wRX - wSAX) < 270){ + wSAX = wRX; + this._setProperty('turnAxis', 'X'); + deviceTurned(); + } + pRotateDirectionX = rotateDirectionX; + startAngleX = wSAX - 180; + + // Y-axis is identical to X-axis except for changing some names. + var wRY = this.rotationY + 180; + var wPRY = this.pRotationY + 180; + var wSAY = startAngleY + 180; + if ((wRY - wPRY > 0 && wRY - wPRY < 270)|| wRY - wPRY < -270){ + rotateDirectionY = 'clockwise'; + } else if (wRY - wPRY < 0 || wRY - this.pRotationY > 270){ + rotateDirectionY = 'counter-clockwise'; + } + if (rotateDirectionY !== pRotateDirectionY){ + wSAY = wRY; + } + if (Math.abs(wRY - wSAY) > 90 && Math.abs(wRY - wSAY) < 270){ + wSAY = wRY; + this._setProperty('turnAxis', 'Y'); + deviceTurned(); + } + pRotateDirectionY = rotateDirectionY; + startAngleY = wSAY - 180; + + // Z-axis is already in the range 0 to 360 + // so no conversion is needed. + if ((this.rotationZ - this.pRotationZ > 0 && + this.rotationZ - this.pRotationZ < 270)|| + this.rotationZ - this.pRotationZ < -270){ + rotateDirectionZ = 'clockwise'; + } else if (this.rotationZ - this.pRotationZ < 0 || + this.rotationZ - this.pRotationZ > 270){ + rotateDirectionZ = 'counter-clockwise'; + } + if (rotateDirectionZ !== pRotateDirectionZ){ + startAngleZ = this.rotationZ; + } + if (Math.abs(this.rotationZ - startAngleZ) > 90 && + Math.abs(this.rotationZ - startAngleZ) < 270){ + startAngleZ = this.rotationZ; + this._setProperty('turnAxis', 'Z'); + deviceTurned(); + } + pRotateDirectionZ = rotateDirectionZ; + this._setProperty('turnAxis', undefined); + } + var deviceShaken = this.deviceShaken || window.deviceShaken; + if (typeof deviceShaken === 'function') { + var accelerationChangeX; + var accelerationChangeY; + // Add accelerationChangeZ if acceleration change on Z is needed + if (this.pAccelerationX !== null) { + accelerationChangeX = Math.abs(this.accelerationX - this.pAccelerationX); + accelerationChangeY = Math.abs(this.accelerationY - this.pAccelerationY); + } + if (accelerationChangeX + accelerationChangeY > shake_threshold) { + deviceShaken(); + } + } +}; + + +module.exports = p5; + +},{"../core/core":37}],51:[function(_dereq_,module,exports){ +/** + * @module Events + * @submodule Keyboard + * @for p5 + * @requires core + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); + +/** + * Holds the key codes of currently pressed keys. + * @private + */ +var downKeys = {}; + +/** + * The boolean system variable keyIsPressed is true if any key is pressed + * and false if no keys are pressed. + * + * @property keyIsPressed + * @example + * <div> + * <code> + * var value = 0; + * function draw() { + * if (keyIsPressed === true) { + * fill(0); + * } else { + * fill(255); + * } + * rect(25, 25, 50, 50); + * } + * </code> + * </div> + * + * @alt + * 50x50 white rect that turns black on keypress. + * + */ +p5.prototype.isKeyPressed = false; +p5.prototype.keyIsPressed = false; // khan + +/** + * The system variable key always contains the value of the most recent + * key on the keyboard that was typed. To get the proper capitalization, it + * is best to use it within keyTyped(). For non-ASCII keys, use the keyCode + * variable. + * + * @property key + * @example + * <div><code> + * // Click any key to display it! + * // (Not Guaranteed to be Case Sensitive) + * function setup() { + * fill(245, 123, 158); + * textSize(50); + * } + * + * function draw() { + * background(200); + * text(key, 33,65); // Display last key pressed. + * } + * </div></code> + * + * @alt + * canvas displays any key value that is pressed in pink font. + * + */ +p5.prototype.key = ''; + +/** + * The variable keyCode is used to detect special keys such as BACKSPACE, + * DELETE, ENTER, RETURN, TAB, ESCAPE, SHIFT, CONTROL, OPTION, ALT, UP_ARROW, + * DOWN_ARROW, LEFT_ARROW, RIGHT_ARROW. + * + * @property keyCode + * @example + * <div><code> + * var fillVal = 126; + * function draw() { + * fill(fillVal); + * rect(25, 25, 50, 50); + * } + * + * function keyPressed() { + * if (keyCode == UP_ARROW) { + * fillVal = 255; + * } else if (keyCode == DOWN_ARROW) { + * fillVal = 0; + * } + * return false; // prevent default + * } + * </code></div> + * + * @alt + * Grey rect center. turns white when up arrow pressed and black when down + * + */ +p5.prototype.keyCode = 0; + +/** + * The keyPressed() function is called once every time a key is pressed. The + * keyCode for the key that was pressed is stored in the keyCode variable. + * <br><br> + * For non-ASCII keys, use the keyCode variable. You can check if the keyCode + * equals BACKSPACE, DELETE, ENTER, RETURN, TAB, ESCAPE, SHIFT, CONTROL, + * OPTION, ALT, UP_ARROW, DOWN_ARROW, LEFT_ARROW, RIGHT_ARROW. + * <br><br> + * For ASCII keys that was pressed is stored in the key variable. However, it + * does not distinguish between uppercase and lowercase. For this reason, it + * is recommended to use keyTyped() to read the key variable, in which the + * case of the variable will be distinguished. + * <br><br> + * Because of how operating systems handle key repeats, holding down a key + * may cause multiple calls to keyTyped() (and keyReleased() as well). The + * rate of repeat is set by the operating system and how each computer is + * configured.<br><br> + * Browsers may have different default + * behaviors attached to various key events. To prevent any default + * behavior for this event, add "return false" to the end of the method. + * + * @method keyPressed + * @example + * <div> + * <code> + * var value = 0; + * function draw() { + * fill(value); + * rect(25, 25, 50, 50); + * } + * function keyPressed() { + * if (value === 0) { + * value = 255; + * } else { + * value = 0; + * } + * } + * </code> + * </div> + * <div> + * <code> + * var value = 0; + * function draw() { + * fill(value); + * rect(25, 25, 50, 50); + * } + * function keyPressed() { + * if (keyCode === LEFT_ARROW) { + * value = 255; + * } else if (keyCode === RIGHT_ARROW) { + * value = 0; + * } + * } + * </code> + * </div> + * <div class="norender"> + * <code> + * function keyPressed(){ + * // Do something + * return false; // prevent any default behaviour + * } + * </code> + * + * @alt + * black rect center. turns white when key pressed and black when released + * black rect center. turns white when left arrow pressed and black when right. + * + * </div> + */ +p5.prototype._onkeydown = function (e) { + if (downKeys[e.which]) { // prevent multiple firings + return; + } + this._setProperty('isKeyPressed', true); + this._setProperty('keyIsPressed', true); + this._setProperty('keyCode', e.which); + downKeys[e.which] = true; + var key = String.fromCharCode(e.which); + if (!key) { + key = e.which; + } + this._setProperty('key', key); + var keyPressed = this.keyPressed || window.keyPressed; + if (typeof keyPressed === 'function' && !e.charCode) { + var executeDefault = keyPressed(e); + if(executeDefault === false) { + e.preventDefault(); + } + } +}; +/** + * The keyReleased() function is called once every time a key is released. + * See key and keyCode for more information.<br><br> + * Browsers may have different default + * behaviors attached to various key events. To prevent any default + * behavior for this event, add "return false" to the end of the method. + * + * @method keyReleased + * @example + * <div> + * <code> + * var value = 0; + * function draw() { + * fill(value); + * rect(25, 25, 50, 50); + * } + * function keyReleased() { + * if (value === 0) { + * value = 255; + * } else { + * value = 0; + * } + * return false; // prevent any default behavior + * } + * </code> + * </div> + * + * @alt + * black rect center. turns white when key pressed and black when pressed again + * + */ +p5.prototype._onkeyup = function (e) { + var keyReleased = this.keyReleased || window.keyReleased; + this._setProperty('isKeyPressed', false); + this._setProperty('keyIsPressed', false); + this._setProperty('_lastKeyCodeTyped', null); + downKeys[e.which] = false; + //delete this._downKeys[e.which]; + var key = String.fromCharCode(e.which); + if (!key) { + key = e.which; + } + this._setProperty('key', key); + this._setProperty('keyCode', e.which); + if (typeof keyReleased === 'function') { + var executeDefault = keyReleased(e); + if(executeDefault === false) { + e.preventDefault(); + } + } +}; + +/** + * The keyTyped() function is called once every time a key is pressed, but + * action keys such as Ctrl, Shift, and Alt are ignored. The most recent + * key pressed will be stored in the key variable. + * <br><br> + * Because of how operating systems handle key repeats, holding down a key + * will cause multiple calls to keyTyped() (and keyReleased() as well). The + * rate of repeat is set by the operating system and how each computer is + * configured.<br><br> + * Browsers may have different default behaviors attached to various key + * events. To prevent any default behavior for this event, add "return false" + * to the end of the method. + * + * @method keyTyped + * @example + * <div> + * <code> + * var value = 0; + * function draw() { + * fill(value); + * rect(25, 25, 50, 50); + * } + * function keyTyped() { + * if (key === 'a') { + * value = 255; + * } else if (key === 'b') { + * value = 0; + * } + * // uncomment to prevent any default behavior + * // return false; + * } + * </code> + * </div> + * + * @alt + * black rect center. turns white when 'a' key typed and black when 'b' pressed + * + */ +p5.prototype._onkeypress = function (e) { + if (e.which === this._lastKeyCodeTyped) { // prevent multiple firings + return; + } + this._setProperty('keyCode', e.which); + this._setProperty('_lastKeyCodeTyped', e.which); // track last keyCode + this._setProperty('key', String.fromCharCode(e.which)); + var keyTyped = this.keyTyped || window.keyTyped; + if (typeof keyTyped === 'function') { + var executeDefault = keyTyped(e); + if(executeDefault === false) { + e.preventDefault(); + } + } +}; +/** + * The onblur function is called when the user is no longer focused + * on the p5 element. Because the keyup events will not fire if the user is + * not focused on the element we must assume all keys currently down have + * been released. + */ +p5.prototype._onblur = function (e) { + downKeys = {}; +}; + +/** + * The keyIsDown() function checks if the key is currently down, i.e. pressed. + * It can be used if you have an object that moves, and you want several keys + * to be able to affect its behaviour simultaneously, such as moving a + * sprite diagonally. You can put in any number representing the keyCode of + * the key, or use any of the variable keyCode names listed + * <a href="http://p5js.org/reference/#p5/keyCode">here</a>. + * + * @method keyIsDown + * @param {Number} code The key to check for. + * @return {Boolean} whether key is down or not + * @example + * <div><code> + * var x = 100; + * var y = 100; + * + * function setup() { + * createCanvas(512, 512); + * } + * + * function draw() { + * if (keyIsDown(LEFT_ARROW)) + * x-=5; + * + * if (keyIsDown(RIGHT_ARROW)) + * x+=5; + * + * if (keyIsDown(UP_ARROW)) + * y-=5; + * + * if (keyIsDown(DOWN_ARROW)) + * y+=5; + * + * clear(); + * fill(255, 0, 0); + * ellipse(x, y, 50, 50); + * } + * </code></div> + * + * @alt + * 50x50 red ellipse moves left, right, up and down with arrow presses. + * + */ +p5.prototype.keyIsDown = function(code) { + return downKeys[code]; +}; + +module.exports = p5; + +},{"../core/core":37}],52:[function(_dereq_,module,exports){ +/** + * @module Events + * @submodule Mouse + * @for p5 + * @requires core + * @requires constants + */ + + +'use strict'; + +var p5 = _dereq_('../core/core'); +var constants = _dereq_('../core/constants'); + +/* + * This is a flag which is false until the first time + * we receive a mouse event. The pmouseX and pmouseY + * values will match the mouseX and mouseY values until + * this interaction takes place. + */ +p5.prototype._hasMouseInteracted = false; + +/** + * The system variable mouseX always contains the current horizontal + * position of the mouse, relative to (0, 0) of the canvas. + * + * @property mouseX + * + * @example + * <div> + * <code> + * // Move the mouse across the canvas + * function draw() { + * background(244, 248, 252); + * line(mouseX, 0, mouseX, 100); + * } + * </code> + * </div> + * + * @alt + * horizontal black line moves left and right with mouse x-position + * + */ +p5.prototype.mouseX = 0; + +/** + * The system variable mouseY always contains the current vertical position + * of the mouse, relative to (0, 0) of the canvas. + * + * @property mouseY + * + * @example + * <div> + * <code> + * // Move the mouse across the canvas + * function draw() { + * background(244, 248, 252); + * line(0, mouseY, 100, mouseY); + *} + * </code> + * </div> + * + * @alt + * vertical black line moves up and down with mouse y-position + * + */ +p5.prototype.mouseY = 0; + +/** + * The system variable pmouseX always contains the horizontal position of + * the mouse in the frame previous to the current frame, relative to (0, 0) + * of the canvas. + * + * @property pmouseX + * + * @example + * <div> + * <code> + * // Move the mouse across the canvas to leave a trail + * function setup() { + * //slow down the frameRate to make it more visible + * frameRate(10); + * } + * + * function draw() { + * background(244, 248, 252); + * line(mouseX, mouseY, pmouseX, pmouseY); + * print(pmouseX + " -> " + mouseX); + * } + * + * </code> + * </div> + * + * @alt + * line trail is created from cursor movements. faster movement make longer line. + * + */ +p5.prototype.pmouseX = 0; + +/** + * The system variable pmouseY always contains the vertical position of the + * mouse in the frame previous to the current frame, relative to (0, 0) of + * the canvas. + * + * @property pmouseY + * + * @example + * <div> + * <code> + * function draw() { + * background(237, 34, 93); + * fill(0); + * //draw a square only if the mouse is not moving + * if(mouseY == pmouseY && mouseX == pmouseX) + * rect(20,20,60,60); + * + * print(pmouseY + " -> " + mouseY); + * } + * + * </code> + * </div> + * + * @alt + * 60x60 black rect center, fuschia background. rect flickers on mouse movement + * + */ +p5.prototype.pmouseY = 0; + +/** + * The system variable winMouseX always contains the current horizontal + * position of the mouse, relative to (0, 0) of the window. + * + * @property winMouseX + * + * @example + * <div> + * <code> + * var myCanvas; + * + * function setup() { + * //use a variable to store a pointer to the canvas + * myCanvas = createCanvas(100, 100); + * } + * + * function draw() { + * background(237, 34, 93); + * fill(0); + * + * //move the canvas to the horizontal mouse position + * //relative to the window + * myCanvas.position(winMouseX+1, windowHeight/2); + * + * //the y of the square is relative to the canvas + * rect(20,mouseY,60,60); + * } + * + * </code> + * </div> + * + * @alt + * 60x60 black rect y moves with mouse y and fuschia canvas moves with mouse x + * + */ +p5.prototype.winMouseX = 0; + +/** + * The system variable winMouseY always contains the current vertical + * position of the mouse, relative to (0, 0) of the window. + * + * @property winMouseY + * + * @example + * <div> + * <code> + *var myCanvas; + * + * function setup() { + * //use a variable to store a pointer to the canvas + * myCanvas = createCanvas(100, 100); + * } + * + * function draw() { + * background(237, 34, 93); + * fill(0); + * + * //move the canvas to the vertical mouse position + * //relative to the window + * myCanvas.position(windowWidth/2, winMouseY+1); + * + * //the x of the square is relative to the canvas + * rect(mouseX,20,60,60); + * } + * + * </code> + * </div> + * + * @alt + * 60x60 black rect x moves with mouse x and fuschia canvas y moves with mouse y + * + */ +p5.prototype.winMouseY = 0; + +/** + * The system variable pwinMouseX always contains the horizontal position + * of the mouse in the frame previous to the current frame, relative to + * (0, 0) of the window. + * + * @property pwinMouseX + * + * @example + * <div> + * <code> + * + * var myCanvas; + * + * function setup() { + * //use a variable to store a pointer to the canvas + * myCanvas = createCanvas(100, 100); + * noStroke(); + * fill(237, 34, 93); + * } + * + * function draw() { + * clear(); + * //the difference between previous and + * //current x position is the horizontal mouse speed + * var speed = abs(winMouseX-pwinMouseX); + * //change the size of the circle + * //according to the horizontal speed + * ellipse(50, 50, 10+speed*5, 10+speed*5); + * //move the canvas to the mouse position + * myCanvas.position( winMouseX+1, winMouseY+1); + * } + * + * </code> + * </div> + * + * @alt + * fuschia ellipse moves with mouse x and y. Grows and shrinks with mouse speed + * + */ +p5.prototype.pwinMouseX = 0; + +/** + * The system variable pwinMouseY always contains the vertical position of + * the mouse in the frame previous to the current frame, relative to (0, 0) + * of the window. + * + * @property pwinMouseY + * + * + * @example + * <div> + * <code> + * + * var myCanvas; + * + * function setup() { + * //use a variable to store a pointer to the canvas + * myCanvas = createCanvas(100, 100); + * noStroke(); + * fill(237, 34, 93); + * } + * + * function draw() { + * clear(); + * //the difference between previous and + * //current y position is the vertical mouse speed + * var speed = abs(winMouseY-pwinMouseY); + * //change the size of the circle + * //according to the vertical speed + * ellipse(50, 50, 10+speed*5, 10+speed*5); + * //move the canvas to the mouse position + * myCanvas.position( winMouseX+1, winMouseY+1); + * } + * + * </code> + * </div> + * + * @alt + * fuschia ellipse moves with mouse x and y. Grows and shrinks with mouse speed + * + */ +p5.prototype.pwinMouseY = 0; + +/** + * Processing automatically tracks if the mouse button is pressed and which + * button is pressed. The value of the system variable mouseButton is either + * LEFT, RIGHT, or CENTER depending on which button was pressed last. + * Warning: different browsers may track mouseButton differently. + * + * @property mouseButton + * + * @example + * <div> + * <code> + * function draw() { + * background(237, 34, 93); + * fill(0); + * + * if (mouseIsPressed) { + * if (mouseButton == LEFT) + * ellipse(50, 50, 50, 50); + * if (mouseButton == RIGHT) + * rect(25, 25, 50, 50); + * if (mouseButton == CENTER) + * triangle(23, 75, 50, 20, 78, 75); + * } + * + * print(mouseButton); + * } + * </code> + * </div> + * + * @alt + * 50x50 black ellipse appears on center of fuschia canvas on mouse click/press. + * + */ +p5.prototype.mouseButton = 0; + +/** + * The boolean system variable mouseIsPressed is true if the mouse is pressed + * and false if not. + * + * @property mouseIsPressed + * + * @example + * <div> + * <code> + * function draw() { + * background(237, 34, 93); + * fill(0); + * + * if (mouseIsPressed) + * ellipse(50, 50, 50, 50); + * else + * rect(25, 25, 50, 50); + * + * print(mouseIsPressed); + * } + * </code> + * </div> + * + * @alt + * black 50x50 rect becomes ellipse with mouse click/press. fuschia background. + * + */ +p5.prototype.mouseIsPressed = false; +p5.prototype.isMousePressed = false; // both are supported + +p5.prototype._updateNextMouseCoords = function(e) { + var x = this.mouseX; + var y = this.mouseY; + var winX = this.winMouseX; + var winY = this.winMouseY; + if(e.type === 'touchstart' || + e.type === 'touchmove' || + e.type === 'touchend' || e.touches) { + x = this.touchX; + y = this.touchY; + winX = this.winTouchX; + winY = this.winTouchY; + } else if(this._curElement !== null) { + var mousePos = getMousePos(this._curElement.elt, e); + x = mousePos.x; + y = mousePos.y; + winX = mousePos.winX; + winY = mousePos.winY; + } + this._setProperty('mouseX', x); + this._setProperty('mouseY', y); + this._setProperty('winMouseX', winX); + this._setProperty('winMouseY', winY); + if (!this._hasMouseInteracted) { + // For first draw, make previous and next equal + this._updateMouseCoords(); + this._setProperty('_hasMouseInteracted', true); + } +}; + +p5.prototype._updateMouseCoords = function() { + this._setProperty('pmouseX', this.mouseX); + this._setProperty('pmouseY', this.mouseY); + this._setProperty('pwinMouseX', this.winMouseX); + this._setProperty('pwinMouseY', this.winMouseY); +}; + +function getMousePos(canvas, evt) { + var rect = canvas.getBoundingClientRect(); + return { + x: evt.clientX - rect.left, + y: evt.clientY - rect.top, + winX: evt.clientX, + winY: evt.clientY + }; +} + +p5.prototype._setMouseButton = function(e) { + if (e.button === 1) { + this._setProperty('mouseButton', constants.CENTER); + } else if (e.button === 2) { + this._setProperty('mouseButton', constants.RIGHT); + } else { + this._setProperty('mouseButton', constants.LEFT); + } +}; + +/** + * The mouseMoved() function is called every time the mouse moves and a mouse + * button is not pressed.<br><br> + * Browsers may have different default + * behaviors attached to various mouse events. To prevent any default + * behavior for this event, add "return false" to the end of the method. + * + * @method mouseMoved + * @example + * <div> + * <code> + * // Move the mouse across the page + * // to change its value + * + * var value = 0; + * function draw() { + * fill(value); + * rect(25, 25, 50, 50); + * } + * function mouseMoved() { + * value = value + 5; + * if (value > 255) { + * value = 0; + * } + * } + * </code> + * </div> + * + * <div class="norender"> + * <code> + * function mouseMoved() { + * ellipse(mouseX, mouseY, 5, 5); + * // prevent default + * return false; + * } + * </code> + * </div> + * + * @alt + * black 50x50 rect becomes lighter with mouse movements until white then resets + * no image displayed + * + */ + +/** + * The mouseDragged() function is called once every time the mouse moves and + * a mouse button is pressed. If no mouseDragged() function is defined, the + * touchMoved() function will be called instead if it is defined.<br><br> + * Browsers may have different default + * behaviors attached to various mouse events. To prevent any default + * behavior for this event, add "return false" to the end of the method. + * + * @method mouseDragged + * @example + * <div> + * <code> + * // Drag the mouse across the page + * // to change its value + * + * var value = 0; + * function draw() { + * fill(value); + * rect(25, 25, 50, 50); + * } + * function mouseDragged() { + * value = value + 5; + * if (value > 255) { + * value = 0; + * } + * } + * </code> + * </div> + * + * <div class="norender"> + * <code> + * function mouseDragged() { + * ellipse(mouseX, mouseY, 5, 5); + * // prevent default + * return false; + * } + * </code> + * </div> + * + * @alt + * black 50x50 rect turns lighter with mouse click and drag until white, resets + * no image displayed + * + */ +p5.prototype._onmousemove = function(e){ + var context = this._isGlobal ? window : this; + var executeDefault; + this._updateNextMouseCoords(e); + this._updateNextTouchCoords(e); + if (!this.isMousePressed) { + if (typeof context.mouseMoved === 'function') { + executeDefault = context.mouseMoved(e); + if(executeDefault === false) { + e.preventDefault(); + } + } + } + else { + if (typeof context.mouseDragged === 'function') { + executeDefault = context.mouseDragged(e); + if(executeDefault === false) { + e.preventDefault(); + } + } else if (typeof context.touchMoved === 'function') { + executeDefault = context.touchMoved(e); + if(executeDefault === false) { + e.preventDefault(); + } + } + } +}; + +/** + * The mousePressed() function is called once after every time a mouse button + * is pressed. The mouseButton variable (see the related reference entry) + * can be used to determine which button has been pressed. If no + * mousePressed() function is defined, the touchStarted() function will be + * called instead if it is defined.<br><br> + * Browsers may have different default + * behaviors attached to various mouse events. To prevent any default + * behavior for this event, add "return false" to the end of the method. + * + * @method mousePressed + * @example + * <div> + * <code> + * // Click within the image to change + * // the value of the rectangle + * + * var value = 0; + * function draw() { + * fill(value); + * rect(25, 25, 50, 50); + * } + * function mousePressed() { + * if (value == 0) { + * value = 255; + * } else { + * value = 0; + * } + * } + * </code> + * </div> + * + * <div class="norender"> + * <code> + * function mousePressed() { + * ellipse(mouseX, mouseY, 5, 5); + * // prevent default + * return false; + * } + * </code> + * </div> + * + * @alt + * black 50x50 rect turns white with mouse click/press. + * no image displayed + * + */ +p5.prototype._onmousedown = function(e) { + var context = this._isGlobal ? window : this; + var executeDefault; + this._setProperty('isMousePressed', true); + this._setProperty('mouseIsPressed', true); + this._setMouseButton(e); + this._updateNextMouseCoords(e); + this._updateNextTouchCoords(e); + if (typeof context.mousePressed === 'function') { + executeDefault = context.mousePressed(e); + if(executeDefault === false) { + e.preventDefault(); + } + } else if (typeof context.touchStarted === 'function') { + executeDefault = context.touchStarted(e); + if(executeDefault === false) { + e.preventDefault(); + } + } +}; + +/** + * The mouseReleased() function is called every time a mouse button is + * released. If no mouseReleased() function is defined, the touchEnded() + * function will be called instead if it is defined.<br><br> + * Browsers may have different default + * behaviors attached to various mouse events. To prevent any default + * behavior for this event, add "return false" to the end of the method. + * + * + * @method mouseReleased + * @example + * <div> + * <code> + * // Click within the image to change + * // the value of the rectangle + * // after the mouse has been clicked + * + * var value = 0; + * function draw() { + * fill(value); + * rect(25, 25, 50, 50); + * } + * function mouseReleased() { + * if (value == 0) { + * value = 255; + * } else { + * value = 0; + * } + * } + * </code> + * </div> + * + * <div class="norender"> + * <code> + * function mouseReleased() { + * ellipse(mouseX, mouseY, 5, 5); + * // prevent default + * return false; + * } + * </code> + * </div> + * + * @alt + * black 50x50 rect turns white with mouse click/press. + * no image displayed + * + */ +p5.prototype._onmouseup = function(e) { + var context = this._isGlobal ? window : this; + var executeDefault; + this._setProperty('isMousePressed', false); + this._setProperty('mouseIsPressed', false); + if (typeof context.mouseReleased === 'function') { + executeDefault = context.mouseReleased(e); + if(executeDefault === false) { + e.preventDefault(); + } + } else if (typeof context.touchEnded === 'function') { + executeDefault = context.touchEnded(e); + if(executeDefault === false) { + e.preventDefault(); + } + } +}; + +p5.prototype._ondragend = p5.prototype._onmouseup; +p5.prototype._ondragover = p5.prototype._onmousemove; + +/** + * The mouseClicked() function is called once after a mouse button has been + * pressed and then released.<br><br> + * Browsers may have different default + * behaviors attached to various mouse events. To prevent any default + * behavior for this event, add "return false" to the end of the method. + * + * @method mouseClicked + * @example + * <div> + * <code> + * // Click within the image to change + * // the value of the rectangle + * // after the mouse has been clicked + * + * var value = 0; + * function draw() { + * fill(value); + * rect(25, 25, 50, 50); + * } + * function mouseClicked() { + * if (value == 0) { + * value = 255; + * } else { + * value = 0; + * } + * } + * </code> + * </div> + * + * <div class="norender"> + * <code> + * function mouseClicked() { + * ellipse(mouseX, mouseY, 5, 5); + * // prevent default + * return false; + * } + * </code> + * </div> + * + * @alt + * black 50x50 rect turns white with mouse click/press. + * no image displayed + * + */ +p5.prototype._onclick = function(e) { + var context = this._isGlobal ? window : this; + if (typeof context.mouseClicked === 'function') { + var executeDefault = context.mouseClicked(e); + if(executeDefault === false) { + e.preventDefault(); + } + } +}; + +/** + * The function mouseWheel() is executed every time a vertical mouse wheel + * event is detected either triggered by an actual mouse wheel or by a + * touchpad.<br><br> + * The event.delta property returns the amount the mouse wheel + * have scrolled. The values can be positive or negative depending on the + * scroll direction (on OS X with "natural" scrolling enabled, the signs + * are inverted).<br><br> + * Browsers may have different default behaviors attached to various + * mouse events. To prevent any default behavior for this event, add + * "return false" to the end of the method.<br><br> + * Due to the current support of the "wheel" event on Safari, the function + * may only work as expected if "return false" is included while using Safari. + * + * @method mouseWheel + * + * @example + * <div> + * <code> + * var pos = 25; + * + * function draw() { + * background(237, 34, 93); + * fill(0); + * rect(25, pos, 50, 50); + * } + * + * function mouseWheel(event) { + * print(event.delta); + * //move the square according to the vertical scroll amount + * pos += event.delta; + * //uncomment to block page scrolling + * //return false; + * } + * </code> + * </div> + * + * @alt + * black 50x50 rect moves up and down with vertical scroll. fuschia background + * + */ +p5.prototype._onwheel = function(e) { + var context = this._isGlobal ? window : this; + if (typeof context.mouseWheel === 'function') { + e.delta = e.deltaY; + var executeDefault = context.mouseWheel(e); + if(executeDefault === false) { + e.preventDefault(); + } + } +}; + +module.exports = p5; + +},{"../core/constants":36,"../core/core":37}],53:[function(_dereq_,module,exports){ +/** + * @module Events + * @submodule Touch + * @for p5 + * @requires core + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); + +/* + * This is a flag which is false until the first time + * we receive a touch event. The ptouchX and ptouchY + * values will match the touchX and touchY values until + * this interaction takes place. + */ +p5.prototype._hasTouchInteracted = false; + +/** + * The system variable touchX always contains the horizontal position of + * one finger, relative to (0, 0) of the canvas. This is best used for + * single touch interactions. For multi-touch interactions, use the + * touches[] array. + * + * @property touchX + * @method touchX + * @example + * <div> + * <code> + * // Touch and move the finger in horizontally across the canvas + * function setup() { + * createCanvas(100, 100); + * } + * + * function draw() { + * background(51); + * stroke(255, 204, 0); + * strokeWeight(4); + * rect(touchX, 50, 10, 10); + * } + * </code> + * </div> + * + * @alt + * 10x10 white rect with thick gold outline moves left and right with touch x. + * + */ +p5.prototype.touchX = 0; + +/** + * The system variable touchY always contains the vertical position of + * one finger, relative to (0, 0) of the canvas. This is best used for + * single touch interactions. For multi-touch interactions, use the + * touches[] array. + * + * @property touchY + * @method touchY + * @example + * <div> + * <code> + * // Touch and move the finger vertically across the canvas + * function setup() { + * createCanvas(100, 100); + * } + * + * function draw() { + * background(51); + * stroke(255, 204, 0); + * strokeWeight(4); + * rect(50, touchY, 10, 10); + * } + * </code> + * </div> + * + * @alt + * 10x10 white rect with thick gold outline moves up and down with touch y. + * + */ +p5.prototype.touchY = 0; + +/** + * The system variable ptouchX always contains the horizontal position of + * one finger, relative to (0, 0) of the canvas, in the frame previous to the + * current frame. + * + * @property ptouchX + */ +p5.prototype.ptouchX = 0; + +/** + * The system variable ptouchY always contains the vertical position of + * one finger, relative to (0, 0) of the canvas, in the frame previous to the + * current frame. + * + * @property ptouchY + */ +p5.prototype.ptouchY = 0; + +/** + * The system variable winTouchX always contains the horizontal position of + * one finger, relative to (0, 0) of the window. + * + * @property winTouchX + */ +p5.prototype.winTouchX = 0; + +/** + * The system variable winTouchY always contains the vertical position of + * one finger, relative to (0, 0) of the window. + * + * @property winTouchY + */ +p5.prototype.winTouchY = 0; + +/** + * The system variable pwinTouchX always contains the horizontal position of + * one finger, relative to (0, 0) of the window, in the frame previous to the + * current frame. + * + * @property pwinTouchX + */ +p5.prototype.pwinTouchX = 0; + +/** + * The system variable pwinTouchY always contains the vertical position of + * one finger, relative to (0, 0) of the window, in the frame previous to the + * current frame. + * + * @property pwinTouchY + */ +p5.prototype.pwinTouchY = 0; + +/** + * The system variable touches[] contains an array of the positions of all + * current touch points, relative to (0, 0) of the canvas, and IDs identifying a + * unique touch as it moves. Each element in the array is an object with x, y, + * and id properties. + * + * @property touches[] + */ +p5.prototype.touches = []; + +/** + * The boolean system variable touchIsDown is true if the screen is + * touched and false if not. + * + * @property touchIsDown + */ +p5.prototype.touchIsDown = false; + +p5.prototype._updateNextTouchCoords = function(e) { + var x = this.touchX; + var y = this.touchY; + var winX = this.winTouchX; + var winY = this.winTouchY; + if(e.type === 'mousedown' || + e.type === 'mousemove' || + e.type === 'mouseup' || !e.touches) { + x = this.mouseX; + y = this.mouseY; + winX = this.winMouseX; + winY = this.winMouseY; + } else { + if(this._curElement !== null) { + var touchInfo = getTouchInfo(this._curElement.elt, e, 0); + x = touchInfo.x; + y = touchInfo.y; + winX = touchInfo.winX; + winY = touchInfo.winY; + + var touches = []; + for(var i = 0; i < e.touches.length; i++){ + touches[i] = getTouchInfo(this._curElement.elt, e, i); + } + this._setProperty('touches', touches); + } + } + this._setProperty('touchX', x); + this._setProperty('touchY', y); + this._setProperty('winTouchX', winX); + this._setProperty('winTouchY', winY); + if (!this._hasTouchInteracted) { + // For first draw, make previous and next equal + this._updateTouchCoords(); + this._setProperty('_hasTouchInteracted', true); + } +}; + +p5.prototype._updateTouchCoords = function() { + this._setProperty('ptouchX', this.touchX); + this._setProperty('ptouchY', this.touchY); + this._setProperty('pwinTouchX', this.winTouchX); + this._setProperty('pwinTouchY', this.winTouchY); +}; + +function getTouchInfo(canvas, e, i) { + i = i || 0; + var rect = canvas.getBoundingClientRect(); + var touch = e.touches[i] || e.changedTouches[i]; + return { + x: touch.clientX - rect.left, + y: touch.clientY - rect.top, + winX: touch.clientX, + winY: touch.clientY, + id: touch.identifier + }; +} + +/** + * The touchStarted() function is called once after every time a touch is + * registered. If no touchStarted() function is defined, the mousePressed() + * function will be called instead if it is defined.<br><br> + * Browsers may have different default behaviors attached to various touch + * events. To prevent any default behavior for this event, add "return false" + * to the end of the method. + * + * @method touchStarted + * @example + * <div> + * <code> + * // Touch within the image to change + * // the value of the rectangle + * + * var value = 0; + * function draw() { + * fill(value); + * rect(25, 25, 50, 50); + * } + * function touchStarted() { + * if (value == 0) { + * value = 255; + * } else { + * value = 0; + * } + * } + * </code> + * </div> + * + * <div class="norender"> + * <code> + * function touchStarted() { + * ellipse(touchX, touchY, 5, 5); + * // prevent default + * return false; + * } + * </code> + * </div> + * + * @alt + * 50x50 black rect turns white with touch event. + * no image displayed + */ +p5.prototype._ontouchstart = function(e) { + var context = this._isGlobal ? window : this; + var executeDefault; + this._updateNextTouchCoords(e); + this._updateNextMouseCoords(e); + this._setProperty('touchIsDown', true); + if(typeof context.touchStarted === 'function') { + executeDefault = context.touchStarted(e); + if(executeDefault === false) { + e.preventDefault(); + } + } else if (typeof context.mousePressed === 'function') { + executeDefault = context.mousePressed(e); + if(executeDefault === false) { + e.preventDefault(); + } + //this._setMouseButton(e); + } +}; + +/** + * The touchMoved() function is called every time a touch move is registered. + * If no touchMoved() function is defined, the mouseDragged() function will + * be called instead if it is defined.<br><br> + * Browsers may have different default behaviors attached to various touch + * events. To prevent any default behavior for this event, add "return false" + * to the end of the method. + * + * @method touchMoved + * @example + * <div> + * <code> + * // Move your finger across the page + * // to change its value + * + * var value = 0; + * function draw() { + * fill(value); + * rect(25, 25, 50, 50); + * } + * function touchMoved() { + * value = value + 5; + * if (value > 255) { + * value = 0; + * } + * } + * </code> + * </div> + * + * <div class="norender"> + * <code> + * function touchMoved() { + * ellipse(touchX, touchY, 5, 5); + * // prevent default + * return false; + * } + * </code> + * </div> + * + * @alt + * 50x50 black rect turns lighter with touch until white. resets + * no image displayed + * + */ +p5.prototype._ontouchmove = function(e) { + var context = this._isGlobal ? window : this; + var executeDefault; + this._updateNextTouchCoords(e); + this._updateNextMouseCoords(e); + if (typeof context.touchMoved === 'function') { + executeDefault = context.touchMoved(e); + if(executeDefault === false) { + e.preventDefault(); + } + } else if (typeof context.mouseDragged === 'function') { + executeDefault = context.mouseDragged(e); + if(executeDefault === false) { + e.preventDefault(); + } + } +}; + +/** + * The touchEnded() function is called every time a touch ends. If no + * touchEnded() function is defined, the mouseReleased() function will be + * called instead if it is defined.<br><br> + * Browsers may have different default behaviors attached to various touch + * events. To prevent any default behavior for this event, add "return false" + * to the end of the method. + * + * @method touchEnded + * @example + * <div> + * <code> + * // Release touch within the image to + * // change the value of the rectangle + * + * var value = 0; + * function draw() { + * fill(value); + * rect(25, 25, 50, 50); + * } + * function touchEnded() { + * if (value == 0) { + * value = 255; + * } else { + * value = 0; + * } + * } + * </code> + * </div> + * + * <div class="norender"> + * <code> + * function touchEnded() { + * ellipse(touchX, touchY, 5, 5); + * // prevent default + * return false; + * } + * </code> + * </div> + * + * @alt + * 50x50 black rect turns white with touch. + * no image displayed + * + */ +p5.prototype._ontouchend = function(e) { + this._updateNextTouchCoords(e); + this._updateNextMouseCoords(e); + if (this.touches.length === 0) { + this._setProperty('touchIsDown', false); + } + var context = this._isGlobal ? window : this; + var executeDefault; + if (typeof context.touchEnded === 'function') { + executeDefault = context.touchEnded(e); + if(executeDefault === false) { + e.preventDefault(); + } + } else if (typeof context.mouseReleased === 'function') { + executeDefault = context.mouseReleased(e); + if(executeDefault === false) { + e.preventDefault(); + } + } +}; + +module.exports = p5; + +},{"../core/core":37}],54:[function(_dereq_,module,exports){ +/*global ImageData:false */ + +/** + * This module defines the filters for use with image buffers. + * + * This module is basically a collection of functions stored in an object + * as opposed to modules. The functions are destructive, modifying + * the passed in canvas rather than creating a copy. + * + * Generally speaking users of this module will use the Filters.apply method + * on a canvas to create an effect. + * + * A number of functions are borrowed/adapted from + * http://www.html5rocks.com/en/tutorials/canvas/imagefilters/ + * or the java processing implementation. + */ + +'use strict'; + +var Filters = {}; + + +/* + * Helper functions + */ + + +/** + * Returns the pixel buffer for a canvas + * + * @private + * + * @param {Canvas|ImageData} canvas the canvas to get pixels from + * @return {Uint8ClampedArray} a one-dimensional array containing + * the data in thc RGBA order, with integer + * values between 0 and 255 + */ +Filters._toPixels = function (canvas) { + if (canvas instanceof ImageData) { + return canvas.data; + } else { + return canvas.getContext('2d').getImageData( + 0, + 0, + canvas.width, + canvas.height + ).data; + } +}; + +/** + * Returns a 32 bit number containing ARGB data at ith pixel in the + * 1D array containing pixels data. + * + * @private + * + * @param {Uint8ClampedArray} data array returned by _toPixels() + * @param {Integer} i index of a 1D Image Array + * @return {Integer} 32 bit integer value representing + * ARGB value. + */ +Filters._getARGB = function (data, i) { + var offset = i * 4; + return (data[offset+3] << 24) & 0xff000000 | + (data[offset] << 16) & 0x00ff0000 | + (data[offset+1] << 8) & 0x0000ff00 | + data[offset+2] & 0x000000ff; +}; + +/** + * Modifies pixels RGBA values to values contained in the data object. + * + * @private + * + * @param {Uint8ClampedArray} pixels array returned by _toPixels() + * @param {Int32Array} data source 1D array where each value + * represents ARGB values + */ +Filters._setPixels = function (pixels, data) { + var offset = 0; + for( var i = 0, al = pixels.length; i < al; i++) { + offset = i*4; + pixels[offset + 0] = (data[i] & 0x00ff0000)>>>16; + pixels[offset + 1] = (data[i] & 0x0000ff00)>>>8; + pixels[offset + 2] = (data[i] & 0x000000ff); + pixels[offset + 3] = (data[i] & 0xff000000)>>>24; + } +}; + +/** + * Returns the ImageData object for a canvas + * https://developer.mozilla.org/en-US/docs/Web/API/ImageData + * + * @private + * + * @param {Canvas|ImageData} canvas canvas to get image data from + * @return {ImageData} Holder of pixel data (and width and + * height) for a canvas + */ +Filters._toImageData = function (canvas) { + if (canvas instanceof ImageData) { + return canvas; + } else { + return canvas.getContext('2d').getImageData( + 0, + 0, + canvas.width, + canvas.height + ); + } +}; + +/** + * Returns a blank ImageData object. + * + * @private + * + * @param {Integer} width + * @param {Integer} height + * @return {ImageData} + */ +Filters._createImageData = function (width, height) { + Filters._tmpCanvas = document.createElement('canvas'); + Filters._tmpCtx = Filters._tmpCanvas.getContext('2d'); + return this._tmpCtx.createImageData(width, height); +}; + + +/** + * Applys a filter function to a canvas. + * + * The difference between this and the actual filter functions defined below + * is that the filter functions generally modify the pixel buffer but do + * not actually put that data back to the canvas (where it would actually + * update what is visible). By contrast this method does make the changes + * actually visible in the canvas. + * + * The apply method is the method that callers of this module would generally + * use. It has been separated from the actual filters to support an advanced + * use case of creating a filter chain that executes without actually updating + * the canvas in between everystep. + * + * @param {[type]} func [description] + * @param {[type]} canvas [description] + * @param {[type]} level [description] + * @return {[type]} [description] + */ +Filters.apply = function (canvas, func, filterParam) { + var ctx = canvas.getContext('2d'); + var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); + + //Filters can either return a new ImageData object, or just modify + //the one they received. + var newImageData = func(imageData, filterParam); + if (newImageData instanceof ImageData) { + ctx.putImageData(newImageData, 0, 0, 0, 0, canvas.width, canvas.height); + } else { + ctx.putImageData(imageData, 0, 0, 0, 0, canvas.width, canvas.height); + } +}; + + +/* + * Filters + */ + + +/** + * Converts the image to black and white pixels depending if they are above or + * below the threshold defined by the level parameter. The parameter must be + * between 0.0 (black) and 1.0 (white). If no level is specified, 0.5 is used. + * + * Borrowed from http://www.html5rocks.com/en/tutorials/canvas/imagefilters/ + * + * @param {Canvas} canvas + * @param {Float} level + */ +Filters.threshold = function (canvas, level) { + var pixels = Filters._toPixels(canvas); + + if (level === undefined) { + level = 0.5; + } + var thresh = Math.floor(level * 255); + + for (var i = 0; i < pixels.length; i += 4) { + var r = pixels[i]; + var g = pixels[i + 1]; + var b = pixels[i + 2]; + var gray = (0.2126 * r + 0.7152 * g + 0.0722 * b); + var val; + if (gray >= thresh) { + val = 255; + } else { + val = 0; + } + pixels[i] = pixels[i + 1] = pixels[i + 2] = val; + } + +}; + + +/** + * Converts any colors in the image to grayscale equivalents. + * No parameter is used. + * + * Borrowed from http://www.html5rocks.com/en/tutorials/canvas/imagefilters/ + * + * @param {Canvas} canvas + */ +Filters.gray = function (canvas) { + var pixels = Filters._toPixels(canvas); + + for (var i = 0; i < pixels.length; i += 4) { + var r = pixels[i]; + var g = pixels[i + 1]; + var b = pixels[i + 2]; + + // CIE luminance for RGB + var gray = (0.2126 * r + 0.7152 * g + 0.0722 * b); + pixels[i] = pixels[i + 1] = pixels[i + 2] = gray; + } +}; + +/** + * Sets the alpha channel to entirely opaque. No parameter is used. + * + * @param {Canvas} canvas + */ +Filters.opaque = function (canvas) { + var pixels = Filters._toPixels(canvas); + + for (var i = 0; i < pixels.length; i += 4) { + pixels[i + 3] = 255; + } + + return pixels; +}; + +/** + * Sets each pixel to its inverse value. No parameter is used. + * @param {Invert} + */ +Filters.invert = function (canvas) { + var pixels = Filters._toPixels(canvas); + + for (var i = 0; i < pixels.length; i += 4) { + pixels[i] = 255 - pixels[i]; + pixels[i + 1] = 255 - pixels[i + 1]; + pixels[i + 2] = 255 - pixels[i + 2]; + } + +}; + + +/** + * Limits each channel of the image to the number of colors specified as + * the parameter. The parameter can be set to values between 2 and 255, but + * results are most noticeable in the lower ranges. + * + * Adapted from java based processing implementation + * + * @param {Canvas} canvas + * @param {Integer} level + */ +Filters.posterize = function (canvas, level) { + var pixels = Filters._toPixels(canvas); + + if ((level < 2) || (level > 255)) { + throw new Error( + 'Level must be greater than 2 and less than 255 for posterize' + ); + } + + var levels1 = level - 1; + for (var i = 0; i < pixels.length; i+=4) { + var rlevel = pixels[i]; + var glevel = pixels[i + 1]; + var blevel = pixels[i + 2]; + + pixels[i] = (((rlevel * level) >> 8) * 255) / levels1; + pixels[i + 1] = (((glevel * level) >> 8) * 255) / levels1; + pixels[i + 2] = (((blevel * level) >> 8) * 255) / levels1; + } +}; + +/** + * reduces the bright areas in an image + * @param {Canvas} canvas + * + */ +Filters.dilate = function (canvas) { + var pixels = Filters._toPixels(canvas); + var currIdx = 0; + var maxIdx = pixels.length ? pixels.length/4 : 0; + var out = new Int32Array(maxIdx); + var currRowIdx, maxRowIdx, colOrig, colOut, currLum; + var idxRight, idxLeft, idxUp, idxDown, + colRight, colLeft, colUp, colDown, + lumRight, lumLeft, lumUp, lumDown; + + while(currIdx < maxIdx) { + currRowIdx = currIdx; + maxRowIdx = currIdx + canvas.width; + while (currIdx < maxRowIdx) { + colOrig = colOut = Filters._getARGB(pixels, currIdx); + idxLeft = currIdx - 1; + idxRight = currIdx + 1; + idxUp = currIdx - canvas.width; + idxDown = currIdx + canvas.width; + + if (idxLeft < currRowIdx) { + idxLeft = currIdx; + } + if (idxRight >= maxRowIdx) { + idxRight = currIdx; + } + if (idxUp < 0){ + idxUp = 0; + } + if (idxDown >= maxIdx) { + idxDown = currIdx; + } + colUp = Filters._getARGB(pixels, idxUp); + colLeft = Filters._getARGB(pixels, idxLeft); + colDown = Filters._getARGB(pixels, idxDown); + colRight = Filters._getARGB(pixels, idxRight); + + //compute luminance + currLum = 77*(colOrig>>16&0xff) + + 151*(colOrig>>8&0xff) + + 28*(colOrig&0xff); + lumLeft = 77*(colLeft>>16&0xff) + + 151*(colLeft>>8&0xff) + + 28*(colLeft&0xff); + lumRight = 77*(colRight>>16&0xff) + + 151*(colRight>>8&0xff) + + 28*(colRight&0xff); + lumUp = 77*(colUp>>16&0xff) + + 151*(colUp>>8&0xff) + + 28*(colUp&0xff); + lumDown = 77*(colDown>>16&0xff) + + 151*(colDown>>8&0xff) + + 28*(colDown&0xff); + + if (lumLeft > currLum) { + colOut = colLeft; + currLum = lumLeft; + } + if (lumRight > currLum) { + colOut = colRight; + currLum = lumRight; + } + if (lumUp > currLum) { + colOut = colUp; + currLum = lumUp; + } + if (lumDown > currLum) { + colOut = colDown; + currLum = lumDown; + } + out[currIdx++]=colOut; + } + } + Filters._setPixels(pixels, out); +}; + +/** + * increases the bright areas in an image + * @param {Canvas} canvas + * + */ +Filters.erode = function(canvas) { + var pixels = Filters._toPixels(canvas); + var currIdx = 0; + var maxIdx = pixels.length ? pixels.length/4 : 0; + var out = new Int32Array(maxIdx); + var currRowIdx, maxRowIdx, colOrig, colOut, currLum; + var idxRight, idxLeft, idxUp, idxDown, + colRight, colLeft, colUp, colDown, + lumRight, lumLeft, lumUp, lumDown; + + while(currIdx < maxIdx) { + currRowIdx = currIdx; + maxRowIdx = currIdx + canvas.width; + while (currIdx < maxRowIdx) { + colOrig = colOut = Filters._getARGB(pixels, currIdx); + idxLeft = currIdx - 1; + idxRight = currIdx + 1; + idxUp = currIdx - canvas.width; + idxDown = currIdx + canvas.width; + + if (idxLeft < currRowIdx) { + idxLeft = currIdx; + } + if (idxRight >= maxRowIdx) { + idxRight = currIdx; + } + if (idxUp < 0) { + idxUp = 0; + } + if (idxDown >= maxIdx) { + idxDown = currIdx; + } + colUp = Filters._getARGB(pixels, idxUp); + colLeft = Filters._getARGB(pixels, idxLeft); + colDown = Filters._getARGB(pixels, idxDown); + colRight = Filters._getARGB(pixels, idxRight); + + //compute luminance + currLum = 77*(colOrig>>16&0xff) + + 151*(colOrig>>8&0xff) + + 28*(colOrig&0xff); + lumLeft = 77*(colLeft>>16&0xff) + + 151*(colLeft>>8&0xff) + + 28*(colLeft&0xff); + lumRight = 77*(colRight>>16&0xff) + + 151*(colRight>>8&0xff) + + 28*(colRight&0xff); + lumUp = 77*(colUp>>16&0xff) + + 151*(colUp>>8&0xff) + + 28*(colUp&0xff); + lumDown = 77*(colDown>>16&0xff) + + 151*(colDown>>8&0xff) + + 28*(colDown&0xff); + + if (lumLeft < currLum) { + colOut = colLeft; + currLum = lumLeft; + } + if (lumRight < currLum) { + colOut = colRight; + currLum = lumRight; + } + if (lumUp < currLum) { + colOut = colUp; + currLum = lumUp; + } + if (lumDown < currLum) { + colOut = colDown; + currLum = lumDown; + } + + out[currIdx++]=colOut; + } + } + Filters._setPixels(pixels, out); +}; + +// BLUR + +// internal kernel stuff for the gaussian blur filter +var blurRadius; +var blurKernelSize; +var blurKernel; +var blurMult; + +/* + * Port of https://github.com/processing/processing/blob/ + * master/core/src/processing/core/PImage.java#L1250 + * + * Optimized code for building the blur kernel. + * further optimized blur code (approx. 15% for radius=20) + * bigger speed gains for larger radii (~30%) + * added support for various image types (ALPHA, RGB, ARGB) + * [toxi 050728] + */ +function buildBlurKernel(r) { + var radius = (r * 3.5)|0; + radius = (radius < 1) ? 1 : ((radius < 248) ? radius : 248); + + if (blurRadius !== radius) { + blurRadius = radius; + blurKernelSize = 1 + blurRadius<<1; + blurKernel = new Int32Array(blurKernelSize); + blurMult = new Array(blurKernelSize); + for(var l = 0; l < blurKernelSize; l++){ + blurMult[l] = new Int32Array(256); + } + + var bk,bki; + var bm,bmi; + + for (var i = 1, radiusi = radius - 1; i < radius; i++) { + blurKernel[radius+i] = blurKernel[radiusi] = bki = radiusi * radiusi; + bm = blurMult[radius+i]; + bmi = blurMult[radiusi--]; + for (var j = 0; j < 256; j++){ + bm[j] = bmi[j] = bki * j; + } + } + bk = blurKernel[radius] = radius * radius; + bm = blurMult[radius]; + + for (var k = 0; k < 256; k++){ + bm[k] = bk * k; + } + } + +} + +// Port of https://github.com/processing/processing/blob/ +// master/core/src/processing/core/PImage.java#L1433 +function blurARGB(canvas, radius) { + var pixels = Filters._toPixels(canvas); + var width = canvas.width; + var height = canvas.height; + var numPackedPixels = width * height; + var argb = new Int32Array(numPackedPixels); + for (var j = 0; j < numPackedPixels; j++) { + argb[j] = Filters._getARGB(pixels, j); + } + var sum, cr, cg, cb, ca; + var read, ri, ym, ymi, bk0; + var a2 = new Int32Array(numPackedPixels); + var r2 = new Int32Array(numPackedPixels); + var g2 = new Int32Array(numPackedPixels); + var b2 = new Int32Array(numPackedPixels); + var yi = 0; + buildBlurKernel(radius); + var x, y, i; + var bm; + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + cb = cg = cr = ca = sum = 0; + read = x - blurRadius; + if (read < 0) { + bk0 = -read; + read = 0; + } else { + if (read >= width) { + break; + } + bk0 = 0; + } + for (i = bk0; i < blurKernelSize; i++) { + if (read >= width) { + break; + } + var c = argb[read + yi]; + bm = blurMult[i]; + ca += bm[(c & -16777216) >>> 24]; + cr += bm[(c & 16711680) >> 16]; + cg += bm[(c & 65280) >> 8]; + cb += bm[c & 255]; + sum += blurKernel[i]; + read++; + } + ri = yi + x; + a2[ri] = ca / sum; + r2[ri] = cr / sum; + g2[ri] = cg / sum; + b2[ri] = cb / sum; + } + yi += width; + } + yi = 0; + ym = -blurRadius; + ymi = ym * width; + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + cb = cg = cr = ca = sum = 0; + if (ym < 0) { + bk0 = ri = -ym; + read = x; + } else { + if (ym >= height) { + break; + } + bk0 = 0; + ri = ym; + read = x + ymi; + } + for (i = bk0; i < blurKernelSize; i++) { + if (ri >= height) { + break; + } + bm = blurMult[i]; + ca += bm[a2[read]]; + cr += bm[r2[read]]; + cg += bm[g2[read]]; + cb += bm[b2[read]]; + sum += blurKernel[i]; + ri++; + read += width; + } + argb[x + yi] = (ca/sum)<<24 | (cr/sum)<<16 | (cg/sum)<<8 | (cb/sum); + } + yi += width; + ymi += width; + ym++; + } + Filters._setPixels(pixels, argb); +} + +Filters.blur = function(canvas, radius){ + blurARGB(canvas, radius); +}; + + +module.exports = Filters; + +},{}],55:[function(_dereq_,module,exports){ +/** + * @module Image + * @submodule Image + * @for p5 + * @requires core + */ + +/** + * This module defines the p5 methods for the p5.Image class + * for drawing images to the main display canvas. + */ +'use strict'; + + +var p5 = _dereq_('../core/core'); + +/* global frames:true */// This is not global, but JSHint is not aware that +// this module is implicitly enclosed with Browserify: this overrides the +// redefined-global error and permits using the name "frames" for the array +// of saved animation frames. +var frames = []; + + +/** + * Creates a new p5.Image (the datatype for storing images). This provides a + * fresh buffer of pixels to play with. Set the size of the buffer with the + * width and height parameters. + * <br><br> + * .pixels gives access to an array containing the values for all the pixels + * in the display window. + * These values are numbers. This array is the size (including an appropriate + * factor for the pixelDensity) of the display window x4, + * representing the R, G, B, A values in order for each pixel, moving from + * left to right across each row, then down each column. See .pixels for + * more info. It may also be simpler to use set() or get(). + * <br><br> + * Before accessing the pixels of an image, the data must loaded with the + * loadPixels() function. After the array data has been modified, the + * updatePixels() function must be run to update the changes. + * + * @method createImage + * @param {Integer} width width in pixels + * @param {Integer} height height in pixels + * @return {p5.Image} the p5.Image object + * @example + * <div> + * <code> + * img = createImage(66, 66); + * img.loadPixels(); + * for (i = 0; i < img.width; i++) { + * for (j = 0; j < img.height; j++) { + * img.set(i, j, color(0, 90, 102)); + * } + * } + * img.updatePixels(); + * image(img, 17, 17); + * </code> + * </div> + * + * <div> + * <code> + * img = createImage(66, 66); + * img.loadPixels(); + * for (i = 0; i < img.width; i++) { + * for (j = 0; j < img.height; j++) { + * img.set(i, j, color(0, 90, 102, i % img.width * 2)); + * } + * } + * img.updatePixels(); + * image(img, 17, 17); + * image(img, 34, 34); + * </code> + * </div> + * + * <div> + * <code> + * var pink = color(255, 102, 204); + * img = createImage(66, 66); + * img.loadPixels(); + * var d = pixelDensity; + * var halfImage = 4 * (width * d) * (height/2 * d); + * for (var i = 0; i < halfImage; i+=4) { + * img.pixels[i] = red(pink); + * img.pixels[i+1] = green(pink); + * img.pixels[i+2] = blue(pink); + * img.pixels[i+3] = alpha(pink); + * } + * img.updatePixels(); + * image(img, 17, 17); + * </code> + * </div> + * + * @alt + * 66x66 dark turquoise rect in center of canvas. + * 2 gradated dark turquoise rects fade left. 1 center 1 bottom right of canvas + * no image displayed + * + */ +p5.prototype.createImage = function(width, height) { + return new p5.Image(width, height); +}; + +/** + * Save the current canvas as an image. In Safari, this will open the + * image in the window and the user must provide their own + * filename on save-as. Other browsers will either save the + * file immediately, or prompt the user with a dialogue window. + * + * @method saveCanvas + * @param {[selectedCanvas]} canvas a variable representing a + * specific html5 canvas (optional) + * @param {[String]} filename + * @param {[String]} extension 'jpg' or 'png' + * @example + * <div class='norender'><code> + * function setup() { + * var c = createCanvas(100, 100); + * background(255, 0, 0); + * saveCanvas(c, 'myCanvas', 'jpg'); + * } + * </code></div> + * <div class='norender'><code> + * // note that this example has the same result as above + * // if no canvas is specified, defaults to main canvas + * function setup() { + * createCanvas(100, 100); + * background(255, 0, 0); + * saveCanvas('myCanvas', 'jpg'); + * } + * </code></div> + * <div class='norender'><code> + * // all of the following are valid + * saveCanvas(c, 'myCanvas', 'jpg'); + * saveCanvas(c, 'myCanvas'); + * saveCanvas(c); + * saveCanvas('myCanvas', 'png'); + * saveCanvas('myCanvas'); + * saveCanvas(); + * </code></div> + * + * @alt + * no image displayed + * no image displayed + * no image displayed + * + */ +p5.prototype.saveCanvas = function() { + + var cnv, filename, extension; + if (arguments.length === 3) { + cnv = arguments[0]; + filename = arguments[1]; + extension = arguments[2]; + } else if (arguments.length === 2) { + if (typeof arguments[0] === 'object') { + cnv = arguments[0]; + filename = arguments[1]; + } else { + filename = arguments[0]; + extension = arguments[1]; + } + } else if (arguments.length === 1) { + if (typeof arguments[0] === 'object') { + cnv = arguments[0]; + } else { + filename = arguments[0]; + } + } + + if (cnv instanceof p5.Element) { + cnv = cnv.elt; + } + if (!(cnv instanceof HTMLCanvasElement)) { + cnv = null; + } + + if (!extension) { + extension = p5.prototype._checkFileExtension(filename, extension)[1]; + if (extension === '') { + extension = 'png'; + } + } + + if (!cnv) { + if (this._curElement && this._curElement.elt) { + cnv = this._curElement.elt; + } + } + + if ( p5.prototype._isSafari() ) { + var aText = 'Hello, Safari user!\n'; + aText += 'Now capturing a screenshot...\n'; + aText += 'To save this image,\n'; + aText += 'go to File --> Save As.\n'; + alert(aText); + window.location.href = cnv.toDataURL(); + } else { + var mimeType; + if (typeof(extension) === 'undefined') { + extension = 'png'; + mimeType = 'image/png'; + } + else { + switch(extension){ + case 'png': + mimeType = 'image/png'; + break; + case 'jpeg': + mimeType = 'image/jpeg'; + break; + case 'jpg': + mimeType = 'image/jpeg'; + break; + default: + mimeType = 'image/png'; + break; + } + } + var downloadMime = 'image/octet-stream'; + var imageData = cnv.toDataURL(mimeType); + imageData = imageData.replace(mimeType, downloadMime); + + p5.prototype.downloadFile(imageData, filename, extension); + } +}; + +/** + * Capture a sequence of frames that can be used to create a movie. + * Accepts a callback. For example, you may wish to send the frames + * to a server where they can be stored or converted into a movie. + * If no callback is provided, the browser will pop up save dialogues in an + * attempt to download all of the images that have just been created. With the + * callback provided the image data isn't saved by default but instead passed + * as an argument to the callback function as an array of objects, with the + * size of array equal to the total number of frames. + * + * @method saveFrames + * @param {String} filename + * @param {String} extension 'jpg' or 'png' + * @param {Number} duration Duration in seconds to save the frames for. + * @param {Number} framerate Framerate to save the frames in. + * @param {Function} [callback] A callback function that will be executed + to handle the image data. This function + should accept an array as argument. The + array will contain the specified number of + frames of objects. Each object has three + properties: imageData - an + image/octet-stream, filename and extension. + * @example + * <div><code> + * function draw() { + * background(mouseX); + * } + * + * function mousePressed() { + * saveFrames("out", "png", 1, 25, function(data){ + * print(data); + * }); + * } + * </code></div> + * + * @alt + * canvas background goes from light to dark with mouse x. + * + */ +p5.prototype.saveFrames = function(fName, ext, _duration, _fps, callback) { + var duration = _duration || 3; + duration = p5.prototype.constrain(duration, 0, 15); + duration = duration * 1000; + var fps = _fps || 15; + fps = p5.prototype.constrain(fps, 0, 22); + var count = 0; + + var makeFrame = p5.prototype._makeFrame; + var cnv = this._curElement.elt; + var frameFactory = setInterval(function(){ + makeFrame(fName + count, ext, cnv); + count++; + },1000/fps); + + setTimeout(function(){ + clearInterval(frameFactory); + if (callback) { + callback(frames); + } + else { + for (var i = 0; i < frames.length; i++) { + var f = frames[i]; + p5.prototype.downloadFile(f.imageData, f.filename, f.ext); + } + } + frames = []; // clear frames + }, duration + 0.01); +}; + +p5.prototype._makeFrame = function(filename, extension, _cnv) { + var cnv; + if (this) { + cnv = this._curElement.elt; + } else { + cnv = _cnv; + } + var mimeType; + if (!extension) { + extension = 'png'; + mimeType = 'image/png'; + } + else { + switch(extension.toLowerCase()){ + case 'png': + mimeType = 'image/png'; + break; + case 'jpeg': + mimeType = 'image/jpeg'; + break; + case 'jpg': + mimeType = 'image/jpeg'; + break; + default: + mimeType = 'image/png'; + break; + } + } + var downloadMime = 'image/octet-stream'; + var imageData = cnv.toDataURL(mimeType); + imageData = imageData.replace(mimeType, downloadMime); + + var thisFrame = {}; + thisFrame.imageData = imageData; + thisFrame.filename = filename; + thisFrame.ext = extension; + frames.push(thisFrame); +}; + +module.exports = p5; + +},{"../core/core":37}],56:[function(_dereq_,module,exports){ +/** + * @module Image + * @submodule Loading & Displaying + * @for p5 + * @requires core + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); +var Filters = _dereq_('./filters'); +var canvas = _dereq_('../core/canvas'); +var constants = _dereq_('../core/constants'); + +_dereq_('../core/error_helpers'); + +/** + * Loads an image from a path and creates a p5.Image from it. + * <br><br> + * The image may not be immediately available for rendering + * If you want to ensure that the image is ready before doing + * anything with it, place the loadImage() call in preload(). + * You may also supply a callback function to handle the image when it's ready. + * <br><br> + * The path to the image should be relative to the HTML file + * that links in your sketch. Loading an from a URL or other + * remote location may be blocked due to your browser's built-in + * security. + * + * @method loadImage + * @param {String} path Path of the image to be loaded + * @param {Function(p5.Image)} [successCallback] Function to be called once + * the image is loaded. Will be passed the + * p5.Image. + * @param {Function(Event)} [failureCallback] called with event error if + * the image fails to load. + * @return {p5.Image} the p5.Image object + * @example + * <div> + * <code> + * var img; + * function preload() { + * img = loadImage("assets/laDefense.jpg"); + * } + * function setup() { + * image(img, 0, 0); + * } + * </code> + * </div> + * <div> + * <code> + * function setup() { + * // here we use a callback to display the image after loading + * loadImage("assets/laDefense.jpg", function(img) { + * image(img, 0, 0); + * }); + * } + * </code> + * </div> + * + * @alt + * image of the underside of a white umbrella and grided ceililng above + * image of the underside of a white umbrella and grided ceililng above + * + */ +p5.prototype.loadImage = function(path, successCallback, failureCallback) { + var img = new Image(); + var pImg = new p5.Image(1, 1, this); + var decrementPreload = p5._getDecrementPreload.apply(this, arguments); + + img.onload = function() { + pImg.width = pImg.canvas.width = img.width; + pImg.height = pImg.canvas.height = img.height; + + // Draw the image into the backing canvas of the p5.Image + pImg.drawingContext.drawImage(img, 0, 0); + + if (typeof successCallback === 'function') { + successCallback(pImg); + } + if (decrementPreload && (successCallback !== decrementPreload)) { + decrementPreload(); + } + }; + img.onerror = function(e) { + p5._friendlyFileLoadError(0,img.src); + // don't get failure callback mixed up with decrementPreload + if ((typeof failureCallback === 'function') && + (failureCallback !== decrementPreload)) { + failureCallback(e); + } + }; + + //set crossOrigin in case image is served which CORS headers + //this will let us draw to canvas without tainting it. + //see https://developer.mozilla.org/en-US/docs/HTML/CORS_Enabled_Image + // When using data-uris the file will be loaded locally + // so we don't need to worry about crossOrigin with base64 file types + if(path.indexOf('data:image/') !== 0) { + img.crossOrigin = 'Anonymous'; + } + + //start loading the image + img.src = path; + + return pImg; +}; + +/** + * Validates clipping params. Per drawImage spec sWidth and sHight cannot be + * negative or greater than image intrinsic width and height + * @private + * @param {Number} sVal + * @param {Number} iVal + * @returns {Number} + * @private + */ +function _sAssign(sVal, iVal) { + if (sVal > 0 && sVal < iVal) { + return sVal; + } + else { + return iVal; + } +} + +/** + * Draw an image to the main canvas of the p5js sketch + * + * @method image + * @param {p5.Image} img the image to display + * @param {Number} [sx=0] The X coordinate of the top left corner of the + * sub-rectangle of the source image to draw into + * the destination canvas. + * @param {Number} [sy=0] The Y coordinate of the top left corner of the + * sub-rectangle of the source image to draw into + * the destination canvas. + * @param {Number} [sWidth=img.width] The width of the sub-rectangle of the + * source image to draw into the destination + * canvas. + * @param {Number} [sHeight=img.height] The height of the sub-rectangle of the + * source image to draw into the + * destination context. + * @param {Number} [dx=0] The X coordinate in the destination canvas at + * which to place the top-left corner of the + * source image. + * @param {Number} [dy=0] The Y coordinate in the destination canvas at + * which to place the top-left corner of the + * source image. + * @param {Number} [dWidth] The width to draw the image in the destination + * canvas. This allows scaling of the drawn image. + * @param {Number} [dHeight] The height to draw the image in the destination + * canvas. This allows scaling of the drawn image. + * @example + * <div> + * <code> + * var img; + * function preload() { + * img = loadImage("assets/laDefense.jpg"); + * } + * function setup() { + * image(img, 0, 0); + * image(img, 0, 0, 100, 100); + * image(img, 0, 0, 100, 100, 0, 0, 100, 100); + * } + * </code> + * </div> + * <div> + * <code> + * function setup() { + * // here we use a callback to display the image after loading + * loadImage("assets/laDefense.jpg", function(img) { + * image(img, 0, 0); + * }); + * } + * </code> + * </div> + * + * @alt + * image of the underside of a white umbrella and grided ceiling above + * image of the underside of a white umbrella and grided ceiling above + * + */ +p5.prototype.image = + function(img, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight) { + // Temporarily disabling until options for p5.Graphics are added. + // var args = new Array(arguments.length); + // for (var i = 0; i < args.length; ++i) { + // args[i] = arguments[i]; + // } + // this._validateParameters( + // 'image', + // args, + // [ + // ['p5.Image', 'Number', 'Number'], + // ['p5.Image', 'Number', 'Number', 'Number', 'Number'] + // ] + // ); + + // set defaults per spec: https://goo.gl/3ykfOq + if (arguments.length <= 5) { + dx = sx || 0; + dy = sy || 0; + sx = 0; + sy = 0; + if (img.elt && img.elt.videoWidth && !img.canvas) { // video no canvas + var actualW = img.elt.videoWidth; + var actualH = img.elt.videoHeight; + dWidth = sWidth || img.elt.width; + dHeight = sHeight || img.elt.width*actualH/actualW; + sWidth = actualW; + sHeight = actualH; + } else { + dWidth = sWidth || img.width; + dHeight = sHeight || img.height; + sWidth = img.width; + sHeight = img.height; + } + } else if (arguments.length === 9) { + sx = sx || 0; + sy = sy || 0; + sWidth = _sAssign(sWidth, img.width); + sHeight = _sAssign(sHeight, img.height); + + dx = dx || 0; + dy = dy || 0; + dWidth = dWidth || img.width; + dHeight = dHeight || img.height; + } else { + throw 'Wrong number of arguments to image()'; + } + + var vals = canvas.modeAdjust(dx, dy, dWidth, dHeight, + this._renderer._imageMode); + + // tint the image if there is a tint + this._renderer.image(img, sx, sy, sWidth, sHeight, vals.x, vals.y, vals.w, + vals.h); +}; + +/** + * Sets the fill value for displaying images. Images can be tinted to + * specified colors or made transparent by including an alpha value. + * <br><br> + * To apply transparency to an image without affecting its color, use + * white as the tint color and specify an alpha value. For instance, + * tint(255, 128) will make an image 50% transparent (assuming the default + * alpha range of 0-255, which can be changed with colorMode()). + * <br><br> + * The value for the gray parameter must be less than or equal to the current + * maximum value as specified by colorMode(). The default maximum value is + * 255. + * + * @method tint + * @param {Number|Array} v1 gray value, red or hue value (depending on the + * current color mode), or color Array + * @param {Number|Array} [v2] green or saturation value (depending on the + * current color mode) + * @param {Number|Array} [v3] blue or brightness value (depending on the + * current color mode) + * @param {Number|Array} [a] opacity of the background + * @example + * <div> + * <code> + * var img; + * function preload() { + * img = loadImage("assets/laDefense.jpg"); + * } + * function setup() { + * image(img, 0, 0); + * tint(0, 153, 204); // Tint blue + * image(img, 50, 0); + * } + * </code> + * </div> + * + * <div> + * <code> + * var img; + * function preload() { + * img = loadImage("assets/laDefense.jpg"); + * } + * function setup() { + * image(img, 0, 0); + * tint(0, 153, 204, 126); // Tint blue and set transparency + * image(img, 50, 0); + * } + * </code> + * </div> + * + * <div> + * <code> + * var img; + * function preload() { + * img = loadImage("assets/laDefense.jpg"); + * } + * function setup() { + * image(img, 0, 0); + * tint(255, 126); // Apply transparency without changing color + * image(img, 50, 0); + * } + * </code> + * </div> + * + * @alt + * 2 side by side images of umbrella and ceiling, one image with blue tint + * Images of umbrella and ceiling, one half of image with blue tint + * 2 side by side images of umbrella and ceiling, one image translucent + * + */ +p5.prototype.tint = function () { + var c = this.color.apply(this, arguments); + this._renderer._tint = c.levels; +}; + +/** + * Removes the current fill value for displaying images and reverts to + * displaying images with their original hues. + * + * @method noTint + * @example + * <div> + * <code> + * var img; + * function preload() { + * img = loadImage("assets/bricks.jpg"); + * } + * function setup() { + * tint(0, 153, 204); // Tint blue + * image(img, 0, 0); + * noTint(); // Disable tint + * image(img, 50, 0); + * } + * </code> + * </div> + * + * @alt + * 2 side by side images of bricks, left image with blue tint + * + */ +p5.prototype.noTint = function() { + this._renderer._tint = null; +}; + +/** + * Apply the current tint color to the input image, return the resulting + * canvas. + * + * @param {p5.Image} The image to be tinted + * @return {canvas} The resulting tinted canvas + * + */ +p5.prototype._getTintedImageCanvas = function(img) { + if (!img.canvas) { + return img; + } + var pixels = Filters._toPixels(img.canvas); + var tmpCanvas = document.createElement('canvas'); + tmpCanvas.width = img.canvas.width; + tmpCanvas.height = img.canvas.height; + var tmpCtx = tmpCanvas.getContext('2d'); + var id = tmpCtx.createImageData(img.canvas.width, img.canvas.height); + var newPixels = id.data; + + for(var i = 0; i < pixels.length; i += 4) { + var r = pixels[i]; + var g = pixels[i+1]; + var b = pixels[i+2]; + var a = pixels[i+3]; + + newPixels[i] = r*this._renderer._tint[0]/255; + newPixels[i+1] = g*this._renderer._tint[1]/255; + newPixels[i+2] = b*this._renderer._tint[2]/255; + newPixels[i+3] = a*this._renderer._tint[3]/255; + } + + tmpCtx.putImageData(id, 0, 0); + return tmpCanvas; +}; + +/** + * Set image mode. Modifies the location from which images are drawn by + * changing the way in which parameters given to image() are interpreted. + * The default mode is imageMode(CORNER), which interprets the second and + * third parameters of image() as the upper-left corner of the image. If + * two additional parameters are specified, they are used to set the image's + * width and height. + * <br><br> + * imageMode(CORNERS) interprets the second and third parameters of image() + * as the location of one corner, and the fourth and fifth parameters as the + * opposite corner. + * <br><br> + * imageMode(CENTER) interprets the second and third parameters of image() + * as the image's center point. If two additional parameters are specified, + * they are used to set the image's width and height. + * + * @method imageMode + * @param {Constant} mode either CORNER, CORNERS, or CENTER + * @example + * + * <div> + * <code> + * var img; + * function preload() { + * img = loadImage("assets/bricks.jpg"); + * } + * function setup() { + * imageMode(CORNER); + * image(img, 10, 10, 50, 50); + * } + * </code> + * </div> + * + * <div> + * <code> + * var img; + * function preload() { + * img = loadImage("assets/bricks.jpg"); + * } + * function setup() { + * imageMode(CORNERS); + * image(img, 10, 10, 90, 40); + * } + * </code> + * </div> + * + * <div> + * <code> + * var img; + * function preload() { + * img = loadImage("assets/bricks.jpg"); + * } + * function setup() { + * imageMode(CENTER); + * image(img, 50, 50, 80, 80); + * } + * </code> + * </div> + * + * @alt + * small square image of bricks + * horizontal rectangle image of bricks + * large square image of bricks + * + */ +p5.prototype.imageMode = function(m) { + if (m === constants.CORNER || + m === constants.CORNERS || + m === constants.CENTER) { + this._renderer._imageMode = m; + } +}; + + +module.exports = p5; + +},{"../core/canvas":35,"../core/constants":36,"../core/core":37,"../core/error_helpers":40,"./filters":54}],57:[function(_dereq_,module,exports){ +/** + * @module Image + * @submodule Image + * @requires core + * @requires constants + * @requires filters + */ + +/** + * This module defines the p5.Image class and P5 methods for + * drawing images to the main display canvas. + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); +var Filters = _dereq_('./filters'); + +/* + * Class methods + */ + +/** + * Creates a new p5.Image. A p5.Image is a canvas backed representation of an + * image. + * <br><br> + * p5 can display .gif, .jpg and .png images. Images may be displayed + * in 2D and 3D space. Before an image is used, it must be loaded with the + * loadImage() function. The p5.Image class contains fields for the width and + * height of the image, as well as an array called pixels[] that contains the + * values for every pixel in the image. + * <br><br> + * The methods described below allow easy access to the image's pixels and + * alpha channel and simplify the process of compositing. + * <br><br> + * Before using the pixels[] array, be sure to use the loadPixels() method on + * the image to make sure that the pixel data is properly loaded. + * + * @class p5.Image + * @constructor + * @param {Number} width + * @param {Number} height + * @param {Object} pInst An instance of a p5 sketch. + */ +p5.Image = function(width, height){ + /** + * Image width. + * @property width + * @example + * <div><code> + * var img; + * function preload() { + * img = loadImage("assets/rockies.jpg"); + * } + * + * function setup() { + * createCanvas(100, 100); + * image(img, 0, 0); + * for (var i=0; i < img.width; i++) { + * var c = img.get(i, img.height/2); + * stroke(c); + * line(i, height/2, i, height); + * } + * } + * </code></div> + * + * @alt + * rocky mountains in top and horizontal lines in corresponding colors in bottom. + * + */ + this.width = width; + /** + * Image height. + * @property height + * @example + * <div><code> + * var img; + * function preload() { + * img = loadImage("assets/rockies.jpg"); + * } + * + * function setup() { + * createCanvas(100, 100); + * image(img, 0, 0); + * for (var i=0; i < img.height; i++) { + * var c = img.get(img.width/2, i); + * stroke(c); + * line(0, i, width/2, i); + * } + * } + * </code></div> + * + * @alt + * rocky mountains on right and vertical lines in corresponding colors on left. + * + */ + this.height = height; + this.canvas = document.createElement('canvas'); + this.canvas.width = this.width; + this.canvas.height = this.height; + this.drawingContext = this.canvas.getContext('2d'); + this._pixelDensity = 1; + //used for webgl texturing only + this.isTexture = false; + /** + * Array containing the values for all the pixels in the display window. + * These values are numbers. This array is the size (include an appropriate + * factor for pixelDensity) of the display window x4, + * representing the R, G, B, A values in order for each pixel, moving from + * left to right across each row, then down each column. Retina and other + * high denisty displays may have more pixels[] (by a factor of + * pixelDensity^2). + * For example, if the image is 100x100 pixels, there will be 40,000. With + * pixelDensity = 2, there will be 160,000. The first four values + * (indices 0-3) in the array will be the R, G, B, A values of the pixel at + * (0, 0). The second four values (indices 4-7) will contain the R, G, B, A + * values of the pixel at (1, 0). More generally, to set values for a pixel + * at (x, y): + * <code><pre>var d = pixelDensity; + * for (var i = 0; i < d; i++) { + * for (var j = 0; j < d; j++) { + * // loop over + * idx = 4*((y * d + j) * width * d + (x * d + i)); + * pixels[idx] = r; + * pixels[idx+1] = g; + * pixels[idx+2] = b; + * pixels[idx+3] = a; + * } + * } + * </pre></code> + * <br><br> + * Before accessing this array, the data must loaded with the loadPixels() + * function. After the array data has been modified, the updatePixels() + * function must be run to update the changes. + * @property pixels[] + * @example + * <div> + * <code> + * img = createImage(66, 66); + * img.loadPixels(); + * for (i = 0; i < img.width; i++) { + * for (j = 0; j < img.height; j++) { + * img.set(i, j, color(0, 90, 102)); + * } + * } + * img.updatePixels(); + * image(img, 17, 17); + * </code> + * </div> + * <div> + * <code> + * var pink = color(255, 102, 204); + * img = createImage(66, 66); + * img.loadPixels(); + * for (var i = 0; i < 4*(width*height/2); i+=4) { + * img.pixels[i] = red(pink); + * img.pixels[i+1] = green(pink); + * img.pixels[i+2] = blue(pink); + * img.pixels[i+3] = alpha(pink); + * } + * img.updatePixels(); + * image(img, 17, 17); + * </code> + * </div> + * + * @alt + * 66x66 turquoise rect in center of canvas + * 66x66 pink rect in center of canvas + * + */ + this.pixels = []; +}; + +/** + * Helper fxn for sharing pixel methods + * + */ +p5.Image.prototype._setProperty = function (prop, value) { + this[prop] = value; +}; + +/** + * Loads the pixels data for this image into the [pixels] attribute. + * + * @method loadPixels + * @example + * <div><code> + * var myImage; + * var halfImage; + * + * function preload() { + * myImage = loadImage("assets/rockies.jpg"); + * } + * + * function setup() { + * myImage.loadPixels(); + * halfImage = 4 * width * height/2; + * for(var i = 0; i < halfImage; i++){ + * myImage.pixels[i+halfImage] = myImage.pixels[i]; + * } + * myImage.updatePixels(); + * } + * + * function draw() { + * image(myImage, 0, 0); + * } + * </code></div> + * + * @alt + * 2 images of rocky mountains vertically stacked + * + */ +p5.Image.prototype.loadPixels = function(){ + p5.Renderer2D.prototype.loadPixels.call(this); +}; + +/** + * Updates the backing canvas for this image with the contents of + * the [pixels] array. + * + * @method updatePixels + * @param {Integer|undefined} x x-offset of the target update area for the + * underlying canvas + * @param {Integer|undefined} y y-offset of the target update area for the + * underlying canvas + * @param {Integer|undefined} w height of the target update area for the + * underlying canvas + * @param {Integer|undefined} h height of the target update area for the + * underlying canvas + * @example + * <div><code> + * var myImage; + * var halfImage; + * + * function preload() { + * myImage = loadImage("assets/rockies.jpg"); + * } + * + * function setup() { + * myImage.loadPixels(); + * halfImage = 4 * width * height/2; + * for(var i = 0; i < halfImage; i++){ + * myImage.pixels[i+halfImage] = myImage.pixels[i]; + * } + * myImage.updatePixels(); + * } + * + * function draw() { + * image(myImage, 0, 0); + * } + * </code></div> + * + * @alt + * 2 images of rocky mountains vertically stacked + * + */ +p5.Image.prototype.updatePixels = function(x, y, w, h){ + p5.Renderer2D.prototype.updatePixels.call(this, x, y, w, h); +}; + +/** + * Get a region of pixels from an image. + * + * If no params are passed, those whole image is returned, + * if x and y are the only params passed a single pixel is extracted + * if all params are passed a rectangle region is extracted and a p5.Image + * is returned. + * + * Returns undefined if the region is outside the bounds of the image + * + * @method get + * @param {Number} [x] x-coordinate of the pixel + * @param {Number} [y] y-coordinate of the pixel + * @param {Number} [w] width + * @param {Number} [h] height + * @return {Array/Color | p5.Image} color of pixel at x,y in array format + * [R, G, B, A] or p5.Image + * @example + * <div><code> + * var myImage; + * var c; + * + * function preload() { + * myImage = loadImage("assets/rockies.jpg"); + * } + * + * function setup() { + * background(myImage); + * noStroke(); + * c = myImage.get(60, 90); + * fill(c); + * rect(25, 25, 50, 50); + * } + * + * //get() returns color here + * </code></div> + * + * @alt + * image of rocky mountains with 50x50 green rect in front + * + */ +p5.Image.prototype.get = function(x, y, w, h){ + return p5.Renderer2D.prototype.get.call(this, x, y, w, h); +}; + +/** + * Set the color of a single pixel or write an image into + * this p5.Image. + * + * Note that for a large number of pixels this will + * be slower than directly manipulating the pixels array + * and then calling updatePixels(). + * + * @method set + * @param {Number} x x-coordinate of the pixel + * @param {Number} y y-coordinate of the pixel + * @param {Number|Array|Object} a grayscale value | pixel array | + * a p5.Color | image to copy + * @example + * <div> + * <code> + * img = createImage(66, 66); + * img.loadPixels(); + * for (i = 0; i < img.width; i++) { + * for (j = 0; j < img.height; j++) { + * img.set(i, j, color(0, 90, 102, i % img.width * 2)); + * } + * } + * img.updatePixels(); + * image(img, 17, 17); + * image(img, 34, 34); + * </code> + * </div> + * + * @alt + * 2 gradated dark turquoise rects fade left. 1 center 1 bottom right of canvas + * + */ +p5.Image.prototype.set = function(x, y, imgOrCol){ + p5.Renderer2D.prototype.set.call(this, x, y, imgOrCol); +}; + +/** + * Resize the image to a new width and height. To make the image scale + * proportionally, use 0 as the value for the wide or high parameter. + * For instance, to make the width of an image 150 pixels, and change + * the height using the same proportion, use resize(150, 0). + * + * @method resize + * @param {Number} width the resized image width + * @param {Number} height the resized image height + * @example + * <div><code> + * var img; + * + * function setup() { + * img = loadImage("assets/rockies.jpg"); + * } + + * function draw() { + * image(img, 0, 0); + * } + * + * function mousePressed() { + * img.resize(50, 100); + * } + * </code></div> + * + * @alt + * image of rocky mountains. zoomed in + * + */ +p5.Image.prototype.resize = function(width, height){ + + // Copy contents to a temporary canvas, resize the original + // and then copy back. + // + // There is a faster approach that involves just one copy and swapping the + // this.canvas reference. We could switch to that approach if (as i think + // is the case) there an expectation that the user would not hold a + // reference to the backing canvas of a p5.Image. But since we do not + // enforce that at the moment, I am leaving in the slower, but safer + // implementation. + + // auto-resize + if (width === 0 && height === 0) { + width = this.canvas.width; + height = this.canvas.height; + } else if (width === 0) { + width = this.canvas.width * height / this.canvas.height; + } else if (height === 0) { + height = this.canvas.height * width / this.canvas.width; + } + + width = Math.floor(width); + height = Math.floor(height); + + var tempCanvas = document.createElement('canvas'); + tempCanvas.width = width; + tempCanvas.height = height; + tempCanvas.getContext('2d').drawImage(this.canvas, + 0, 0, this.canvas.width, this.canvas.height, + 0, 0, tempCanvas.width, tempCanvas.height + ); + + + // Resize the original canvas, which will clear its contents + this.canvas.width = this.width = width; + this.canvas.height = this.height = height; + + //Copy the image back + + this.drawingContext.drawImage(tempCanvas, + 0, 0, width, height, + 0, 0, width, height + ); + + if(this.pixels.length > 0){ + this.loadPixels(); + } +}; + +/** + * Copies a region of pixels from one image to another. If no + * srcImage is specified this is used as the source. If the source + * and destination regions aren't the same size, it will + * automatically resize source pixels to fit the specified + * target region. + * + * @method copy + * @param {p5.Image|undefined} srcImage source image + * @param {Integer} sx X coordinate of the source's upper left corner + * @param {Integer} sy Y coordinate of the source's upper left corner + * @param {Integer} sw source image width + * @param {Integer} sh source image height + * @param {Integer} dx X coordinate of the destination's upper left corner + * @param {Integer} dy Y coordinate of the destination's upper left corner + * @param {Integer} dw destination image width + * @param {Integer} dh destination image height + * @example + * <div><code> + * var photo; + * var bricks; + * var x; + * var y; + * + * function preload() { + * photo = loadImage("assets/rockies.jpg"); + * bricks = loadImage("assets/bricks.jpg"); + * } + * + * function setup() { + * x = bricks.width/2; + * y = bricks.height/2; + * photo.copy(bricks, 0, 0, x, y, 0, 0, x, y); + * image(photo, 0, 0); + * } + * </code></div> + * + * @alt + * image of rocky mountains and smaller image on top of bricks at top left + * + */ +p5.Image.prototype.copy = function () { + p5.prototype.copy.apply(this, arguments); +}; + +/** + * Masks part of an image from displaying by loading another + * image and using it's blue channel as an alpha channel for + * this image. + * + * @method mask + * @param {p5.Image} srcImage source image + * @example + * <div><code> + * var photo, maskImage; + * function preload() { + * photo = loadImage("assets/rockies.jpg"); + * maskImage = loadImage("assets/mask2.png"); + * } + * + * function setup() { + * createCanvas(100, 100); + * photo.mask(maskImage); + * image(photo, 0, 0); + * } + * </code></div> + * + * @alt + * image of rocky mountains with white at right + * + * + * http://blogs.adobe.com/webplatform/2013/01/28/blending-features-in-canvas/ + * + */ +// TODO: - Accept an array of alpha values. +// - Use other channels of an image. p5 uses the +// blue channel (which feels kind of arbitrary). Note: at the +// moment this method does not match native processings original +// functionality exactly. +p5.Image.prototype.mask = function(p5Image) { + if(p5Image === undefined){ + p5Image = this; + } + var currBlend = this.drawingContext.globalCompositeOperation; + + var scaleFactor = 1; + if (p5Image instanceof p5.Renderer) { + scaleFactor = p5Image._pInst._pixelDensity; + } + + var copyArgs = [ + p5Image, + 0, + 0, + scaleFactor*p5Image.width, + scaleFactor*p5Image.height, + 0, + 0, + this.width, + this.height + ]; + + this.drawingContext.globalCompositeOperation = 'destination-in'; + p5.Image.prototype.copy.apply(this, copyArgs); + this.drawingContext.globalCompositeOperation = currBlend; +}; + +/** + * Applies an image filter to a p5.Image + * + * @method filter + * @param {String} operation one of threshold, gray, invert, posterize and + * opaque see Filters.js for docs on each available + * filter + * @param {Number|undefined} value + * @example + * <div><code> + * var photo1; + * var photo2; + * + * function preload() { + * photo1 = loadImage("assets/rockies.jpg"); + * photo2 = loadImage("assets/rockies.jpg"); + * } + * + * function setup() { + * photo2.filter("gray"); + * image(photo1, 0, 0); + * image(photo2, width/2, 0); + * } + * </code></div> + * + * @alt + * 2 images of rocky mountains left one in color, right in black and white + * + */ +p5.Image.prototype.filter = function(operation, value) { + Filters.apply(this.canvas, Filters[operation.toLowerCase()], value); +}; + +/** + * Copies a region of pixels from one image to another, using a specified + * blend mode to do the operation. + * + * @method blend + * @param {p5.Image|undefined} srcImage source image + * @param {Integer} sx X coordinate of the source's upper left corner + * @param {Integer} sy Y coordinate of the source's upper left corner + * @param {Integer} sw source image width + * @param {Integer} sh source image height + * @param {Integer} dx X coordinate of the destination's upper left corner + * @param {Integer} dy Y coordinate of the destination's upper left corner + * @param {Integer} dw destination image width + * @param {Integer} dh destination image height + * @param {Integer} blendMode the blend mode + * + * Available blend modes are: normal | multiply | screen | overlay | + * darken | lighten | color-dodge | color-burn | hard-light | + * soft-light | difference | exclusion | hue | saturation | + * color | luminosity + * + * + * http://blogs.adobe.com/webplatform/2013/01/28/blending-features-in-canvas/ + * @example + * <div><code> + * var mountains; + * var bricks; + * + * function preload() { + * mountains = loadImage("assets/rockies.jpg"); + * bricks = loadImage("assets/bricks_third.jpg"); + * } + * + * function setup() { + * mountains.blend(bricks, 0, 0, 33, 100, 67, 0, 33, 100, ADD); + * image(mountains, 0, 0); + * image(bricks, 0, 0); + * } + * </code></div> + * <div><code> + * var mountains; + * var bricks; + * + * function preload() { + * mountains = loadImage("assets/rockies.jpg"); + * bricks = loadImage("assets/bricks_third.jpg"); + * } + * + * function setup() { + * mountains.blend(bricks, 0, 0, 33, 100, 67, 0, 33, 100, DARKEST); + * image(mountains, 0, 0); + * image(bricks, 0, 0); + * } + * </code></div> + * <div><code> + * var mountains; + * var bricks; + * + * function preload() { + * mountains = loadImage("assets/rockies.jpg"); + * bricks = loadImage("assets/bricks_third.jpg"); + * } + * + * function setup() { + * mountains.blend(bricks, 0, 0, 33, 100, 67, 0, 33, 100, LIGHTEST); + * image(mountains, 0, 0); + * image(bricks, 0, 0); + * } + * </code></div> + * + * @alt + * image of rocky mountains. Brick images on left and right. Right overexposed + * image of rockies. Brickwall images on left and right. Right mortar transparent + * image of rockies. Brickwall images on left and right. Right translucent + * + */ +p5.Image.prototype.blend = function() { + p5.prototype.blend.apply(this, arguments); +}; + +/** + * Saves the image to a file and force the browser to download it. + * Accepts two strings for filename and file extension + * Supports png (default) and jpg. + * + * @method save + * @param {String} filename give your file a name + * @param {String} extension 'png' or 'jpg' + * @example + * <div><code> + * var photo; + * + * function preload() { + * photo = loadImage("assets/rockies.jpg"); + * } + * + * function draw() { + * image(photo, 0, 0); + * } + * + * function keyTyped() { + * if (key == 's') { + * photo.save("photo", "png"); + * } + * } + * </code></div> + * + * @alt + * image of rocky mountains. + * + */ +p5.Image.prototype.save = function(filename, extension) { + var mimeType; + if (!extension) { + extension = 'png'; + mimeType = 'image/png'; + } + else { + // en.wikipedia.org/wiki/Comparison_of_web_browsers#Image_format_support + switch(extension.toLowerCase()){ + case 'png': + mimeType = 'image/png'; + break; + case 'jpeg': + mimeType = 'image/jpeg'; + break; + case 'jpg': + mimeType = 'image/jpeg'; + break; + default: + mimeType = 'image/png'; + break; + } + } + var downloadMime = 'image/octet-stream'; + var imageData = this.canvas.toDataURL(mimeType); + imageData = imageData.replace(mimeType, downloadMime); + + //Make the browser download the file + p5.prototype.downloadFile(imageData, filename, extension); +}; + +module.exports = p5.Image; +},{"../core/core":37,"./filters":54}],58:[function(_dereq_,module,exports){ +/** + * @module Image + * @submodule Pixels + * @for p5 + * @requires core + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); +var Filters = _dereq_('./filters'); +_dereq_('../color/p5.Color'); + +/** + * <a href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference + * /Global_Objects/Uint8ClampedArray' target='_blank'>Uint8ClampedArray</a> + * containing the values for all the pixels in the display window. + * These values are numbers. This array is the size (include an appropriate + * factor for pixelDensity) of the display window x4, + * representing the R, G, B, A values in order for each pixel, moving from + * left to right across each row, then down each column. Retina and other + * high denisty displays will have more pixels[] (by a factor of + * pixelDensity^2). + * For example, if the image is 100x100 pixels, there will be 40,000. On a + * retina display, there will be 160,000. + * <br><br> + * The first four values (indices 0-3) in the array will be the R, G, B, A + * values of the pixel at (0, 0). The second four values (indices 4-7) will + * contain the R, G, B, A values of the pixel at (1, 0). More generally, to + * set values for a pixel at (x, y): + * <code><pre> + * var d = pixelDensity; + * for (var i = 0; i < d; i++) { + * for (var j = 0; j < d; j++) { + * // loop over + * idx = 4 * ((y * d + j) * width * d + (x * d + i)); + * pixels[idx] = r; + * pixels[idx+1] = g; + * pixels[idx+2] = b; + * pixels[idx+3] = a; + * } + * } + * </pre></code> + * + * <p>While the above method is complex, it is flexible enough to work with + * any pixelDensity. Note that set() will automatically take care of + * setting all the appropriate values in pixels[] for a given (x, y) at + * any pixelDensity, but the performance may not be as fast when lots of + * modifications are made to the pixel array. + * <br><br> + * Before accessing this array, the data must loaded with the loadPixels() + * function. After the array data has been modified, the updatePixels() + * function must be run to update the changes. + * <br><br> + * Note that this is not a standard javascript array. This means that + * standard javascript functions such as <code>slice()</code> or + * <code>arrayCopy()</code> do not + * work.</p> + * + * @property pixels[] + * @example + * <div> + * <code> + * var pink = color(255, 102, 204); + * loadPixels(); + * var d = pixelDensity(); + * var halfImage = 4 * (width * d) * (height/2 * d); + * for (var i = 0; i < halfImage; i+=4) { + * pixels[i] = red(pink); + * pixels[i+1] = green(pink); + * pixels[i+2] = blue(pink); + * pixels[i+3] = alpha(pink); + * } + * updatePixels(); + * </code> + * </div> + * + * @alt + * top half of canvas pink, bottom grey + * + */ +p5.prototype.pixels = []; + +/** + * Copies a region of pixels from one image to another, using a specified + * blend mode to do the operation.<br><br> + * Available blend modes are: BLEND | DARKEST | LIGHTEST | DIFFERENCE | + * MULTIPLY| EXCLUSION | SCREEN | REPLACE | OVERLAY | HARD_LIGHT | + * SOFT_LIGHT | DODGE | BURN | ADD | NORMAL + * + * + * @method blend + * @param {p5.Image|undefined} srcImage source image + * @param {Integer} sx X coordinate of the source's upper left corner + * @param {Integer} sy Y coordinate of the source's upper left corner + * @param {Integer} sw source image width + * @param {Integer} sh source image height + * @param {Integer} dx X coordinate of the destination's upper left corner + * @param {Integer} dy Y coordinate of the destination's upper left corner + * @param {Integer} dw destination image width + * @param {Integer} dh destination image height + * @param {Integer} blendMode the blend mode + * + * @example + * <div><code> + * var img0; + * var img1; + * + * function preload() { + * img0 = loadImage("assets/rockies.jpg"); + * img1 = loadImage("assets/bricks_third.jpg"); + * } + * + * function setup() { + * background(img0); + * image(img1, 0, 0); + * blend(img1, 0, 0, 33, 100, 67, 0, 33, 100, LIGHTEST); + * } + * </code></div> + * <div><code> + * var img0; + * var img1; + * + * function preload() { + * img0 = loadImage("assets/rockies.jpg"); + * img1 = loadImage("assets/bricks_third.jpg"); + * } + * + * function setup() { + * background(img0); + * image(img1, 0, 0); + * blend(img1, 0, 0, 33, 100, 67, 0, 33, 100, DARKEST); + * } + * </code></div> + * <div><code> + * var img0; + * var img1; + * + * function preload() { + * img0 = loadImage("assets/rockies.jpg"); + * img1 = loadImage("assets/bricks_third.jpg"); + * } + * + * function setup() { + * background(img0); + * image(img1, 0, 0); + * blend(img1, 0, 0, 33, 100, 67, 0, 33, 100, ADD); + * } + * </code></div> + * + * @alt + * image of rocky mountains. Brick images on left and right. Right overexposed + * image of rockies. Brickwall images on left and right. Right mortar transparent + * image of rockies. Brickwall images on left and right. Right translucent + * + * + */ +p5.prototype.blend = function() { + if (this._renderer) { + this._renderer.blend.apply(this._renderer, arguments); + } else { + p5.Renderer2D.prototype.blend.apply(this, arguments); + } +}; + +/** + * Copies a region of the canvas to another region of the canvas + * and copies a region of pixels from an image used as the srcImg parameter + * into the canvas srcImage is specified this is used as the source. If + * the source and destination regions aren't the same size, it will + * automatically resize source pixels to fit the specified + * target region. + * + * @method copy + * @param {p5.Image|undefined} srcImage source image + * @param {Integer} sx X coordinate of the source's upper left corner + * @param {Integer} sy Y coordinate of the source's upper left corner + * @param {Integer} sw source image width + * @param {Integer} sh source image height + * @param {Integer} dx X coordinate of the destination's upper left corner + * @param {Integer} dy Y coordinate of the destination's upper left corner + * @param {Integer} dw destination image width + * @param {Integer} dh destination image height + * + * @example + * <div><code> + * var img; + * + * function preload() { + * img = loadImage("assets/rockies.jpg"); + * } + * + * function setup() { + * background(img); + * copy(img, 7, 22, 10, 10, 35, 25, 50, 50); + * stroke(255); + * noFill(); + * // Rectangle shows area being copied + * rect(7, 22, 10, 10); + * } + * </code></div> + * + * @alt + * image of rocky mountains. Brick images on left and right. Right overexposed + * image of rockies. Brickwall images on left and right. Right mortar transparent + * image of rockies. Brickwall images on left and right. Right translucent + * + */ +p5.prototype.copy = function () { + p5.Renderer2D._copyHelper.apply(this, arguments); +}; + +/** + * Applies a filter to the canvas. + * <br><br> + * + * The presets options are: + * <br><br> + * + * THRESHOLD + * Converts the image to black and white pixels depending if they are above or + * below the threshold defined by the level parameter. The parameter must be + * between 0.0 (black) and 1.0 (white). If no level is specified, 0.5 is used. + * <br><br> + * + * GRAY + * Converts any colors in the image to grayscale equivalents. No parameter + * is used. + * <br><br> + * + * OPAQUE + * Sets the alpha channel to entirely opaque. No parameter is used. + * <br><br> + * + * INVERT + * Sets each pixel to its inverse value. No parameter is used. + * <br><br> + * + * POSTERIZE + * Limits each channel of the image to the number of colors specified as the + * parameter. The parameter can be set to values between 2 and 255, but + * results are most noticeable in the lower ranges. + * <br><br> + * + * BLUR + * Executes a Guassian blur with the level parameter specifying the extent + * of the blurring. If no parameter is used, the blur is equivalent to + * Guassian blur of radius 1. Larger values increase the blur. + * <br><br> + * + * ERODE + * Reduces the light areas. No parameter is used. + * <br><br> + * + * DILATE + * Increases the light areas. No parameter is used. + * + * @method filter + * @param {Constant} filterType + * @param {Number} filterParam an optional parameter unique + * to each filter, see above + * + * + * @example + * <div> + * <code> + * var img; + * function preload() { + * img = loadImage("assets/bricks.jpg"); + * } + * function setup() { + * image(img, 0, 0); + * filter(THRESHOLD); + * } + * </code> + * </div> + * + * <div> + * <code> + * var img; + * function preload() { + * img = loadImage("assets/bricks.jpg"); + * } + * function setup() { + * image(img, 0, 0); + * filter(GRAY); + * } + * </code> + * </div> + * + * <div> + * <code> + * var img; + * function preload() { + * img = loadImage("assets/bricks.jpg"); + * } + * function setup() { + * image(img, 0, 0); + * filter(OPAQUE); + * } + * </code> + * </div> + * + * <div> + * <code> + * var img; + * function preload() { + * img = loadImage("assets/bricks.jpg"); + * } + * function setup() { + * image(img, 0, 0); + * filter(INVERT); + * } + * </code> + * </div> + * + * <div> + * <code> + * var img; + * function preload() { + * img = loadImage("assets/bricks.jpg"); + * } + * function setup() { + * image(img, 0, 0); + * filter(POSTERIZE,3); + * } + * </code> + * </div> + * + * <div> + * <code> + * var img; + * function preload() { + * img = loadImage("assets/bricks.jpg"); + * } + * function setup() { + * image(img, 0, 0); + * filter(DILATE); + * } + * </code> + * </div> + * + * <div> + * <code> + * var img; + * function preload() { + * img = loadImage("assets/bricks.jpg"); + * } + * function setup() { + * image(img, 0, 0); + * filter(BLUR,3); + * } + * </code> + * </div> + * + * <div> + * <code> + * var img; + * function preload() { + * img = loadImage("assets/bricks.jpg"); + * } + * function setup() { + * image(img, 0, 0); + * filter(ERODE); + * } + * </code> + * </div> + * + * @alt + * black and white image of a brick wall. + * greyscale image of a brickwall + * image of a brickwall + * jade colored image of a brickwall + * red and pink image of a brickwall + * image of a brickwall + * blurry image of a brickwall + * image of a brickwall + * image of a brickwall with less detail + * + */ +p5.prototype.filter = function(operation, value) { + Filters.apply(this.canvas, Filters[operation.toLowerCase()], value); +}; + +/** + * Returns an array of [R,G,B,A] values for any pixel or grabs a section of + * an image. If no parameters are specified, the entire image is returned. + * Use the x and y parameters to get the value of one pixel. Get a section of + * the display window by specifying additional w and h parameters. When + * getting an image, the x and y parameters define the coordinates for the + * upper-left corner of the image, regardless of the current imageMode(). + * <br><br> + * If the pixel requested is outside of the image window, [0,0,0,255] is + * returned. To get the numbers scaled according to the current color ranges + * and taking into account colorMode, use getColor instead of get. + * <br><br> + * Getting the color of a single pixel with get(x, y) is easy, but not as fast + * as grabbing the data directly from pixels[]. The equivalent statement to + * get(x, y) using pixels[] with pixel density d is + * <code> + * var off = (y * width + x) * d * 4; + * [pixels[off], + * pixels[off+1], + * pixels[off+2], + * pixels[off+3]]</code> + * <br><br> + * See the reference for pixels[] for more information. + * + * @method get + * @param {Number} [x] x-coordinate of the pixel + * @param {Number} [y] y-coordinate of the pixel + * @param {Number} [w] width + * @param {Number} [h] height + * @return {Array|p5.Image} values of pixel at x,y in array format + * [R, G, B, A] or p5.Image + * @example + * <div> + * <code> + * var img; + * function preload() { + * img = loadImage("assets/rockies.jpg"); + * } + * function setup() { + * image(img, 0, 0); + * var c = get(); + * image(c, width/2, 0); + * } + * </code> + * </div> + * + * <div> + * <code> + * var img; + * function preload() { + * img = loadImage("assets/rockies.jpg"); + * } + * function setup() { + * image(img, 0, 0); + * var c = get(50, 90); + * fill(c); + * noStroke(); + * rect(25, 25, 50, 50); + * } + * </code> + * </div> + * + * @alt + * 2 images of the rocky mountains, side-by-side + * Image of the rocky mountains with 50x50 green rect in center of canvas + * + */ +p5.prototype.get = function(x, y, w, h){ + return this._renderer.get(x, y, w, h); +}; + +/** + * Loads the pixel data for the display window into the pixels[] array. This + * function must always be called before reading from or writing to pixels[]. + * + * @method loadPixels + * @example + * <div> + * <code> + * var img; + * function preload() { + * img = loadImage("assets/rockies.jpg"); + * } + * + * function setup() { + * image(img, 0, 0); + * var d = pixelDensity(); + * var halfImage = 4 * (img.width * d) * + (img.height/2 * d); + * loadPixels(); + * for (var i = 0; i < halfImage; i++) { + * pixels[i+halfImage] = pixels[i]; + * } + * updatePixels(); + * } + * </code> + * </div> + * + * @alt + * two images of the rocky mountains. one on top, one on bottom of canvas. + * + */ +p5.prototype.loadPixels = function() { + this._renderer.loadPixels(); +}; + +/** + * <p>Changes the color of any pixel, or writes an image directly to the + * display window.</p> + * <p>The x and y parameters specify the pixel to change and the c parameter + * specifies the color value. This can be a p5.Color object, or [R, G, B, A] + * pixel array. It can also be a single grayscale value. + * When setting an image, the x and y parameters define the coordinates for + * the upper-left corner of the image, regardless of the current imageMode(). + * </p> + * <p> + * After using set(), you must call updatePixels() for your changes to + * appear. This should be called once all pixels have been set. + * </p> + * <p>Setting the color of a single pixel with set(x, y) is easy, but not as + * fast as putting the data directly into pixels[]. Setting the pixels[] + * values directly may be complicated when working with a retina display, + * but will perform better when lots of pixels need to be set directly on + * every loop.</p> + * <p>See the reference for pixels[] for more information.</p> + * + * @method set + * @param {Number} x x-coordinate of the pixel + * @param {Number} y y-coordinate of the pixel + * @param {Number|Array|Object} c insert a grayscale value | a pixel array | + * a p5.Color object | a p5.Image to copy + * @example + * <div> + * <code> + * var black = color(0); + * set(30, 20, black); + * set(85, 20, black); + * set(85, 75, black); + * set(30, 75, black); + * updatePixels(); + * </code> + * </div> + * + * <div> + * <code> + * for (var i = 30; i < width-15; i++) { + * for (var j = 20; j < height-25; j++) { + * var c = color(204-j, 153-i, 0); + * set(i, j, c); + * } + * } + * updatePixels(); + * </code> + * </div> + * + * <div> + * <code> + * var img; + * function preload() { + * img = loadImage("assets/rockies.jpg"); + * } + * + * function setup() { + * set(0, 0, img); + * updatePixels(); + * line(0, 0, width, height); + * line(0, height, width, 0); + * } + * </code> + * </div> + * + * @alt + * 4 black points in the shape of a square middle-right of canvas. + * square with orangey-brown gradient lightening at bottom right. + * image of the rocky mountains. with lines like an 'x' through the center. + */ +p5.prototype.set = function (x, y, imgOrCol) { + this._renderer.set(x, y, imgOrCol); +}; +/** + * Updates the display window with the data in the pixels[] array. + * Use in conjunction with loadPixels(). If you're only reading pixels from + * the array, there's no need to call updatePixels() — updating is only + * necessary to apply changes. updatePixels() should be called anytime the + * pixels array is manipulated or set() is called. + * + * @method updatePixels + * @param {Number} [x] x-coordinate of the upper-left corner of region + * to update + * @param {Number} [y] y-coordinate of the upper-left corner of region + * to update + * @param {Number} [w] width of region to update + * @param {Number} [w] height of region to update + * @example + * <div> + * <code> + * var img; + * function preload() { + * img = loadImage("assets/rockies.jpg"); + * } + * + * function setup() { + * image(img, 0, 0); + * var halfImage = 4 * (img.width * pixelDensity()) * + * (img.height * pixelDensity()/2); + * loadPixels(); + * for (var i = 0; i < halfImage; i++) { + * pixels[i+halfImage] = pixels[i]; + * } + * updatePixels(); + * } + * </code> + * </div> + * @alt + * two images of the rocky mountains. one on top, one on bottom of canvas. + */ +p5.prototype.updatePixels = function (x, y, w, h) { + // graceful fail - if loadPixels() or set() has not been called, pixel + // array will be empty, ignore call to updatePixels() + if (this.pixels.length === 0) { + return; + } + this._renderer.updatePixels(x, y, w, h); +}; + +module.exports = p5; + +},{"../color/p5.Color":31,"../core/core":37,"./filters":54}],59:[function(_dereq_,module,exports){ +/** + * @module IO + * @submodule Input + * @for p5 + * @requires core + * @requires reqwest + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); +var reqwest = _dereq_('reqwest'); +var opentype = _dereq_('opentype.js'); +_dereq_('../core/error_helpers'); + +/** + * Checks if we are in preload and returns the last arg which will be the + * _decrementPreload function if called from a loadX() function. Should + * only be used in loadX() functions. + * @private + */ +p5._getDecrementPreload = function () { + var decrementPreload = arguments[arguments.length - 1]; + + // when in preload decrementPreload will always be the last arg as it is set + // with args.push() before invocation in _wrapPreload + if ((window.preload || (this && this.preload)) && + typeof decrementPreload === 'function') { + return decrementPreload; + } else { + return null; + } +}; + +/** + * Loads an opentype font file (.otf, .ttf) from a file or a URL, + * and returns a PFont Object. This method is asynchronous, + * meaning it may not finish before the next line in your sketch + * is executed. + * <br><br> + * The path to the font should be relative to the HTML file + * that links in your sketch. Loading an from a URL or other + * remote location may be blocked due to your browser's built-in + * security. + * + * @method loadFont + * @param {String} path name of the file or url to load + * @param {Function} [callback] function to be executed after + * loadFont() + * completes + * @return {Object} p5.Font object + * @example + * + * <p>Calling loadFont() inside preload() guarantees that the load + * operation will have completed before setup() and draw() are called.</p> + * + * <div><code> + * var myFont; + * function preload() { + * myFont = loadFont('assets/AvenirNextLTPro-Demi.otf'); + * } + * + * function setup() { + * fill('#ED225D'); + * textFont(myFont); + * textSize(36); + * text('p5*js', 10, 50); + * } + * </code></div> + * + * Outside of preload(), you may supply a callback function to handle the + * object: + * + * <div><code> + * function setup() { + * loadFont('assets/AvenirNextLTPro-Demi.otf', drawText); + * } + * + * function drawText(font) { + * fill('#ED225D'); + * textFont(font, 36); + * text('p5*js', 10, 50); + * } + * + * </code></div> + * + * <p>You can also use the string name of the font to style other HTML + * elements.</p> + * + * <div><code> + * var myFont; + * + * function preload() { + * myFont = loadFont('assets/Avenir.otf'); + * } + * + * function setup() { + * var myDiv = createDiv('hello there'); + * myDiv.style('font-family', 'Avenir'); + * } + * </code></div> + * + * @alt + * p5*js in p5's theme dark pink + * p5*js in p5's theme dark pink + * + */ +p5.prototype.loadFont = function (path, onSuccess, onError) { + + var p5Font = new p5.Font(this); + var decrementPreload = p5._getDecrementPreload.apply(this, arguments); + + opentype.load(path, function (err, font) { + + if (err) { + + if ((typeof onError !== 'undefined') && (onError !== decrementPreload)) { + return onError(err); + } + p5._friendlyFileLoadError(4, path); + console.error(err, path); + return; + } + + p5Font.font = font; + + if (typeof onSuccess !== 'undefined') { + onSuccess(p5Font); + } + + if (decrementPreload && (onSuccess !== decrementPreload)) { + decrementPreload(); + } + + // check that we have an acceptable font type + var validFontTypes = [ 'ttf', 'otf', 'woff', 'woff2' ], + fileNoPath = path.split('\\').pop().split('/').pop(), + lastDotIdx = fileNoPath.lastIndexOf('.'), fontFamily, newStyle, + fileExt = lastDotIdx < 1 ? null : fileNoPath.substr(lastDotIdx + 1); + + // if so, add it to the DOM (name-only) for use with p5.dom + if (validFontTypes.indexOf(fileExt) > -1) { + + fontFamily = fileNoPath.substr(0, lastDotIdx); + newStyle = document.createElement('style'); + newStyle.appendChild(document.createTextNode('\n@font-face {' + + '\nfont-family: ' + fontFamily + ';\nsrc: url(' + path + ');\n}\n')); + document.head.appendChild(newStyle); + } + + }); + + return p5Font; +}; + +//BufferedReader +p5.prototype.createInput = function () { + // TODO + throw 'not yet implemented'; +}; + +p5.prototype.createReader = function () { + // TODO + throw 'not yet implemented'; +}; + +p5.prototype.loadBytes = function () { + // TODO + throw 'not yet implemented'; +}; + +/** + * Loads a JSON file from a file or a URL, and returns an Object or Array. + * This method is asynchronous, meaning it may not finish before the next + * line in your sketch is executed. + * + * @method loadJSON + * @param {String} path name of the file or url to load + * @param {Function} [callback] function to be executed after + * loadJSON() completes, data is passed + * in as first argument + * @param {Function} [errorCallback] function to be executed if + * there is an error, response is passed + * in as first argument + * @param {String} [datatype] "json" or "jsonp" + * @return {Object|Array} JSON data + * @example + * + * <p>Calling loadJSON() inside preload() guarantees to complete the + * operation before setup() and draw() are called.</p> + * + * <div><code> + * var weather; + * function preload() { + * var url = 'http://api.openweathermap.org/data/2.5/weather?q=London,UK'+ + * '&APPID=7bbbb47522848e8b9c26ba35c226c734'; + * weather = loadJSON(url); + * } + * + * function setup() { + * noLoop(); + * } + * + * function draw() { + * background(200); + * // get the humidity value out of the loaded JSON + * var humidity = weather.main.humidity; + * fill(0, humidity); // use the humidity value to set the alpha + * ellipse(width/2, height/2, 50, 50); + * } + * </code></div> + * + * + * <p>Outside of preload(), you may supply a callback function to handle the + * object:</p> + * <div><code> + * function setup() { + * noLoop(); + * var url = 'http://api.openweathermap.org/data/2.5/weather?q=NewYork'+ + * '&APPID=7bbbb47522848e8b9c26ba35c226c734'; + * loadJSON(url, drawWeather); + * } + * + * function draw() { + * background(200); + * } + * + * function drawWeather(weather) { + * // get the humidity value out of the loaded JSON + * var humidity = weather.main.humidity; + * fill(0, humidity); // use the humidity value to set the alpha + * ellipse(width/2, height/2, 50, 50); + * } + * </code></div> + * + * @alt + * 50x50 ellipse that changes from black to white depending on the current humidity + * 50x50 ellipse that changes from black to white depending on the current humidity + * + */ +p5.prototype.loadJSON = function () { + var path = arguments[0]; + var callback = arguments[1]; + var errorCallback; + var decrementPreload = p5._getDecrementPreload.apply(this, arguments); + + var ret = {}; // object needed for preload + // assume jsonp for URLs + var t = 'json'; //= path.indexOf('http') === -1 ? 'json' : 'jsonp'; + + // check for explicit data type argument + for (var i = 2; i < arguments.length; i++) { + var arg = arguments[i]; + if (typeof arg === 'string') { + if (arg === 'jsonp' || arg === 'json') { + t = arg; + } + } else if (typeof arg === 'function') { + errorCallback = arg; + } + } + + reqwest({ + url: path, + type: t, + crossOrigin: true, + error: function (resp) { + // pass to error callback if defined + if (errorCallback) { + errorCallback(resp); + } else { // otherwise log error msg + console.log(resp.statusText); + } + }, + success: function (resp) { + for (var k in resp) { + ret[k] = resp[k]; + } + if (typeof callback !== 'undefined') { + callback(resp); + } + if (decrementPreload && (callback !== decrementPreload)) { + decrementPreload(); + } + } + }); + + return ret; +}; + +/** + * Reads the contents of a file and creates a String array of its individual + * lines. If the name of the file is used as the parameter, as in the above + * example, the file must be located in the sketch directory/folder. + * <br><br> + * Alternatively, the file maybe be loaded from anywhere on the local + * computer using an absolute path (something that starts with / on Unix and + * Linux, or a drive letter on Windows), or the filename parameter can be a + * URL for a file found on a network. + * <br><br> + * This method is asynchronous, meaning it may not finish before the next + * line in your sketch is executed. + * + * @method loadStrings + * @param {String} filename name of the file or url to load + * @param {Function} [callback] function to be executed after loadStrings() + * completes, Array is passed in as first + * argument + * @param {Function} [errorCallback] function to be executed if + * there is an error, response is passed + * in as first argument + * @return {Array} Array of Strings + * @example + * + * <p>Calling loadStrings() inside preload() guarantees to complete the + * operation before setup() and draw() are called.</p> + * + * <div><code> + * var result; + * function preload() { + * result = loadStrings('assets/test.txt'); + * } + + * function setup() { + * background(200); + * var ind = floor(random(result.length)); + * text(result[ind], 10, 10, 80, 80); + * } + * </code></div> + * + * <p>Outside of preload(), you may supply a callback function to handle the + * object:</p> + * + * <div><code> + * function setup() { + * loadStrings('assets/test.txt', pickString); + * } + * + * function pickString(result) { + * background(200); + * var ind = floor(random(result.length)); + * text(result[ind], 10, 10, 80, 80); + * } + * </code></div> + * + * @alt + * randomly generated text from a file, for example "i smell like butter" + * randomly generated text from a file, for example "i have three feet" + * + */ +p5.prototype.loadStrings = function (path, callback, errorCallback) { + var ret = []; + var req = new XMLHttpRequest(); + var decrementPreload = p5._getDecrementPreload.apply(this, arguments); + + req.addEventListener('error', function (resp) { + if (errorCallback) { + errorCallback(resp); + } else { + console.log(resp.responseText); + } + }); + + req.open('GET', path, true); + req.onreadystatechange = function () { + if (req.readyState === 4) { + if (req.status === 200) { + var arr = req.responseText.match(/[^\r\n]+/g); + for (var k in arr) { + ret[k] = arr[k]; + } + if (typeof callback !== 'undefined') { + callback(ret); + } + if (decrementPreload && (callback !== decrementPreload)) { + decrementPreload(); + } + } else { + if (errorCallback) { + errorCallback(req); + } else { + console.log(req.statusText); + } + //p5._friendlyFileLoadError(3, path); + } + } + }; + req.send(null); + return ret; +}; + +/** + * <p>Reads the contents of a file or URL and creates a p5.Table object with + * its values. If a file is specified, it must be located in the sketch's + * "data" folder. The filename parameter can also be a URL to a file found + * online. By default, the file is assumed to be comma-separated (in CSV + * format). Table only looks for a header row if the 'header' option is + * included.</p> + * + * <p>Possible options include: + * <ul> + * <li>csv - parse the table as comma-separated values</li> + * <li>tsv - parse the table as tab-separated values</li> + * <li>header - this table has a header (title) row</li> + * </ul> + * </p> + * + * <p>When passing in multiple options, pass them in as separate parameters, + * seperated by commas. For example: + * <br><br> + * <code> + * loadTable("my_csv_file.csv", "csv", "header") + * </code> + * </p> + * + * <p> All files loaded and saved use UTF-8 encoding.</p> + * + * <p>This method is asynchronous, meaning it may not finish before the next + * line in your sketch is executed. Calling loadTable() inside preload() + * guarantees to complete the operation before setup() and draw() are called. + * <p>Outside of preload(), you may supply a callback function to handle the + * object:</p> + * </p> + * + * @method loadTable + * @param {String} filename name of the file or URL to load + * @param {String|Strings} [options] "header" "csv" "tsv" + * @param {Function} [callback] function to be executed after + * loadTable() completes. On success, the + * Table object is passed in as the + * first argument; otherwise, false + * is passed in. + * @return {Object} Table object containing data + * + * @example + * <div class="norender"> + * <code> + * // Given the following CSV file called "mammals.csv" + * // located in the project's "assets" folder: + * // + * // id,species,name + * // 0,Capra hircus,Goat + * // 1,Panthera pardus,Leopard + * // 2,Equus zebra,Zebra + * + * var table; + * + * function preload() { + * //my table is comma separated value "csv" + * //and has a header specifying the columns labels + * table = loadTable("assets/mammals.csv", "csv", "header"); + * //the file can be remote + * //table = loadTable("http://p5js.org/reference/assets/mammals.csv", + * // "csv", "header"); + * } + * + * function setup() { + * //count the columns + * print(table.getRowCount() + " total rows in table"); + * print(table.getColumnCount() + " total columns in table"); + * + * print(table.getColumn("name")); + * //["Goat", "Leopard", "Zebra"] + * + * //cycle through the table + * for (var r = 0; r < table.getRowCount(); r++) + * for (var c = 0; c < table.getColumnCount(); c++) { + * print(table.getString(r, c)); + * } + * } + * </code> + * </div> + * + * @alt + * randomly generated text from a file, for example "i smell like butter" + * randomly generated text from a file, for example "i have three feet" + * + */ +p5.prototype.loadTable = function (path) { + var callback = null; + var options = []; + var header = false; + var sep = ','; + var separatorSet = false; + var decrementPreload = p5._getDecrementPreload.apply(this, arguments); + + for (var i = 1; i < arguments.length; i++) { + if ((typeof (arguments[i]) === 'function') && + (arguments[i] !== decrementPreload)) { + callback = arguments[i]; + } else if (typeof (arguments[i]) === 'string') { + options.push(arguments[i]); + if (arguments[i] === 'header') { + header = true; + } + if (arguments[i] === 'csv') { + if (separatorSet) { + throw new Error('Cannot set multiple separator types.'); + } else { + sep = ','; + separatorSet = true; + } + } else if (arguments[i] === 'tsv') { + if (separatorSet) { + throw new Error('Cannot set multiple separator types.'); + } else { + sep = '\t'; + separatorSet = true; + } + } + } + } + + var t = new p5.Table(); + reqwest({ + url: path, + crossOrigin: true, + type: 'csv' + }) + .then(function (resp) { + resp = resp.responseText; + + var state = {}; + + // define constants + var PRE_TOKEN = 0, + MID_TOKEN = 1, + POST_TOKEN = 2, + POST_RECORD = 4; + + var QUOTE = '\"', + CR = '\r', + LF = '\n'; + + var records = []; + var offset = 0; + var currentRecord = null; + var currentChar; + + var recordBegin = function () { + state.escaped = false; + currentRecord = []; + tokenBegin(); + }; + + var recordEnd = function () { + state.currentState = POST_RECORD; + records.push(currentRecord); + currentRecord = null; + }; + + var tokenBegin = function () { + state.currentState = PRE_TOKEN; + state.token = ''; + }; + + var tokenEnd = function () { + currentRecord.push(state.token); + tokenBegin(); + }; + + while (true) { + currentChar = resp[offset++]; + + // EOF + if (currentChar == null) { + if (state.escaped) { + throw new Error('Unclosed quote in file.'); + } + if (currentRecord) { + tokenEnd(); + recordEnd(); + break; + } + } + if (currentRecord === null) { + recordBegin(); + } + + // Handle opening quote + if (state.currentState === PRE_TOKEN) { + if (currentChar === QUOTE) { + state.escaped = true; + state.currentState = MID_TOKEN; + continue; + } + state.currentState = MID_TOKEN; + } + + // mid-token and escaped, look for sequences and end quote + if (state.currentState === MID_TOKEN && state.escaped) { + if (currentChar === QUOTE) { + if (resp[offset] === QUOTE) { + state.token += QUOTE; + offset++; + } else { + state.escaped = false; + state.currentState = POST_TOKEN; + } + } else { + state.token += currentChar; + } + continue; + } + + // fall-through: mid-token or post-token, not escaped + if (currentChar === CR) { + if (resp[offset] === LF) { + offset++; + } + tokenEnd(); + recordEnd(); + } else if (currentChar === LF) { + tokenEnd(); + recordEnd(); + } else if (currentChar === sep) { + tokenEnd(); + } else if (state.currentState === MID_TOKEN) { + state.token += currentChar; + } + } + + // set up column names + if (header) { + t.columns = records.shift(); + } else { + for (i = 0; i < records[0].length; i++) { + t.columns[i] = 'null'; + } + } + var row; + for (i = 0; i < records.length; i++) { + //Handles row of 'undefined' at end of some CSVs + if (i === records.length - 1 && records[i].length === 1) { + if (records[i][0] === 'undefined') { + break; + } + } + row = new p5.TableRow(); + row.arr = records[i]; + row.obj = makeObject(records[i], t.columns); + t.addRow(row); + } + if (callback !== null) { + callback(t); + } + if (decrementPreload && (callback !== decrementPreload)) { + decrementPreload(); + } + }) + .fail(function (err, msg) { + p5._friendlyFileLoadError(2, path); + // don't get error callback mixed up with decrementPreload + if ((typeof callback === 'function') && + (callback !== decrementPreload)) { + callback(false); + } + }); + + return t; +}; + +// helper function to turn a row into a JSON object +function makeObject(row, headers) { + var ret = {}; + headers = headers || []; + if (typeof (headers) === 'undefined') { + for (var j = 0; j < row.length; j++) { + headers[j.toString()] = j; + } + } + for (var i = 0; i < headers.length; i++) { + var key = headers[i]; + var val = row[i]; + ret[key] = val; + } + return ret; +} + +/*global parseXML */ +p5.prototype.parseXML = function (two) { + var one = new p5.XML(); + var i; + if (two.children.length) { + for ( i = 0; i < two.children.length; i++ ) { + var node = parseXML(two.children[i]); + one.addChild(node); + } + one.setName(two.nodeName); + one._setCont(two.textContent); + one._setAttributes(two); + for (var j = 0; j < one.children.length; j++) { + one.children[j].parent = one; + } + return one; + } + else { + one.setName(two.nodeName); + one._setCont(two.textContent); + one._setAttributes(two); + return one; + } +}; + +/** + * Reads the contents of a file and creates an XML object with its values. + * If the name of the file is used as the parameter, as in the above example, + * the file must be located in the sketch directory/folder. + * + * Alternatively, the file maybe be loaded from anywhere on the local + * computer using an absolute path (something that starts with / on Unix and + * Linux, or a drive letter on Windows), or the filename parameter can be a + * URL for a file found on a network. + * + * This method is asynchronous, meaning it may not finish before the next + * line in your sketch is executed. Calling loadXML() inside preload() + * guarantees to complete the operation before setup() and draw() are called. + * + * <p>Outside of preload(), you may supply a callback function to handle the + * object:</p> + * + * @method loadXML + * @param {String} filename name of the file or URL to load + * @param {Function} [callback] function to be executed after loadXML() + * completes, XML object is passed in as + * first argument + * @param {Function} [errorCallback] function to be executed if + * there is an error, response is passed + * in as first argument + * @return {Object} XML object containing data + */ +p5.prototype.loadXML = function (path, callback, errorCallback) { + var ret = {}; + var decrementPreload = p5._getDecrementPreload.apply(this, arguments); + reqwest({ + url: path, + type: 'xml', + crossOrigin: true, + error: function (resp) { + // pass to error callback if defined + if (errorCallback) { + errorCallback(resp); + } else { // otherwise log error msg + console.log(resp.statusText); + } + //p5._friendlyFileLoadError(1,path); + } + }) + .then(function (resp) { + var xml = parseXML(resp.documentElement); + for(var key in xml) { + ret[key] = xml[key]; + } + if (typeof callback !== 'undefined') { + callback(ret); + } + if (decrementPreload && (callback !== decrementPreload)) { + decrementPreload(); + } + }); + return ret; +}; + +// name clash with window.open +// p5.prototype.open = function() { +// // TODO + +// }; + +p5.prototype.selectFolder = function () { + // TODO + throw 'not yet implemented'; + +}; + +p5.prototype.selectInput = function () { + // TODO + throw 'not yet implemented'; + +}; + +/** + * Method for executing an HTTP GET request. If data type is not specified, + * p5 will try to guess based on the URL, defaulting to text. + * + * @method httpGet + * @param {String} path name of the file or url to load + * @param {Object} [data] param data passed sent with request + * @param {String} [datatype] "json", "jsonp", "xml", or "text" + * @param {Function} [callback] function to be executed after + * httpGet() completes, data is passed in + * as first argument + * @param {Function} [errorCallback] function to be executed if + * there is an error, response is passed + * in as first argument + */ +p5.prototype.httpGet = function () { + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + args.push('GET'); + p5.prototype.httpDo.apply(this, args); +}; + +/** + * Method for executing an HTTP POST request. If data type is not specified, + * p5 will try to guess based on the URL, defaulting to text. + * + * @method httpPost + * @param {String} path name of the file or url to load + * @param {Object} [data] param data passed sent with request + * @param {String} [datatype] "json", "jsonp", "xml", or "text" + * @param {Function} [callback] function to be executed after + * httpGet() completes, data is passed in + * as first argument + * @param {Function} [errorCallback] function to be executed if + * there is an error, response is passed + * in as first argument + */ +p5.prototype.httpPost = function () { + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + args.push('POST'); + p5.prototype.httpDo.apply(this, args); +}; + +/** + * Method for executing an HTTP request. If data type is not specified, + * p5 will try to guess based on the URL, defaulting to text.<br><br> + * You may also pass a single object specifying all parameters for the + * request following the examples inside the reqwest() calls here: + * <a href='https://github.com/ded/reqwest#api'> + * https://github.com/ded/reqwest#api</a> + * + * @method httpDo + * @param {String} path name of the file or url to load + * @param {String} [method] either "GET", "POST", or "PUT", + * defaults to "GET" + * @param {Object} [data] param data passed sent with request + * @param {String} [datatype] "json", "jsonp", "xml", or "text" + * @param {Function} [callback] function to be executed after + * httpGet() completes, data is passed in + * as first argument + * @param {Function} [errorCallback] function to be executed if + * there is an error, response is passed + * in as first argument + */ +p5.prototype.httpDo = function () { + if (typeof arguments[0] === 'object') { + reqwest(arguments[0]); + } else { + var method = 'GET'; + var path = arguments[0]; + var data = {}; + var type = ''; + var callback; + var errorCallback; + + for (var i = 1; i < arguments.length; i++) { + var a = arguments[i]; + if (typeof a === 'string') { + if (a === 'GET' || a === 'POST' || a === 'PUT') { + method = a; + } else { + type = a; + } + } else if (typeof a === 'object') { + data = a; + } else if (typeof a === 'function') { + if (!callback) { + callback = a; + } else { + errorCallback = a; + } + } + } + + // do some sort of smart type checking + if (type === '') { + if (path.indexOf('json') !== -1) { + type = 'json'; + } else if (path.indexOf('xml') !== -1) { + type = 'xml'; + } else { + type = 'text'; + } + } + + reqwest({ + url: path, + method: method, + data: data, + type: type, + crossOrigin: true, + success: function (resp) { + if (typeof callback !== 'undefined') { + if (type === 'text') { + callback(resp.response); + } else { + callback(resp); + } + } + }, + error: function (resp) { + if (errorCallback) { + errorCallback(resp); + } else { + console.log(resp.statusText); + } + } + }); + } +}; + +/** + * @module IO + * @submodule Output + * @for p5 + */ + +window.URL = window.URL || window.webkitURL; + +// private array of p5.PrintWriter objects +p5.prototype._pWriters = []; + +p5.prototype.beginRaw = function () { + // TODO + throw 'not yet implemented'; + +}; + +p5.prototype.beginRecord = function () { + // TODO + throw 'not yet implemented'; + +}; + +p5.prototype.createOutput = function () { + // TODO + + throw 'not yet implemented'; +}; + +p5.prototype.createWriter = function (name, extension) { + var newPW; + // check that it doesn't already exist + for (var i in p5.prototype._pWriters) { + if (p5.prototype._pWriters[i].name === name) { + // if a p5.PrintWriter w/ this name already exists... + // return p5.prototype._pWriters[i]; // return it w/ contents intact. + // or, could return a new, empty one with a unique name: + newPW = new p5.PrintWriter(name + window.millis(), extension); + p5.prototype._pWriters.push(newPW); + return newPW; + } + } + newPW = new p5.PrintWriter(name, extension); + p5.prototype._pWriters.push(newPW); + return newPW; +}; + +p5.prototype.endRaw = function () { + // TODO + + throw 'not yet implemented'; +}; + +p5.prototype.endRecord = function () { + // TODO + throw 'not yet implemented'; + +}; + +p5.PrintWriter = function (filename, extension) { + var self = this; + this.name = filename; + this.content = ''; + this.print = function (data) { + this.content += data; + }; + this.print = function (data) { + this.content += data + '\n'; + }; + this.flush = function () { + this.content = ''; + }; + this.close = function () { + // convert String to Array for the writeFile Blob + var arr = []; + arr.push(this.content); + p5.prototype.writeFile(arr, filename, extension); + // remove from _pWriters array and delete self + for (var i in p5.prototype._pWriters) { + if (p5.prototype._pWriters[i].name === this.name) { + // remove from _pWriters array + p5.prototype._pWriters.splice(i, 1); + } + } + self.flush(); + self = {}; + }; +}; + +p5.prototype.saveBytes = function () { + // TODO + throw 'not yet implemented'; + +}; + +// object, filename, options --> saveJSON, saveStrings, saveTable +// filename, [extension] [canvas] --> saveImage + +/** + * <p>Save an image, text, json, csv, wav, or html. Prompts download to + * the client's computer. <b>Note that it is not recommended to call save() + * within draw if it's looping, as the save() function will open a new save + * dialog every frame.</b></p> + * <p>The default behavior is to save the canvas as an image. You can + * optionally specify a filename. + * For example:</p> + * <pre class='language-javascript'><code> + * save(); + * save('myCanvas.jpg'); // save a specific canvas with a filename + * </code></pre> + * + * <p>Alternately, the first parameter can be a pointer to a canvas + * p5.Element, an Array of Strings, + * an Array of JSON, a JSON object, a p5.Table, a p5.Image, or a + * p5.SoundFile (requires p5.sound). The second parameter is a filename + * (including extension). The third parameter is for options specific + * to this type of object. This method will save a file that fits the + * given paramaters. For example:</p> + * + * <pre class='language-javascript'><code> + * + * save('myCanvas.jpg'); // Saves canvas as an image + * + * var cnv = createCanvas(100, 100); + * save(cnv, 'myCanvas.jpg'); // Saves canvas as an image + * + * var gb = createGraphics(100, 100); + * save(gb, 'myGraphics.jpg'); // Saves p5.Renderer object as an image + * + * save(myTable, 'myTable.html'); // Saves table as html file + * save(myTable, 'myTable.csv',); // Comma Separated Values + * save(myTable, 'myTable.tsv'); // Tab Separated Values + * + * save(myJSON, 'my.json'); // Saves pretty JSON + * save(myJSON, 'my.json', true); // Optimizes JSON filesize + * + * save(img, 'my.png'); // Saves pImage as a png image + * + * save(arrayOfStrings, 'my.txt'); // Saves strings to a text file with line + * // breaks after each item in the array + * </code></pre> + * + * @method save + * @param {[Object|String]} objectOrFilename If filename is provided, will + * save canvas as an image with + * either png or jpg extension + * depending on the filename. + * If object is provided, will + * save depending on the object + * and filename (see examples + * above). + * @param {[String]} filename If an object is provided as the first + * parameter, then the second parameter + * indicates the filename, + * and should include an appropriate + * file extension (see examples above). + * @param {[Boolean/String]} options Additional options depend on + * filetype. For example, when saving JSON, + * <code>true</code> indicates that the + * output will be optimized for filesize, + * rather than readability. + */ +p5.prototype.save = function (object, _filename, _options) { + // parse the arguments and figure out which things we are saving + var args = arguments; + // ================================================= + // OPTION 1: saveCanvas... + + // if no arguments are provided, save canvas + var cnv = this._curElement.elt; + if (args.length === 0) { + p5.prototype.saveCanvas(cnv); + return; + } + // otherwise, parse the arguments + + // if first param is a p5Graphics, then saveCanvas + else if (args[0] instanceof p5.Renderer || + args[0] instanceof p5.Graphics) { + p5.prototype.saveCanvas(args[0].elt, args[1], args[2]); + return; + } + + // if 1st param is String and only one arg, assume it is canvas filename + else if (args.length === 1 && typeof (args[0]) === 'string') { + p5.prototype.saveCanvas(cnv, args[0]); + } + + // ================================================= + // OPTION 2: extension clarifies saveStrings vs. saveJSON + else { + var extension = _checkFileExtension(args[1], args[2])[1]; + switch (extension) { + case 'json': + p5.prototype.saveJSON(args[0], args[1], args[2]); + return; + case 'txt': + p5.prototype.saveStrings(args[0], args[1], args[2]); + return; + // ================================================= + // OPTION 3: decide based on object... + default: + if (args[0] instanceof Array) { + p5.prototype.saveStrings(args[0], args[1], args[2]); + } else if (args[0] instanceof p5.Table) { + p5.prototype.saveTable(args[0], args[1], args[2], args[3]); + } else if (args[0] instanceof p5.Image) { + p5.prototype.saveCanvas(args[0].canvas, args[1]); + } else if (args[0] instanceof p5.SoundFile) { + p5.prototype.saveSound(args[0], args[1], args[2], args[3]); + } + } + } +}; + +/** + * Writes the contents of an Array or a JSON object to a .json file. + * The file saving process and location of the saved file will + * vary between web browsers. + * + * @method saveJSON + * @param {Array|Object} json + * @param {String} filename + * @param {Boolean} [optimize] If true, removes line breaks + * and spaces from the output + * file to optimize filesize + * (but not readability). + * @example + * <div><code> + * var json; + * + * function setup() { + * + * json = {}; // new JSON Object + * + * json.id = 0; + * json.species = 'Panthera leo'; + * json.name = 'Lion'; + * + * // To save, un-comment the line below, then click 'run' + * // saveJSON(json, 'lion.json'); + * } + * + * // Saves the following to a file called "lion.json": + * // { + * // "id": 0, + * // "species": "Panthera leo", + * // "name": "Lion" + * // } + * </div></code> + * + * @alt + * no image displayed + * + */ +p5.prototype.saveJSON = function (json, filename, opt) { + var stringify; + if (opt) { + stringify = JSON.stringify(json); + } else { + stringify = JSON.stringify(json, undefined, 2); + } + console.log(stringify); + this.saveStrings(stringify.split('\n'), filename, 'json'); +}; + +p5.prototype.saveJSONObject = p5.prototype.saveJSON; +p5.prototype.saveJSONArray = p5.prototype.saveJSON; + +p5.prototype.saveStream = function () { + // TODO + throw 'not yet implemented'; + +}; + +/** + * Writes an array of Strings to a text file, one line per String. + * The file saving process and location of the saved file will + * vary between web browsers. + * + * @method saveStrings + * @param {Array} list string array to be written + * @param {String} filename filename for output + * @example + * <div><code> + * var words = 'apple bear cat dog'; + * + * // .split() outputs an Array + * var list = split(words, ' '); + * + * // To save the file, un-comment next line and click 'run' + * // saveStrings(list, 'nouns.txt'); + * + * // Saves the following to a file called 'nouns.txt': + * // + * // apple + * // bear + * // cat + * // dog + * </code></div> + * + * @alt + * no image displayed + * + */ +p5.prototype.saveStrings = function (list, filename, extension) { + var ext = extension || 'txt'; + var pWriter = this.createWriter(filename, ext); + for (var i = 0; i < list.length; i++) { + if (i < list.length - 1) { + pWriter.print(list[i]); + } else { + pWriter.print(list[i]); + } + } + pWriter.close(); + pWriter.flush(); +}; + +p5.prototype.saveXML = function () { + // TODO + throw 'not yet implemented'; + +}; + +p5.prototype.selectOutput = function () { + // TODO + throw 'not yet implemented'; + +}; + +// ======= +// HELPERS +// ======= + +function escapeHelper(content) { + return content + .replace(/&/g, '&') + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +} + +/** + * Writes the contents of a Table object to a file. Defaults to a + * text file with comma-separated-values ('csv') but can also + * use tab separation ('tsv'), or generate an HTML table ('html'). + * The file saving process and location of the saved file will + * vary between web browsers. + * + * @method saveTable + * @param {p5.Table} Table the Table object to save to a file + * @param {String} filename the filename to which the Table should be saved + * @param {String} [options] can be one of "tsv", "csv", or "html" + * @example + * <div><code> + * var table; + * + * function setup() { + * table = new p5.Table(); + * + * table.addColumn('id'); + * table.addColumn('species'); + * table.addColumn('name'); + * + * var newRow = table.addRow(); + * newRow.setNum('id', table.getRowCount() - 1); + * newRow.setString('species', 'Panthera leo'); + * newRow.setString('name', 'Lion'); + * + * // To save, un-comment next line then click 'run' + * // saveTable(table, 'new.csv'); + * } + * + * // Saves the following to a file called 'new.csv': + * // id,species,name + * // 0,Panthera leo,Lion + * </code></div> + * + * @alt + * no image displayed + * + */ +p5.prototype.saveTable = function (table, filename, options) { + var pWriter = this.createWriter(filename, options); + + var header = table.columns; + + var sep = ','; // default to CSV + if (options === 'tsv') { + sep = '\t'; + } + if (options !== 'html') { + // make header if it has values + if (header[0] !== '0') { + for (var h = 0; h < header.length; h++) { + if (h < header.length - 1) { + pWriter.print(header[h] + sep); + } else { + pWriter.print(header[h]); + } + } + } + + // make rows + for (var i = 0; i < table.rows.length; i++) { + var j; + for (j = 0; j < table.rows[i].arr.length; j++) { + if (j < table.rows[i].arr.length - 1) { + pWriter.print(table.rows[i].arr[j] + sep); + } else if (i < table.rows.length - 1) { + pWriter.print(table.rows[i].arr[j]); + } else { + pWriter.print(table.rows[i].arr[j]); // no line break + } + } + } + } + + // otherwise, make HTML + else { + pWriter.print('<html>'); + pWriter.print('<head>'); + var str = ' <meta http-equiv=\"content-type\" content'; + str += '=\"text/html;charset=utf-8\" />'; + pWriter.print(str); + pWriter.print('</head>'); + + pWriter.print('<body>'); + pWriter.print(' <table>'); + + // make header if it has values + if (header[0] !== '0') { + pWriter.print(' <tr>'); + for (var k = 0; k < header.length; k++) { + var e = escapeHelper(header[k]); + pWriter.print(' <td>' + e); + pWriter.print(' </td>'); + } + pWriter.print(' </tr>'); + } + + // make rows + for (var row = 0; row < table.rows.length; row++) { + pWriter.print(' <tr>'); + for (var col = 0; col < table.columns.length; col++) { + var entry = table.rows[row].getString(col); + var htmlEntry = escapeHelper(entry); + pWriter.print(' <td>' + htmlEntry); + pWriter.print(' </td>'); + } + pWriter.print(' </tr>'); + } + pWriter.print(' </table>'); + pWriter.print('</body>'); + pWriter.print('</html>'); + } + // close and flush the pWriter + pWriter.close(); + pWriter.flush(); +}; // end saveTable() + +/** + * Generate a blob of file data as a url to prepare for download. + * Accepts an array of data, a filename, and an extension (optional). + * This is a private function because it does not do any formatting, + * but it is used by saveStrings, saveJSON, saveTable etc. + * + * @param {Array} dataToDownload + * @param {String} filename + * @param {[String]} extension + * @private + */ +p5.prototype.writeFile = function (dataToDownload, filename, extension) { + var type = 'application\/octet-stream'; + if (p5.prototype._isSafari()) { + type = 'text\/plain'; + } + var blob = new Blob(dataToDownload, { + 'type': type + }); + var href = window.URL.createObjectURL(blob); + p5.prototype.downloadFile(href, filename, extension); +}; + +/** + * Forces download. Accepts a url to filedata/blob, a filename, + * and an extension (optional). + * This is a private function because it does not do any formatting, + * but it is used by saveStrings, saveJSON, saveTable etc. + * + * @param {String} href i.e. an href generated by createObjectURL + * @param {[String]} filename + * @param {[String]} extension + */ +p5.prototype.downloadFile = function (href, fName, extension) { + var fx = _checkFileExtension(fName, extension); + var filename = fx[0]; + var ext = fx[1]; + + var a = document.createElement('a'); + a.href = href; + a.download = filename; + + // Firefox requires the link to be added to the DOM before click() + a.onclick = destroyClickedElement; + a.style.display = 'none'; + document.body.appendChild(a); + + // Safari will open this file in the same page as a confusing Blob. + if (p5.prototype._isSafari()) { + var aText = 'Hello, Safari user! To download this file...\n'; + aText += '1. Go to File --> Save As.\n'; + aText += '2. Choose "Page Source" as the Format.\n'; + aText += '3. Name it with this extension: .\"' + ext + '\"'; + alert(aText); + } + a.click(); + href = null; +}; + +/** + * Returns a file extension, or another string + * if the provided parameter has no extension. + * + * @param {String} filename + * @return {Array} [fileName, fileExtension] + * + * @private + */ +function _checkFileExtension(filename, extension) { + if (!extension || extension === true || extension === 'true') { + extension = ''; + } + if (!filename) { + filename = 'untitled'; + } + var ext = ''; + // make sure the file will have a name, see if filename needs extension + if (filename && filename.indexOf('.') > -1) { + ext = filename.split('.').pop(); + } + // append extension if it doesn't exist + if (extension) { + if (ext !== extension) { + ext = extension; + filename = filename + '.' + ext; + } + } + return [filename, ext]; +} +p5.prototype._checkFileExtension = _checkFileExtension; + +/** + * Returns true if the browser is Safari, false if not. + * Safari makes trouble for downloading files. + * + * @return {Boolean} [description] + * @private + */ +p5.prototype._isSafari = function () { + var x = Object.prototype.toString.call(window.HTMLElement); + return x.indexOf('Constructor') > 0; +}; + +/** + * Helper function, a callback for download that deletes + * an invisible anchor element from the DOM once the file + * has been automatically downloaded. + * + * @private + */ +function destroyClickedElement(event) { + document.body.removeChild(event.target); +} + +module.exports = p5; + +},{"../core/core":37,"../core/error_helpers":40,"opentype.js":8,"reqwest":27}],60:[function(_dereq_,module,exports){ +/** + * @module IO + * @submodule Table + * @requires core + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); + + +/** + * Table Options + * <p>Generic class for handling tabular data, typically from a + * CSV, TSV, or other sort of spreadsheet file.</p> + * <p>CSV files are + * <a href="http://en.wikipedia.org/wiki/Comma-separated_values"> + * comma separated values</a>, often with the data in quotes. TSV + * files use tabs as separators, and usually don't bother with the + * quotes.</p> + * <p>File names should end with .csv if they're comma separated.</p> + * <p>A rough "spec" for CSV can be found + * <a href="http://tools.ietf.org/html/rfc4180">here</a>.</p> + * <p>To load files, use the loadTable method.</p> + * <p>To save tables to your computer, use the save method + * or the saveTable method.</p> + * + * Possible options include: + * <ul> + * <li>csv - parse the table as comma-separated values + * <li>tsv - parse the table as tab-separated values + * <li>header - this table has a header (title) row + * </ul> + */ + +/** + * Table objects store data with multiple rows and columns, much + * like in a traditional spreadsheet. Tables can be generated from + * scratch, dynamically, or using data from an existing file. + * + * @class p5.Table + * @constructor + * @param {Array} [rows] An array of p5.TableRow objects + * @return {p5.Table} p5.Table generated + */ +p5.Table = function (rows) { + /** + * @property columns + * @type {Array} + */ + this.columns = []; + + /** + * @property rows + * @type {Array} + */ + this.rows = []; +}; + +/** + * Use addRow() to add a new row of data to a p5.Table object. By default, + * an empty row is created. Typically, you would store a reference to + * the new row in a TableRow object (see newRow in the example above), + * and then set individual values using set(). + * + * If a p5.TableRow object is included as a parameter, then that row is + * duplicated and added to the table. + * + * @method addRow + * @param {p5.TableRow} [row] row to be added to the table + * + * @example + * <div class="norender"> + * <code> + * // Given the CSV file "mammals.csv" + * // in the project's "assets" folder: + * // + * // id,species,name + * // 0,Capra hircus,Goat + * // 1,Panthera pardus,Leopard + * // 2,Equus zebra,Zebra + * + * var table; + * + * function preload() { + * //my table is comma separated value "csv" + * //and has a header specifying the columns labels + * table = loadTable("assets/mammals.csv", "csv", "header"); + * } + * + * function setup() { + * //add a row + * var newRow = table.addRow(); + * newRow.setString("id", table.getRowCount() - 1); + * newRow.setString("species", "Canis Lupus"); + * newRow.setString("name", "Wolf"); + * + * //print the results + * for (var r = 0; r < table.getRowCount(); r++) + * for (var c = 0; c < table.getColumnCount(); c++) + * print(table.getString(r, c)); + * } + * </code> + * </div> + * + * @alt + * no image displayed + * + */ +p5.Table.prototype.addRow = function(row) { + // make sure it is a valid TableRow + var r = row || new p5.TableRow(); + + if (typeof(r.arr) === 'undefined' || typeof(r.obj) === 'undefined') { + //r = new p5.prototype.TableRow(r); + throw 'invalid TableRow: ' + r; + } + r.table = this; + this.rows.push(r); + return r; +}; + +/** + * Removes a row from the table object. + * + * @method removeRow + * @param {Number} id ID number of the row to remove + * + * @example + * <div class="norender"> + * <code> + * // Given the CSV file "mammals.csv" + * // in the project's "assets" folder: + * // + * // id,species,name + * // 0,Capra hircus,Goat + * // 1,Panthera pardus,Leopard + * // 2,Equus zebra,Zebra + * + * var table; + * + * function preload() { + * //my table is comma separated value "csv" + * //and has a header specifying the columns labels + * table = loadTable("assets/mammals.csv", "csv", "header"); + * } + * + * function setup() { + * //remove the first row + * var r = table.removeRow(0); + * + * //print the results + * for (var r = 0; r < table.getRowCount(); r++) + * for (var c = 0; c < table.getColumnCount(); c++) + * print(table.getString(r, c)); + * } + * </code> + * </div> + * + * @alt + * no image displayed + * + */ +p5.Table.prototype.removeRow = function(id) { + this.rows[id].table = null; // remove reference to table + var chunk = this.rows.splice(id+1, this.rows.length); + this.rows.pop(); + this.rows = this.rows.concat(chunk); +}; + + +/** + * Returns a reference to the specified p5.TableRow. The reference + * can then be used to get and set values of the selected row. + * + * @method getRow + * @param {Number} rowID ID number of the row to get + * @return {TableRow} p5.TableRow object + * + * @example + * <div class="norender"> + * <code> + * // Given the CSV file "mammals.csv" + * // in the project's "assets" folder: + * // + * // id,species,name + * // 0,Capra hircus,Goat + * // 1,Panthera pardus,Leopard + * // 2,Equus zebra,Zebra + * + * var table; + * + * function preload() { + * //my table is comma separated value "csv" + * //and has a header specifying the columns labels + * table = loadTable("assets/mammals.csv", "csv", "header"); + * } + * + * function setup() { + * var row = table.getRow(1); + * //print it column by column + * //note: a row is an object, not an array + * for (var c = 0; c < table.getColumnCount(); c++) + * print(row.getString(c)); + * } + * </code> + * </div> + * + *@alt + * no image displayed + * + */ +p5.Table.prototype.getRow = function(r) { + return this.rows[r]; +}; + +/** + * Gets all rows from the table. Returns an array of p5.TableRows. + * + * @method getRows + * @return {Array} Array of p5.TableRows + * + * @example + * <div class="norender"> + * <code> + * // Given the CSV file "mammals.csv" + * // in the project's "assets" folder: + * // + * // id,species,name + * // 0,Capra hircus,Goat + * // 1,Panthera pardus,Leopard + * // 2,Equus zebra,Zebra + * + * var table; + * + * function preload() { + * //my table is comma separated value "csv" + * //and has a header specifying the columns labels + * table = loadTable("assets/mammals.csv", "csv", "header"); + * } + * + * function setup() { + * var rows = table.getRows(); + * + * //warning: rows is an array of objects + * for (var r = 0; r < rows.length; r++) + * rows[r].set("name", "Unicorn"); + * + * //print the results + * for (var r = 0; r < table.getRowCount(); r++) + * for (var c = 0; c < table.getColumnCount(); c++) + * print(table.getString(r, c)); + * } + * </code> + * </div> + * + * @alt + * no image displayed + * + */ +p5.Table.prototype.getRows = function() { + return this.rows; +}; + +/** + * Finds the first row in the Table that contains the value + * provided, and returns a reference to that row. Even if + * multiple rows are possible matches, only the first matching + * row is returned. The column to search may be specified by + * either its ID or title. + * + * @method findRow + * @param {String} value The value to match + * @param {Number|String} column ID number or title of the + * column to search + * @return {TableRow} + * + * @example + * <div class="norender"> + * <code> + * // Given the CSV file "mammals.csv" + * // in the project's "assets" folder: + * // + * // id,species,name + * // 0,Capra hircus,Goat + * // 1,Panthera pardus,Leopard + * // 2,Equus zebra,Zebra + * + * var table; + * + * function preload() { + * //my table is comma separated value "csv" + * //and has a header specifying the columns labels + * table = loadTable("assets/mammals.csv", "csv", "header"); + * } + * + * function setup() { + * //find the animal named zebra + * var row = table.findRow("Zebra", "name"); + * //find the corresponding species + * print(row.getString("species")); + * } + * </code> + * </div> + * + * @alt + * no image displayed + * + */ +p5.Table.prototype.findRow = function(value, column) { + // try the Object + if (typeof(column) === 'string') { + for (var i = 0; i < this.rows.length; i++){ + if (this.rows[i].obj[column] === value) { + return this.rows[i]; + } + } + } + // try the Array + else { + for (var j = 0; j < this.rows.length; j++){ + if (this.rows[j].arr[column] === value) { + return this.rows[j]; + } + } + } + // otherwise... + return null; +}; + +/** + * Finds the rows in the Table that contain the value + * provided, and returns references to those rows. Returns an + * Array, so for must be used to iterate through all the rows, + * as shown in the example above. The column to search may be + * specified by either its ID or title. + * + * @method findRows + * @param {String} value The value to match + * @param {Number|String} column ID number or title of the + * column to search + * @return {Array} An Array of TableRow objects + * + * @example + * <div class="norender"> + * <code> + * // Given the CSV file "mammals.csv" + * // in the project's "assets" folder: + * // + * // id,species,name + * // 0,Capra hircus,Goat + * // 1,Panthera pardus,Leopard + * // 2,Equus zebra,Zebra + * + * var table; + * + * function preload() { + * //my table is comma separated value "csv" + * //and has a header specifying the columns labels + * table = loadTable("assets/mammals.csv", "csv", "header"); + * } + * + * function setup() { + * //add another goat + * var newRow = table.addRow(); + * newRow.setString("id", table.getRowCount() - 1); + * newRow.setString("species", "Scape Goat"); + * newRow.setString("name", "Goat"); + * + * //find the rows containing animals named Goat + * var rows = table.findRows("Goat", "name"); + * print(rows.length + " Goats found"); + * } + * </code> + * </div> + * + *@alt + * no image displayed + * + */ +p5.Table.prototype.findRows = function(value, column) { + var ret = []; + if (typeof(column) === 'string') { + for (var i = 0; i < this.rows.length; i++){ + if (this.rows[i].obj[column] === value) { + ret.push( this.rows[i] ); + } + } + } + // try the Array + else { + for (var j = 0; j < this.rows.length; j++){ + if (this.rows[j].arr[column] === value) { + ret.push( this.rows[j] ); + } + } + } + return ret; +}; + +/** + * Finds the first row in the Table that matches the regular + * expression provided, and returns a reference to that row. + * Even if multiple rows are possible matches, only the first + * matching row is returned. The column to search may be + * specified by either its ID or title. + * + * @method matchRow + * @param {String} regexp The regular expression to match + * @param {String|Number} column The column ID (number) or + * title (string) + * @return {TableRow} TableRow object + */ +p5.Table.prototype.matchRow = function(regexp, column) { + if (typeof(column) === 'number') { + for (var j = 0; j < this.rows.length; j++) { + if ( this.rows[j].arr[column].match(regexp) ) { + return this.rows[j]; + } + } + } + + else { + for (var i = 0; i < this.rows.length; i++) { + if ( this.rows[i].obj[column].match(regexp) ) { + return this.rows[i]; + } + } + } + return null; +}; + +/** + * Finds the rows in the Table that match the regular expression provided, + * and returns references to those rows. Returns an array, so for must be + * used to iterate through all the rows, as shown in the example. The + * column to search may be specified by either its ID or title. + * + * @method matchRows + * @param {String} regexp The regular expression to match + * @param {String|Number} [column] The column ID (number) or + * title (string) + * @return {Array} An Array of TableRow objects + * @example + * var table; + * + * function setup() { + * + * table = new p5.Table(); + * + * table.addColumn('name'); + * table.addColumn('type'); + * + * var newRow = table.addRow(); + * newRow.setString('name', 'Lion'); + * newRow.setString('type', 'Mammal'); + * + * newRow = table.addRow(); + * newRow.setString('name', 'Snake'); + * newRow.setString('type', 'Reptile'); + * + * newRow = table.addRow(); + * newRow.setString('name', 'Mosquito'); + * newRow.setString('type', 'Insect'); + * + * newRow = table.addRow(); + * newRow.setString('name', 'Lizard'); + * newRow.setString('type', 'Reptile'); + * + * var rows = table.matchRows('R.*', 'type'); + * for (var i = 0; i < rows.length; i++) { + * print(rows[i].getString('name') + ': ' + rows[i].getString('type')); + * } + * } + * // Sketch prints: + * // Snake: Reptile + * // Lizard: Reptile + */ +p5.Table.prototype.matchRows = function(regexp, column) { + var ret = []; + if (typeof(column) === 'number') { + for (var j = 0; j < this.rows.length; j++) { + if ( this.rows[j].arr[column].match(regexp) ) { + ret.push( this.rows[j] ); + } + } + } + + else { + for (var i = 0; i < this.rows.length; i++) { + if ( this.rows[i].obj[column].match(regexp) ) { + ret.push( this.rows[i] ); + } + } + } + return ret; +}; + + +/** + * Retrieves all values in the specified column, and returns them + * as an array. The column may be specified by either its ID or title. + * + * @method getColumn + * @param {String|Number} column String or Number of the column to return + * @return {Array} Array of column values + * + * @example + * <div class="norender"> + * <code> + * // Given the CSV file "mammals.csv" + * // in the project's "assets" folder: + * // + * // id,species,name + * // 0,Capra hircus,Goat + * // 1,Panthera pardus,Leopard + * // 2,Equus zebra,Zebra + * + * var table; + * + * function preload() { + * //my table is comma separated value "csv" + * //and has a header specifying the columns labels + * table = loadTable("assets/mammals.csv", "csv", "header"); + * } + * + * function setup() { + * //getColumn returns an array that can be printed directly + * print(table.getColumn("species")); + * //outputs ["Capra hircus", "Panthera pardus", "Equus zebra"] + * } + * </code> + * </div> + * + *@alt + * no image displayed + * + */ +p5.Table.prototype.getColumn = function(value) { + var ret = []; + if (typeof(value) === 'string'){ + for (var i = 0; i < this.rows.length; i++){ + ret.push (this.rows[i].obj[value]); + } + } else { + for (var j = 0; j < this.rows.length; j++){ + ret.push (this.rows[j].arr[value]); + } + } + return ret; +}; + +/** + * Removes all rows from a Table. While all rows are removed, + * columns and column titles are maintained. + * + * @method clearRows + * + * @example + * <div class="norender"> + * <code> + * // Given the CSV file "mammals.csv" + * // in the project's "assets" folder: + * // + * // id,species,name + * // 0,Capra hircus,Goat + * // 1,Panthera pardus,Leopard + * // 2,Equus zebra,Zebra + * + * var table; + * + * function preload() { + * //my table is comma separated value "csv" + * //and has a header specifying the columns labels + * table = loadTable("assets/mammals.csv", "csv", "header"); + * } + * + * function setup() { + * table.clearRows(); + * print(table.getRowCount() + " total rows in table"); + * print(table.getColumnCount() + " total columns in table"); + * } + * </code> + * </div> + * + *@alt + * no image displayed + * + */ +p5.Table.prototype.clearRows = function() { + delete this.rows; + this.rows = []; +}; + +/** + * Use addColumn() to add a new column to a Table object. + * Typically, you will want to specify a title, so the column + * may be easily referenced later by name. (If no title is + * specified, the new column's title will be null.) + * + * @method addColumn + * @param {String} [title] title of the given column + * + * @example + * <div class="norender"> + * <code> + * // Given the CSV file "mammals.csv" + * // in the project's "assets" folder: + * // + * // id,species,name + * // 0,Capra hircus,Goat + * // 1,Panthera pardus,Leopard + * // 2,Equus zebra,Zebra + * + * var table; + * + * function preload() { + * //my table is comma separated value "csv" + * //and has a header specifying the columns labels + * table = loadTable("assets/mammals.csv", "csv", "header"); + * } + * + * function setup() { + * table.addColumn("carnivore"); + * table.set(0, "carnivore", "no"); + * table.set(1, "carnivore", "yes"); + * table.set(2, "carnivore", "no"); + * + * //print the results + * for (var r = 0; r < table.getRowCount(); r++) + * for (var c = 0; c < table.getColumnCount(); c++) + * print(table.getString(r, c)); + * } + * </code> + * </div> + * + *@alt + * no image displayed + * + */ +p5.Table.prototype.addColumn = function(title) { + var t = title || null; + this.columns.push(t); +}; + +/** + * Returns the total number of columns in a Table. + * + * @return {Number} Number of columns in this table + */ +p5.Table.prototype.getColumnCount = function() { + return this.columns.length; +}; + +/** + * Returns the total number of rows in a Table. + * + * @method getRowCount + * @return {Number} Number of rows in this table + + */ +p5.Table.prototype.getRowCount = function() { + return this.rows.length; +}; + +/** + * <p>Removes any of the specified characters (or "tokens").</p> + * + * <p>If no column is specified, then the values in all columns and + * rows are processed. A specific column may be referenced by + * either its ID or title.</p> + * + * @method removeTokens + * @param {String} chars String listing characters to be removed + * @param {String|Number} [column] Column ID (number) + * or name (string) + */ +p5.Table.prototype.removeTokens = function(chars, column) { + var escape= function(s) { + return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); + }; + var charArray = []; + for (var i = 0; i < chars.length; i++) { + charArray.push( escape( chars.charAt(i) ) ); + } + var regex = new RegExp(charArray.join('|'), 'g'); + + if (typeof(column) === 'undefined'){ + for (var c = 0; c < this.columns.length; c++) { + for (var d = 0; d < this.rows.length; d++) { + var s = this.rows[d].arr[c]; + s = s.replace(regex, ''); + this.rows[d].arr[c] = s; + this.rows[d].obj[this.columns[c]] = s; + } + } + } + else if (typeof(column) === 'string'){ + for (var j = 0; j < this.rows.length; j++) { + var val = this.rows[j].obj[column]; + val = val.replace(regex, ''); + this.rows[j].obj[column] = val; + var pos = this.columns.indexOf(column); + this.rows[j].arr[pos] = val; + } + } + else { + for (var k = 0; k < this.rows.length; k++) { + var str = this.rows[k].arr[column]; + str = str.replace(regex, ''); + this.rows[k].arr[column] = str; + this.rows[k].obj[this.columns[column]] = str; + } + } +}; + +/** + * Trims leading and trailing whitespace, such as spaces and tabs, + * from String table values. If no column is specified, then the + * values in all columns and rows are trimmed. A specific column + * may be referenced by either its ID or title. + * + * @method trim + * @param {String|Number} column Column ID (number) + * or name (string) + */ +p5.Table.prototype.trim = function(column) { + var regex = new RegExp( (' '), 'g'); + + if (typeof(column) === 'undefined'){ + for (var c = 0; c < this.columns.length; c++) { + for (var d = 0; d < this.rows.length; d++) { + var s = this.rows[d].arr[c]; + s = s.replace(regex, ''); + this.rows[d].arr[c] = s; + this.rows[d].obj[this.columns[c]] = s; + } + } + } + else if (typeof(column) === 'string'){ + for (var j = 0; j < this.rows.length; j++) { + var val = this.rows[j].obj[column]; + val = val.replace(regex, ''); + this.rows[j].obj[column] = val; + var pos = this.columns.indexOf(column); + this.rows[j].arr[pos] = val; + } + } + else { + for (var k = 0; k < this.rows.length; k++) { + var str = this.rows[k].arr[column]; + str = str.replace(regex, ''); + this.rows[k].arr[column] = str; + this.rows[k].obj[this.columns[column]] = str; + } + } +}; + +/** + * Use removeColumn() to remove an existing column from a Table + * object. The column to be removed may be identified by either + * its title (a String) or its index value (an int). + * removeColumn(0) would remove the first column, removeColumn(1) + * would remove the second column, and so on. + * + * @method removeColumn + * @param {String|Number} column columnName (string) or ID (number) + * + * @example + * <div class="norender"> + * <code> + * // Given the CSV file "mammals.csv" + * // in the project's "assets" folder: + * // + * // id,species,name + * // 0,Capra hircus,Goat + * // 1,Panthera pardus,Leopard + * // 2,Equus zebra,Zebra + * + * var table; + * + * function preload() { + * //my table is comma separated value "csv" + * //and has a header specifying the columns labels + * table = loadTable("assets/mammals.csv", "csv", "header"); + * } + * + * function setup() { + * table.removeColumn("id"); + * print(table.getColumnCount()); + * } + * </code> + * </div> + * + *@alt + * no image displayed + * + */ +p5.Table.prototype.removeColumn = function(c) { + var cString; + var cNumber; + if (typeof(c) === 'string') { + // find the position of c in the columns + cString = c; + cNumber = this.columns.indexOf(c); + console.log('string'); + } + else{ + cNumber = c; + cString = this.columns[c]; + } + + var chunk = this.columns.splice(cNumber+1, this.columns.length); + this.columns.pop(); + this.columns = this.columns.concat(chunk); + + for (var i = 0; i < this.rows.length; i++){ + var tempR = this.rows[i].arr; + var chip = tempR.splice(cNumber+1, tempR.length); + tempR.pop(); + this.rows[i].arr = tempR.concat(chip); + delete this.rows[i].obj[cString]; + } + +}; + + +/** + * Stores a value in the Table's specified row and column. + * The row is specified by its ID, while the column may be specified + * by either its ID or title. + * + * @method set + * @param {String|Number} column column ID (Number) + * or title (String) + * @param {String|Number} value value to assign + * + * @example + * <div class="norender"> + * <code> + * // Given the CSV file "mammals.csv" + * // in the project's "assets" folder: + * // + * // id,species,name + * // 0,Capra hircus,Goat + * // 1,Panthera pardus,Leopard + * // 2,Equus zebra,Zebra + * + * var table; + * + * function preload() { + * //my table is comma separated value "csv" + * //and has a header specifying the columns labels + * table = loadTable("assets/mammals.csv", "csv", "header"); + * } + * + * function setup() { + * table.set(0, "species", "Canis Lupus"); + * table.set(0, "name", "Wolf"); + * + * //print the results + * for (var r = 0; r < table.getRowCount(); r++) + * for (var c = 0; c < table.getColumnCount(); c++) + * print(table.getString(r, c)); + * } + * </code> + * </div> + * + *@alt + * no image displayed + * + */ +p5.Table.prototype.set = function(row, column, value) { + this.rows[row].set(column, value); +}; + +/** + * Stores a Float value in the Table's specified row and column. + * The row is specified by its ID, while the column may be specified + * by either its ID or title. + * + * @method setNum + * @param {Number} row row ID + * @param {String|Number} column column ID (Number) + * or title (String) + * @param {Number} value value to assign + * + * @example + * <div class="norender"> + * <code> + * // Given the CSV file "mammals.csv" + * // in the project's "assets" folder: + * // + * // id,species,name + * // 0,Capra hircus,Goat + * // 1,Panthera pardus,Leopard + * // 2,Equus zebra,Zebra + * + * var table; + * + * function preload() { + * //my table is comma separated value "csv" + * //and has a header specifying the columns labels + * table = loadTable("assets/mammals.csv", "csv", "header"); + * } + * + * function setup() { + * table.setNum(1, "id", 1); + * + * print(table.getColumn(0)); + * //["0", 1, "2"] + * } + * </code> + * </div> + * + *@alt + * no image displayed + */ +p5.Table.prototype.setNum = function(row, column, value){ + this.rows[row].setNum(column, value); +}; + + +/** + * Stores a String value in the Table's specified row and column. + * The row is specified by its ID, while the column may be specified + * by either its ID or title. + * + * @method setString + * @param {Number} row row ID + * @param {String|Number} column column ID (Number) + * or title (String) + * @param {String} value value to assign + */ +p5.Table.prototype.setString = function(row, column, value){ + this.rows[row].setString(column, value); +}; + +/** + * Retrieves a value from the Table's specified row and column. + * The row is specified by its ID, while the column may be specified by + * either its ID or title. + * + * @method get + * @param {Number} row row ID + * @param {String|Number} column columnName (string) or + * ID (number) + * @return {String|Number} + * + * @example + * <div class="norender"> + * <code> + * // Given the CSV file "mammals.csv" + * // in the project's "assets" folder: + * // + * // id,species,name + * // 0,Capra hircus,Goat + * // 1,Panthera pardus,Leopard + * // 2,Equus zebra,Zebra + * + * var table; + * + * function preload() { + * //my table is comma separated value "csv" + * //and has a header specifying the columns labels + * table = loadTable("assets/mammals.csv", "csv", "header"); + * } + * + * function setup() { + * print(table.get(0, 1)); + * //Capra hircus + * print(table.get(0, "species")); + * //Capra hircus + * } + * </code> + * </div> + * + *@alt + * no image displayed + * + */ +p5.Table.prototype.get = function(row, column) { + return this.rows[row].get(column); +}; + +/** + * Retrieves a Float value from the Table's specified row and column. + * The row is specified by its ID, while the column may be specified by + * either its ID or title. + * + * @method getNum + * @param {Number} row row ID + * @param {String|Number} column columnName (string) or + * ID (number) + * @return {Number} + * + * @example + * <div class="norender"> + * <code> + * // Given the CSV file "mammals.csv" + * // in the project's "assets" folder: + * // + * // id,species,name + * // 0,Capra hircus,Goat + * // 1,Panthera pardus,Leopard + * // 2,Equus zebra,Zebra + * + * var table; + * + * function preload() { + * //my table is comma separated value "csv" + * //and has a header specifying the columns labels + * table = loadTable("assets/mammals.csv", "csv", "header"); + * } + * + * function setup() { + * print(table.getNum(1, 0) + 100); + * //id 1 + 100 = 101 + * } + * </code> + * </div> + * + *@alt + * no image displayed + * + */ +p5.Table.prototype.getNum = function(row, column) { + return this.rows[row].getNum(column); +}; + +/** + * Retrieves a String value from the Table's specified row and column. + * The row is specified by its ID, while the column may be specified by + * either its ID or title. + * + * @method getString + * @param {Number} row row ID + * @param {String|Number} column columnName (string) or + * ID (number) + * @return {String} + * + * @example + * <div class="norender"> + * <code> + * // Given the CSV file "mammals.csv" + * // in the project's "assets" folder: + * // + * // id,species,name + * // 0,Capra hircus,Goat + * // 1,Panthera pardus,Leopard + * // 2,Equus zebra,Zebra + * + * var table; + * + * function preload() { + * //my table is comma separated value "csv" + * //and has a header specifying the columns labels + * table = loadTable("assets/mammals.csv", "csv", "header"); + * } + * + * function setup() { + * var tableArray = table.getArray(); + * + * //output each row as array + * for (var i = 0; i < tableArray.length; i++) + * print(tableArray[i]); + * } + * </code> + * </div> + * + *@alt + * no image displayed + * + */ +p5.Table.prototype.getString = function(row, column) { + return this.rows[row].getString(column); +}; + +/** + * Retrieves all table data and returns as an object. If a column name is + * passed in, each row object will be stored with that attribute as its + * title. + * + * @method getObject + * @param {String} headerColumn Name of the column which should be used to + * title each row object (optional) + * @return {Object} + * + * @example + * <div class="norender"> + * <code> + * // Given the CSV file "mammals.csv" + * // in the project's "assets" folder: + * // + * // id,species,name + * // 0,Capra hircus,Goat + * // 1,Panthera pardus,Leopard + * // 2,Equus zebra,Zebra + * + * var table; + * + * function preload() { + * //my table is comma separated value "csv" + * //and has a header specifying the columns labels + * table = loadTable("assets/mammals.csv", "csv", "header"); + * } + * + * function setup() { + * var tableObject = table.getObject(); + * + * print(tableObject); + * //outputs an object + * } + * </code> + * </div> + * + *@alt + * no image displayed + * + */ +p5.Table.prototype.getObject = function (headerColumn) { + var tableObject = {}; + var obj, cPos, index; + + for(var i = 0; i < this.rows.length; i++) { + obj = this.rows[i].obj; + + if (typeof(headerColumn) === 'string'){ + cPos = this.columns.indexOf(headerColumn); // index of columnID + if (cPos >= 0) { + index = obj[headerColumn]; + tableObject[index] = obj; + } else { + throw 'This table has no column named "' + headerColumn +'"'; + } + } else { + tableObject[i] = this.rows[i].obj; + } + } + return tableObject; +}; + +/** + * Retrieves all table data and returns it as a multidimensional array. + * + * @method getArray + * @return {Array} + */ +p5.Table.prototype.getArray = function () { + var tableArray = []; + for(var i = 0; i < this.rows.length; i++) { + tableArray.push(this.rows[i].arr); + } + return tableArray; +}; + +module.exports = p5.Table; + +},{"../core/core":37}],61:[function(_dereq_,module,exports){ +/** + * @module IO + * @submodule Table + * @requires core + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); + +/** + * A TableRow object represents a single row of data values, + * stored in columns, from a table. + * + * A Table Row contains both an ordered array, and an unordered + * JSON object. + * + * @class p5.TableRow + * @constructor + * @param {String} [str] optional: populate the row with a + * string of values, separated by the + * separator + * @param {String} [separator] comma separated values (csv) by default + */ +p5.TableRow = function (str, separator) { + var arr = []; + var obj = {}; + if (str){ + separator = separator || ','; + arr = str.split(separator); + } + for (var i = 0; i < arr.length; i++){ + var key = i; + var val = arr[i]; + obj[key] = val; + } + this.arr = arr; + this.obj = obj; + this.table = null; +}; + +/** + * Stores a value in the TableRow's specified column. + * The column may be specified by either its ID or title. + * + * @method set + * @param {String|Number} column Column ID (Number) + * or Title (String) + * @param {String|Number} value The value to be stored + */ +p5.TableRow.prototype.set = function(column, value) { + // if typeof column is string, use .obj + if (typeof(column) === 'string'){ + var cPos = this.table.columns.indexOf(column); // index of columnID + if (cPos >= 0) { + this.obj[column] = value; + this.arr[cPos] = value; + } + else { + throw 'This table has no column named "' + column +'"'; + } + } + + // if typeof column is number, use .arr + else { + if (column < this.table.columns.length) { + this.arr[column] = value; + var cTitle = this.table.columns[column]; + this.obj[cTitle] = value; + } + else { + throw 'Column #' + column + ' is out of the range of this table'; + } + } +}; + + +/** + * Stores a Float value in the TableRow's specified column. + * The column may be specified by either its ID or title. + * + * @method setNum + * @param {String|Number} column Column ID (Number) + * or Title (String) + * @param {Number} value The value to be stored + * as a Float + */ +p5.TableRow.prototype.setNum = function(column, value){ + var floatVal = parseFloat(value, 10); + this.set(column, floatVal); +}; + + +/** + * Stores a String value in the TableRow's specified column. + * The column may be specified by either its ID or title. + * + * @method setString + * @param {String|Number} column Column ID (Number) + * or Title (String) + * @param {String} value The value to be stored + * as a String + */ +p5.TableRow.prototype.setString = function(column, value){ + var stringVal = value.toString(); + this.set(column, stringVal); +}; + +/** + * Retrieves a value from the TableRow's specified column. + * The column may be specified by either its ID or title. + * + * @method get + * @param {String|Number} column columnName (string) or + * ID (number) + * @return {String|Number} + */ +p5.TableRow.prototype.get = function(column) { + if (typeof(column) === 'string'){ + return this.obj[column]; + } else { + return this.arr[column]; + } +}; + +/** + * Retrieves a Float value from the TableRow's specified + * column. The column may be specified by either its ID or + * title. + * + * @method getNum + * @param {String|Number} column columnName (string) or + * ID (number) + * @return {Number} Float Floating point number + */ +p5.TableRow.prototype.getNum = function(column) { + var ret; + if (typeof(column) === 'string'){ + ret = parseFloat(this.obj[column], 10); + } else { + ret = parseFloat(this.arr[column], 10); + } + + if (ret.toString() === 'NaN') { + throw 'Error: ' + this.obj[column]+ ' is NaN (Not a Number)'; + } + return ret; +}; + +/** + * Retrieves an String value from the TableRow's specified + * column. The column may be specified by either its ID or + * title. + * + * @method getString + * @param {String|Number} column columnName (string) or + * ID (number) + * @return {String} String + */ +p5.TableRow.prototype.getString = function(column) { + if (typeof(column) === 'string'){ + return this.obj[column].toString(); + } else { + return this.arr[column].toString(); + } +}; + +module.exports = p5.TableRow; + +},{"../core/core":37}],62:[function(_dereq_,module,exports){ +/** + * @module IO + * @submodule XML + * @requires core + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); + +/** + * XML is a representation of an XML object, able to parse XML code. Use + * loadXML() to load external XML files and create XML objects. + * + * @class p5.XML + * @constructor + * @return {p5.XML} p5.XML object generated + * @example + * <div class='norender'><code> + * // The following short XML file called "mammals.xml" is parsed + * // in the code below. + * // + * // <?xml version="1.0"?> + * // <mammals> + * // <animal id="0" species="Capra hircus">Goat</animal> + * // <animal id="1" species="Panthera pardus">Leopard</animal> + * // <animal id="2" species="Equus zebra">Zebra</animal> + * // </mammals> + * + * var xml; + * + * function preload() { + * xml = loadXML("assets/mammals.xml"); + * } + * + * function setup() { + * var children = xml.getChildren("animal"); + * + * for (var i = 0; i < children.length; i++) { + * var id = children[i].getNumber("id"); + * var coloring = children[i].getString("species"); + * var name = children[i].getContent(); + * print(id + ", " + coloring + ", " + name); + * } + * } + * + * // Sketch prints: + * // 0, Capra hircus, Goat + * // 1, Panthera pardus, Leopard + * // 2, Equus zebra, Zebra + * </code></div> + * + * @alt + * no image displayed + * + */ +p5.XML = function () { + this.name = null; //done + this.attributes = {}; //done + this.children = []; + this.parent = null; + this.content = null; //done +}; + + +/** + * Gets a copy of the element's parent. Returns the parent as another + * p5.XML object. + * + * @method getParent + * @return {Object} element parent + * @example + * <div class='norender'><code> + * // The following short XML file called "mammals.xml" is parsed + * // in the code below. + * // + * // <?xml version="1.0"?> + * // <mammals> + * // <animal id="0" species="Capra hircus">Goat</animal> + * // <animal id="1" species="Panthera pardus">Leopard</animal> + * // <animal id="2" species="Equus zebra">Zebra</animal> + * // </mammals> + * + * var xml; + * + * function preload() { + * xml = loadXML("assets/mammals.xml"); + * } + * + * function setup() { + * var children = xml.getChildren("animal"); + * var parent = children[1].getParent(); + * print(parent.getName()); + * } + * + * // Sketch prints: + * // mammals + * </code></div> + */ +p5.XML.prototype.getParent = function() { + return this.parent; +}; + +/** + * Gets the element's full name, which is returned as a String. + * + * @method getName + * @return {String} the name of the node + * @example<animal + * <div class='norender'><code> + * // The following short XML file called "mammals.xml" is parsed + * // in the code below. + * // + * // <?xml version="1.0"?> + * // <mammals> + * // <animal id="0" species="Capra hircus">Goat</animal> + * // <animal id="1" species="Panthera pardus">Leopard</animal> + * // <animal id="2" species="Equus zebra">Zebra</animal> + * // </mammals> + * + * var xml; + * + * function preload() { + * xml = loadXML("assets/mammals.xml"); + * } + * + * function setup() { + * print(xml.getName()); + * } + * + * // Sketch prints: + * // mammals + * </code></div> + */ +p5.XML.prototype.getName = function() { + return this.name; +}; + +/** + * Sets the element's name, which is specified as a String. + * + * @method setName + * @param {String} the new name of the node + * @example<animal + * <div class='norender'><code> + * // The following short XML file called "mammals.xml" is parsed + * // in the code below. + * // + * // <?xml version="1.0"?> + * // <mammals> + * // <animal id="0" species="Capra hircus">Goat</animal> + * // <animal id="1" species="Panthera pardus">Leopard</animal> + * // <animal id="2" species="Equus zebra">Zebra</animal> + * // </mammals> + * + * var xml; + * + * function preload() { + * xml = loadXML("assets/mammals.xml"); + * } + * + * function setup() { + * print(xml.getName()); + * xml.setName("fish"); + * print(xml.getName()); + * } + * + * // Sketch prints: + * // mammals + * // fish + * </code></div> + */ +p5.XML.prototype.setName = function(name) { + this.name = name; +}; + +/** + * Checks whether or not the element has any children, and returns the result + * as a boolean. + * + * @method hasChildren + * @return {boolean} + * @example<animal + * <div class='norender'><code> + * // The following short XML file called "mammals.xml" is parsed + * // in the code below. + * // + * // <?xml version="1.0"?> + * // <mammals> + * // <animal id="0" species="Capra hircus">Goat</animal> + * // <animal id="1" species="Panthera pardus">Leopard</animal> + * // <animal id="2" species="Equus zebra">Zebra</animal> + * // </mammals> + * + * var xml; + * + * function preload() { + * xml = loadXML("assets/mammals.xml"); + * } + * + * function setup() { + * print(xml.hasChildren()); + * } + * + * // Sketch prints: + * // true + * </code></div> + */ +p5.XML.prototype.hasChildren = function() { + return this.children.length > 0; +}; + +/** + * Get the names of all of the element's children, and returns the names as an + * array of Strings. This is the same as looping through and calling getName() + * on each child element individually. + * + * @method listChildren + * @return {Array} names of the children of the element + * @example<animal + * <div class='norender'><code> + * // The following short XML file called "mammals.xml" is parsed + * // in the code below. + * // + * // <?xml version="1.0"?> + * // <mammals> + * // <animal id="0" species="Capra hircus">Goat</animal> + * // <animal id="1" species="Panthera pardus">Leopard</animal> + * // <animal id="2" species="Equus zebra">Zebra</animal> + * // </mammals> + * + * var xml; + * + * function preload() { + * xml = loadXML("assets/mammals.xml"); + * } + * + * function setup() { + * print(xml.listChildren()); + * } + * + * // Sketch prints: + * // ["animal", "animal", "animal"] + * </code></div> + */ +p5.XML.prototype.listChildren = function() { + return this.children.map(function(c) { return c.name; }); +}; + +/** + * Returns all of the element's children as an array of p5.XML objects. When + * the name parameter is specified, then it will return all children that match + * that name. + * + * @method getChildren + * @param {String} [name] element name + * @return {Array} children of the element + * @example<animal + * <div class='norender'><code> + * // The following short XML file called "mammals.xml" is parsed + * // in the code below. + * // + * // <?xml version="1.0"?> + * // <mammals> + * // <animal id="0" species="Capra hircus">Goat</animal> + * // <animal id="1" species="Panthera pardus">Leopard</animal> + * // <animal id="2" species="Equus zebra">Zebra</animal> + * // </mammals> + * + * var xml; + * + * function preload() { + * xml = loadXML("assets/mammals.xml"); + * } + * + * function setup() { + * var animals = xml.getChildren("animal"); + * + * for (var i = 0; i < animals.length; i++) { + * print(animals[i].getContent()); + * } + * } + * + * // Sketch prints: + * // "Goat" + * // "Leopard" + * // "Zebra" + * </code></div> + */ +p5.XML.prototype.getChildren = function(param) { + if (param) { + return this.children.filter(function(c) { return c.name === param; }); + } + else { + return this.children; + } +}; + +/** + * Returns the first of the element's children that matches the name parameter + * or the child of the given index.It returns undefined if no matching + * child is found. + * + * @method getChild + * @param {String|Number} name element name or index + * @return {p5.XML} + * @example<animal + * <div class='norender'><code> + * // The following short XML file called "mammals.xml" is parsed + * // in the code below. + * // + * // <?xml version="1.0"?> + * // <mammals> + * // <animal id="0" species="Capra hircus">Goat</animal> + * // <animal id="1" species="Panthera pardus">Leopard</animal> + * // <animal id="2" species="Equus zebra">Zebra</animal> + * // </mammals> + * + * var xml; + * + * function preload() { + * xml = loadXML("assets/mammals.xml"); + * } + * + * function setup() { + * var firstChild = xml.getChild("animal"); + * print(firstChild.getContent()); + * } + * + * // Sketch prints: + * // "Goat" + * </code></div> + * <div class='norender'><code> + * var xml; + * + * function preload() { + * xml = loadXML("assets/mammals.xml"); + * } + * + * function setup() { + * var secondChild = xml.getChild(1); + * print(secondChild.getContent()); + * } + * + * // Sketch prints: + * // "Leopard" + * </code></div> + */ +p5.XML.prototype.getChild = function(param) { + if(typeof param === 'string') { + return this.children.find(function(c) { + return c.name === param; + }); + } + else { + return this.children[param]; + } +}; + +/** + * Appends a new child to the element. The child can be specified with + * either a String, which will be used as the new tag's name, or as a + * reference to an existing p5.XML object. + * A reference to the newly created child is returned as an p5.XML object. + * + * @method addChild + * @param {Object} a p5.XML Object which will be the child to be added + */ +p5.XML.prototype.addChild = function(node) { + if (node instanceof p5.XML) { + this.children.push(node); + } else { + // PEND + } +}; + +/** + * Removes the element specified by name or index. + * + * @method removeChild + * @param {String|Number} name element name or index + * @example + * <div class='norender'><code> + * // The following short XML file called "mammals.xml" is parsed + * // in the code below. + * // + * // <?xml version="1.0"?> + * // <mammals> + * // <animal id="0" species="Capra hircus">Goat</animal> + * // <animal id="1" species="Panthera pardus">Leopard</animal> + * // <animal id="2" species="Equus zebra">Zebra</animal> + * // </mammals> + * + * var xml; + * + * function preload() { + * xml = loadXML("assets/mammals.xml"); + * } + * + * function setup() { + * xml.removeChild("animal"); + * var children = xml.getChildren(); + * for (var i=0; i<children.length; i++) { + * print(children[i].getContent()); + * } + * } + * + * // Sketch prints: + * // "Leopard" + * // "Zebra" + * </code></div> + * <div class='norender'><code> + * var xml; + * + * function preload() { + * xml = loadXML("assets/mammals.xml"); + * } + * + * function setup() { + * xml.removeChild(1); + * var children = xml.getChildren(); + * for (var i=0; i<children.length; i++) { + * print(children[i].getContent()); + * } + * } + * + * // Sketch prints: + * // "Goat" + * // "Zebra" + * </code></div> + */ +p5.XML.prototype.removeChild = function(param) { + var ind = -1; + if(typeof param === 'string') { + for (var i=0; i<this.children.length; i++) { + if (this.children[i].name === param) { + ind = i; + break; + } + } + } else { + ind = param; + } + if (ind !== -1) { + this.children.splice(ind, 1); + } +}; + + +/** + * Counts the specified element's number of attributes, returned as an Number. + * + * @method getAttributeCount + * @return {Number} + * @example + * <div class='norender'><code> + * // The following short XML file called "mammals.xml" is parsed + * // in the code below. + * // + * // <?xml version="1.0"?> + * // <mammals> + * // <animal id="0" species="Capra hircus">Goat</animal> + * // <animal id="1" species="Panthera pardus">Leopard</animal> + * // <animal id="2" species="Equus zebra">Zebra</animal> + * // </mammals> + * + * var xml; + * + * function preload() { + * xml = loadXML("assets/mammals.xml"); + * } + * + * function setup() { + * var firstChild = xml.getChild("animal"); + * print(firstChild.getAttributeCount()); + * } + * + * // Sketch prints: + * // 2 + * </code></div> + */ +p5.XML.prototype.getAttributeCount = function() { + return Object.keys(this.attributes).length; +}; + +/** + * Gets all of the specified element's attributes, and returns them as an + * array of Strings. + * + * @method listAttributes + * @return {Array} an array of strings containing the names of attributes + * @example + * <div class='norender'><code> + * // The following short XML file called "mammals.xml" is parsed + * // in the code below. + * // + * // <?xml version="1.0"?> + * // <mammals> + * // <animal id="0" species="Capra hircus">Goat</animal> + * // <animal id="1" species="Panthera pardus">Leopard</animal> + * // <animal id="2" species="Equus zebra">Zebra</animal> + * // </mammals> + * + * var xml; + * + * function preload() { + * xml = loadXML("assets/mammals.xml"); + * } + * + * function setup() { + * var firstChild = xml.getChild("animal"); + * print(firstChild.listAttributes()); + * } + * + * // Sketch prints: + * // ["id", "species"] + * </code></div> + */ +p5.XML.prototype.listAttributes = function() { + return Object.keys(this.attributes); +}; + +/** + * Checks whether or not an element has the specified attribute. + * + * @method hasAttribute + * @param {String} the attribute to be checked + * @return {boolean} true if attribute found else false + * @example + * <div class='norender'><code> + * // The following short XML file called "mammals.xml" is parsed + * // in the code below. + * // + * // <?xml version="1.0"?> + * // <mammals> + * // <animal id="0" species="Capra hircus">Goat</animal> + * // <animal id="1" species="Panthera pardus">Leopard</animal> + * // <animal id="2" species="Equus zebra">Zebra</animal> + * // </mammals> + * + * var xml; + * + * function preload() { + * xml = loadXML("assets/mammals.xml"); + * } + * + * function setup() { + * var firstChild = xml.getChild("animal"); + * print(firstChild.hasAttribute("species")); + * print(firstChild.hasAttribute("color")); + * } + * + * // Sketch prints: + * // true + * // false + * </code></div> + */ +p5.XML.prototype.hasAttribute = function(name) { + return this.attributes[name] ? true : false; +}; + +/** + * Returns an attribute value of the element as an Number. If the defaultValue + * parameter is specified and the attribute doesn't exist, then defaultValue + * is returned. If no defaultValue is specified and the attribute doesn't + * exist, the value 0 is returned. + * + * @method getNumber + * @param {String} name the non-null full name of the attribute + * @param {Number} [defaultValue] the default value of the attribute + * @return {Number} + * @example + * <div class='norender'><code> + * // The following short XML file called "mammals.xml" is parsed + * // in the code below. + * // + * // <?xml version="1.0"?> + * // <mammals> + * // <animal id="0" species="Capra hircus">Goat</animal> + * // <animal id="1" species="Panthera pardus">Leopard</animal> + * // <animal id="2" species="Equus zebra">Zebra</animal> + * // </mammals> + * + * var xml; + * + * function preload() { + * xml = loadXML("assets/mammals.xml"); + * } + * + * function setup() { + * var firstChild = xml.getChild("animal"); + * print(firstChild.getNumber("id")); + * } + * + * // Sketch prints: + * // 0 + * </code></div> + */ +p5.XML.prototype.getNumber = function(name, defaultValue) { + return Number(this.attributes[name]) || defaultValue || 0; +}; + +/** + * Returns an attribute value of the element as an String. If the defaultValue + * parameter is specified and the attribute doesn't exist, then defaultValue + * is returned. If no defaultValue is specified and the attribute doesn't + * exist, null is returned. + * + * @method getString + * @param {String} name the non-null full name of the attribute + * @param {Number} [defaultValue] the default value of the attribute + * @return {Number} + * @example + * <div class='norender'><code> + * // The following short XML file called "mammals.xml" is parsed + * // in the code below. + * // + * // <?xml version="1.0"?> + * // <mammals> + * // <animal id="0" species="Capra hircus">Goat</animal> + * // <animal id="1" species="Panthera pardus">Leopard</animal> + * // <animal id="2" species="Equus zebra">Zebra</animal> + * // </mammals> + * + * var xml; + * + * function preload() { + * xml = loadXML("assets/mammals.xml"); + * } + * + * function setup() { + * var firstChild = xml.getChild("animal"); + * print(firstChild.getString("species")); + * } + * + * // Sketch prints: + * // "Capra hircus" + * </code></div> + */ +p5.XML.prototype.getString = function(name, defaultValue) { + return String(this.attributes[name]) || defaultValue || null; +}; + +/** + * Sets the content of an element's attribute. The first parameter specifies + * the attribute name, while the second specifies the new content. + * + * @method setAttribute + * @param {String} name the full name of the attribute + * @param {Number} value the value of the attribute + * @example + * <div class='norender'><code> + * // The following short XML file called "mammals.xml" is parsed + * // in the code below. + * // + * // <?xml version="1.0"?> + * // <mammals> + * // <animal id="0" species="Capra hircus">Goat</animal> + * // <animal id="1" species="Panthera pardus">Leopard</animal> + * // <animal id="2" species="Equus zebra">Zebra</animal> + * // </mammals> + * + * var xml; + * + * function preload() { + * xml = loadXML("assets/mammals.xml"); + * } + * + * function setup() { + * var firstChild = xml.getChild("animal"); + * print(firstChild.getString("species")); + * firstChild.setAttribute("species", "Jamides zebra"); + * print(firstChild.getString("species")); + * } + * + * // Sketch prints: + * // "Capra hircus" + * // "Jamides zebra" + * </code></div> + */ +p5.XML.prototype.setAttribute = function(name, value) { + if (this.attributes[name]) { + this.attributes[name] = value; + } +}; + +/** + * Returns the content of an element. If there is no such content, + * defaultValue is returned if specified, otherwise null is returned. + * + * @method getContent + * @param {String} [defaultValue] value returned if no content is found + * @return {String} + * @example + * <div class='norender'><code> + * // The following short XML file called "mammals.xml" is parsed + * // in the code below. + * // + * // <?xml version="1.0"?> + * // <mammals> + * // <animal id="0" species="Capra hircus">Goat</animal> + * // <animal id="1" species="Panthera pardus">Leopard</animal> + * // <animal id="2" species="Equus zebra">Zebra</animal> + * // </mammals> + * + * var xml; + * + * function preload() { + * xml = loadXML("assets/mammals.xml"); + * } + * + * function setup() { + * var firstChild = xml.getChild("animal"); + * print(firstChild.getContent()); + * } + * + * // Sketch prints: + * // "Goat" + * </code></div> + */ +p5.XML.prototype.getContent = function(defaultValue) { + return this.content || defaultValue || null; +}; + +/** + * Sets the element's content. + * + * @method setContent + * @param {String} text the new content + * @example + * <div class='norender'><code> + * // The following short XML file called "mammals.xml" is parsed + * // in the code below. + * // + * // <?xml version="1.0"?> + * // <mammals> + * // <animal id="0" species="Capra hircus">Goat</animal> + * // <animal id="1" species="Panthera pardus">Leopard</animal> + * // <animal id="2" species="Equus zebra">Zebra</animal> + * // </mammals> + * + * var xml; + * + * function preload() { + * xml = loadXML("assets/mammals.xml"); + * } + * + * function setup() { + * var firstChild = xml.getChild("animal"); + * print(firstChild.getContent()); + * firstChild.setContent("Mountain Goat"); + * print(firstChild.getContent()); + * } + * + * // Sketch prints: + * // "Goat" + * // "Mountain Goat" + * </code></div> + */ +p5.XML.prototype.setContent = function( content ) { + if(!this.children.length) { + this.content = content; + } +}; + +/* HELPERS */ +/** + * This method is called while the parsing of XML (when loadXML() is + * called). The difference between this method and the setContent() + * method defined later is that this one is used to set the content + * when the node in question has more nodes under it and so on and + * not directly text content. While in the other one is used when + * the node in question directly has text inside it. + * + */ +p5.XML.prototype._setCont = function(content) { + var str; + str = content; + str = str.replace(/\s\s+/g, ','); + //str = str.split(','); + this.content = str; +}; + +/** + * This method is called while the parsing of XML (when loadXML() is + * called). The XML node is passed and its attributes are stored in the + * p5.XML's attribute Object. + * + */ +p5.XML.prototype._setAttributes = function(node) { + var i, att = {}; + for( i = 0; i < node.attributes.length; i++) { + att[node.attributes[i].nodeName] = node.attributes[i].nodeValue; + } + this.attributes = att; +}; + +module.exports = p5.XML; +},{"../core/core":37}],63:[function(_dereq_,module,exports){ +/** + * @module Math + * @submodule Calculation + * @for p5 + * @requires core + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); + +/** + * Calculates the absolute value (magnitude) of a number. Maps to Math.abs(). + * The absolute value of a number is always positive. + * + * @method abs + * @param {Number} n number to compute + * @return {Number} absolute value of given number + * @example + * <div class = "norender"><code> + * function setup() { + * var x = -3; + * var y = abs(x); + * + * print(x); // -3 + * print(y); // 3 + * } + * </code></div> + * + * @alt + * no image displayed + * + */ +p5.prototype.abs = Math.abs; + +/** + * Calculates the closest int value that is greater than or equal to the + * value of the parameter. Maps to Math.ceil(). For example, ceil(9.03) + * returns the value 10. + * + * @method ceil + * @param {Number} n number to round up + * @return {Number} rounded up number + * @example + * <div><code> + * function draw() { + * background(200); + * // map, mouseX between 0 and 5. + * var ax = map(mouseX, 0, 100, 0, 5); + * var ay = 66; + * + * //Get the ceiling of the mapped number. + * var bx = ceil(map(mouseX, 0, 100, 0,5)); + * var by = 33; + * + * // Multiply the mapped numbers by 20 to more easily + * // see the changes. + * stroke(0); + * fill(0); + * line(0, ay, ax * 20, ay); + * line(0, by, bx * 20, by); + * + * // Reformat the float returned by map and draw it. + * noStroke(); + * text(nfc(ax, 2,2), ax, ay - 5); + * text(nfc(bx,1,1), bx, by - 5); + * } + * </code></div> + * + * @alt + * 2 horizontal lines & number sets. increase with mouse x. bottom to 2 decimals + * + */ +p5.prototype.ceil = Math.ceil; + +/** + * Constrains a value between a minimum and maximum value. + * + * @method constrain + * @param {Number} n number to constrain + * @param {Number} low minimum limit + * @param {Number} high maximum limit + * @return {Number} constrained number + * @example + * <div><code> + * function draw() { + * background(200); + * + * var leftWall = 25; + * var rightWall = 75; + * + * // xm is just the mouseX, while + * // xc is the mouseX, but constrained + * // between the leftWall and rightWall! + * var xm = mouseX; + * var xc = constrain(mouseX, leftWall, rightWall); + * + * // Draw the walls. + * stroke(150); + * line(leftWall, 0, leftWall, height); + * line(rightWall, 0, rightWall, height); + * + * // Draw xm and xc as circles. + * noStroke(); + * fill(150); + * ellipse(xm, 33, 9,9); // Not Constrained + * fill(0); + * ellipse(xc, 66, 9,9); // Constrained + * } + * </code></div> + * + * @alt + * 2 vertical lines. 2 ellipses move with mouse X 1 does not move passed lines + * + */ +p5.prototype.constrain = function(n, low, high) { + return Math.max(Math.min(n, high), low); +}; + +/** + * Calculates the distance between two points. + * + * @method dist + * @param {Number} x1 x-coordinate of the first point + * @param {Number} y1 y-coordinate of the first point + * @param {Number} [z1] z-coordinate of the first point + * @param {Number} x2 x-coordinate of the second point + * @param {Number} y2 y-coordinate of the second point + * @param {Number} [z2] z-coordinate of the second point + * @return {Number} distance between the two points + * @example + * <div><code> + * // Move your mouse inside the canvas to see the + * // change in distance between two points! + * function draw() { + * background(200); + * fill(0); + * + * var x1 = 10; + * var y1 = 90; + * var x2 = mouseX; + * var y2 = mouseY; + * + * line(x1, y1, x2, y2); + * ellipse(x1, y1, 7, 7); + * ellipse(x2, y2, 7, 7); + * + * // d is the length of the line + * // the distance from point 1 to point 2. + * var d = int(dist(x1, y1, x2, y2)); + * + * // Let's write d along the line we are drawing! + * push(); + * translate( (x1+x2)/2, (y1+y2)/2 ); + * rotate( atan2(y2-y1,x2-x1) ); + * text(nfc(d,1,1), 0, -5); + * pop(); + * // Fancy! + * } + * </code></div> + * + * @alt + * 2 ellipses joined by line. 1 ellipse moves with mouse X&Y. Distance displayed. + * + */ +p5.prototype.dist = function(x1, y1, z1, x2, y2, z2) { + if (arguments.length === 4) { + // In the case of 2d: z1 means x2 and x2 means y2 + return Math.sqrt( (z1-x1)*(z1-x1) + (x2-y1)*(x2-y1) ); + } else if (arguments.length === 6) { + return Math.sqrt( (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) + (z2-z1)*(z2-z1) ); + } +}; + +/** + * Returns Euler's number e (2.71828...) raised to the power of the n + * parameter. Maps to Math.exp(). + * + * @method exp + * @param {Number} n exponent to raise + * @return {Number} e^n + * @example + * <div><code> + * function draw() { + * background(200); + * + * // Compute the exp() function with a value between 0 and 2 + * var xValue = map(mouseX, 0, width, 0, 2); + * var yValue = exp(xValue); + * + * var y = map(yValue, 0, 8, height, 0); + * + * var legend = "exp (" + nfc(xValue, 3) +")\n= " + nf(yValue, 1, 4); + * stroke(150); + * line(mouseX, y, mouseX, height); + * fill(0); + * text(legend, 5, 15); + * noStroke(); + * ellipse (mouseX,y, 7, 7); + * + * // Draw the exp(x) curve, + * // over the domain of x from 0 to 2 + * noFill(); + * stroke(0); + * beginShape(); + * for (var x = 0; x < width; x++) { + * xValue = map(x, 0, width, 0, 2); + * yValue = exp(xValue); + * y = map(yValue, 0, 8, height, 0); + * vertex(x, y); + * } + * + * endShape(); + * line(0, 0, 0, height); + * line(0, height-1, width, height-1); + * } + * </code></div> + * + * @alt + * ellipse moves along a curve with mouse x. e^n displayed. + * + */ +p5.prototype.exp = Math.exp; + +/** + * Calculates the closest int value that is less than or equal to the + * value of the parameter. Maps to Math.floor(). + * + * @method floor + * @param {Number} n number to round down + * @return {Number} rounded down number + * @example + * <div><code> + * function draw() { + * background(200); + * //map, mouseX between 0 and 5. + * var ax = map(mouseX, 0, 100, 0, 5); + * var ay = 66; + * + * //Get the floor of the mapped number. + * var bx = floor(map(mouseX, 0, 100, 0,5)); + * var by = 33; + * + * // Multiply the mapped numbers by 20 to more easily + * // see the changes. + * stroke(0); + * fill(0); + * line(0, ay, ax * 20, ay); + * line(0, by, bx * 20, by); + * + * // Reformat the float returned by map and draw it. + * noStroke(); + * text(nfc(ax, 2,2), ax, ay - 5); + * text(nfc(bx,1,1), bx, by - 5); + * } + * </code></div> + * + * @alt + * 2 horizontal lines & number sets. increase with mouse x. bottom to 2 decimals + * + */ +p5.prototype.floor = Math.floor; + +/** + * Calculates a number between two numbers at a specific increment. The amt + * parameter is the amount to interpolate between the two values where 0.0 + * equal to the first point, 0.1 is very near the first point, 0.5 is + * half-way in between, etc. The lerp function is convenient for creating + * motion along a straight path and for drawing dotted lines. + * + * @method lerp + * @param {Number} start first value + * @param {Number} stop second value + * @param {Number} amt number between 0.0 and 1.0 + * @return {Number} lerped value + * @example + * <div><code> + * function setup() { + * background(200); + * var a = 20; + * var b = 80; + * var c = lerp(a,b, .2); + * var d = lerp(a,b, .5); + * var e = lerp(a,b, .8); + * + * var y = 50 + * + * strokeWeight(5); + * stroke(0); // Draw the original points in black + * point(a, y); + * point(b, y); + * + * stroke(100); // Draw the lerp points in gray + * point(c, y); + * point(d, y); + * point(e, y); + * } + * </code></div> + * + * @alt + * 5 points horizontally staggered mid-canvas. mid 3 are grey, outer black + * + */ +p5.prototype.lerp = function(start, stop, amt) { + return amt*(stop-start)+start; +}; + +/** + * Calculates the natural logarithm (the base-e logarithm) of a number. This + * function expects the n parameter to be a value greater than 0.0. Maps to + * Math.log(). + * + * @method log + * @param {Number} n number greater than 0 + * @return {Number} natural logarithm of n + * @example + * <div><code> + * function draw() { + * background(200); + * var maxX = 2.8; + * var maxY = 1.5; + * + * // Compute the natural log of a value between 0 and maxX + * var xValue = map(mouseX, 0, width, 0, maxX); + * if (xValue > 0) { // Cannot take the log of a negative number. + * var yValue = log(xValue); + * var y = map(yValue, -maxY, maxY, height, 0); + * + * // Display the calculation occurring. + * var legend = "log(" + nf(xValue, 1, 2) + ")\n= " + nf(yValue, 1, 3); + * stroke(150); + * line(mouseX, y, mouseX, height); + * fill(0); + * text (legend, 5, 15); + * noStroke(); + * ellipse (mouseX, y, 7, 7); + * } + * + * // Draw the log(x) curve, + * // over the domain of x from 0 to maxX + * noFill(); + * stroke(0); + * beginShape(); + * for(var x=0; x < width; x++) { + * xValue = map(x, 0, width, 0, maxX); + * yValue = log(xValue); + * y = map(yValue, -maxY, maxY, height, 0); + * vertex(x, y); + * } + * endShape(); + * line(0,0,0,height); + * line(0,height/2,width, height/2); + * } + * </code></div> + * + * @alt + * ellipse moves along a curve with mouse x. natural logarithm of n displayed. + * + */ +p5.prototype.log = Math.log; + +/** + * Calculates the magnitude (or length) of a vector. A vector is a direction + * in space commonly used in computer graphics and linear algebra. Because it + * has no "start" position, the magnitude of a vector can be thought of as + * the distance from the coordinate 0,0 to its x,y value. Therefore, mag() is + * a shortcut for writing dist(0, 0, x, y). + * + * @method mag + * @param {Number} a first value + * @param {Number} b second value + * @return {Number} magnitude of vector from (0,0) to (a,b) + * @example + * <div><code> + * function setup() { + * var x1 = 20; + * var x2 = 80; + * var y1 = 30; + * var y2 = 70; + * + * line(0, 0, x1, y1); + * print(mag(x1, y1)); // Prints "36.05551" + * line(0, 0, x2, y1); + * print(mag(x2, y1)); // Prints "85.44004" + * line(0, 0, x1, y2); + * print(mag(x1, y2)); // Prints "72.8011" + * line(0, 0, x2, y2); + * print(mag(x2, y2)); // Prints "106.30146" + * } + * </code></div> + * + * @alt + * 4 lines of different length radiate from top left of canvas. + * + */ +p5.prototype.mag = function(x, y) { + return Math.sqrt(x*x+y*y); +}; + +/** + * Re-maps a number from one range to another. + * <br><br> + * In the first example above, the number 25 is converted from a value in the + * range of 0 to 100 into a value that ranges from the left edge of the + * window (0) to the right edge (width). + * + * @method map + * @param {Number} value the incoming value to be converted + * @param {Number} start1 lower bound of the value's current range + * @param {Number} stop1 upper bound of the value's current range + * @param {Number} start2 lower bound of the value's target range + * @param {Number} stop2 upper bound of the value's target range + * @return {Number} remapped number + * @example + * <div><code> + * var value = 25; + * var m = map(value, 0, 100, 0, width); + * ellipse(m, 50, 10, 10); + * </code></div> + * + * <div><code> + * function setup() { + * noStroke(); + * } + * + * function draw() { + * background(204); + * var x1 = map(mouseX, 0, width, 25, 75); + * ellipse(x1, 25, 25, 25); + * var x2 = map(mouseX, 0, width, 0, 100); + * ellipse(x2, 75, 25, 25); + * } + * </code></div> + * + * @alt + * 10 by 10 white ellipse with in mid left canvas + * 2 25 by 25 white ellipses move with mouse x. Bottom has more range from X + * + */ +p5.prototype.map = function(n, start1, stop1, start2, stop2) { + return ((n-start1)/(stop1-start1))*(stop2-start2)+start2; +}; + +/** + * Determines the largest value in a sequence of numbers, and then returns + * that value. max() accepts any number of Number parameters, or an Array + * of any length. + * + * @method max + * @param {Number|Array} n0 Numbers to compare + * @return {Number} maximum Number + * @example + * <div><code> + * function setup() { + * // Change the elements in the array and run the sketch + * // to show how max() works! + * numArray = new Array(2,1,5,4,8,9); + * fill(0); + * noStroke(); + * text("Array Elements", 0, 10); + * // Draw all numbers in the array + * var spacing = 15; + * var elemsY = 25; + * for(var i = 0; i < numArray.length; i++) { + * text(numArray[i], i * spacing, elemsY); + * } + * maxX = 33; + * maxY = 80; + * // Draw the Maximum value in the array. + * textSize(32); + * text(max(numArray), maxX, maxY); + * } + * </code></div> + * + * @alt + * Small text at top reads: Array Elements 2 1 5 4 8 9. Large text at center: 9 + * + */ +p5.prototype.max = function() { + if (arguments[0] instanceof Array) { + return Math.max.apply(null,arguments[0]); + } else { + return Math.max.apply(null,arguments); + } +}; + +/** + * Determines the smallest value in a sequence of numbers, and then returns + * that value. min() accepts any number of Number parameters, or an Array + * of any length. + * + * @method min + * @param {Number|Array} n0 Numbers to compare + * @return {Number} minimum Number + * @example + * <div><code> + * function setup() { + * // Change the elements in the array and run the sketch + * // to show how min() works! + * numArray = new Array(2,1,5,4,8,9); + * fill(0); + * noStroke(); + * text("Array Elements", 0, 10); + * // Draw all numbers in the array + * var spacing = 15; + * var elemsY = 25; + * for(var i = 0; i < numArray.length; i++) { + * text(numArray[i], i * spacing, elemsY); + * } + * maxX = 33; + * maxY = 80; + * // Draw the Minimum value in the array. + * textSize(32); + * text(min(numArray), maxX, maxY); + * } + * </code></div> + * + * @alt + * Small text at top reads: Array Elements 2 1 5 4 8 9. Large text at center: 1 + * + */ +p5.prototype.min = function() { + if (arguments[0] instanceof Array) { + return Math.min.apply(null,arguments[0]); + } else { + return Math.min.apply(null,arguments); + } +}; + +/** + * Normalizes a number from another range into a value between 0 and 1. + * Identical to map(value, low, high, 0, 1). + * Numbers outside of the range are not clamped to 0 and 1, because + * out-of-range values are often intentional and useful. (See the second + * example above.) + * + * @method norm + * @param {Number} value incoming value to be normalized + * @param {Number} start lower bound of the value's current range + * @param {Number} stop upper bound of the value's current range + * @return {Number} normalized number + * @example + * <div><code> + * function draw() { + * background(200); + * currentNum = mouseX; + * lowerBound = 0; + * upperBound = width; //100; + * normalized = norm(currentNum, lowerBound, upperBound); + * lineY = 70 + * line(0, lineY, width, lineY); + * //Draw an ellipse mapped to the non-normalized value. + * noStroke(); + * fill(50) + * var s = 7; // ellipse size + * ellipse(currentNum, lineY, s, s); + * + * // Draw the guide + * guideY = lineY + 15; + * text("0", 0, guideY); + * textAlign(RIGHT); + * text("100", width, guideY); + * + * // Draw the normalized value + * textAlign(LEFT); + * fill(0); + * textSize(32); + * normalY = 40; + * normalX = 20; + * text(normalized, normalX, normalY); + * } + * </code></div> + * + * @alt + * ellipse moves with mouse. 0 shown left & 100 right and updating values center + * + */ +p5.prototype.norm = function(n, start, stop) { + return this.map(n, start, stop, 0, 1); +}; + +/** + * Facilitates exponential expressions. The pow() function is an efficient + * way of multiplying numbers by themselves (or their reciprocals) in large + * quantities. For example, pow(3, 5) is equivalent to the expression + * 3*3*3*3*3 and pow(3, -5) is equivalent to 1 / 3*3*3*3*3. Maps to + * Math.pow(). + * + * @method pow + * @param {Number} n base of the exponential expression + * @param {Number} e power by which to raise the base + * @return {Number} n^e + * @example + * <div><code> + * function setup() { + * //Exponentially increase the size of an ellipse. + * eSize = 3; // Original Size + * eLoc = 10; // Original Location + * + * ellipse(eLoc, eLoc, eSize, eSize); + * + * ellipse(eLoc*2, eLoc*2, pow(eSize, 2), pow(eSize, 2)); + * + * ellipse(eLoc*4, eLoc*4, pow(eSize, 3), pow(eSize, 3)); + * + * ellipse(eLoc*8, eLoc*8, pow(eSize, 4), pow(eSize, 4)); + * } + * </code></div> + * + * @alt + * small to large ellipses radiating from top left of canvas + * + */ +p5.prototype.pow = Math.pow; + +/** + * Calculates the integer closest to the n parameter. For example, + * round(133.8) returns the value 134. Maps to Math.round(). + * + * @method round + * @param {Number} n number to round + * @return {Number} rounded number + * @example + * <div><code> + * function draw() { + * background(200); + * //map, mouseX between 0 and 5. + * var ax = map(mouseX, 0, 100, 0, 5); + * var ay = 66; + * + * // Round the mapped number. + * var bx = round(map(mouseX, 0, 100, 0,5)); + * var by = 33; + * + * // Multiply the mapped numbers by 20 to more easily + * // see the changes. + * stroke(0); + * fill(0); + * line(0, ay, ax * 20, ay); + * line(0, by, bx * 20, by); + * + * // Reformat the float returned by map and draw it. + * noStroke(); + * text(nfc(ax, 2,2), ax, ay - 5); + * text(nfc(bx,1,1), bx, by - 5); + * } + * </code></div> + * + * @alt + * horizontal center line squared values displayed on top and regular on bottom. + * + */ +p5.prototype.round = Math.round; + +/** + * Squares a number (multiplies a number by itself). The result is always a + * positive number, as multiplying two negative numbers always yields a + * positive result. For example, -1 * -1 = 1. + * + * @method sq + * @param {Number} n number to square + * @return {Number} squared number + * @example + * <div><code> + * function draw() { + * background(200); + * eSize = 7; + * x1 = map(mouseX, 0, width, 0, 10); + * y1 = 80; + * x2 = sq(x1); + * y2 = 20; + * + * // Draw the non-squared. + * line(0, y1, width, y1); + * ellipse(x1, y1, eSize, eSize); + * + * // Draw the squared. + * line(0, y2, width, y2); + * ellipse(x2, y2, eSize, eSize); + * + * // Draw dividing line. + * stroke(100) + * line(0, height/2, width, height/2); + * + * // Draw text. + * var spacing = 15; + * noStroke(); + * fill(0); + * text("x = " + x1, 0, y1 + spacing); + * text("sq(x) = " + x2, 0, y2 + spacing); + * } + * </code></div> + * + * @alt + * horizontal center line squared values displayed on top and regular on bottom. + * + */ +p5.prototype.sq = function(n) { return n*n; }; + +/** + * Calculates the square root of a number. The square root of a number is + * always positive, even though there may be a valid negative root. The + * square root s of number a is such that s*s = a. It is the opposite of + * squaring. Maps to Math.sqrt(). + * + * @method sqrt + * @param {Number} n non-negative number to square root + * @return {Number} square root of number + * @example + * <div><code> + * function draw() { + * background(200); + * eSize = 7; + * x1 = mouseX; + * y1 = 80; + * x2 = sqrt(x1); + * y2 = 20; + * + * // Draw the non-squared. + * line(0, y1, width, y1); + * ellipse(x1, y1, eSize, eSize); + * + * // Draw the squared. + * line(0, y2, width, y2); + * ellipse(x2, y2, eSize, eSize); + * + * // Draw dividing line. + * stroke(100) + * line(0, height/2, width, height/2); + * + * // Draw text. + * noStroke(); + * fill(0); + * var spacing = 15; + * text("x = " + x1, 0, y1 + spacing); + * text("sqrt(x) = " + x2, 0, y2 + spacing); + * } + * </code></div> + * + * @alt + * horizontal center line squareroot values displayed on top and regular on bottom. + * + */ +p5.prototype.sqrt = Math.sqrt; + +module.exports = p5; + +},{"../core/core":37}],64:[function(_dereq_,module,exports){ +/** + * @module Math + * @submodule Math + * @for p5 + * @requires core + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); + + +/** + * Creates a new p5.Vector (the datatype for storing vectors). This provides a + * two or three dimensional vector, specifically a Euclidean (also known as + * geometric) vector. A vector is an entity that has both magnitude and + * direction. + * + * @method createVector + * @param {Number} [x] x component of the vector + * @param {Number} [y] y component of the vector + * @param {Number} [z] z component of the vector + */ +p5.prototype.createVector = function (x, y, z) { + if (this instanceof p5) { + return new p5.Vector(this, arguments); + } else { + return new p5.Vector(x, y, z); + } +}; + +module.exports = p5; + +},{"../core/core":37}],65:[function(_dereq_,module,exports){ +////////////////////////////////////////////////////////////// + +// http://mrl.nyu.edu/~perlin/noise/ +// Adapting from PApplet.java +// which was adapted from toxi +// which was adapted from the german demo group farbrausch +// as used in their demo "art": http://www.farb-rausch.de/fr010src.zip + +// someday we might consider using "improved noise" +// http://mrl.nyu.edu/~perlin/paper445.pdf +// See: https://github.com/shiffman/The-Nature-of-Code-Examples-p5.js/ +// blob/master/introduction/Noise1D/noise.js + +/** + * @module Math + * @submodule Noise + * @for p5 + * @requires core + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); + +var PERLIN_YWRAPB = 4; +var PERLIN_YWRAP = 1<<PERLIN_YWRAPB; +var PERLIN_ZWRAPB = 8; +var PERLIN_ZWRAP = 1<<PERLIN_ZWRAPB; +var PERLIN_SIZE = 4095; + +var perlin_octaves = 4; // default to medium smooth +var perlin_amp_falloff = 0.5; // 50% reduction/octave + +var scaled_cosine = function(i) { + return 0.5*(1.0-Math.cos(i*Math.PI)); +}; + +var perlin; // will be initialized lazily by noise() or noiseSeed() + + +/** + * Returns the Perlin noise value at specified coordinates. Perlin noise is + * a random sequence generator producing a more natural ordered, harmonic + * succession of numbers compared to the standard <b>random()</b> function. + * It was invented by Ken Perlin in the 1980s and been used since in + * graphical applications to produce procedural textures, natural motion, + * shapes, terrains etc.<br /><br /> The main difference to the + * <b>random()</b> function is that Perlin noise is defined in an infinite + * n-dimensional space where each pair of coordinates corresponds to a + * fixed semi-random value (fixed only for the lifespan of the program; see + * the noiseSeed() function). p5.js can compute 1D, 2D and 3D noise, + * depending on the number of coordinates given. The resulting value will + * always be between 0.0 and 1.0. The noise value can be animated by moving + * through the noise space as demonstrated in the example above. The 2nd + * and 3rd dimension can also be interpreted as time.<br /><br />The actual + * noise is structured similar to an audio signal, in respect to the + * function's use of frequencies. Similar to the concept of harmonics in + * physics, perlin noise is computed over several octaves which are added + * together for the final result. <br /><br />Another way to adjust the + * character of the resulting sequence is the scale of the input + * coordinates. As the function works within an infinite space the value of + * the coordinates doesn't matter as such, only the distance between + * successive coordinates does (eg. when using <b>noise()</b> within a + * loop). As a general rule the smaller the difference between coordinates, + * the smoother the resulting noise sequence will be. Steps of 0.005-0.03 + * work best for most applications, but this will differ depending on use. + * + * + * @method noise + * @param {Number} x x-coordinate in noise space + * @param {Number} y y-coordinate in noise space + * @param {Number} z z-coordinate in noise space + * @return {Number} Perlin noise value (between 0 and 1) at specified + * coordinates + * @example + * <div> + * <code>var xoff = 0.0; + * + * function draw() { + * background(204); + * xoff = xoff + .01; + * var n = noise(xoff) * width; + * line(n, 0, n, height); + * } + * </code> + * </div> + * <div> + * <code>var noiseScale=0.02; + * + * function draw() { + * background(0); + * for (var x=0; x < width; x++) { + * var noiseVal = noise((mouseX+x)*noiseScale, mouseY*noiseScale); + * stroke(noiseVal*255); + * line(x, mouseY+noiseVal*80, x, height); + * } + * } + * </code> + * </div> + * + * @alt + * vertical line moves left to right with updating noise values. + * horizontal wave pattern effected by mouse x-position & updating noise values. + * + */ + +p5.prototype.noise = function(x,y,z) { + y = y || 0; + z = z || 0; + + if (perlin == null) { + perlin = new Array(PERLIN_SIZE + 1); + for (var i = 0; i < PERLIN_SIZE + 1; i++) { + perlin[i] = Math.random(); + } + } + + if (x<0) { x=-x; } + if (y<0) { y=-y; } + if (z<0) { z=-z; } + + var xi=Math.floor(x), yi=Math.floor(y), zi=Math.floor(z); + var xf = x - xi; + var yf = y - yi; + var zf = z - zi; + var rxf, ryf; + + var r=0; + var ampl=0.5; + + var n1,n2,n3; + + for (var o=0; o<perlin_octaves; o++) { + var of=xi+(yi<<PERLIN_YWRAPB)+(zi<<PERLIN_ZWRAPB); + + rxf = scaled_cosine(xf); + ryf = scaled_cosine(yf); + + n1 = perlin[of&PERLIN_SIZE]; + n1 += rxf*(perlin[(of+1)&PERLIN_SIZE]-n1); + n2 = perlin[(of+PERLIN_YWRAP)&PERLIN_SIZE]; + n2 += rxf*(perlin[(of+PERLIN_YWRAP+1)&PERLIN_SIZE]-n2); + n1 += ryf*(n2-n1); + + of += PERLIN_ZWRAP; + n2 = perlin[of&PERLIN_SIZE]; + n2 += rxf*(perlin[(of+1)&PERLIN_SIZE]-n2); + n3 = perlin[(of+PERLIN_YWRAP)&PERLIN_SIZE]; + n3 += rxf*(perlin[(of+PERLIN_YWRAP+1)&PERLIN_SIZE]-n3); + n2 += ryf*(n3-n2); + + n1 += scaled_cosine(zf)*(n2-n1); + + r += n1*ampl; + ampl *= perlin_amp_falloff; + xi<<=1; + xf*=2; + yi<<=1; + yf*=2; + zi<<=1; + zf*=2; + + if (xf>=1.0) { xi++; xf--; } + if (yf>=1.0) { yi++; yf--; } + if (zf>=1.0) { zi++; zf--; } + } + return r; +}; + + +/** + * + * Adjusts the character and level of detail produced by the Perlin noise + * function. Similar to harmonics in physics, noise is computed over + * several octaves. Lower octaves contribute more to the output signal and + * as such define the overall intensity of the noise, whereas higher octaves + * create finer grained details in the noise sequence. + * <br><br> + * By default, noise is computed over 4 octaves with each octave contributing + * exactly half than its predecessor, starting at 50% strength for the 1st + * octave. This falloff amount can be changed by adding an additional function + * parameter. Eg. a falloff factor of 0.75 means each octave will now have + * 75% impact (25% less) of the previous lower octave. Any value between + * 0.0 and 1.0 is valid, however note that values greater than 0.5 might + * result in greater than 1.0 values returned by <b>noise()</b>. + * <br><br> + * By changing these parameters, the signal created by the <b>noise()</b> + * function can be adapted to fit very specific needs and characteristics. + * + * @method noiseDetail + * @param {Number} lod number of octaves to be used by the noise + * @param {Number} falloff falloff factor for each octave + * @example + * <div> + * <code> + * + * var noiseVal; + * var noiseScale=0.02; + * + * function setup() { + * createCanvas(100,100); + * } + * + * function draw() { + * background(0); + * for (var y = 0; y < height; y++) { + * for (var x = 0; x < width/2; x++) { + * noiseDetail(2,0.2); + * noiseVal = noise((mouseX+x) * noiseScale, + * (mouseY+y) * noiseScale); + * stroke(noiseVal*255); + * point(x,y); + * noiseDetail(8,0.65); + * noiseVal = noise((mouseX + x + width/2) * noiseScale, + * (mouseY + y) * noiseScale); + * stroke(noiseVal*255); + * point(x + width/2, y); + * } + * } + * } + * </code> + * </div> + * + * @alt + * 2 vertical grey smokey patterns affected my mouse x-position and noise. + * + */ +p5.prototype.noiseDetail = function(lod, falloff) { + if (lod>0) { perlin_octaves=lod; } + if (falloff>0) { perlin_amp_falloff=falloff; } +}; + +/** + * Sets the seed value for <b>noise()</b>. By default, <b>noise()</b> + * produces different results each time the program is run. Set the + * <b>value</b> parameter to a constant to return the same pseudo-random + * numbers each time the software is run. + * + * @method noiseSeed + * @param {Number} seed the seed value + * @example + * <div> + * <code>var xoff = 0.0; + * + * function setup() { + * noiseSeed(99); + * stroke(0, 10); + * } + * + * function draw() { + * xoff = xoff + .01; + * var n = noise(xoff) * width; + * line(n, 0, n, height); + * } + * </code> + * </div> + * + * @alt + * vertical grey lines drawing in pattern affected by noise. + * + */ +p5.prototype.noiseSeed = function(seed) { + // Linear Congruential Generator + // Variant of a Lehman Generator + var lcg = (function() { + // Set to values from http://en.wikipedia.org/wiki/Numerical_Recipes + // m is basically chosen to be large (as it is the max period) + // and for its relationships to a and c + var m = 4294967296, + // a - 1 should be divisible by m's prime factors + a = 1664525, + // c and m should be co-prime + c = 1013904223, + seed, z; + return { + setSeed : function(val) { + // pick a random seed if val is undefined or null + // the >>> 0 casts the seed to an unsigned 32-bit integer + z = seed = (val == null ? Math.random() * m : val) >>> 0; + }, + getSeed : function() { + return seed; + }, + rand : function() { + // define the recurrence relationship + z = (a * z + c) % m; + // return a float in [0, 1) + // if z = m then z / m = 0 therefore (z % m) / m < 1 always + return z / m; + } + }; + }()); + + lcg.setSeed(seed); + perlin = new Array(PERLIN_SIZE + 1); + for (var i = 0; i < PERLIN_SIZE + 1; i++) { + perlin[i] = lcg.rand(); + } +}; + +module.exports = p5; + +},{"../core/core":37}],66:[function(_dereq_,module,exports){ +/** + * @module Math + * @submodule Math + * @requires constants + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); +var polarGeometry = _dereq_('./polargeometry'); +var constants = _dereq_('../core/constants'); + +/** + * A class to describe a two or three dimensional vector, specifically + * a Euclidean (also known as geometric) vector. A vector is an entity + * that has both magnitude and direction. The datatype, however, stores + * the components of the vector (x, y for 2D, and x, y, z for 3D). The magnitude + * and direction can be accessed via the methods mag() and heading(). + * <br><br> + * In many of the p5.js examples, you will see p5.Vector used to describe a + * position, velocity, or acceleration. For example, if you consider a rectangle + * moving across the screen, at any given instant it has a position (a vector + * that points from the origin to its location), a velocity (the rate at which + * the object's position changes per time unit, expressed as a vector), and + * acceleration (the rate at which the object's velocity changes per time + * unit, expressed as a vector). + * <br><br> + * Since vectors represent groupings of values, we cannot simply use + * traditional addition/multiplication/etc. Instead, we'll need to do some + * "vector" math, which is made easy by the methods inside the p5.Vector class. + * + * @class p5.Vector + * @constructor + * @param {Number} [x] x component of the vector + * @param {Number} [y] y component of the vector + * @param {Number} [z] z component of the vector + * @example + * <div> + * <code> + * var v1 = createVector(40, 50); + * var v2 = createVector(40, 50); + * + * ellipse(v1.x, v1.y, 50, 50); + * ellipse(v2.x, v2.y, 50, 50); + * v1.add(v2); + * ellipse(v1.x, v1.y, 50, 50); + * </code> + * </div> + * + * @alt + * 2 white ellipses. One center-left the other bottom right and off canvas + * + */ +p5.Vector = function() { + var x,y,z; + // This is how it comes in with createVector() + if(arguments[0] instanceof p5) { + // save reference to p5 if passed in + this.p5 = arguments[0]; + x = arguments[1][0] || 0; + y = arguments[1][1] || 0; + z = arguments[1][2] || 0; + // This is what we'll get with new p5.Vector() + } else { + x = arguments[0] || 0; + y = arguments[1] || 0; + z = arguments[2] || 0; + } + /** + * The x component of the vector + * @property x + * @type {Number} + */ + this.x = x; + /** + * The y component of the vector + * @property y + * @type {Number} + */ + this.y = y; + /** + * The z component of the vector + * @property z + * @type {Number} + */ + this.z = z; +}; + +/** + * Returns a string representation of a vector v by calling String(v) + * or v.toString(). This method is useful for logging vectors in the + * console. + * @method toString + * @example + * <div class = "norender"><code> + * function setup() { + * var v = createVector(20,30); + * print(String(v)); // prints "p5.Vector Object : [20, 30, 0]" + * } + * </div></code> + * + */ +p5.Vector.prototype.toString = function p5VectorToString() { + return 'p5.Vector Object : ['+ this.x +', '+ this.y +', '+ this.z + ']'; +}; + +/** + * Sets the x, y, and z component of the vector using two or three separate + * variables, the data from a p5.Vector, or the values from a float array. + * @method set + * + * @param {Number|p5.Vector|Array} [x] the x component of the vector or a + * p5.Vector or an Array + * @param {Number} [y] the y component of the vector + * @param {Number} [z] the z component of the vector + * @example + * <div class="norender"> + * <code> + * function setup() { + * var v = createVector(1, 2, 3); + * v.set(4,5,6); // Sets vector to [4, 5, 6] + * + * var v1 = createVector(0, 0, 0); + * var arr = [1, 2, 3]; + * v1.set(arr); // Sets vector to [1, 2, 3] + * } + * </code> + * </div> + */ +p5.Vector.prototype.set = function (x, y, z) { + if (x instanceof p5.Vector) { + this.x = x.x || 0; + this.y = x.y || 0; + this.z = x.z || 0; + return this; + } + if (x instanceof Array) { + this.x = x[0] || 0; + this.y = x[1] || 0; + this.z = x[2] || 0; + return this; + } + this.x = x || 0; + this.y = y || 0; + this.z = z || 0; + return this; +}; + +/** + * Gets a copy of the vector, returns a p5.Vector object. + * + * @method copy + * @return {p5.Vector} the copy of the p5.Vector object + * @example + * <div class="norender"> + * <code> + * var v1 = createVector(1, 2, 3); + * var v2 = v1.copy(); + * print(v1.x == v2.x && v1.y == v2.y && v1.z == v2.z); + * // Prints "true" + * </code> + * </div> + */ +p5.Vector.prototype.copy = function () { + if (this.p5) { + return new p5.Vector(this.p5,[this.x, this.y, this.z]); + } else { + return new p5.Vector(this.x,this.y,this.z); + } +}; + +/** + * Adds x, y, and z components to a vector, adds one vector to another, or + * adds two independent vectors together. The version of the method that adds + * two vectors together is a static method and returns a p5.Vector, the others + * acts directly on the vector. See the examples for more context. + * + * @method add + * @chainable + * @param {Number|p5.Vector|Array} x the x component of the vector to be + * added or a p5.Vector or an Array + * @param {Number} [y] the y component of the vector to be + * added + * @param {Number} [z] the z component of the vector to be + * added + * @return {p5.Vector} the p5.Vector object. + * @example + * <div class="norender"> + * <code> + * var v = createVector(1, 2, 3); + * v.add(4,5,6); + * // v's compnents are set to [5, 7, 9] + * </code> + * </div> + * <div class="norender"> + * <code> + * // Static method + * var v1 = createVector(1, 2, 3); + * var v2 = createVector(2, 3, 4); + * + * var v3 = p5.Vector.add(v1, v2); + * // v3 has components [3, 5, 7] + * </code> + * </div> + */ +p5.Vector.prototype.add = function (x, y, z) { + if (x instanceof p5.Vector) { + this.x += x.x || 0; + this.y += x.y || 0; + this.z += x.z || 0; + return this; + } + if (x instanceof Array) { + this.x += x[0] || 0; + this.y += x[1] || 0; + this.z += x[2] || 0; + return this; + } + this.x += x || 0; + this.y += y || 0; + this.z += z || 0; + return this; +}; + +/** + * Subtracts x, y, and z components from a vector, subtracts one vector from + * another, or subtracts two independent vectors. The version of the method + * that subtracts two vectors is a static method and returns a p5.Vector, the + * other acts directly on the vector. See the examples for more context. + * + * @method sub + * @chainable + * @param {Number|p5.Vector|Array} x the x component of the vector or a + * p5.Vector or an Array + * @param {Number} [y] the y component of the vector + * @param {Number} [z] the z component of the vector + * @return {p5.Vector} p5.Vector object. + * @example + * <div class="norender"> + * <code> + * var v = createVector(4, 5, 6); + * v.sub(1, 1, 1); + * // v's compnents are set to [3, 4, 5] + * </code> + * </div> + * + * <div class="norender"> + * <code> + * // Static method + * var v1 = createVector(2, 3, 4); + * var v2 = createVector(1, 2, 3); + * + * var v3 = p5.Vector.sub(v1, v2); + * // v3 has compnents [1, 1, 1] + * </code> + * </div> + */ +p5.Vector.prototype.sub = function (x, y, z) { + if (x instanceof p5.Vector) { + this.x -= x.x || 0; + this.y -= x.y || 0; + this.z -= x.z || 0; + return this; + } + if (x instanceof Array) { + this.x -= x[0] || 0; + this.y -= x[1] || 0; + this.z -= x[2] || 0; + return this; + } + this.x -= x || 0; + this.y -= y || 0; + this.z -= z || 0; + return this; +}; + +/** + * Multiply the vector by a scalar. The static version of this method + * creates a new p5.Vector while the non static version acts on the vector + * directly. See the examples for more context. + * + * @method mult + * @chainable + * @param {Number} n the number to multiply with the vector + * @return {p5.Vector} a reference to the p5.Vector object (allow chaining) + * @example + * <div class="norender"> + * <code> + * var v = createVector(1, 2, 3); + * v.mult(2); + * // v's compnents are set to [2, 4, 6] + * </code> + * </div> + * + * <div class="norender"> + * <code> + * // Static method + * var v1 = createVector(1, 2, 3); + * var v2 = p5.Vector.mult(v1, 2); + * // v2 has compnents [2, 4, 6] + * </code> + * </div> + */ +p5.Vector.prototype.mult = function (n) { + this.x *= n || 0; + this.y *= n || 0; + this.z *= n || 0; + return this; +}; + +/** + * Divide the vector by a scalar. The static version of this method creates a + * new p5.Vector while the non static version acts on the vector directly. + * See the examples for more context. + * + * @method div + * @chainable + * @param {number} n the number to divide the vector by + * @return {p5.Vector} a reference to the p5.Vector object (allow chaining) + * @example + * <div class="norender"> + * <code> + * var v = createVector(6, 4, 2); + * v.div(2); //v's compnents are set to [3, 2, 1] + * </code> + * </div> + * + * <div class="norender"> + * <code> + * // Static method + * var v1 = createVector(6, 4, 2); + * var v2 = p5.Vector.div(v, 2); + * // v2 has compnents [3, 2, 1] + * </code> + * </div> + */ +p5.Vector.prototype.div = function (n) { + this.x /= n; + this.y /= n; + this.z /= n; + return this; +}; + +/** + * Calculates the magnitude (length) of the vector and returns the result as + * a float (this is simply the equation sqrt(x*x + y*y + z*z).) + * + * @method mag + * @return {Number} magnitude of the vector + * @example + * <div class="norender"> + * <code> + * var v = createVector(20.0, 30.0, 40.0); + * var m = v.mag(); + * print(m); // Prints "53.85164807134504" + * </code> + * </div> + */ +p5.Vector.prototype.mag = function () { + return Math.sqrt(this.magSq()); +}; + +/** + * Calculates the squared magnitude of the vector and returns the result + * as a float (this is simply the equation <em>(x*x + y*y + z*z)</em>.) + * Faster if the real length is not required in the + * case of comparing vectors, etc. + * + * @method magSq + * @return {number} squared magnitude of the vector + * @example + * <div class="norender"> + * <code> + * // Static method + * var v1 = createVector(6, 4, 2); + * print(v1.magSq()); // Prints "56" + * </code> + * </div> + */ +p5.Vector.prototype.magSq = function () { + var x = this.x, y = this.y, z = this.z; + return (x * x + y * y + z * z); +}; + +/** + * Calculates the dot product of two vectors. The version of the method + * that computes the dot product of two independent vectors is a static + * method. See the examples for more context. + * + * + * @method dot + * @param {Number|p5.Vector} x x component of the vector or a p5.Vector + * @param {Number} [y] y component of the vector + * @param {Number} [z] z component of the vector + * @return {Number} the dot product + * + * @example + * <div class="norender"> + * <code> + * var v1 = createVector(1, 2, 3); + * var v2 = createVector(2, 3, 4); + * + * print(v1.dot(v2)); // Prints "20" + * </code> + * </div> + * + * <div class="norender"> + * <code> + * //Static method + * var v1 = createVector(1, 2, 3); + * var v2 = createVector(3, 2, 1); + * print (p5.Vector.dot(v1, v2)); // Prints "10" + * </code> + * </div> + */ +p5.Vector.prototype.dot = function (x, y, z) { + if (x instanceof p5.Vector) { + return this.dot(x.x, x.y, x.z); + } + return this.x * (x || 0) + + this.y * (y || 0) + + this.z * (z || 0); +}; + +/** + * Calculates and returns a vector composed of the cross product between + * two vectors. Both the static and non static methods return a new p5.Vector. + * See the examples for more context. + * + * @method cross + * @param {p5.Vector} v p5.Vector to be crossed + * @return {p5.Vector} p5.Vector composed of cross product + * @example + * <div class="norender"> + * <code> + * var v1 = createVector(1, 2, 3); + * var v2 = createVector(1, 2, 3); + * + * v1.cross(v2); // v's components are [0, 0, 0] + * </code> + * </div> + * + * <div class="norender"> + * <code> + * // Static method + * var v1 = createVector(1, 0, 0); + * var v2 = createVector(0, 1, 0); + * + * var crossProduct = p5.Vector.cross(v1, v2); + * // crossProduct has components [0, 0, 1] + * </code> + * </div> + */ +p5.Vector.prototype.cross = function (v) { + var x = this.y * v.z - this.z * v.y; + var y = this.z * v.x - this.x * v.z; + var z = this.x * v.y - this.y * v.x; + if (this.p5) { + return new p5.Vector(this.p5,[x,y,z]); + } else { + return new p5.Vector(x,y,z); + } +}; + +/** + * Calculates the Euclidean distance between two points (considering a + * point as a vector object). + * + * @method dist + * @param {p5.Vector} v the x, y, and z coordinates of a p5.Vector + * @return {Number} the distance + * @example + * <div class="norender"> + * <code> + * var v1 = createVector(1, 0, 0); + * var v2 = createVector(0, 1, 0); + * + * var distance = v1.dist(v2); // distance is 1.4142... + * </code> + * </div> + * <div class="norender"> + * <code> + * // Static method + * var v1 = createVector(1, 0, 0); + * var v2 = createVector(0, 1, 0); + * + * var distance = p5.Vector.dist(v1,v2); + * // distance is 1.4142... + * </code> + * </div> + */ +p5.Vector.prototype.dist = function (v) { + var d = v.copy().sub(this); + return d.mag(); +}; + +/** + * Normalize the vector to length 1 (make it a unit vector). + * + * @method normalize + * @return {p5.Vector} normalized p5.Vector + * @example + * <div class="norender"> + * <code> + * var v = createVector(10, 20, 2); + * // v has compnents [10.0, 20.0, 2.0] + * v.normalize(); + * // v's compnents are set to + * // [0.4454354, 0.8908708, 0.089087084] + * </code> + * </div> + * + */ +p5.Vector.prototype.normalize = function () { + return this.mag() === 0 ? this : this.div(this.mag()); +}; + +/** + * Limit the magnitude of this vector to the value used for the <b>max</b> + * parameter. + * + * @method limit + * @param {Number} max the maximum magnitude for the vector + * @return {p5.Vector} the modified p5.Vector + * @example + * <div class="norender"> + * <code> + * var v = createVector(10, 20, 2); + * // v has compnents [10.0, 20.0, 2.0] + * v.limit(5); + * // v's compnents are set to + * // [2.2271771, 4.4543543, 0.4454354] + * </code> + * </div> + */ +p5.Vector.prototype.limit = function (max) { + var mSq = this.magSq(); + if(mSq > max*max) { + this.div(Math.sqrt(mSq)); //normalize it + this.mult(max); + } + return this; +}; + +/** + * Set the magnitude of this vector to the value used for the <b>len</b> + * parameter. + * + * @method setMag + * @param {number} len the new length for this vector + * @return {p5.Vector} the modified p5.Vector + * @example + * <div class="norender"> + * <code> + * var v1 = createVector(10, 20, 2); + * // v has compnents [10.0, 20.0, 2.0] + * v1.setMag(10); + * // v's compnents are set to [6.0, 8.0, 0.0] + * </code> + * </div> + */ +p5.Vector.prototype.setMag = function (n) { + return this.normalize().mult(n); +}; + +/** + * Calculate the angle of rotation for this vector (only 2D vectors) + * + * @method heading + * @return {Number} the angle of rotation + * @example + * <div class = "norender"><code> + * function setup() { + * var v1 = createVector(30,50); + * print(v1.heading()); // 1.0303768265243125 + * + * var v1 = createVector(40,50); + * print(v1.heading()); // 0.8960553845713439 + * + * var v1 = createVector(30,70); + * print(v1.heading()); // 1.1659045405098132 + * } + * </div></code> + */ +p5.Vector.prototype.heading = function () { + var h = Math.atan2(this.y, this.x); + if (this.p5) { + if (this.p5._angleMode === constants.RADIANS) { + return h; + } else { + return polarGeometry.radiansToDegrees(h); + } + } else { + return h; + } +}; + +/** + * Rotate the vector by an angle (only 2D vectors), magnitude remains the + * same + * + * @method rotate + * @param {number} angle the angle of rotation + * @return {p5.Vector} the modified p5.Vector + * @example + * <div class="norender"> + * <code> + * var v = createVector(10.0, 20.0); + * // v has compnents [10.0, 20.0, 0.0] + * v.rotate(HALF_PI); + * // v's compnents are set to [-20.0, 9.999999, 0.0] + * </code> + * </div> + */ +p5.Vector.prototype.rotate = function (a) { + if (this.p5) { + if (this.p5._angleMode === constants.DEGREES) { + a = polarGeometry.degreesToRadians(a); + } + } + var newHeading = this.heading() + a; + var mag = this.mag(); + this.x = Math.cos(newHeading) * mag; + this.y = Math.sin(newHeading) * mag; + return this; +}; + +/** + * Linear interpolate the vector to another vector + * + * @method lerp + * @param {p5.Vector} x the x component or the p5.Vector to lerp to + * @param {p5.Vector} [y] y the y component + * @param {p5.Vector} [z] z the z component + * @param {Number} amt the amount of interpolation; some value between 0.0 + * (old vector) and 1.0 (new vector). 0.1 is very near + * the new vector. 0.5 is halfway in between. + * @return {p5.Vector} the modified p5.Vector + * @example + * <div class="norender"> + * <code> + * var v = createVector(1, 1, 0); + * + * v.lerp(3, 3, 0, 0.5); // v now has components [2,2,0] + * </code> + * </div> + * + * <div class="norender"> + * <code> + * var v1 = createVector(0, 0, 0); + * var v2 = createVector(100, 100, 0); + * + * var v3 = p5.Vector.lerp(v1, v2, 0.5); + * // v3 has components [50,50,0] + * </code> + * </div> + */ +p5.Vector.prototype.lerp = function (x, y, z, amt) { + if (x instanceof p5.Vector) { + return this.lerp(x.x, x.y, x.z, y); + } + this.x += (x - this.x) * amt || 0; + this.y += (y - this.y) * amt || 0; + this.z += (z - this.z) * amt || 0; + return this; +}; + +/** + * Return a representation of this vector as a float array. This is only + * for temporary use. If used in any other fashion, the contents should be + * copied by using the <b>p5.Vector.copy()</b> method to copy into your own + * array. + * + * @method array + * @return {Array} an Array with the 3 values + * @example + * <div class = "norender"><code> + * function setup() { + * var v = createVector(20,30); + * print(v.array()); // Prints : Array [20, 30, 0] + * } + * </div></code> + * <div class="norender"> + * <code> + * var v = createVector(10.0, 20.0, 30.0); + * var f = v.array(); + * print(f[0]); // Prints "10.0" + * print(f[1]); // Prints "20.0" + * print(f[2]); // Prints "30.0" + * </code> + * </div> + */ +p5.Vector.prototype.array = function () { + return [this.x || 0, this.y || 0, this.z || 0]; +}; + +/** + * Equality check against a p5.Vector + * + * @method equals + * @param {Number|p5.Vector|Array} [x] the x component of the vector or a + * p5.Vector or an Array + * @param {Number} [y] the y component of the vector + * @param {Number} [z] the z component of the vector + * @return {Boolean} whether the vectors are equals + * @example + * <div class = "norender"><code> + * v1 = createVector(5,10,20); + * v2 = createVector(5,10,20); + * v3 = createVector(13,10,19); + * + * print(v1.equals(v2.x,v2.y,v2.z)); // true + * print(v1.equals(v3.x,v3.y,v3.z)); // false + * </div></code> + * <div class="norender"> + * <code> + * var v1 = createVector(10.0, 20.0, 30.0); + * var v2 = createVector(10.0, 20.0, 30.0); + * var v3 = createVector(0.0, 0.0, 0.0); + * print (v1.equals(v2)) // true + * print (v1.equals(v3)) // false + * </code> + * </div> + */ +p5.Vector.prototype.equals = function (x, y, z) { + var a, b, c; + if (x instanceof p5.Vector) { + a = x.x || 0; + b = x.y || 0; + c = x.z || 0; + } else if (x instanceof Array) { + a = x[0] || 0; + b = x[1] || 0; + c = x[2] || 0; + } else { + a = x || 0; + b = y || 0; + c = z || 0; + } + return this.x === a && this.y === b && this.z === c; +}; + + +// Static Methods + + +/** + * Make a new 2D unit vector from an angle + * + * @method fromAngle + * @static + * @param {Number} angle the desired angle + * @return {p5.Vector} the new p5.Vector object + * @example + * <div> + * <code> + * function draw() { + * background (200); + * + * // Create a variable, proportional to the mouseX, + * // varying from 0-360, to represent an angle in degrees. + * angleMode(DEGREES); + * var myDegrees = map(mouseX, 0,width, 0,360); + * + * // Display that variable in an onscreen text. + * // (Note the nfc() function to truncate additional decimal places, + * // and the "\xB0" character for the degree symbol.) + * var readout = "angle = " + nfc(myDegrees,1,1) + "\xB0" + * noStroke(); + * fill (0); + * text (readout, 5, 15); + * + * // Create a p5.Vector using the fromAngle function, + * // and extract its x and y components. + * var v = p5.Vector.fromAngle(radians(myDegrees)); + * var vx = v.x; + * var vy = v.y; + * + * push(); + * translate (width/2, height/2); + * noFill(); + * stroke (150); + * line (0,0, 30,0); + * stroke (0); + * line (0,0, 30*vx, 30*vy); + * pop() + * } + * </code> + * </div> + */ +p5.Vector.fromAngle = function(angle) { + if (this.p5) { + if (this.p5._angleMode === constants.DEGREES) { + angle = polarGeometry.degreesToRadians(angle); + } + } + if (this.p5) { + return new p5.Vector(this.p5,[Math.cos(angle),Math.sin(angle),0]); + } else { + return new p5.Vector(Math.cos(angle),Math.sin(angle),0); + } +}; + +/** + * Make a new 2D unit vector from a random angle + * + * @method random2D + * @static + * @return {p5.Vector} the new p5.Vector object + * @example + * <div class="norender"> + * <code> + * var v = p5.Vector.random2D(); + * // May make v's attributes something like: + * // [0.61554617, -0.51195765, 0.0] or + * // [-0.4695841, -0.14366731, 0.0] or + * // [0.6091097, -0.22805278, 0.0] + * </code> + * </div> + */ +p5.Vector.random2D = function () { + var angle; + // A lot of nonsense to determine if we know about a + // p5 sketch and whether we should make a random angle in degrees or radians + if (this.p5) { + if (this.p5._angleMode === constants.DEGREES) { + angle = this.p5.random(360); + } else { + angle = this.p5.random(constants.TWO_PI); + } + } else { + angle = Math.random()*Math.PI*2; + } + return this.fromAngle(angle); +}; + +/** + * Make a new random 3D unit vector. + * + * @method random3D + * @static + * @return {p5.Vector} the new p5.Vector object + * @example + * <div class="norender"> + * <code> + * var v = p5.Vector.random3D(); + * // May make v's attributes something like: + * // [0.61554617, -0.51195765, 0.599168] or + * // [-0.4695841, -0.14366731, -0.8711202] or + * // [0.6091097, -0.22805278, -0.7595902] + * </code> + * </div> + */ +p5.Vector.random3D = function () { + var angle,vz; + // If we know about p5 + if (this.p5) { + angle = this.p5.random(0,constants.TWO_PI); + vz = this.p5.random(-1,1); + } else { + angle = Math.random()*Math.PI*2; + vz = Math.random()*2-1; + } + var vx = Math.sqrt(1-vz*vz)*Math.cos(angle); + var vy = Math.sqrt(1-vz*vz)*Math.sin(angle); + if (this.p5) { + return new p5.Vector(this.p5,[vx,vy,vz]); + } else { + return new p5.Vector(vx,vy,vz); + } +}; + + +/** + * Adds two vectors together and returns a new one. + * + * @static + * @param {p5.Vector} v1 a p5.Vector to add + * @param {p5.Vector} v2 a p5.Vector to add + * @param {p5.Vector} target if undefined a new vector will be created + * @return {p5.Vector} the resulting p5.Vector + * + */ + +p5.Vector.add = function (v1, v2, target) { + if (!target) { + target = v1.copy(); + } else { + target.set(v1); + } + target.add(v2); + return target; +}; + +/** + * Subtracts one p5.Vector from another and returns a new one. The second + * vector (v2) is subtracted from the first (v1), resulting in v1-v2. + * + * @static + * @param {p5.Vector} v1 a p5.Vector to subtract from + * @param {p5.Vector} v2 a p5.Vector to subtract + * @param {p5.Vector} target if undefined a new vector will be created + * @return {p5.Vector} the resulting p5.Vector + */ + +p5.Vector.sub = function (v1, v2, target) { + if (!target) { + target = v1.copy(); + } else { + target.set(v1); + } + target.sub(v2); + return target; +}; + + +/** + * Multiplies a vector by a scalar and returns a new vector. + * + * @static + * @param {p5.Vector} v the p5.Vector to multiply + * @param {Number} n the scalar + * @param {p5.Vector} target if undefined a new vector will be created + * @return {p5.Vector} the resulting new p5.Vector + */ +p5.Vector.mult = function (v, n, target) { + if (!target) { + target = v.copy(); + } else { + target.set(v); + } + target.mult(n); + return target; +}; + +/** + * Divides a vector by a scalar and returns a new vector. + * + * @static + * @param {p5.Vector} v the p5.Vector to divide + * @param {Number} n the scalar + * @param {p5.Vector} target if undefined a new vector will be created + * @return {p5.Vector} the resulting new p5.Vector + */ +p5.Vector.div = function (v, n, target) { + if (!target) { + target = v.copy(); + } else { + target.set(v); + } + target.div(n); + return target; +}; + + +/** + * Calculates the dot product of two vectors. + * + * @static + * @param {p5.Vector} v1 the first p5.Vector + * @param {p5.Vector} v2 the second p5.Vector + * @return {Number} the dot product + */ +p5.Vector.dot = function (v1, v2) { + return v1.dot(v2); +}; + +/** + * Calculates the cross product of two vectors. + * + * @static + * @param {p5.Vector} v1 the first p5.Vector + * @param {p5.Vector} v2 the second p5.Vector + * @return {Number} the cross product + */ +p5.Vector.cross = function (v1, v2) { + return v1.cross(v2); +}; + +/** + * Calculates the Euclidean distance between two points (considering a + * point as a vector object). + * + * @static + * @param {p5.Vector} v1 the first p5.Vector + * @param {p5.Vector} v2 the second p5.Vector + * @return {Number} the distance + */ +p5.Vector.dist = function (v1,v2) { + return v1.dist(v2); +}; + +/** + * Linear interpolate a vector to another vector and return the result as a + * new vector. + * + * @static + * @param {p5.Vector} v1 a starting p5.Vector + * @param {p5.Vector} v2 the p5.Vector to lerp to + * @param {Number} the amount of interpolation; some value between 0.0 + * (old vector) and 1.0 (new vector). 0.1 is very near + * the new vector. 0.5 is halfway in between. + */ +p5.Vector.lerp = function (v1, v2, amt, target) { + if (!target) { + target = v1.copy(); + } else { + target.set(v1); + } + target.lerp(v2, amt); + return target; +}; + +/** + * Calculates and returns the angle (in radians) between two vectors. + * @method angleBetween + * @static + * @param {p5.Vector} v1 the x, y, and z components of a p5.Vector + * @param {p5.Vector} v2 the x, y, and z components of a p5.Vector + * @return {Number} the angle between (in radians) + * @example + * <div class="norender"> + * <code> + * var v1 = createVector(1, 0, 0); + * var v2 = createVector(0, 1, 0); + * + * var angle = p5.Vector.angleBetween(v1, v2); + * // angle is PI/2 + * </code> + * </div> + */ +p5.Vector.angleBetween = function (v1, v2) { + var angle = Math.acos(v1.dot(v2) / (v1.mag() * v2.mag())); + if (this.p5) { + if (this.p5._angleMode === constants.DEGREES) { + angle = polarGeometry.radiansToDegrees(angle); + } + } + return angle; +}; + +/** + * @static + */ +p5.Vector.mag = function (vecT){ + var x = vecT.x, + y = vecT.y, + z = vecT.z; + var magSq = x * x + y * y + z * z; + return Math.sqrt(magSq); +}; + +module.exports = p5.Vector; + +},{"../core/constants":36,"../core/core":37,"./polargeometry":67}],67:[function(_dereq_,module,exports){ + +module.exports = { + + degreesToRadians: function(x) { + return 2 * Math.PI * x / 360; + }, + + radiansToDegrees: function(x) { + return 360 * x / (2 * Math.PI); + } + +}; + +},{}],68:[function(_dereq_,module,exports){ +/** + * @module Math + * @submodule Random + * @for p5 + * @requires core + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); + +var seeded = false; + +// Linear Congruential Generator +// Variant of a Lehman Generator +var lcg = (function() { + // Set to values from http://en.wikipedia.org/wiki/Numerical_Recipes + // m is basically chosen to be large (as it is the max period) + // and for its relationships to a and c + var m = 4294967296, + // a - 1 should be divisible by m's prime factors + a = 1664525, + // c and m should be co-prime + c = 1013904223, + seed, z; + return { + setSeed : function(val) { + // pick a random seed if val is undefined or null + // the >>> 0 casts the seed to an unsigned 32-bit integer + z = seed = (val == null ? Math.random() * m : val) >>> 0; + }, + getSeed : function() { + return seed; + }, + rand : function() { + // define the recurrence relationship + z = (a * z + c) % m; + // return a float in [0, 1) + // if z = m then z / m = 0 therefore (z % m) / m < 1 always + return z / m; + } + }; +}()); + +/** + * Sets the seed value for random(). + * + * By default, random() produces different results each time the program + * is run. Set the seed parameter to a constant to return the same + * pseudo-random numbers each time the software is run. + * + * @method randomSeed + * @param {Number} seed the seed value + * @example + * <div> + * <code> + * randomSeed(99); + * for (var i=0; i < 100; i++) { + * var r = random(0, 255); + * stroke(r); + * line(i, 0, i, 100); + * } + * </code> + * </div> + * + * @alt + * many vertical lines drawn in white, black or grey. + * + */ +p5.prototype.randomSeed = function(seed) { + lcg.setSeed(seed); + seeded = true; +}; + +/** + * Return a random floating-point number. + * + * Takes either 0, 1 or 2 arguments. + * + * If no argument is given, returns a random number from 0 + * up to (but not including) 1. + * + * If one argument is given and it is a number, returns a random number from 0 + * up to (but not including) the number. + * + * If one argument is given and it is an array, returns a random element from + * that array. + * + * If two arguments are given, returns a random number from the + * first argument up to (but not including) the second argument. + * + * @method random + * @param {Number} [min] the lower bound (inclusive) + * @param {Number} [max] the upper bound (exclusive) + * @return {Number|mixed} the random number or a random element in choices + * @example + * <div> + * <code> + * for (var i = 0; i < 100; i++) { + * var r = random(50); + * stroke(r*5); + * line(50, i, 50+r, i); + * } + * </code> + * </div> + * <div> + * <code> + * for (var i = 0; i < 100; i++) { + * var r = random(-50, 50); + * line(50,i,50+r,i); + * } + * </code> + * </div> + * <div> + * <code> + * // Get a random element from an array using the random(Array) syntax + * var words = [ "apple", "bear", "cat", "dog" ]; + * var word = random(words); // select random word + * text(word,10,50); // draw the word + * </code> + * </div> + * + * @alt + * 100 horizontal lines from center canvas to right. size+fill change each time + * 100 horizontal lines from center of canvas. height & side change each render + * word displayed at random. Either apple, bear, cat, or dog + * + */ +/** + * @method random + * @param {Array} choices the array to choose from + * @return {mixed} the random element from the array + * @example + */ +p5.prototype.random = function (min, max) { + + var rand; + + if (seeded) { + rand = lcg.rand(); + } else { + rand = Math.random(); + } + if (typeof min === 'undefined') { + return rand; + } else + if (typeof max === 'undefined') { + if (min instanceof Array) { + return min[Math.floor(rand * min.length)]; + } else { + return rand * min; + } + } else { + if (min > max) { + var tmp = min; + min = max; + max = tmp; + } + + return rand * (max-min) + min; + } +}; + + +/** + * + * Returns a random number fitting a Gaussian, or + * normal, distribution. There is theoretically no minimum or maximum + * value that randomGaussian() might return. Rather, there is + * just a very low probability that values far from the mean will be + * returned; and a higher probability that numbers near the mean will + * be returned. + * <br><br> + * Takes either 0, 1 or 2 arguments.<br> + * If no args, returns a mean of 0 and standard deviation of 1.<br> + * If one arg, that arg is the mean (standard deviation is 1).<br> + * If two args, first is mean, second is standard deviation. + * + * @method randomGaussian + * @param {Number} mean the mean + * @param {Number} sd the standard deviation + * @return {Number} the random number + * @example + * <div> + * <code>for (var y = 0; y < 100; y++) { + * var x = randomGaussian(50,15); + * line(50, y, x, y); + *} + * </code> + * </div> + * <div> + * <code> + *var distribution = new Array(360); + * + *function setup() { + * createCanvas(100, 100); + * for (var i = 0; i < distribution.length; i++) { + * distribution[i] = floor(randomGaussian(0,15)); + * } + *} + * + *function draw() { + * background(204); + * + * translate(width/2, width/2); + * + * for (var i = 0; i < distribution.length; i++) { + * rotate(TWO_PI/distribution.length); + * stroke(0); + * var dist = abs(distribution[i]); + * line(0, 0, dist, 0); + * } + *} + * </code> + * </div> + * @alt + * 100 horizontal lines from center of canvas. height & side change each render + * black lines radiate from center of canvas. size determined each render + */ +var y2; +var previous = false; +p5.prototype.randomGaussian = function(mean, sd) { + var y1,x1,x2,w; + if (previous) { + y1 = y2; + previous = false; + } else { + do { + x1 = this.random(2) - 1; + x2 = this.random(2) - 1; + w = x1 * x1 + x2 * x2; + } while (w >= 1); + w = Math.sqrt((-2 * Math.log(w))/w); + y1 = x1 * w; + y2 = x2 * w; + previous = true; + } + + var m = mean || 0; + var s = sd || 1; + return y1*s + m; +}; + +module.exports = p5; + +},{"../core/core":37}],69:[function(_dereq_,module,exports){ +/** + * @module Math + * @submodule Trigonometry + * @for p5 + * @requires core + * @requires polargeometry + * @requires constants + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); +var polarGeometry = _dereq_('./polargeometry'); +var constants = _dereq_('../core/constants'); + +p5.prototype._angleMode = constants.RADIANS; + +/** + * The inverse of cos(), returns the arc cosine of a value. This function + * expects the values in the range of -1 to 1 and values are returned in + * the range 0 to PI (3.1415927). + * + * @method acos + * @param {Number} value the value whose arc cosine is to be returned + * @return {Number} the arc cosine of the given value + * + * @example + * <div class= “norender"> + * <code> + * var a = PI; + * var c = cos(a); + * var ac = acos(c); + * // Prints: "3.1415927 : -1.0 : 3.1415927" + * print(a + " : " + c + " : " + ac); + * </code> + * </div> + * + * <div class= “norender"> + * <code> + * var a = PI + PI/4.0; + * var c = cos(a); + * var ac = acos(c); + * // Prints: "3.926991 : -0.70710665 : 2.3561943" + * print(a + " : " + c + " : " + ac); + * </code> + * </div> + */ +p5.prototype.acos = function(ratio) { + if (this._angleMode === constants.RADIANS) { + return Math.acos(ratio); + } else { + return polarGeometry.radiansToDegrees(Math.acos(ratio)); + } +}; + +/** + * The inverse of sin(), returns the arc sine of a value. This function + * expects the values in the range of -1 to 1 and values are returned + * in the range -PI/2 to PI/2. + * + * @method asin + * @param {Number} value the value whose arc sine is to be returned + * @return {Number} the arc sine of the given value + * + * @example + * <div class= “norender"> + * <code> + * var a = PI + PI/3; + * var s = sin(a); + * var as = asin(s); + * // Prints: "1.0471976 : 0.86602545 : 1.0471976" + * print(a + " : " + s + " : " + as); + * </code> + * </div> + * + * <div class= “norender"> + * <code> + * var a = PI + PI/3.0; + * var s = sin(a); + * var as = asin(s); + * // Prints: "4.1887903 : -0.86602545 : -1.0471976" + * print(a + " : " + s + " : " + as); + * </code> + * </div> + * + */ +p5.prototype.asin = function(ratio) { + if (this._angleMode === constants.RADIANS) { + return Math.asin(ratio); + } else { + return polarGeometry.radiansToDegrees(Math.asin(ratio)); + } +}; + +/** + * The inverse of tan(), returns the arc tangent of a value. This function + * expects the values in the range of -Infinity to Infinity (exclusive) and + * values are returned in the range -PI/2 to PI/2. + * + * @method atan + * @param {Number} value the value whose arc tangent is to be returned + * @return {Number} the arc tangent of the given value + * + * @example + * <div class= “norender"> + * <code> + * var a = PI + PI/3; + * var t = tan(a); + * var at = atan(t); + * // Prints: "1.0471976 : 1.7320509 : 1.0471976" + * print(a + " : " + t + " : " + at); + * </code> + * </div> + * + * <div class= “norender"> + * <code> + * var a = PI + PI/3.0; + * var t = tan(a); + * var at = atan(t); + * // Prints: "4.1887903 : 1.7320513 : 1.0471977" + * print(a + " : " + t + " : " + at); + * </code> + * </div> + * + */ +p5.prototype.atan = function(ratio) { + if (this._angleMode === constants.RADIANS) { + return Math.atan(ratio); + } else { + return polarGeometry.radiansToDegrees(Math.atan(ratio)); + } +}; + +/** + * Calculates the angle (in radians) from a specified point to the coordinate + * origin as measured from the positive x-axis. Values are returned as a + * float in the range from PI to -PI. The atan2() function is most often used + * for orienting geometry to the position of the cursor. + * <br><br> + * Note: The y-coordinate of the point is the first parameter, and the + * x-coordinate is the second parameter, due the the structure of calculating + * the tangent. + * + * @method atan2 + * @param {Number} y y-coordinate of the point + * @param {Number} x x-coordinate of the point + * @return {Number} the arc tangent of the given point + * + * @example + * <div> + * <code> + * function draw() { + * background(204); + * translate(width/2, height/2); + * var a = atan2(mouseY-height/2, mouseX-width/2); + * rotate(a); + * rect(-30, -5, 60, 10); + * } + * </code> + * </div> + * + * @alt + * 60 by 10 rect at center of canvas rotates with mouse movements + * + */ +p5.prototype.atan2 = function (y, x) { + if (this._angleMode === constants.RADIANS) { + return Math.atan2(y, x); + } else { + return polarGeometry.radiansToDegrees(Math.atan2(y, x)); + } +}; + +/** + * Calculates the cosine of an angle. This function takes into account the + * current angleMode. Values are returned in the range -1 to 1. + * + * @method cos + * @param {Number} angle the angle + * @return {Number} the cosine of the angle + * + * @example + * <div> + * <code> + * var a = 0.0; + * var inc = TWO_PI/25.0; + * for (var i = 0; i < 25; i++) { + * line(i*4, 50, i*4, 50+cos(a)*40.0); + * a = a + inc; + * } + * </code> + * </div> + * + * @alt + * vertical black lines form wave patterns, extend-down on left and right side + * + */ +p5.prototype.cos = function(angle) { + if (this._angleMode === constants.RADIANS) { + return Math.cos(angle); + } else { + return Math.cos(this.radians(angle)); + } +}; + +/** + * Calculates the sine of an angle. This function takes into account the + * current angleMode. Values are returned in the range -1 to 1. + * + * @method sin + * @param {Number} angle the angle + * @return {Number} the sine of the angle + * + * @example + * <div> + * <code> + * var a = 0.0; + * var inc = TWO_PI/25.0; + * for (var i = 0; i < 25; i++) { + * line(i*4, 50, i*4, 50+sin(a)*40.0); + * a = a + inc; + * } + * </code> + * </div> + * + * @alt + * vertical black lines extend down and up from center to form wave pattern + * + */ +p5.prototype.sin = function(angle) { + if (this._angleMode === constants.RADIANS) { + return Math.sin(angle); + } else { + return Math.sin(this.radians(angle)); + } +}; + +/** + * Calculates the tangent of an angle. This function takes into account + * the current angleMode. Values are returned in the range -1 to 1. + * + * @method tan + * @param {Number} angle the angle + * @return {Number} the tangent of the angle + * + * @example + * <div> + * <code> + * var a = 0.0; + * var inc = TWO_PI/50.0; + * for (var i = 0; i < 100; i = i+2) { + * line(i, 50, i, 50+tan(a)*2.0); + * a = a + inc; + * } + * </code> + * + * + * @alt + * vertical black lines end down and up from center to form spike pattern + * + */ +p5.prototype.tan = function(angle) { + if (this._angleMode === constants.RADIANS) { + return Math.tan(angle); + } else { + return Math.tan(this.radians(angle)); + } +}; + +/** + * Converts a radian measurement to its corresponding value in degrees. + * Radians and degrees are two ways of measuring the same thing. There are + * 360 degrees in a circle and 2*PI radians in a circle. For example, + * 90° = PI/2 = 1.5707964. + * + * @method degrees + * @param {Number} radians the radians value to convert to degrees + * @return {Number} the converted angle + * + * + * @example + * <div class= “norender"> + * <code> + * var rad = PI/4; + * var deg = degrees(rad); + * print(rad + " radians is " + deg + " degrees"); + * // Prints: 0.7853981633974483 radians is 45 degrees + * </code> + * </div> + * + */ +p5.prototype.degrees = function(angle) { + return polarGeometry.radiansToDegrees(angle); +}; + +/** + * Converts a degree measurement to its corresponding value in radians. + * Radians and degrees are two ways of measuring the same thing. There are + * 360 degrees in a circle and 2*PI radians in a circle. For example, + * 90° = PI/2 = 1.5707964. + * + * @method radians + * @param {Number} degrees the degree value to convert to radians + * @return {Number} the converted angle + * + * @example + * <div class= “norender"> + * <code> + * var deg = 45.0; + * var rad = radians(deg); + * print(deg + " degrees is " + rad + " radians"); + * // Prints: 45 degrees is 0.7853981633974483 radians + * </code> + * </div> + */ +p5.prototype.radians = function(angle) { + return polarGeometry.degreesToRadians(angle); +}; + +/** + * Sets the current mode of p5 to given mode. Default mode is RADIANS. + * + * @method angleMode + * @param {Constant} mode either RADIANS or DEGREES + * + * @example + * <div> + * <code> + * function draw(){ + * background(204); + * angleMode(DEGREES); // Change the mode to DEGREES + * var a = atan2(mouseY-height/2, mouseX-width/2); + * translate(width/2, height/2); + * push(); + * rotate(a); + * rect(-20, -5, 40, 10); // Larger rectangle is rotating in degrees + * pop(); + * angleMode(RADIANS); // Change the mode to RADIANS + * rotate(a); // var a stays the same + * rect(-40, -5, 20, 10); // Smaller rectangle is rotating in radians + * } + * </code> + * </div> + * + * @alt + * 40 by 10 rect in center rotates with mouse moves. 20 by 10 rect moves faster. + * + * + */ +p5.prototype.angleMode = function(mode) { + if (mode === constants.DEGREES || mode === constants.RADIANS) { + this._angleMode = mode; + } +}; + +module.exports = p5; + +},{"../core/constants":36,"../core/core":37,"./polargeometry":67}],70:[function(_dereq_,module,exports){ +/** + * @module Typography + * @submodule Attributes + * @for p5 + * @requires core + * @requires constants + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); + +/** + * Sets the current alignment for drawing text. Accepts two + * arguments: horizAlign (LEFT, CENTER, or RIGHT) and + * vertAlign (TOP, BOTTOM, CENTER, or BASELINE). + * + * The horizAlign parameter is in reference to the x value + * of the text() function, while the vertAlign parameter is + * in reference to the y value. + * + * So if you write textAlign(LEFT), you are aligning the left + * edge of your text to the x value you give in text(). If you + * write textAlign(RIGHT, TOP), you are aligning the right edge + * of your text to the x value and the top of edge of the text + * to the y value. + * + * @method textAlign + * @param {Constant} horizAlign horizontal alignment, either LEFT, + * CENTER, or RIGHT + * @param {Constant} vertAlign vertical alignment, either TOP, + * BOTTOM, CENTER, or BASELINE + * @return {Number} + * @example + * <div> + * <code> + * textSize(16); + * textAlign(RIGHT); + * text("ABCD", 50, 30); + * textAlign(CENTER); + * text("EFGH", 50, 50); + * textAlign(LEFT); + * text("IJKL", 50, 70); + * </code> + * </div> + * + * @alt + *Letters ABCD displayed at top right, EFGH at center and IJKL at bottom left. + * + */ +p5.prototype.textAlign = function(horizAlign, vertAlign) { + return this._renderer.textAlign.apply(this._renderer, arguments); +}; + +/** + * Sets/gets the spacing, in pixels, between lines of text. This + * setting will be used in all subsequent calls to the text() function. + * + * @method textLeading + * @param {Number} leading the size in pixels for spacing between lines + * @return {Object|Number} + * @example + * <div> + * <code> + * // Text to display. The "\n" is a "new line" character + * lines = "L1\nL2\nL3"; + * textSize(12); + * + * textLeading(10); // Set leading to 10 + * text(lines, 10, 25); + * + * textLeading(20); // Set leading to 20 + * text(lines, 40, 25); + * + * textLeading(30); // Set leading to 30 + * text(lines, 70, 25); + * </code> + * </div> + * + * @alt + *set L1 L2 & L3 displayed vertically 3 times. spacing increases for each set + * + */ +p5.prototype.textLeading = function(theLeading) { + return this._renderer.textLeading.apply(this._renderer, arguments); +}; + +/** + * Sets/gets the current font size. This size will be used in all subsequent + * calls to the text() function. Font size is measured in pixels. + * + * @method textSize + * @param {Number} theSize the size of the letters in units of pixels + * @return {Object|Number} + * @example + * <div> + * <code> + * textSize(12); + * text("Font Size 12", 10, 30); + * textSize(14); + * text("Font Size 14", 10, 60); + * textSize(16); + * text("Font Size 16", 10, 90); + * </code> + * </div> + * + * @alt + *Font Size 12 displayed small, Font Size 14 medium & Font Size 16 large + * + */ +p5.prototype.textSize = function(theSize) { + return this._renderer.textSize.apply(this._renderer, arguments); +}; + +/** + * Sets/gets the style of the text for system fonts to NORMAL, ITALIC, or BOLD. + * Note: this may be is overridden by CSS styling. For non-system fonts + * (opentype, truetype, etc.) please load styled fonts instead. + * + * @method textStyle + * @param {Number/Constant} theStyle styling for text, either NORMAL, + * ITALIC, or BOLD + * @return {Object|String} + * @example + * <div> + * <code> + * strokeWeight(0); + * textSize(12); + * textStyle(NORMAL); + * text("Font Style Normal", 10, 30); + * textStyle(ITALIC); + * text("Font Style Italic", 10, 60); + * textStyle(BOLD); + * text("Font Style Bold", 10, 90); + * </code> + * </div> + * + * @alt + *words Font Style Normal displayed normally, Italic in italic and bold in bold + * + */ +p5.prototype.textStyle = function(theStyle) { + return this._renderer.textStyle.apply(this._renderer, arguments); +}; + +/** + * Calculates and returns the width of any character or text string. + * + * @method textWidth + * @param {String} theText the String of characters to measure + * @return {Number} + * @example + * <div> + * <code> + * textSize(28); + * + * var aChar = 'P'; + * var cWidth = textWidth(aChar); + * text(aChar, 0, 40); + * line(cWidth, 0, cWidth, 50); + * + * var aString = "p5.js"; + * var sWidth = textWidth(aString); + * text(aString, 0, 85); + * line(sWidth, 50, sWidth, 100); + * </code> + * </div> + * + * @alt + *Letter P and p5.js are displayed with vertical lines at end. P is wide + * + */ +p5.prototype.textWidth = function(theText) { + if (theText.length === 0) { + return 0; + } + return this._renderer.textWidth.apply(this._renderer, arguments); +}; + +/** + * Returns the ascent of the current font at its current size. The ascent + * represents the distance, in pixels, of the tallest character above + * the baseline. + * + * @return {Number} + * @example + * <div> + * <code> + * var base = height * 0.75; + * var scalar = 0.8; // Different for each font + * + * textSize(32); // Set initial text size + * var asc = textAscent() * scalar; // Calc ascent + * line(0, base - asc, width, base - asc); + * text("dp", 0, base); // Draw text on baseline + * + * textSize(64); // Increase text size + * asc = textAscent() * scalar; // Recalc ascent + * line(40, base - asc, width, base - asc); + * text("dp", 40, base); // Draw text on baseline + * </code> + * </div> + */ +p5.prototype.textAscent = function() { + return this._renderer.textAscent(); +}; + +/** + * Returns the descent of the current font at its current size. The descent + * represents the distance, in pixels, of the character with the longest + * descender below the baseline. + * + * @return {Number} + * @example + * <div> + * <code> + * var base = height * 0.75; + * var scalar = 0.8; // Different for each font + * + * textSize(32); // Set initial text size + * var desc = textDescent() * scalar; // Calc ascent + * line(0, base+desc, width, base+desc); + * text("dp", 0, base); // Draw text on baseline + * + * textSize(64); // Increase text size + * desc = textDescent() * scalar; // Recalc ascent + * line(40, base + desc, width, base + desc); + * text("dp", 40, base); // Draw text on baseline + * </code> + * </div> + */ +p5.prototype.textDescent = function() { + return this._renderer.textDescent(); +}; + +/** + * Helper function to measure ascent and descent. + */ +p5.prototype._updateTextMetrics = function() { + return this._renderer._updateTextMetrics(); +}; + +module.exports = p5; + +},{"../core/core":37}],71:[function(_dereq_,module,exports){ +/** + * @module Typography + * @submodule Loading & Displaying + * @for p5 + * @requires core + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); +var constants = _dereq_('../core/constants'); + +_dereq_('../core/error_helpers'); + + +/** + * Draws text to the screen. Displays the information specified in the first + * parameter on the screen in the position specified by the additional + * parameters. A default font will be used unless a font is set with the + * textFont() function and a default size will be used unless a font is set + * with textSize(). Change the color of the text with the fill() function. + * Change the outline of the text with the stroke() and strokeWeight() + * functions. + * <br><br> + * The text displays in relation to the textAlign() function, which gives the + * option to draw to the left, right, and center of the coordinates. + * <br><br> + * The x2 and y2 parameters define a rectangular area to display within and + * may only be used with string data. When these parameters are specified, + * they are interpreted based on the current rectMode() setting. Text that + * does not fit completely within the rectangle specified will not be drawn + * to the screen. + * + * @method text + * @param {String} str the alphanumeric symbols to be displayed + * @param {Number} x x-coordinate of text + * @param {Number} y y-coordinate of text + * @param {Number} x2 by default, the width of the text box, + * see rectMode() for more info + * @param {Number} y2 by default, the height of the text box, + * see rectMode() for more info + * @return {Object} this + * @example + * <div> + * <code> + * textSize(32); + * text("word", 10, 30); + * fill(0, 102, 153); + * text("word", 10, 60); + * fill(0, 102, 153, 51); + * text("word", 10, 90); + * </code> + * </div> + * <div> + * <code> + * s = "The quick brown fox jumped over the lazy dog."; + * fill(50); + * text(s, 10, 10, 70, 80); // Text wraps within text box + * </code> + * </div> + * + * @alt + *'word' displayed 3 times going from black, blue to translucent blue + * The quick brown fox jumped over the lazy dog. + * + */ +p5.prototype.text = function(str, x, y, maxWidth, maxHeight) { + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + this._validateParameters( + 'text', + args, + [ + ['*', 'Number', 'Number'], + ['*', 'Number', 'Number', 'Number', 'Number'] + ] + ); + + return (!(this._renderer._doFill || this._renderer._doStroke)) ? this : + this._renderer.text.apply(this._renderer, arguments); +}; + +/** + * Sets the current font that will be drawn with the text() function. + * + * @method textFont + * @param {Object|String} f a font loaded via loadFont(), or a String + * representing a <a href="https://mzl.la/2dOw8WD">web safe font</a> (a font + * that is generally available across all systems). + * @return {Object} this + * @example + * <div> + * <code> + * fill(0); + * textSize(12); + * textFont("Georgia"); + * text("Georgia", 12, 30); + * textFont("Helvetica"); + * text("Helvetica", 12, 60); + * </code> + * </div> + * <div> + * <code> + * var fontRegular, fontItalic, fontBold; + * function preload() { + * fontRegular = loadFont("assets/Regular.otf"); + * fontItalic = loadFont("assets/Italic.ttf"); + * fontBold = loadFont("assets/Bold.ttf"); + * } + * function setup() { + * background(210); + * fill(0).strokeWeight(0).textSize(10); + * textFont(fontRegular); + * text("Font Style Normal", 10, 30); + * textFont(fontItalic); + * text("Font Style Italic", 10, 50); + * textFont(fontBold); + * text("Font Style Bold", 10, 70); + * } + * </code> + * </div> + * + * @alt + *words Font Style Normal displayed normally, Italic in italic and bold in bold + * + */ +p5.prototype.textFont = function(theFont, theSize) { + + if (arguments.length) { + + if (!theFont) { + + throw Error('null font passed to textFont'); + } + + this._renderer._setProperty('_textFont', theFont); + + if (theSize) { + + this._renderer._setProperty('_textSize', theSize); + this._renderer._setProperty('_textLeading', + theSize * constants._DEFAULT_LEADMULT); + } + + return this._renderer._applyTextProperties(); + } + + return this; +}; + +module.exports = p5; + +},{"../core/constants":36,"../core/core":37,"../core/error_helpers":40}],72:[function(_dereq_,module,exports){ +/** + * This module defines the p5.Font class and functions for + * drawing text to the display canvas. + * @module Typography + * @submodule Font + * @requires core + * @requires constants + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); +var constants = _dereq_('../core/constants'); + +/* + * TODO: + * + * API: + * -- textBounds() + * -- getPath() + * -- getPoints() + * + * =========================================== + * -- PFont functions: + * PFont.list() + * + * -- kerning + * -- alignment: justified? + * -- integrate p5.dom? (later) + */ + +/** + * Base class for font handling + * @class p5.Font + * @constructor + * @param {Object} [pInst] pointer to p5 instance + */ +p5.Font = function(p) { + + this.parent = p; + + this.cache = {}; + + /** + * Underlying opentype font implementation + * @property font + */ + this.font = undefined; +}; + +p5.Font.prototype.list = function() { + + // TODO + throw 'not yet implemented'; +}; + +/** + * Returns a tight bounding box for the given text string using this + * font (currently only supports single lines) + * + * @method textBounds + * @param {String} line a line of text + * @param {Number} x x-position + * @param {Number} y y-position + * @param {Number} fontSize font size to use (optional) + * @param {Object} options opentype options (optional) + * + * @return {Object} a rectangle object with properties: x, y, w, h + * + * @example + * <div> + * <code> + * var font; + * var textString = 'Lorem ipsum dolor sit amet.'; + * function preload() { + * font = loadFont('./assets/Regular.otf'); + * }; + * function setup() { + * background(210); + * + * var bbox = font.textBounds(textString, 10, 30, 12); + * fill(255); + * stroke(0); + * rect(bbox.x, bbox.y, bbox.w, bbox.h); + * fill(0); + * noStroke(); + * + * textFont(font); + * textSize(12); + * text(textString, 10, 30); + * }; + * </code> + * </div> + * + * @alt + *words Lorem ipsum dol go off canvas and contained by white bounding box + * + */ +p5.Font.prototype.textBounds = function(str, x, y, fontSize, options) { + + x = x !== undefined ? x : 0; + y = y !== undefined ? y : 0; + fontSize = fontSize || this.parent._renderer._textSize; + + // Check cache for existing bounds. Take into consideration the text alignment + // settings. Default alignment should match opentype's origin: left-aligned & + // alphabetic baseline. + var p = (options && options.renderer && options.renderer._pInst) || + this.parent, + ctx = p._renderer.drawingContext, + alignment = ctx.textAlign || constants.LEFT, + baseline = ctx.textBaseline || constants.BASELINE; + var result = this.cache[cacheKey('textBounds', str, x, y, fontSize, alignment, + baseline)]; + + if (!result) { + + var xCoords = [], yCoords = [], self = this, + scale = this._scale(fontSize), minX, minY, maxX, maxY; + + this.font.forEachGlyph(str, x, y, fontSize, options, + function(glyph, gX, gY, gFontSize) { + + xCoords.push(gX); + yCoords.push(gY); + + var gm = glyph.getMetrics(); + + if (glyph.name !== 'space') { + + xCoords.push(gX + (gm.xMax * scale)); + yCoords.push(gY + (-gm.yMin * scale)); + yCoords.push(gY + (-gm.yMax * scale)); + + } else { // NOTE: deals with broken metrics for spaces in opentype.js + + xCoords.push(gX + self.font.charToGlyph(' ').advanceWidth * + self._scale(fontSize)); + } + }); + + // fix to #1409 (not sure why these max() functions were here) + /*minX = Math.max(0, Math.min.apply(null, xCoords)); + minY = Math.max(0, Math.min.apply(null, yCoords)); + maxX = Math.max(0, Math.max.apply(null, xCoords)); + maxY = Math.max(0, Math.max.apply(null, yCoords));*/ + minX = Math.min.apply(null, xCoords); + minY = Math.min.apply(null, yCoords); + maxX = Math.max.apply(null, xCoords); + maxY = Math.max.apply(null, yCoords); + + result = { + x: minX, + y: minY, + h: maxY - minY, + w: maxX - minX, + advance: minX - x + }; + + // Bounds are now calculated, so shift the x & y to match alignment settings + var textWidth = result.w + result.advance; + var pos = this._handleAlignment(p, ctx, str, result.x, result.y, textWidth); + result.x = pos.x; + result.y = pos.y; + + this.cache[cacheKey('textBounds', str, x, y, fontSize, alignment, + baseline)] = result; + } + //else console.log('cache-hit'); + + return result; +}; + + +/** + * Computes an array of points following the path for specified text + * + * @param {String} txt a line of text + * @param {Number} x x-position + * @param {Number} y y-position + * @param {Number} fontSize font size to use (optional) + * @param {Object} options an (optional) object that can contain: + * + * <br>sampleFactor - the ratio of path-length to number of samples + * (default=.25); higher values yield more points and are therefore + * more precise + * + * <br>simplifyThreshold - if set to a non-zero value, collinear points will be + * be removed from the polygon; the value represents the threshold angle to use + * when determining whether two edges are collinear + * + * @return {Array} an array of points, each with x, y, alpha (the path angle) + */ +p5.Font.prototype.textToPoints = function(txt, x, y, fontSize, options) { + + var xoff = 0, result = [], glyphs = this._getGlyphs(txt); + + fontSize = fontSize || this.parent._renderer._textSize; + + for (var i = 0; i < glyphs.length; i++) { + + var gpath = glyphs[i].getPath(x, y, fontSize), + paths = splitPaths(gpath.commands); + + for (var j = 0; j < paths.length; j++) { + + var pts = pathToPoints(paths[j], options); + + for (var k = 0; k < pts.length; k++) { + pts[k].x += xoff; + result.push(pts[k]); + } + } + + xoff += glyphs[i].advanceWidth * this._scale(fontSize); + } + + return result; +}; + +// ----------------------------- End API ------------------------------ + +/** + * Returns the set of opentype glyphs for the supplied string. + * + * Note that there is not a strict one-to-one mapping between characters + * and glyphs, so the list of returned glyphs can be larger or smaller + * than the length of the given string. + * + * @param {String} str the string to be converted + * @return {array} the opentype glyphs + */ +p5.Font.prototype._getGlyphs = function(str) { + + return this.font.stringToGlyphs(str); +}; + +/** + * Returns an opentype path for the supplied string and position. + * + * @param {String} line a line of text + * @param {Number} x x-position + * @param {Number} y y-position + * @param {Object} options opentype options (optional) + * @return {Object} the opentype path + */ +p5.Font.prototype._getPath = function(line, x, y, options) { + + var p = (options && options.renderer && options.renderer._pInst) || + this.parent, + ctx = p._renderer.drawingContext, + pos = this._handleAlignment(p, ctx, line, x, y); + + return this.font.getPath(line, pos.x, pos.y, p._renderer._textSize, options); +}; + +/* + * Creates an SVG-formatted path-data string + * (See http://www.w3.org/TR/SVG/paths.html#PathData) + * from the given opentype path or string/position + * + * @param {Object} path an opentype path, OR the following: + * + * @param {String} line a line of text + * @param {Number} x x-position + * @param {Number} y y-position + * @param {Object} options opentype options (optional), set options.decimals + * to set the decimal precision of the path-data + * + * @return {Object} this p5.Font object + */ +p5.Font.prototype._getPathData = function(line, x, y, options) { + + var decimals = 3; + + // create path from string/position + if (typeof line === 'string' && arguments.length > 2) { + + line = this._getPath(line, x, y, options); + } + // handle options specified in 2nd arg + else if (typeof x === 'object') { + + options = x; + } + + // handle svg arguments + if (options && typeof options.decimals === 'number') { + + decimals = options.decimals; + } + + return line.toPathData(decimals); +}; + +/* + * Creates an SVG <path> element, as a string, + * from the given opentype path or string/position + * + * @param {Object} path an opentype path, OR the following: + * + * @param {String} line a line of text + * @param {Number} x x-position + * @param {Number} y y-position + * @param {Object} options opentype options (optional), set options.decimals + * to set the decimal precision of the path-data in the <path> element, + * options.fill to set the fill color for the <path> element, + * options.stroke to set the stroke color for the <path> element, + * options.strokeWidth to set the strokeWidth for the <path> element. + * + * @return {Object} this p5.Font object + */ +p5.Font.prototype._getSVG = function(line, x, y, options) { + + var decimals = 3; + + // create path from string/position + if (typeof line === 'string' && arguments.length > 2) { + + line = this._getPath(line, x, y, options); + } + // handle options specified in 2nd arg + else if (typeof x === 'object') { + + options = x; + } + + // handle svg arguments + if (options) { + if (typeof options.decimals === 'number') { + decimals = options.decimals; + } + if (typeof options.strokeWidth === 'number') { + line.strokeWidth = options.strokeWidth; + } + if (typeof options.fill !== 'undefined') { + line.fill = options.fill; + } + if (typeof options.stroke !== 'undefined') { + line.stroke = options.stroke; + } + } + + return line.toSVG(decimals); +}; + +/* + * Renders an opentype path or string/position + * to the current graphics context + * + * @param {Object} path an opentype path, OR the following: + * + * @param {String} line a line of text + * @param {Number} x x-position + * @param {Number} y y-position + * @param {Object} options opentype options (optional) + * + * @return {Object} this p5.Font object + */ +p5.Font.prototype._renderPath = function(line, x, y, options) { + + var pdata, pg = (options && options.renderer) || this.parent._renderer, + ctx = pg.drawingContext; + + if (typeof line === 'object' && line.commands) { + + pdata = line.commands; + } else { + + //pos = handleAlignment(p, ctx, line, x, y); + pdata = this._getPath(line, x, y, options).commands; + } + + ctx.beginPath(); + for (var i = 0; i < pdata.length; i += 1) { + + var cmd = pdata[i]; + if (cmd.type === 'M') { + ctx.moveTo(cmd.x, cmd.y); + } else if (cmd.type === 'L') { + ctx.lineTo(cmd.x, cmd.y); + } else if (cmd.type === 'C') { + ctx.bezierCurveTo(cmd.x1, cmd.y1, cmd.x2, cmd.y2, cmd.x, cmd.y); + } else if (cmd.type === 'Q') { + ctx.quadraticCurveTo(cmd.x1, cmd.y1, cmd.x, cmd.y); + } else if (cmd.type === 'Z') { + ctx.closePath(); + } + } + + // only draw stroke if manually set by user + if (pg._doStroke && pg._strokeSet) { + + ctx.stroke(); + } + + if (pg._doFill) { + + // if fill hasn't been set by user, use default-text-fill + ctx.fillStyle = pg._fillSet ? ctx.fillStyle : constants._DEFAULT_TEXT_FILL; + ctx.fill(); + } + + return this; +}; + +p5.Font.prototype._textWidth = function(str, fontSize) { + + if (str === ' ') { // special case for now + + return this.font.charToGlyph(' ').advanceWidth * this._scale(fontSize); + } + + var bounds = this.textBounds(str, 0, 0, fontSize); + return bounds.w + bounds.advance; +}; + +p5.Font.prototype._textAscent = function(fontSize) { + + return this.font.ascender * this._scale(fontSize); +}; + +p5.Font.prototype._textDescent = function(fontSize) { + + return -this.font.descender * this._scale(fontSize); +}; + +p5.Font.prototype._scale = function(fontSize) { + + return (1 / this.font.unitsPerEm) * (fontSize || + this.parent._renderer._textSize); +}; + +p5.Font.prototype._handleAlignment = function(p, ctx, line, x, y, textWidth) { + var fontSize = p._renderer._textSize, + textAscent = this._textAscent(fontSize), + textDescent = this._textDescent(fontSize); + + textWidth = textWidth !== undefined ? textWidth : + this._textWidth(line, fontSize); + + if (ctx.textAlign === constants.CENTER) { + x -= textWidth / 2; + } else if (ctx.textAlign === constants.RIGHT) { + x -= textWidth; + } + + if (ctx.textBaseline === constants.TOP) { + y += textAscent; + } else if (ctx.textBaseline === constants._CTX_MIDDLE) { + y += textAscent / 2; + } else if (ctx.textBaseline === constants.BOTTOM) { + y -= textDescent; + } + + return { x: x, y: y }; +}; + +// path-utils + +function pathToPoints(cmds, options) { + + var opts = parseOpts(options, { + sampleFactor: 0.1, + simplifyThreshold: 0, + }); + + var len = pointAtLength(cmds,0,1), // total-length + t = len / (len * opts.sampleFactor), + pts = []; + + for (var i = 0; i < len; i += t) { + pts.push(pointAtLength(cmds, i)); + } + + if (opts.simplifyThreshold) { + /*var count = */simplify(pts, opts.simplifyThreshold); + //console.log('Simplify: removed ' + count + ' pts'); + } + + return pts; +} + +function simplify(pts, angle) { + + angle = (typeof angle === 'undefined') ? 0 : angle; + + var num = 0; + for (var i = pts.length - 1; pts.length > 3 && i >= 0; --i) { + + if (collinear(at(pts, i - 1), at(pts, i), at(pts, i + 1), angle)) { + + // Remove the middle point + pts.splice(i % pts.length, 1); + num++; + } + } + return num; +} + +function splitPaths(cmds) { + + var paths = [], current; + for (var i = 0; i < cmds.length; i++) { + if (cmds[i].type === 'M') { + if (current) { + paths.push(current); + } + current = []; + } + current.push(cmdToArr(cmds[i])); + } + paths.push(current); + + return paths; +} + +function cmdToArr(cmd) { + + var arr = [ cmd.type ]; + if (cmd.type === 'M' || cmd.type === 'L') { // moveto or lineto + arr.push(cmd.x, cmd.y); + } else if (cmd.type === 'C') { + arr.push(cmd.x1, cmd.y1, cmd.x2, cmd.y2, cmd.x, cmd.y); + } else if (cmd.type === 'Q') { + arr.push(cmd.x1, cmd.y1, cmd.x, cmd.y); + } + // else if (cmd.type === 'Z') { /* no-op */ } + return arr; +} + +function parseOpts(options, defaults) { + + if (typeof options !== 'object') { + options = defaults; + } + else { + for (var key in defaults) { + if (typeof options[key] === 'undefined') { + options[key] = defaults[key]; + } + } + } + return options; +} + +//////////////////////// Helpers //////////////////////////// + +function at(v, i) { + var s = v.length; + return v[i < 0 ? i % s + s : i % s]; +} + +function collinear(a, b, c, thresholdAngle) { + + if (!thresholdAngle) { + return areaTriangle(a, b, c) === 0; + } + + if (typeof collinear.tmpPoint1 === 'undefined') { + collinear.tmpPoint1 = []; + collinear.tmpPoint2 = []; + } + + var ab = collinear.tmpPoint1, bc = collinear.tmpPoint2; + ab.x = b.x - a.x; + ab.y = b.y - a.y; + bc.x = c.x - b.x; + bc.y = c.y - b.y; + + var dot = ab.x * bc.x + ab.y * bc.y, + magA = Math.sqrt(ab.x * ab.x + ab.y * ab.y), + magB = Math.sqrt(bc.x * bc.x + bc.y * bc.y), + angle = Math.acos(dot / (magA * magB)); + + return angle < thresholdAngle; +} + +function areaTriangle(a, b, c) { + return (((b[0] - a[0]) * (c[1] - a[1])) - ((c[0] - a[0]) * (b[1] - a[1]))); +} + +// Portions of below code copyright 2008 Dmitry Baranovskiy (via MIT license) + +function findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) { + + var t1 = 1 - t, t13 = Math.pow(t1, 3), t12 = Math.pow(t1, 2), t2 = t * t, + t3 = t2 * t, x = t13 * p1x + t12 * 3 * t * c1x + t1 * 3 * t * t * c2x + + t3 * p2x, y = t13 * p1y + t12 * 3 * t * c1y + t1 * 3 * t * t * c2y + + t3 * p2y, mx = p1x + 2 * t * (c1x - p1x) + t2 * (c2x - 2 * c1x + p1x), + my = p1y + 2 * t * (c1y - p1y) + t2 * (c2y - 2 * c1y + p1y), + nx = c1x + 2 * t * (c2x - c1x) + t2 * (p2x - 2 * c2x + c1x), + ny = c1y + 2 * t * (c2y - c1y) + t2 * (p2y - 2 * c2y + c1y), + ax = t1 * p1x + t * c1x, ay = t1 * p1y + t * c1y, + cx = t1 * c2x + t * p2x, cy = t1 * c2y + t * p2y, + alpha = (90 - Math.atan2(mx - nx, my - ny) * 180 / Math.PI); + + if (mx > nx || my < ny) { alpha += 180; } + + return { x: x, y: y, m: { x: mx, y: my }, n: { x: nx, y: ny }, + start: { x: ax, y: ay }, end: { x: cx, y: cy }, alpha: alpha + }; +} + +function getPointAtSegmentLength(p1x,p1y,c1x,c1y,c2x,c2y,p2x,p2y,length) { + return (length == null) ? bezlen(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) : + findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, + getTatLen(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, length)); +} + +function pointAtLength(path, length, istotal) { + path = path2curve(path); + var x, y, p, l, sp = '', subpaths = {}, point, len = 0; + for (var i = 0, ii = path.length; i < ii; i++) { + p = path[i]; + if (p[0] === 'M') { + x = +p[1]; + y = +p[2]; + } else { + l = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6]); + if (len + l > length) { + if (!istotal) { + point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], + p[6], length - len); + return { x: point.x, y: point.y, alpha: point.alpha }; + } + } + len += l; + x = +p[5]; + y = +p[6]; + } + sp += p.shift() + p; + } + subpaths.end = sp; + + point = istotal ? len : findDotsAtSegment + (x, y, p[0], p[1], p[2], p[3], p[4], p[5], 1); + + if (point.alpha) { + point = { x: point.x, y: point.y, alpha: point.alpha }; + } + + return point; +} + +function pathToAbsolute(pathArray) { + + var res = [], x = 0, y = 0, mx = 0, my = 0, start = 0; + if (pathArray[0][0] === 'M') { + x = +pathArray[0][1]; + y = +pathArray[0][2]; + mx = x; + my = y; + start++; + res[0] = ['M', x, y]; + } + + var dots,crz = pathArray.length===3 && pathArray[0][0]==='M' && + pathArray[1][0].toUpperCase()==='R' && pathArray[2][0].toUpperCase()==='Z'; + + for (var r, pa, i = start, ii = pathArray.length; i < ii; i++) { + res.push(r = []); + pa = pathArray[i]; + if (pa[0] !== String.prototype.toUpperCase.call(pa[0])) { + r[0] = String.prototype.toUpperCase.call(pa[0]); + switch (r[0]) { + case 'A': + r[1] = pa[1]; + r[2] = pa[2]; + r[3] = pa[3]; + r[4] = pa[4]; + r[5] = pa[5]; + r[6] = +(pa[6] + x); + r[7] = +(pa[7] + y); + break; + case 'V': + r[1] = +pa[1] + y; + break; + case 'H': + r[1] = +pa[1] + x; + break; + case 'R': + dots = [x, y].concat(pa.slice(1)); + for (var j = 2, jj = dots.length; j < jj; j++) { + dots[j] = +dots[j] + x; + dots[++j] = +dots[j] + y; + } + res.pop(); + res = res.concat(catmullRom2bezier(dots, crz)); + break; + case 'M': + mx = +pa[1] + x; + my = +pa[2] + y; + break; + default: + for (j = 1, jj = pa.length; j < jj; j++) { + r[j] = +pa[j] + ((j % 2) ? x : y); + } + } + } else if (pa[0] === 'R') { + dots = [x, y].concat(pa.slice(1)); + res.pop(); + res = res.concat(catmullRom2bezier(dots, crz)); + r = ['R'].concat(pa.slice(-2)); + } else { + for (var k = 0, kk = pa.length; k < kk; k++) { + r[k] = pa[k]; + } + } + switch (r[0]) { + case 'Z': + x = mx; + y = my; + break; + case 'H': + x = r[1]; + break; + case 'V': + y = r[1]; + break; + case 'M': + mx = r[r.length - 2]; + my = r[r.length - 1]; + break; + default: + x = r[r.length - 2]; + y = r[r.length - 1]; + } + } + return res; +} + +function path2curve(path, path2) { + + var p = pathToAbsolute(path), p2 = path2 && pathToAbsolute(path2), + attrs = { x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null }, + attrs2 = { x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null }, + + processPath = function(path, d, pcom) { + var nx, ny, tq = { T: 1, Q: 1 }; + if (!path) { return ['C', d.x, d.y, d.x, d.y, d.x, d.y]; } + if (!(path[0] in tq)) { d.qx = d.qy = null; } + switch (path[0]) { + case 'M': + d.X = path[1]; + d.Y = path[2]; + break; + case 'A': + path = ['C'].concat(a2c.apply(0, [d.x, d.y].concat(path.slice(1)))); + break; + case 'S': + if (pcom === 'C' || pcom === 'S') { + nx = d.x * 2 - d.bx; + ny = d.y * 2 - d.by; + } else { + nx = d.x; + ny = d.y; + } + path = ['C', nx, ny].concat(path.slice(1)); + break; + case 'T': + if (pcom === 'Q' || pcom === 'T') { + d.qx = d.x * 2 - d.qx; + d.qy = d.y * 2 - d.qy; + } else { + d.qx = d.x; + d.qy = d.y; + } + path = ['C'].concat(q2c(d.x, d.y, d.qx, d.qy, path[1], path[2])); + break; + case 'Q': + d.qx = path[1]; + d.qy = path[2]; + path = ['C'].concat(q2c(d.x,d.y,path[1],path[2],path[3],path[4])); + break; + case 'L': + path = ['C'].concat(l2c(d.x, d.y, path[1], path[2])); + break; + case 'H': + path = ['C'].concat(l2c(d.x, d.y, path[1], d.y)); + break; + case 'V': + path = ['C'].concat(l2c(d.x, d.y, d.x, path[1])); + break; + case 'Z': + path = ['C'].concat(l2c(d.x, d.y, d.X, d.Y)); + break; + } + return path; + }, + + fixArc = function(pp, i) { + if (pp[i].length > 7) { + pp[i].shift(); + var pi = pp[i]; + while (pi.length) { + pcoms1[i] = 'A'; + if (p2) { pcoms2[i] = 'A'; } + pp.splice(i++, 0, ['C'].concat(pi.splice(0, 6))); + } + pp.splice(i, 1); + ii = Math.max(p.length, p2 && p2.length || 0); + } + }, + + fixM = function(path1, path2, a1, a2, i) { + if (path1 && path2 && path1[i][0] === 'M' && path2[i][0] !== 'M') { + path2.splice(i, 0, ['M', a2.x, a2.y]); + a1.bx = 0; + a1.by = 0; + a1.x = path1[i][1]; + a1.y = path1[i][2]; + ii = Math.max(p.length, p2 && p2.length || 0); + } + }, + + pcoms1 = [], // path commands of original path p + pcoms2 = [], // path commands of original path p2 + pfirst = '', // temporary holder for original path command + pcom = ''; // holder for previous path command of original path + + for (var i = 0, ii = Math.max(p.length, p2 && p2.length || 0); i < ii; i++) { + if (p[i]) { pfirst = p[i][0]; } // save current path command + + if (pfirst !== 'C') { + pcoms1[i] = pfirst; // Save current path command + if (i) { pcom = pcoms1[i - 1]; } // Get previous path command pcom + } + p[i] = processPath(p[i], attrs, pcom); + + if (pcoms1[i] !== 'A' && pfirst === 'C') { pcoms1[i] = 'C'; } + + fixArc(p, i); // fixArc adds also the right amount of A:s to pcoms1 + + if (p2) { // the same procedures is done to p2 + if (p2[i]) { pfirst = p2[i][0]; } + if (pfirst !== 'C') { + pcoms2[i] = pfirst; + if (i) { pcom = pcoms2[i - 1]; } + } + p2[i] = processPath(p2[i], attrs2, pcom); + + if (pcoms2[i] !== 'A' && pfirst === 'C') { pcoms2[i] = 'C'; } + + fixArc(p2, i); + } + fixM(p, p2, attrs, attrs2, i); + fixM(p2, p, attrs2, attrs, i); + var seg = p[i], seg2 = p2 && p2[i], seglen = seg.length, + seg2len = p2 && seg2.length; + attrs.x = seg[seglen - 2]; + attrs.y = seg[seglen - 1]; + attrs.bx = parseFloat(seg[seglen - 4]) || attrs.x; + attrs.by = parseFloat(seg[seglen - 3]) || attrs.y; + attrs2.bx = p2 && (parseFloat(seg2[seg2len - 4]) || attrs2.x); + attrs2.by = p2 && (parseFloat(seg2[seg2len - 3]) || attrs2.y); + attrs2.x = p2 && seg2[seg2len - 2]; + attrs2.y = p2 && seg2[seg2len - 1]; + } + + return p2 ? [p, p2] : p; +} + +function a2c(x1, y1, rx, ry, angle, lac, sweep_flag, x2, y2, recursive) { + // for more information of where this Math came from visit: + // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes + var PI = Math.PI, _120 = PI * 120 / 180, f1, f2, cx, cy, + rad = PI / 180 * (+angle || 0), res = [], xy, + rotate = function (x, y, rad) { + var X = x * Math.cos(rad) - y * Math.sin(rad), + Y = x * Math.sin(rad) + y * Math.cos(rad); + return { x: X, y: Y }; + }; + if (!recursive) { + xy = rotate(x1, y1, -rad); + x1 = xy.x; + y1 = xy.y; + xy = rotate(x2, y2, -rad); + x2 = xy.x; + y2 = xy.y; + var x = (x1 - x2) / 2, y = (y1 - y2) / 2, + h = (x * x) / (rx * rx) + (y * y) / (ry * ry); + if (h > 1) { + h = Math.sqrt(h); + rx = h * rx; + ry = h * ry; + } + var rx2 = rx * rx, ry2 = ry * ry, + k = (lac === sweep_flag ? -1 : 1) * Math.sqrt(Math.abs + ((rx2 * ry2 - rx2 * y * y - ry2 * x * x)/(rx2 * y * y + ry2 * x * x))); + + cx = k * rx * y / ry + (x1 + x2) / 2; + cy = k * -ry * x / rx + (y1 + y2) / 2; + f1 = Math.asin(((y1 - cy) / ry).toFixed(9)); + f2 = Math.asin(((y2 - cy) / ry).toFixed(9)); + + f1 = x1 < cx ? PI - f1 : f1; + f2 = x2 < cx ? PI - f2 : f2; + + if (f1 < 0) { f1 = PI * 2 + f1; } + if (f2 < 0) { f2 = PI * 2 + f2; } + + if (sweep_flag && f1 > f2) { + f1 = f1 - PI * 2; + } + if (!sweep_flag && f2 > f1) { + f2 = f2 - PI * 2; + } + } else { + f1 = recursive[0]; + f2 = recursive[1]; + cx = recursive[2]; + cy = recursive[3]; + } + var df = f2 - f1; + if (Math.abs(df) > _120) { + var f2old = f2, x2old = x2, y2old = y2; + f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1); + x2 = cx + rx * Math.cos(f2); + y2 = cy + ry * Math.sin(f2); + res = a2c(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, + [f2, f2old, cx, cy]); + } + df = f2 - f1; + var c1 = Math.cos(f1), + s1 = Math.sin(f1), + c2 = Math.cos(f2), + s2 = Math.sin(f2), + t = Math.tan(df / 4), + hx = 4 / 3 * rx * t, + hy = 4 / 3 * ry * t, + m1 = [x1, y1], + m2 = [x1 + hx * s1, y1 - hy * c1], + m3 = [x2 + hx * s2, y2 - hy * c2], + m4 = [x2, y2]; + m2[0] = 2 * m1[0] - m2[0]; + m2[1] = 2 * m1[1] - m2[1]; + if (recursive) { + return [m2, m3, m4].concat(res); + } else { + res = [m2, m3, m4].concat(res).join().split(','); + var newres = []; + for (var i = 0, ii = res.length; i < ii; i++) { + newres[i] = i % 2 ? rotate(res[i - 1], res[i], rad).y : rotate(res[i], + res[i + 1], rad).x; + } + return newres; + } +} + +// http://schepers.cc/getting-to-the-point +function catmullRom2bezier(crp, z) { + var d = []; + for (var i = 0, iLen = crp.length; iLen - 2 * !z > i; i += 2) { + var p = [{ + x: +crp[i - 2], + y: +crp[i - 1] + }, { + x: +crp[i], + y: +crp[i + 1] + }, { + x: +crp[i + 2], + y: +crp[i + 3] + }, { + x: +crp[i + 4], + y: +crp[i + 5] + }]; + if (z) { + if (!i) { + p[0] = { + x: +crp[iLen - 2], + y: +crp[iLen - 1] + }; + } else if (iLen - 4 === i) { + p[3] = { + x: +crp[0], + y: +crp[1] + }; + } else if (iLen - 2 === i) { + p[2] = { + x: +crp[0], + y: +crp[1] + }; + p[3] = { + x: +crp[2], + y: +crp[3] + }; + } + } else { + if (iLen - 4 === i) { + p[3] = p[2]; + } else if (!i) { + p[0] = { + x: +crp[i], + y: +crp[i + 1] + }; + } + } + d.push(['C', (-p[0].x + 6 * p[1].x + p[2].x) / 6, (-p[0].y + 6 * p[1].y + + p[2].y) / 6, (p[1].x + 6 * p[2].x - p[3].x) / 6, (p[1].y + 6 * p[2].y - + p[3].y) / 6, p[2].x, p[2].y ]); + } + + return d; +} + +function l2c(x1, y1, x2, y2) { return [x1, y1, x2, y2, x2, y2]; } + +function q2c(x1, y1, ax, ay, x2, y2) { + var _13 = 1 / 3, _23 = 2 / 3; + return [ + _13 * x1 + _23 * ax, _13 * y1 + _23 * ay, + _13 * x2 + _23 * ax, _13 * y2 + _23 * ay, x2, y2 + ]; +} + +function bezlen(x1, y1, x2, y2, x3, y3, x4, y4, z) { + if (z == null) { z = 1; } + z = z > 1 ? 1 : z < 0 ? 0 : z; + var z2 = z / 2, + n = 12, Tvalues = [-0.1252, 0.1252, -0.3678, 0.3678, -0.5873, 0.5873, + -0.7699, 0.7699, -0.9041, 0.9041, -0.9816, 0.9816], + sum = 0, Cvalues = [0.2491, 0.2491, 0.2335, 0.2335, 0.2032, 0.2032, + 0.1601, 0.1601, 0.1069, 0.1069, 0.0472, 0.0472 ]; + for (var i = 0; i < n; i++) { + var ct = z2 * Tvalues[i] + z2, + xbase = base3(ct, x1, x2, x3, x4), + ybase = base3(ct, y1, y2, y3, y4), + comb = xbase * xbase + ybase * ybase; + sum += Cvalues[i] * Math.sqrt(comb); + } + return z2 * sum; +} + +function getTatLen(x1, y1, x2, y2, x3, y3, x4, y4, ll) { + if (ll < 0 || bezlen(x1, y1, x2, y2, x3, y3, x4, y4) < ll) { + return; + } + var t = 1, step = t / 2, t2 = t - step, l, e = 0.01; + l = bezlen(x1, y1, x2, y2, x3, y3, x4, y4, t2); + while (Math.abs(l - ll) > e) { + step /= 2; + t2 += (l < ll ? 1 : -1) * step; + l = bezlen(x1, y1, x2, y2, x3, y3, x4, y4, t2); + } + return t2; +} + +function base3(t, p1, p2, p3, p4) { + var t1 = -3 * p1 + 9 * p2 - 9 * p3 + 3 * p4, + t2 = t * t1 + 6 * p1 - 12 * p2 + 6 * p3; + return t * t2 - 3 * p1 + 3 * p2; +} + +function cacheKey() { + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + i = args.length; + var hash = ''; + while (i--) { + hash += (args[i] === Object(args[i])) ? + JSON.stringify(args[i]) : args[i]; + } + return hash; +} + +module.exports = p5.Font; + +},{"../core/constants":36,"../core/core":37}],73:[function(_dereq_,module,exports){ +/** + * @module Data + * @submodule Array Functions + * @for p5 + * @requires core + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); + +/** + * Adds a value to the end of an array. Extends the length of + * the array by one. Maps to Array.push(). + * + * @method append + * @param {Array} array Array to append + * @param {any} value to be added to the Array + * @example + * <div class = "norender"><code> + * function setup() { + * + * var myArray = new Array("Mango", "Apple", "Papaya") + * print(myArray) // ["Mango", "Apple", "Papaya"] + * + * append(myArray, "Peach") + * print(myArray) // ["Mango", "Apple", "Papaya", "Peach"] + * + * } + * </div></code> + */ +p5.prototype.append = function(array, value) { + array.push(value); + return array; +}; + +/** + * Copies an array (or part of an array) to another array. The src array is + * copied to the dst array, beginning at the position specified by + * srcPosition and into the position specified by dstPosition. The number of + * elements to copy is determined by length. Note that copying values + * overwrites existing values in the destination array. To append values + * instead of overwriting them, use concat(). + * <br><br> + * The simplified version with only two arguments, arrayCopy(src, dst), + * copies an entire array to another of the same size. It is equivalent to + * arrayCopy(src, 0, dst, 0, src.length). + * <br><br> + * Using this function is far more efficient for copying array data than + * iterating through a for() loop and copying each element individually. + * + * @method arrayCopy + * @param {Array} src the source Array + * @param {Number} [srcPosition] starting position in the source Array + * @param {Array} dst the destination Array + * @param {Number} [dstPosition] starting position in the destination Array + * @param {Number} [length] number of Array elements to be copied + * + * @example + * <div class="norender"><code> + * function setup() { + * + * var src = new Array("A", "B", "C"); + * var dst = new Array( 1 , 2 , 3 ); + * var srcPosition = 1; + * var dstPosition = 0; + * var length = 2; + * + * print(src); // ["A", "B", "C"] + * print(dst); // [ 1 , 2 , 3 ] + * + * arrayCopy(src, srcPosition, dst, dstPosition, length); + * print(dst); // ["B", "C", 3] + * + * } + * </div></code> + */ +p5.prototype.arrayCopy = function( + src, + srcPosition, + dst, + dstPosition, + length) { + + // the index to begin splicing from dst array + var start, + end; + + if (typeof length !== 'undefined') { + + end = Math.min(length, src.length); + start = dstPosition; + src = src.slice(srcPosition, end + srcPosition); + + } else { + + if (typeof dst !== 'undefined') { // src, dst, length + // rename so we don't get confused + end = dst; + end = Math.min(end, src.length); + } else { // src, dst + end = src.length; + } + + start = 0; + // rename so we don't get confused + dst = srcPosition; + src = src.slice(0, end); + } + + // Since we are not returning the array and JavaScript is pass by reference + // we must modify the actual values of the array + // instead of reassigning arrays + Array.prototype.splice.apply(dst, [start, end].concat(src)); + +}; + +/** + * Concatenates two arrays, maps to Array.concat(). Does not modify the + * input arrays. + * + * @method concat + * @param {Array} a first Array to concatenate + * @param {Array} b second Array to concatenate + * @return {Array} concatenated array + * + * @example + * <div class = "norender"><code> + * function setup() { + * var arr1 = new Array("A", "B", "C"); + * var arr2 = new Array( 1 , 2 , 3 ); + * + * print(arr1); // ["A","B","C"] + * print(arr2); // [1,2,3] + * + * var arr3 = concat(arr1, arr2); + * + * print(arr1); // ["A","B","C"] + * print(arr2); // [1,2,3] + * print(arr3); // ["A","B","C",1,2,3] + * + * } + * </div></code> + */ +p5.prototype.concat = function(list0, list1) { + return list0.concat(list1); +}; + +/** + * Reverses the order of an array, maps to Array.reverse() + * + * @method reverse + * @param {Array} list Array to reverse + * @example + * <div class="norender"><code> + * function setup() { + * var myArray = new Array("A", "B", "C"); + * print(myArray); // ["A","B","C"] + * + * reverse(myArray); + * print(myArray); // ["C","B","A"] + * } + * </div></code> + */ +p5.prototype.reverse = function(list) { + return list.reverse(); +}; + +/** + * Decreases an array by one element and returns the shortened array, + * maps to Array.pop(). + * + * @method shorten + * @param {Array} list Array to shorten + * @return {Array} shortened Array + * @example + * <div class = "norender"><code> + * function setup() { + * var myArray = new Array("A", "B", "C"); + * print(myArray); // ["A","B","C"] + * + * var newArray = shorten(myArray); + * print(myArray); // ["A","B","C"] + * print(newArray); // ["A","B"] + * } + * </div></code> + */ +p5.prototype.shorten = function(list) { + list.pop(); + return list; +}; + +/** + * Randomizes the order of the elements of an array. Implements + * <a href="http://Bost.Ocks.org/mike/shuffle/" target=_blank> + * Fisher-Yates Shuffle Algorithm</a>. + * + * @method shuffle + * @param {Array} array Array to shuffle + * @param {Boolean} [bool] modify passed array + * @return {Array} shuffled Array + * @example + * <div><code> + * function setup() { + * var regularArr = ['ABC', 'def', createVector(), TAU, Math.E]; + * print(regularArr); + * shuffle(regularArr, true); // force modifications to passed array + * print(regularArr); + * + * // By default shuffle() returns a shuffled cloned array: + * var newArr = shuffle(regularArr); + * print(regularArr); + * print(newArr); + * } + * </code></div> + */ +p5.prototype.shuffle = function(arr, bool) { + var isView = ArrayBuffer && ArrayBuffer.isView && ArrayBuffer.isView(arr); + arr = bool || isView ? arr : arr.slice(); + + var rnd, tmp, idx = arr.length; + while (idx > 1) { + rnd = Math.random()*idx | 0; + + tmp = arr[--idx]; + arr[idx] = arr[rnd]; + arr[rnd] = tmp; + } + + return arr; +}; + +/** + * Sorts an array of numbers from smallest to largest, or puts an array of + * words in alphabetical order. The original array is not modified; a + * re-ordered array is returned. The count parameter states the number of + * elements to sort. For example, if there are 12 elements in an array and + * count is set to 5, only the first 5 elements in the array will be sorted. + * + * @method sort + * @param {Array} list Array to sort + * @param {Number} [count] number of elements to sort, starting from 0 + * + * @example + * <div class = "norender"><code> + * function setup() { + * var words = new Array("banana", "apple", "pear","lime"); + * print(words); // ["banana", "apple", "pear", "lime"] + * var count = 4; // length of array + * + * words = sort(words, count); + * print(words); // ["apple", "banana", "lime", "pear"] + * } + * </div></code> + * <div class = "norender"><code> + * function setup() { + * var numbers = new Array(2,6,1,5,14,9,8,12); + * print(numbers); // [2,6,1,5,14,9,8,12] + * var count = 5; // Less than the length of the array + * + * numbers = sort(numbers, count); + * print(numbers); // [1,2,5,6,14,9,8,12] + * } + * </div></code> + */ +p5.prototype.sort = function(list, count) { + var arr = count ? list.slice(0, Math.min(count, list.length)) : list; + var rest = count ? list.slice(Math.min(count, list.length)) : []; + if (typeof arr[0] === 'string') { + arr = arr.sort(); + } else { + arr = arr.sort(function(a,b){return a-b;}); + } + return arr.concat(rest); +}; + +/** + * Inserts a value or an array of values into an existing array. The first + * parameter specifies the initial array to be modified, and the second + * parameter defines the data to be inserted. The third parameter is an index + * value which specifies the array position from which to insert data. + * (Remember that array index numbering starts at zero, so the first position + * is 0, the second position is 1, and so on.) + * + * @method splice + * @param {Array} list Array to splice into + * @param {any} value value to be spliced in + * @param {Number} position in the array from which to insert data + * + * @example + * <div class = "norender"><code> + * function setup() { + * var myArray = new Array(0,1,2,3,4); + * var insArray = new Array("A","B","C"); + * print(myArray); // [0,1,2,3,4] + * print(insArray); // ["A","B","C"] + * + * splice(myArray, insArray, 3); + * print(myArray); // [0,1,2,"A","B","C",3,4] + * } + * </div></code> + */ +p5.prototype.splice = function(list, value, index) { + + // note that splice returns spliced elements and not an array + Array.prototype.splice.apply(list, [index, 0].concat(value)); + + return list; +}; + +/** + * Extracts an array of elements from an existing array. The list parameter + * defines the array from which the elements will be copied, and the start + * and count parameters specify which elements to extract. If no count is + * given, elements will be extracted from the start to the end of the array. + * When specifying the start, remember that the first array element is 0. + * This function does not change the source array. + * + * @method subset + * @param {Array} list Array to extract from + * @param {Number} start position to begin + * @param {Number} [count] number of values to extract + * @return {Array} Array of extracted elements + * + * @example + * <div class = "norender"><code> + * function setup() { + * var myArray = new Array(1,2,3,4,5); + * print(myArray); // [1,2,3,4,5] + * + * var sub1 = subset(myArray, 0, 3); + * var sub2 = subset(myArray, 2, 2); + * print(sub1); // [1,2,3] + * print(sub2); // [3,4] + * } + * </div></code> + */ +p5.prototype.subset = function(list, start, count) { + if (typeof count !== 'undefined') { + return list.slice(start, start + count); + } else { + return list.slice(start, list.length); + } +}; + +module.exports = p5; + +},{"../core/core":37}],74:[function(_dereq_,module,exports){ +/** + * @module Data + * @submodule Conversion + * @for p5 + * @requires core + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); + +/** + * Converts a string to its floating point representation. The contents of a + * string must resemble a number, or NaN (not a number) will be returned. + * For example, float("1234.56") evaluates to 1234.56, but float("giraffe") + * will return NaN. + * + * @method float + * @param {String} str float string to parse + * @return {Number} floating point representation of string + * @example + * <div><code> + * var str = '20'; + * var diameter = float(str); + * ellipse(width/2, height/2, diameter, diameter); + * </code></div> + * + * @alt + * 20 by 20 white ellipse in the center of the canvas + * + */ +p5.prototype.float = function(str) { + return parseFloat(str); +}; + +/** + * Converts a boolean, string, or float to its integer representation. + * When an array of values is passed in, then an int array of the same length + * is returned. + * + * @method int + * @param {String|Boolean|Number|Array} n value to parse + * @return {Number} integer representation of value + * @example + * <div class='norender'><code> + * print(int("10")); // 10 + * print(int(10.31)); // 10 + * print(int(-10)); // -10 + * print(int(true)); // 1 + * print(int(false)); // 0 + * print(int([false, true, "10.3", 9.8])); // [0, 1, 10, 9] + * </code></div> + */ +p5.prototype.int = function(n, radix) { + if (typeof n === 'string') { + radix = radix || 10; + return parseInt(n, radix); + } else if (typeof n === 'number') { + return n | 0; + } else if (typeof n === 'boolean') { + return n ? 1 : 0; + } else if (n instanceof Array) { + return n.map(function(n) { return p5.prototype.int(n, radix); }); + } +}; + +/** + * Converts a boolean, string or number to its string representation. + * When an array of values is passed in, then an array of strings of the same + * length is returned. + * + * @method str + * @param {String|Boolean|Number|Array} n value to parse + * @return {String} string representation of value + * @example + * <div class='norender'><code> + * print(str("10")); // "10" + * print(str(10.31)); // "10.31" + * print(str(-10)); // "-10" + * print(str(true)); // "true" + * print(str(false)); // "false" + * print(str([true, "10.3", 9.8])); // [ "true", "10.3", "9.8" ] + * </code></div> + */ +p5.prototype.str = function(n) { + if (n instanceof Array) { + return n.map(p5.prototype.str); + } else { + return String(n); + } +}; + +/** + * Converts a number or string to its boolean representation. + * For a number, any non-zero value (positive or negative) evaluates to true, + * while zero evaluates to false. For a string, the value "true" evaluates to + * true, while any other value evaluates to false. When an array of number or + * string values is passed in, then a array of booleans of the same length is + * returned. + * + * @method boolean + * @param {String|Boolean|Number|Array} n value to parse + * @return {Boolean} boolean representation of value + * @example + * <div class='norender'><code> + * print(boolean(0)); // false + * print(boolean(1)); // true + * print(boolean("true")); // true + * print(boolean("abcd")); // false + * print(boolean([0, 12, "true"])); // [false, true, false] + * </code></div> + */ +p5.prototype.boolean = function(n) { + if (typeof n === 'number') { + return n !== 0; + } else if (typeof n === 'string') { + return n.toLowerCase() === 'true'; + } else if (typeof n === 'boolean') { + return n; + } else if (n instanceof Array) { + return n.map(p5.prototype.boolean); + } +}; + +/** + * Converts a number, string or boolean to its byte representation. + * A byte can be only a whole number between -128 and 127, so when a value + * outside of this range is converted, it wraps around to the corresponding + * byte representation. When an array of number, string or boolean values is + * passed in, then an array of bytes the same length is returned. + * + * @method byte + * @param {String|Boolean|Number|Array} n value to parse + * @return {Number} byte representation of value + * @example + * <div class='norender'><code> + * print(byte(127)); // 127 + * print(byte(128)); // -128 + * print(byte(23.4)); // 23 + * print(byte("23.4")); // 23 + * print(byte(true)); // 1 + * print(byte([0, 255, "100"])); // [0, -1, 100] + * </code></div> + */ +p5.prototype.byte = function(n) { + var nn = p5.prototype.int(n, 10); + if (typeof nn === 'number') { + return ((nn + 128) % 256) - 128; + } else if (nn instanceof Array) { + return nn.map(p5.prototype.byte); + } +}; + +/** + * Converts a number or string to its corresponding single-character + * string representation. If a string parameter is provided, it is first + * parsed as an integer and then translated into a single-character string. + * When an array of number or string values is passed in, then an array of + * single-character strings of the same length is returned. + * + * @method char + * @param {String|Number|Array} n value to parse + * @return {String} string representation of value + * @example + * <div class='norender'><code> + * print(char(65)); // "A" + * print(char("65")); // "A" + * print(char([65, 66, 67])); // [ "A", "B", "C" ] + * print(join(char([65, 66, 67]), '')); // "ABC" + * </code></div> + */ +p5.prototype.char = function(n) { + if (typeof n === 'number' && !isNaN(n)) { + return String.fromCharCode(n); + } else if (n instanceof Array) { + return n.map(p5.prototype.char); + } else if (typeof n === 'string') { + return p5.prototype.char(parseInt(n, 10)); + } +}; + +/** + * Converts a single-character string to its corresponding integer + * representation. When an array of single-character string values is passed + * in, then an array of integers of the same length is returned. + * + * @method unchar + * @param {String|Array} n value to parse + * @return {Number} integer representation of value + * @example + * <div class='norender'><code> + * print(unchar("A")); // 65 + * print(unchar(["A", "B", "C"])); // [ 65, 66, 67 ] + * print(unchar(split("ABC", ""))); // [ 65, 66, 67 ] + * </code></div> + */ +p5.prototype.unchar = function(n) { + if (typeof n === 'string' && n.length === 1) { + return n.charCodeAt(0); + } else if (n instanceof Array) { + return n.map(p5.prototype.unchar); + } +}; + +/** + * Converts a number to a string in its equivalent hexadecimal notation. If a + * second parameter is passed, it is used to set the number of characters to + * generate in the hexadecimal notation. When an array is passed in, an + * array of strings in hexadecimal notation of the same length is returned. + * + * @method hex + * @param {Number|Array} n value to parse + * @return {String} hexadecimal string representation of value + * @example + * <div class='norender'><code> + * print(hex(255)); // "000000FF" + * print(hex(255, 6)); // "0000FF" + * print(hex([0, 127, 255], 6)); // [ "000000", "00007F", "0000FF" ] + * </code></div> + */ +p5.prototype.hex = function(n, digits) { + digits = (digits === undefined || digits === null) ? digits = 8 : digits; + if (n instanceof Array) { + return n.map(function(n) { return p5.prototype.hex(n, digits); }); + } else if (typeof n === 'number') { + if (n < 0) { + n = 0xFFFFFFFF + n + 1; + } + var hex = Number(n).toString(16).toUpperCase(); + while (hex.length < digits) { + hex = '0' + hex; + } + if (hex.length >= digits) { + hex = hex.substring(hex.length - digits, hex.length); + } + return hex; + } +}; + +/** + * Converts a string representation of a hexadecimal number to its equivalent + * integer value. When an array of strings in hexadecimal notation is passed + * in, an array of integers of the same length is returned. + * + * @method unhex + * @param {String|Array} n value to parse + * @return {Number} integer representation of hexadecimal value + * @example + * <div class='norender'><code> + * print(unhex("A")); // 10 + * print(unhex("FF")); // 255 + * print(unhex(["FF", "AA", "00"])); // [ 255, 170, 0 ] + * </code></div> + */ +p5.prototype.unhex = function(n) { + if (n instanceof Array) { + return n.map(p5.prototype.unhex); + } else { + return parseInt('0x' + n, 16); + } +}; + +module.exports = p5; + +},{"../core/core":37}],75:[function(_dereq_,module,exports){ +/** + * @module Data + * @submodule String Functions + * @for p5 + * @requires core + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); + +//return p5; //LM is this a mistake? + +/** + * Combines an array of Strings into one String, each separated by the + * character(s) used for the separator parameter. To join arrays of ints or + * floats, it's necessary to first convert them to Strings using nf() or + * nfs(). + * + * @method join + * @param {Array} list array of Strings to be joined + * @param {String} separator String to be placed between each item + * @return {String} joined String + * @example + * <div> + * <code> + * var array = ["Hello", "world!"] + * var separator = " " + * var message = join(array, separator); + * text(message, 5, 50); + * </code> + * </div> + * + * @alt + * "hello world!" displayed middle left of canvas. + * + */ +p5.prototype.join = function(list, separator) { + return list.join(separator); +}; + +/** + * This function is used to apply a regular expression to a piece of text, + * and return matching groups (elements found inside parentheses) as a + * String array. If there are no matches, a null value will be returned. + * If no groups are specified in the regular expression, but the sequence + * matches, an array of length 1 (with the matched text as the first element + * of the array) will be returned. + * <br><br> + * To use the function, first check to see if the result is null. If the + * result is null, then the sequence did not match at all. If the sequence + * did match, an array is returned. + * <br><br> + * If there are groups (specified by sets of parentheses) in the regular + * expression, then the contents of each will be returned in the array. + * Element [0] of a regular expression match returns the entire matching + * string, and the match groups start at element [1] (the first group is [1], + * the second [2], and so on). + * + * @method match + * @param {String} str the String to be searched + * @param {String} regexp the regexp to be used for matching + * @return {Array} Array of Strings found + * @example + * <div> + * <code> + * var string = "Hello p5js*!" + * var regexp = "p5js\\*" + * var match = match(string, regexp); + * text(match, 5, 50); + * </code> + * </div> + * + * @alt + * "p5js*" displayed middle left of canvas. + * + */ +p5.prototype.match = function(str, reg) { + return str.match(reg); +}; + +/** + * This function is used to apply a regular expression to a piece of text, + * and return a list of matching groups (elements found inside parentheses) + * as a two-dimensional String array. If there are no matches, a null value + * will be returned. If no groups are specified in the regular expression, + * but the sequence matches, a two dimensional array is still returned, but + * the second dimension is only of length one. + * <br><br> + * To use the function, first check to see if the result is null. If the + * result is null, then the sequence did not match at all. If the sequence + * did match, a 2D array is returned. + * <br><br> + * If there are groups (specified by sets of parentheses) in the regular + * expression, then the contents of each will be returned in the array. + * Assuming a loop with counter variable i, element [i][0] of a regular + * expression match returns the entire matching string, and the match groups + * start at element [i][1] (the first group is [i][1], the second [i][2], + * and so on). + * + * @method matchAll + * @param {String} str the String to be searched + * @param {String} regexp the regexp to be used for matching + * @return {Array} 2d Array of Strings found + * @example + * <div class="norender"> + * <code> + * var string = "Hello p5js*! Hello world!" + * var regexp = "Hello" + * matchAll(string, regexp); + * </code> + * </div> + + */ +p5.prototype.matchAll = function(str, reg) { + var re = new RegExp(reg, 'g'); + var match = re.exec(str); + var matches = []; + while (match !== null) { + matches.push(match); + // matched text: match[0] + // match start: match.index + // capturing group n: match[n] + match = re.exec(str); + } + return matches; +}; + +/** + * Utility function for formatting numbers into strings. There are two + * versions: one for formatting floats, and one for formatting ints. + * The values for the digits, left, and right parameters should always + * be positive integers. + * + * @method nf + * @param {Number|Array} num the Number to format + * @param {Number} [left] number of digits to the left of the + * decimal point + * @param {Number} [right] number of digits to the right of the + * decimal point + * @return {String|Array} formatted String + * @example + * <div> + * <code> + * function setup() { + * background(200); + * var num = 112.53106115; + * + * noStroke(); + * fill(0); + * textSize(14); + * // Draw formatted numbers + * text(nf(num, 5, 2), 10, 20); + * + * text(nf(num, 4, 3), 10, 55); + * + * text(nf(num, 3, 6), 10, 85); + * + * // Draw dividing lines + * stroke(120); + * line(0, 30, width, 30); + * line(0, 65, width, 65); + * } + * </code> + * </div> + * + * @alt + * "0011253" top left, "0112.531" mid left, "112.531061" bottom left canvas + * + */ +p5.prototype.nf = function () { + if (arguments[0] instanceof Array) { + var a = arguments[1]; + var b = arguments[2]; + return arguments[0].map(function (x) { + return doNf(x, a, b); + }); + } + else{ + var typeOfFirst = Object.prototype.toString.call(arguments[0]); + if(typeOfFirst === '[object Arguments]'){ + if(arguments[0].length===3){ + return this.nf(arguments[0][0],arguments[0][1],arguments[0][2]); + } + else if(arguments[0].length===2){ + return this.nf(arguments[0][0],arguments[0][1]); + } + else{ + return this.nf(arguments[0][0]); + } + } + else { + return doNf.apply(this, arguments); + } + } +}; + +function doNf() { + var num = arguments[0]; + var neg = num < 0; + var n = neg ? num.toString().substring(1) : num.toString(); + var decimalInd = n.indexOf('.'); + var intPart = decimalInd !== -1 ? n.substring(0, decimalInd) : n; + var decPart = decimalInd !== -1 ? n.substring(decimalInd + 1) : ''; + var str = neg ? '-' : ''; + if (arguments.length === 3) { + var decimal = ''; + if(decimalInd !== -1 || arguments[2] - decPart.length > 0){ + decimal = '.'; + } + if (decPart.length > arguments[2]) { + decPart = decPart.substring(0, arguments[2]); + } + for (var i = 0; i < arguments[1] - intPart.length; i++) { + str += '0'; + } + str += intPart; + str += decimal; + str += decPart; + for (var j = 0; j < arguments[2] - decPart.length; j++) { + str += '0'; + } + return str; + } + else { + for (var k = 0; k < Math.max(arguments[1] - intPart.length, 0); k++) { + str += '0'; + } + str += n; + return str; + } +} + +/** + * Utility function for formatting numbers into strings and placing + * appropriate commas to mark units of 1000. There are two versions: one + * for formatting ints, and one for formatting an array of ints. The value + * for the right parameter should always be a positive integer. + * + * @method nfc + * @param {Number|Array} num the Number to format + * @param {Number} [right] number of digits to the right of the + * decimal point + * @return {String|Array} formatted String + * @example + * <div> + * <code> + * function setup() { + * background(200); + * var num = 11253106.115; + * var numArr = new Array(1,1,2); + * + * noStroke(); + * fill(0); + * textSize(12); + * + * // Draw formatted numbers + * text(nfc(num, 4, 2), 10, 30); + * text(nfc(numArr, 2, 1), 10, 80); + * + * // Draw dividing line + * stroke(120); + * line(0, 50, width, 50); + * } + * </code> + * </div> + * + * @alt + * "11,253,106.115" top middle and "1.00,1.00,2.00" displayed bottom mid + * + */ +p5.prototype.nfc = function () { + if (arguments[0] instanceof Array) { + var a = arguments[1]; + return arguments[0].map(function (x) { + return doNfc(x, a); + }); + } else { + return doNfc.apply(this, arguments); + } +}; +function doNfc() { + var num = arguments[0].toString(); + var dec = num.indexOf('.'); + var rem = dec !== -1 ? num.substring(dec) : ''; + var n = dec !== -1 ? num.substring(0, dec) : num; + n = n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); + if (arguments[1] === 0) { + rem = ''; + } + else if(arguments[1] !== undefined){ + if(arguments[1] > rem.length){ + rem+= dec === -1 ? '.' : ''; + var len = arguments[1] - rem.length + 1; + for(var i =0; i< len; i++){ + rem += '0'; + } + } + else{ + rem = rem.substring(0, arguments[1] + 1); + } + } + return n + rem; +} + +/** + * Utility function for formatting numbers into strings. Similar to nf() but + * puts a "+" in front of positive numbers and a "-" in front of negative + * numbers. There are two versions: one for formatting floats, and one for + * formatting ints. The values for left, and right parameters + * should always be positive integers. + * + * @method nfp + * @param {Number|Array} num the Number to format + * @param {Number} [left] number of digits to the left of the decimal + * point + * @param {Number} [right] number of digits to the right of the + * decimal point + * @return {String|Array} formatted String + * @example + * <div> + * <code> + * function setup() { + * background(200); + * var num1 = 11253106.115; + * var num2 = -11253106.115; + * + * noStroke(); + * fill(0); + * textSize(12); + * + * // Draw formatted numbers + * text(nfp(num1, 4, 2), 10, 30); + * text(nfp(num2, 4, 2), 10, 80); + * + * // Draw dividing line + * stroke(120); + * line(0, 50, width, 50); + * } + * </code> + * </div> + * + * @alt + * "+11253106.11" top middle and "-11253106.11" displayed bottom middle + * + */ +p5.prototype.nfp = function() { + var nfRes = this.nf.apply(this, arguments); + if (nfRes instanceof Array) { + return nfRes.map(addNfp); + } else { + return addNfp(nfRes); + } +}; + +function addNfp() { + return ( + parseFloat(arguments[0]) > 0) ? + '+'+arguments[0].toString() : + arguments[0].toString(); +} + +/** + * Utility function for formatting numbers into strings. Similar to nf() but + * puts a " " (space) in front of positive numbers and a "-" in front of + * negative numbers. There are two versions: one for formatting floats, and + * one for formatting ints. The values for the digits, left, and right + * parameters should always be positive integers. + * + * @method nfs + * @param {Number|Array} num the Number to format + * @param {Number} [left] number of digits to the left of the decimal + * point + * @param {Number} [right] number of digits to the right of the + * decimal point + * @return {String|Array} formatted String + * @example + * <div> + * <code> + * function setup() { + * background(200); + * var num1 = 11253106.115; + * var num2 = -11253106.115; + * + * noStroke(); + * fill(0); + * textSize(12); + * // Draw formatted numbers + * text(nfs(num1, 4, 2), 10, 30); + * + * text(nfs(num2, 4, 2), 10, 80); + * + * // Draw dividing line + * stroke(120); + * line(0, 50, width, 50); + * } + * </code> + * </div> + * + * @alt + * "11253106.11" top middle and "-11253106.11" displayed bottom middle + * + */ +p5.prototype.nfs = function() { + var nfRes = this.nf.apply(this, arguments); + if (nfRes instanceof Array) { + return nfRes.map(addNfs); + } else { + return addNfs(nfRes); + } +}; + +function addNfs() { + return ( + parseFloat(arguments[0]) > 0) ? + ' '+arguments[0].toString() : + arguments[0].toString(); +} + +/** + * The split() function maps to String.split(), it breaks a String into + * pieces using a character or string as the delimiter. The delim parameter + * specifies the character or characters that mark the boundaries between + * each piece. A String[] array is returned that contains each of the pieces. + * + * The splitTokens() function works in a similar fashion, except that it + * splits using a range of characters instead of a specific character or + * sequence. + * + * @method split + * @param {String} value the String to be split + * @param {String} delim the String used to separate the data + * @return {Array} Array of Strings + * @example + * <div> + * <code> + * var names = "Pat,Xio,Alex" + * var splitString = split(names, ","); + * text(splitString[0], 5, 30); + * text(splitString[1], 5, 50); + * text(splitString[2], 5, 70); + * </code> + * </div> + * + * @alt + * "pat" top left, "Xio" mid left and "Alex" displayed bottom left + * + */ +p5.prototype.split = function(str, delim) { + return str.split(delim); +}; + +/** + * The splitTokens() function splits a String at one or many character + * delimiters or "tokens." The delim parameter specifies the character or + * characters to be used as a boundary. + * <br><br> + * If no delim characters are specified, any whitespace character is used to + * split. Whitespace characters include tab (\t), line feed (\n), carriage + * return (\r), form feed (\f), and space. + * + * @method splitTokens + * @param {String} value the String to be split + * @param {String} [delim] list of individual Strings that will be used as + * separators + * @return {Array} Array of Strings + * @example + * <div class = "norender"> + * <code> + * function setup() { + * var myStr = "Mango, Banana, Lime"; + * var myStrArr = splitTokens(myStr, ","); + * + * print(myStrArr); // prints : ["Mango"," Banana"," Lime"] + * } + * </code> + * </div> + */ +p5.prototype.splitTokens = function() { + var d,sqo,sqc,str; + str = arguments[1]; + if (arguments.length > 1) { + sqc = /\]/g.exec(str); + sqo = /\[/g.exec(str); + if ( sqo && sqc ) { + str = str.slice(0, sqc.index) + str.slice(sqc.index+1); + sqo = /\[/g.exec(str); + str = str.slice(0, sqo.index) + str.slice(sqo.index+1); + d = new RegExp('[\\['+str+'\\]]','g'); + } else if ( sqc ) { + str = str.slice(0, sqc.index) + str.slice(sqc.index+1); + d = new RegExp('[' + str + '\\]]', 'g'); + } else if(sqo) { + str = str.slice(0, sqo.index) + str.slice(sqo.index+1); + d = new RegExp('[' + str + '\\[]', 'g'); + } else { + d = new RegExp('[' + str + ']', 'g'); + } + } else { + d = /\s/g; + } + return arguments[0].split(d).filter(function(n){return n;}); +}; + +/** + * Removes whitespace characters from the beginning and end of a String. In + * addition to standard whitespace characters such as space, carriage return, + * and tab, this function also removes the Unicode "nbsp" character. + * + * @method trim + * @param {String|Array} str a String or Array of Strings to be trimmed + * @return {String|Array} a trimmed String or Array of Strings + * @example + * <div> + * <code> + * var string = trim(" No new lines\n "); + * text(string +" here", 2, 50); + * </code> + * </div> + * + * @alt + * "No new lines here" displayed center canvas + * + */ +p5.prototype.trim = function(str) { + if (str instanceof Array) { + return str.map(this.trim); + } else { + return str.trim(); + } +}; + +module.exports = p5; + +},{"../core/core":37}],76:[function(_dereq_,module,exports){ +/** + * @module IO + * @submodule Time & Date + * @for p5 + * @requires core + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); + +/** + * p5.js communicates with the clock on your computer. The day() function + * returns the current day as a value from 1 - 31. + * + * @method day + * @return {Number} the current day + * @example + * <div> + * <code> + * var d = day(); + * text("Current day: \n" + d, 5, 50); + * </code> + * </div> + * + * @alt + * Current day is displayed + * + */ +p5.prototype.day = function() { + return new Date().getDate(); +}; + +/** + * p5.js communicates with the clock on your computer. The hour() function + * returns the current hour as a value from 0 - 23. + * + * @method hour + * @return {Number} the current hour + * @example + * <div> + * <code> + * var h = hour(); + * text("Current hour:\n" + h, 5, 50); + * </code> + * </div> + * + * @alt + * Current hour is displayed + * + */ +p5.prototype.hour = function() { + return new Date().getHours(); +}; + +/** + * p5.js communicates with the clock on your computer. The minute() function + * returns the current minute as a value from 0 - 59. + * + * @method minute + * @return {Number} the current minute + * @example + * <div> + * <code> + * var m = minute(); + * text("Current minute: \n" + m, 5, 50); + * </code> + * </div> + * + * @alt + * Current minute is displayed + * + */ +p5.prototype.minute = function() { + return new Date().getMinutes(); +}; + +/** + * Returns the number of milliseconds (thousandths of a second) since + * starting the program. This information is often used for timing events and + * animation sequences. + * + * @method millis + * @return {Number} the number of milliseconds since starting the program + * @example + * <div> + * <code> + * var millisecond = millis(); + * text("Milliseconds \nrunning: \n" + millisecond, 5, 40); + * </code> + * </div> + * + * @alt + * number of milliseconds since program has started displayed + * + */ +p5.prototype.millis = function() { + return window.performance.now(); +}; + +/** + * p5.js communicates with the clock on your computer. The month() function + * returns the current month as a value from 1 - 12. + * + * @method month + * @return {Number} the current month + * @example + * <div> + * <code> + * var m = month(); + * text("Current month: \n" + m, 5, 50); + * </code> + * </div> + * + * @alt + * Current month is displayed + * + */ +p5.prototype.month = function() { + return new Date().getMonth() + 1; //January is 0! +}; + +/** + * p5.js communicates with the clock on your computer. The second() function + * returns the current second as a value from 0 - 59. + * + * @method second + * @return {Number} the current second + * @example + * <div> + * <code> + * var s = second(); + * text("Current second: \n" + s, 5, 50); + * </code> + * </div> + * + * @alt + * Current second is displayed + * + */ +p5.prototype.second = function() { + return new Date().getSeconds(); +}; + +/** + * p5.js communicates with the clock on your computer. The year() function + * returns the current year as an integer (2014, 2015, 2016, etc). + * + * @method year + * @return {Number} the current year + * @example + * <div> + * <code> + * var y = year(); + * text("Current year: \n" + y, 5, 50); + * </code> + * </div> + * + * @alt + * Current year is displayed + * + */ +p5.prototype.year = function() { + return new Date().getFullYear(); +}; + +module.exports = p5; + +},{"../core/core":37}],77:[function(_dereq_,module,exports){ +/** + * @module Lights, Camera + * @submodule Camera + * @for p5 + * @requires core + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); + +/** + * Sets camera position + * @method camera + * @param {Number} x camera position value on x axis + * @param {Number} y camera position value on y axis + * @param {Number} z camera position value on z axis + * @return {p5} the p5 object + * @example + * <div> + * <code> + * function setup(){ + * createCanvas(100, 100, WEBGL); + * } + * function draw(){ + * //move the camera away from the plane by a sin wave + * camera(0, 0, sin(frameCount * 0.01) * 100); + * plane(120, 120); + * } + * </code> + * </div> + * + * @alt + * blue square shrinks in size grows to fill canvas. disappears then loops. + * + */ +p5.prototype.camera = function(x, y, z){ + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + this._validateParameters( + 'camera', + args, + ['Number', 'Number', 'Number'] + ); + //what it manipulates is the model view matrix + this._renderer.translate(-x, -y, -z); +}; + +/** + * Sets perspective camera + * @method perspective + * @param {Number} fovy camera frustum vertical field of view, + * from bottom to top of view, in degrees + * @param {Number} aspect camera frustum aspect ratio + * @param {Number} near frustum near plane length + * @param {Number} far frustum far plane length + * @return {p5} the p5 object + * @example + * <div> + * <code> + * //drag mouse to toggle the world! + * //you will see there's a vanish point + * function setup(){ + * createCanvas(100, 100, WEBGL); + * perspective(60 / 180 * PI, width/height, 0.1, 100); + * } + * function draw(){ + * background(200); + * orbitControl(); + * for(var i = -1; i < 2; i++){ + * for(var j = -2; j < 3; j++){ + * push(); + * translate(i*160, 0, j*160); + * box(40, 40, 40); + * pop(); + * } + * } + * } + * </code> + * </div> + * + * @alt + * colored 3d boxes toggleable with mouse position + * + */ +p5.prototype.perspective = function(fovy,aspect,near,far) { + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + this._validateParameters( + 'perspective', + args, + ['Number', 'Number', 'Number', 'Number'] + ); + this._renderer.uPMatrix = p5.Matrix.identity(); + this._renderer.uPMatrix.perspective(fovy,aspect,near,far); + this._renderer._curCamera = 'custom'; +}; + +/** + * Setup ortho camera + * @method ortho + * @param {Number} left camera frustum left plane + * @param {Number} right camera frustum right plane + * @param {Number} bottom camera frustum bottom plane + * @param {Number} top camera frustum top plane + * @param {Number} near camera frustum near plane + * @param {Number} far camera frustum far plane + * @return {p5} the p5 object + * @example + * <div> + * <code> + * //drag mouse to toggle the world! + * //there's no vanish point + * function setup(){ + * createCanvas(100, 100, WEBGL); + * ortho(-width/2, width/2, height/2, -height/2, 0.1, 100); + * } + * function draw(){ + * background(200); + * orbitControl(); + * for(var i = -1; i < 2; i++){ + * for(var j = -2; j < 3; j++){ + * push(); + * translate(i*160, 0, j*160); + * box(40, 40, 40); + * pop(); + * } + * } + * } + * </code> + * </div> + * + * @alt + * 3 3d boxes, reveal several more boxes on 3d plane when mouse used to toggle + * + */ +p5.prototype.ortho = function(left,right,bottom,top,near,far) { + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + this._validateParameters( + 'ortho', + args, + ['Number', 'Number', 'Number', 'Number', 'Number', 'Number'] + ); + left /= this.width; + right /= this.width; + top /= this.height; + bottom /= this.height; + this._renderer.uPMatrix = p5.Matrix.identity(); + this._renderer.uPMatrix.ortho(left,right,bottom,top,near,far); + this._renderer._curCamera = 'custom'; +}; + +module.exports = p5; + +},{"../core/core":37}],78:[function(_dereq_,module,exports){ +'use strict'; + +var p5 = _dereq_('../core/core'); + +//@TODO: implement full orbit controls including +//pan, zoom, quaternion rotation, etc. +p5.prototype.orbitControl = function(){ + if(this.mouseIsPressed){ + this.rotateY((this.mouseX - this.width / 2) / (this.width / 2)); + this.rotateX((this.mouseY - this.height / 2) / (this.width / 2)); + } + return this; +}; + +module.exports = p5; +},{"../core/core":37}],79:[function(_dereq_,module,exports){ +/** + * @module Lights, Camera + * @submodule Lights + * @for p5 + * @requires core + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); + +/** + * Creates an ambient light with a color + * @method ambientLight + * @param {Number|Array|String|p5.Color} v1 gray value, + * red or hue value (depending on the current color mode), + * or color Array, or CSS color string + * @param {Number} [v2] optional: green or saturation value + * @param {Number} [v3] optional: blue or brightness value + * @param {Number} [a] optional: opacity + * @return {p5} the p5 object + * @example + * <div> + * <code> + * function setup(){ + * createCanvas(100, 100, WEBGL); + * } + * function draw(){ + * background(0); + * ambientLight(150); + * ambientMaterial(250); + * sphere(50); + * } + * </code> + * </div> + * + * @alt + * nothing displayed + * + */ +p5.prototype.ambientLight = function(v1, v2, v3, a){ + var gl = this._renderer.GL; + var shaderProgram = this._renderer._getShader( + 'lightVert', 'lightTextureFrag'); + + gl.useProgram(shaderProgram); + shaderProgram.uAmbientColor = gl.getUniformLocation( + shaderProgram, + 'uAmbientColor[' + this._renderer.ambientLightCount + ']'); + + var color = this._renderer._pInst.color.apply( + this._renderer._pInst, arguments); + var colors = color._array; + + gl.uniform3f( shaderProgram.uAmbientColor, + colors[0], colors[1], colors[2]); + + //in case there's no material color for the geometry + shaderProgram.uMaterialColor = gl.getUniformLocation( + shaderProgram, 'uMaterialColor' ); + gl.uniform4f( shaderProgram.uMaterialColor, 1, 1, 1, 1); + + this._renderer.ambientLightCount ++; + shaderProgram.uAmbientLightCount = + gl.getUniformLocation(shaderProgram, 'uAmbientLightCount'); + gl.uniform1i(shaderProgram.uAmbientLightCount, + this._renderer.ambientLightCount); + + return this; +}; + +/** + * Creates a directional light with a color and a direction + * @method directionalLight + * @param {Number|Array|String|p5.Color} v1 gray value, + * red or hue value (depending on the current color mode), + * or color Array, or CSS color string + * @param {Number} [v2] optional: green or saturation value + * @param {Number} [v3] optional: blue or brightness value + * @param {Number} [a] optional: opacity + * @param {Number|p5.Vector} x x axis direction or a p5.Vector + * @param {Number} [y] optional: y axis direction + * @param {Number} [z] optional: z axis direction + * @return {p5} the p5 object + * @example + * <div> + * <code> + * function setup(){ + * createCanvas(100, 100, WEBGL); + * } + * function draw(){ + * background(0); + * //move your mouse to change light direction + * var dirX = (mouseX / width - 0.5) *2; + * var dirY = (mouseY / height - 0.5) *(-2); + * directionalLight(250, 250, 250, dirX, dirY, 0.25); + * ambientMaterial(250); + * sphere(50); + * } + * </code> + * </div> + * + * @alt + * light source on canvas changeable with mouse position + * + */ +p5.prototype.directionalLight = function(v1, v2, v3, a, x, y, z) { + // TODO(jgessner): Find an example using this and profile it. + // var args = new Array(arguments.length); + // for (var i = 0; i < args.length; ++i) { + // args[i] = arguments[i]; + // } + // this._validateParameters( + // 'directionalLight', + // args, + // [ + // //rgbaxyz + // ['Number', 'Number', 'Number', 'Number', 'Number', 'Number', 'Number'], + // //rgbxyz + // ['Number', 'Number', 'Number', 'Number', 'Number', 'Number'], + // //caxyz + // ['Number', 'Number', 'Number', 'Number', 'Number'], + // //cxyz + // ['Number', 'Number', 'Number', 'Number'], + // ['String', 'Number', 'Number', 'Number'], + // ['Array', 'Number', 'Number', 'Number'], + // ['Object', 'Number', 'Number', 'Number'], + // //rgbavector + // ['Number', 'Number', 'Number', 'Number', 'Object'], + // //rgbvector + // ['Number', 'Number', 'Number', 'Object'], + // //cavector + // ['Number', 'Number', 'Object'], + // //cvector + // ['Number', 'Object'], + // ['String', 'Object'], + // ['Array', 'Object'], + // ['Object', 'Object'] + // ] + // ); + + var gl = this._renderer.GL; + var shaderProgram = this._renderer._getShader( + 'lightVert', 'lightTextureFrag'); + + gl.useProgram(shaderProgram); + shaderProgram.uDirectionalColor = gl.getUniformLocation( + shaderProgram, + 'uDirectionalColor[' + this._renderer.directionalLightCount + ']'); + + //@TODO: check parameters number + var color = this._renderer._pInst.color.apply( + this._renderer._pInst, [v1, v2, v3]); + var colors = color._array; + + gl.uniform3f( shaderProgram.uDirectionalColor, + colors[0], colors[1], colors[2]); + + var _x, _y, _z; + + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + if(typeof args[args.length-1] === 'number'){ + _x = args[args.length-3]; + _y = args[args.length-2]; + _z = args[args.length-1]; + + }else{ + try{ + _x = args[args.length-1].x; + _y = args[args.length-1].y; + _z = args[args.length-1].z; + } + catch(error){ + throw error; + } + } + + shaderProgram.uLightingDirection = gl.getUniformLocation( + shaderProgram, + 'uLightingDirection[' + this._renderer.directionalLightCount + ']'); + gl.uniform3f( shaderProgram.uLightingDirection, _x, _y, _z); + + //in case there's no material color for the geometry + shaderProgram.uMaterialColor = gl.getUniformLocation( + shaderProgram, 'uMaterialColor' ); + gl.uniform4f( shaderProgram.uMaterialColor, 1, 1, 1, 1); + + this._renderer.directionalLightCount ++; + shaderProgram.uDirectionalLightCount = + gl.getUniformLocation(shaderProgram, 'uDirectionalLightCount'); + gl.uniform1i(shaderProgram.uDirectionalLightCount, + this._renderer.directionalLightCount); + + return this; +}; + +/** + * Creates a point light with a color and a light position + * @method pointLight + * @param {Number|Array|String|p5.Color} v1 gray value, + * red or hue value (depending on the current color mode), + * or color Array, or CSS color string + * @param {Number} [v2] optional: green or saturation value + * @param {Number} [v3] optional: blue or brightness value + * @param {Number} [a] optional: opacity + * @param {Number|p5.Vector} x x axis position or a p5.Vector + * @param {Number} [y] optional: y axis position + * @param {Number} [z] optional: z axis position + * @return {p5} the p5 object + * @example + * <div> + * <code> + * function setup(){ + * createCanvas(100, 100, WEBGL); + * } + * function draw(){ + * background(0); + * //move your mouse to change light position + * var locY = (mouseY / height - 0.5) *(-2); + * var locX = (mouseX / width - 0.5) *2; + * //to set the light position, + * //think of the world's coordinate as: + * // -1,1 -------- 1,1 + * // | | + * // | | + * // | | + * // -1,-1---------1,-1 + * pointLight(250, 250, 250, locX, locY, 0); + * ambientMaterial(250); + * sphere(50); + * } + * </code> + * </div> + * + * @alt + * spot light on canvas changes position with mouse + * + */ +p5.prototype.pointLight = function(v1, v2, v3, a, x, y, z) { + // TODO(jgessner): Find an example using this and profile it. + // var args = new Array(arguments.length); + // for (var i = 0; i < args.length; ++i) { + // args[i] = arguments[i]; + // } + // this._validateParameters( + // 'pointLight', + // arguments, + // [ + // //rgbaxyz + // ['Number', 'Number', 'Number', 'Number', 'Number', 'Number', 'Number'], + // //rgbxyz + // ['Number', 'Number', 'Number', 'Number', 'Number', 'Number'], + // //caxyz + // ['Number', 'Number', 'Number', 'Number', 'Number'], + // //cxyz + // ['Number', 'Number', 'Number', 'Number'], + // ['String', 'Number', 'Number', 'Number'], + // ['Array', 'Number', 'Number', 'Number'], + // ['Object', 'Number', 'Number', 'Number'], + // //rgbavector + // ['Number', 'Number', 'Number', 'Number', 'Object'], + // //rgbvector + // ['Number', 'Number', 'Number', 'Object'], + // //cavector + // ['Number', 'Number', 'Object'], + // //cvector + // ['Number', 'Object'], + // ['String', 'Object'], + // ['Array', 'Object'], + // ['Object', 'Object'] + // ] + // ); + + var gl = this._renderer.GL; + var shaderProgram = this._renderer._getShader( + 'lightVert', 'lightTextureFrag'); + + gl.useProgram(shaderProgram); + shaderProgram.uPointLightColor = gl.getUniformLocation( + shaderProgram, + 'uPointLightColor[' + this._renderer.pointLightCount + ']'); + + //@TODO: check parameters number + var color = this._renderer._pInst.color.apply( + this._renderer._pInst, [v1, v2, v3]); + var colors = color._array; + + gl.uniform3f( shaderProgram.uPointLightColor, + colors[0], colors[1], colors[2]); + + var _x, _y, _z; + + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + if(typeof args[args.length-1] === 'number'){ + _x = args[args.length-3]; + _y = args[args.length-2]; + _z = args[args.length-1]; + + }else{ + try{ + _x = args[args.length-1].x; + _y = args[args.length-1].y; + _z = args[args.length-1].z; + } + catch(error){ + throw error; + } + } + + shaderProgram.uPointLightLocation = gl.getUniformLocation( + shaderProgram, + 'uPointLightLocation[' + this._renderer.pointLightCount + ']'); + gl.uniform3f( shaderProgram.uPointLightLocation, _x, _y, _z); + + //in case there's no material color for the geometry + shaderProgram.uMaterialColor = gl.getUniformLocation( + shaderProgram, 'uMaterialColor' ); + gl.uniform4f( shaderProgram.uMaterialColor, 1, 1, 1, 1); + + this._renderer.pointLightCount ++; + shaderProgram.uPointLightCount = + gl.getUniformLocation(shaderProgram, 'uPointLightCount'); + gl.uniform1i(shaderProgram.uPointLightCount, + this._renderer.pointLightCount); + + return this; +}; + +module.exports = p5; + +},{"../core/core":37}],80:[function(_dereq_,module,exports){ +/** + * @module Shape + * @submodule 3D Models + * @for p5 + * @requires core + * @requires p5.Geometry3D + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); +_dereq_('./p5.Geometry'); + +/** + * Load a 3d model from an OBJ file. + * <br><br> + * One of the limitations of the OBJ format is that it doesn't have a built-in + * sense of scale. This means that models exported from different programs might + * be very different sizes. If your model isn't displaying, try calling + * loadModel() with the normalized parameter set to true. This will resize the + * model to a scale appropriate for p5. You can also make additional changes to + * the final size of your model with the scale() function. + * + * @method loadModel + * @param {String} path Path of the model to be loaded + * @param {Boolean} [normalize] If true, scale the model to a + * standardized size when loading + * @param {Function(p5.Geometry3D)} [successCallback] Function to be called + * once the model is loaded. Will be passed + * the 3D model object. + * @param {Function(Event)} [failureCallback] called with event error if + * the image fails to load. + * @return {p5.Geometry} the p5.Geometry3D object + * @example + * <div> + * <code> + * //draw a spinning teapot + * var teapot; + * + * function setup(){ + * createCanvas(100, 100, WEBGL); + * + * teapot = loadModel('assets/teapot.obj'); + * } + * + * function draw(){ + * background(200); + * rotateX(frameCount * 0.01); + * rotateY(frameCount * 0.01); + * model(teapot); + * } + * </code> + * </div> + * + * @alt + * Vertically rotating 3-d teapot with red, green and blue gradient. + * + */ +p5.prototype.loadModel = function () { + var path = arguments[0]; + var normalize; + var successCallback; + var failureCallback; + if(typeof arguments[1] === 'boolean') { + normalize = arguments[1]; + successCallback = arguments[2]; + failureCallback = arguments[3]; + } else { + normalize = false; + successCallback = arguments[1]; + failureCallback = arguments[2]; + } + + var model = new p5.Geometry(); + model.gid = path + '|' + normalize; + this.loadStrings(path, function(strings) { + parseObj(model, strings); + + if (normalize) { + model.normalize(); + } + + if (typeof successCallback === 'function') { + successCallback(model); + } + }.bind(this), failureCallback); + + return model; +}; + +/** + * Parse OBJ lines into model. For reference, this is what a simple model of a + * square might look like: + * + * v -0.5 -0.5 0.5 + * v -0.5 -0.5 -0.5 + * v -0.5 0.5 -0.5 + * v -0.5 0.5 0.5 + * + * f 4 3 2 1 + */ +function parseObj( model, lines ) { + // OBJ allows a face to specify an index for a vertex (in the above example), + // but it also allows you to specify a custom combination of vertex, UV + // coordinate, and vertex normal. So, "3/4/3" would mean, "use vertex 3 with + // UV coordinate 4 and vertex normal 3". In WebGL, every vertex with different + // parameters must be a different vertex, so loadedVerts is used to + // temporarily store the parsed vertices, normals, etc., and indexedVerts is + // used to map a specific combination (keyed on, for example, the string + // "3/4/3"), to the actual index of the newly created vertex in the final + // object. + var loadedVerts = {'v' : [], + 'vt' : [], + 'vn' : []}; + var indexedVerts = {}; + + for (var line = 0; line < lines.length; ++line) { + // Each line is a separate object (vertex, face, vertex normal, etc) + // For each line, split it into tokens on whitespace. The first token + // describes the type. + var tokens = lines[line].trim().split(/\b\s+/); + + if (tokens.length > 0) { + if (tokens[0] === 'v' || tokens[0] === 'vn') { + // Check if this line describes a vertex or vertex normal. + // It will have three numeric parameters. + var vertex = new p5.Vector(parseFloat(tokens[1]), + parseFloat(tokens[2]), + parseFloat(tokens[3])); + loadedVerts[tokens[0]].push(vertex); + } else if (tokens[0] === 'vt') { + // Check if this line describes a texture coordinate. + // It will have two numeric parameters. + var texVertex = [parseFloat(tokens[1]), parseFloat(tokens[2])]; + loadedVerts[tokens[0]].push(texVertex); + } else if (tokens[0] === 'f') { + // Check if this line describes a face. + // OBJ faces can have more than three points. Triangulate points. + for (var tri = 3; tri < tokens.length; ++tri) { + var face = []; + + var vertexTokens = [1, tri - 1, tri]; + + for (var tokenInd = 0; tokenInd < vertexTokens.length; ++tokenInd) { + // Now, convert the given token into an index + var vertString = tokens[vertexTokens[tokenInd]]; + var vertIndex = 0; + + // TODO: Faces can technically use negative numbers to refer to the + // previous nth vertex. I haven't seen this used in practice, but + // it might be good to implement this in the future. + + if (indexedVerts[vertString] !== undefined) { + vertIndex = indexedVerts[vertString]; + } else { + var vertParts = vertString.split('/'); + for (var i = 0; i < vertParts.length; i++) { + vertParts[i] = parseInt(vertParts[i]) - 1; + } + + vertIndex = indexedVerts[vertString] = model.vertices.length; + model.vertices.push(loadedVerts.v[vertParts[0]].copy()); + if (loadedVerts.vt[vertParts[1]]) { + model.uvs.push(loadedVerts.vt[vertParts[1]].slice()); + } else { + model.uvs.push([0, 0]); + } + + if (loadedVerts.vn[vertParts[2]]) { + model.vertexNormals.push(loadedVerts.vn[vertParts[2]].copy()); + } + } + + face.push(vertIndex); + } + + model.faces.push(face); + } + } + } + } + + // If the model doesn't have normals, compute the normals + if(model.vertexNormals.length === 0) { + model.computeNormals(); + } + + return model; +} + +/** + * Render a 3d model to the screen. + * + * @method model + * @param {p5.Geometry} model Loaded 3d model to be rendered + * @example + * <div> + * <code> + * //draw a spinning teapot + * var teapot; + * + * function setup(){ + * createCanvas(100, 100, WEBGL); + * + * teapot = loadModel('assets/teapot.obj'); + * } + * + * function draw(){ + * background(200); + * rotateX(frameCount * 0.01); + * rotateY(frameCount * 0.01); + * model(teapot); + * } + * </code> + * </div> + * + * @alt + * Vertically rotating 3-d teapot with red, green and blue gradient. + * + */ +p5.prototype.model = function ( model ) { + if (model.vertices.length > 0) { + if (!this._renderer.geometryInHash(model.gid)) { + this._renderer.createBuffers(model.gid, model); + } + + this._renderer.drawBuffers(model.gid); + } +}; + +module.exports = p5; + +},{"../core/core":37,"./p5.Geometry":82}],81:[function(_dereq_,module,exports){ +/** + * @module Lights, Camera + * @submodule Material + * @for p5 + * @requires core + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); +//require('./p5.Texture'); + +/** + * Normal material for geometry. You can view all + * possible materials in this + * <a href="https://p5js.org/examples/examples/3D_Materials.php">example</a>. + * @method normalMaterial + * @return {p5} the p5 object + * @example + * <div> + * <code> + * function setup(){ + * createCanvas(100, 100, WEBGL); + * } + * + * function draw(){ + * background(200); + * normalMaterial(); + * sphere(50); + * } + * </code> + * </div> + * + * @alt + * Red, green and blue gradient. + * + */ +p5.prototype.normalMaterial = function(){ + this._renderer._getShader('normalVert', 'normalFrag'); + return this; +}; + +/** + * Texture for geometry. You can view other possible materials in this + * <a href="https://p5js.org/examples/examples/3D_Materials.php">example</a>. + * @method texture + * @param {p5.Image | p5.MediaElement | p5.Graphics} tex 2-dimensional graphics + * to render as texture + * @return {p5} the p5 object + * @example + * <div> + * <code> + * var img; + * function setup(){ + * createCanvas(100, 100, WEBGL); + * img = loadImage("assets/laDefense.jpg"); + * } + * + * function draw(){ + * background(0); + * rotateZ(frameCount * 0.01); + * rotateX(frameCount * 0.01); + * rotateY(frameCount * 0.01); + * //pass image as texture + * texture(img); + * box(200, 200, 200); + * } + * </code> + * </div> + * + * <div> + * <code> + * var pg; + * function setup(){ + * createCanvas(100, 100, WEBGL); + * pg = createGraphics(200, 200); + * pg.textSize(100); + * } + * + * function draw(){ + * background(0); + * pg.background(255); + * pg.text('hello!', 0, 100); + * //pass image as texture + * texture(pg); + * plane(200); + * } + * </code> + * </div> + * + * <div> + * <code> + * var vid; + * function preload(){ + * vid = createVideo("assets/fingers.mov"); + * vid.hide(); + * vid.loop(); + * } + * function setup(){ + * createCanvas(100, 100, WEBGL); + * } + * + * function draw(){ + * background(0); + * //pass video frame as texture + * texture(vid); + * plane(200); + * } + * </code> + * </div> + * + * @alt + * Rotating view of many images umbrella and grid roof on a 3d plane + * black canvas + * black canvas + * + */ +p5.prototype.texture = function(){ + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + var gl = this._renderer.GL; + var shaderProgram = this._renderer._getShader('lightVert', + 'lightTextureFrag'); + gl.useProgram(shaderProgram); + var textureData; + //if argument is not already a texture + //create a new one + if(!args[0].isTexture){ + if (args[0] instanceof p5.Image) { + textureData = args[0].canvas; + } + //if param is a video + else if (typeof p5.MediaElement !== 'undefined' && + args[0] instanceof p5.MediaElement){ + if(!args[0].loadedmetadata) {return;} + textureData = args[0].elt; + } + //used with offscreen 2d graphics renderer + else if(args[0] instanceof p5.Graphics){ + textureData = args[0].elt; + } + var tex = gl.createTexture(); + args[0]._setProperty('tex', tex); + args[0]._setProperty('isTexture', true); + this._renderer._bind.call(this, tex, textureData); + } + else { + if(args[0] instanceof p5.Graphics || + (typeof p5.MediaElement !== 'undefined' && + args[0] instanceof p5.MediaElement)){ + textureData = args[0].elt; + } + else if(args[0] instanceof p5.Image){ + textureData = args[0].canvas; + } + this._renderer._bind.call(this, args[0].tex, textureData); + } + //this is where we'd activate multi textures + //eg. gl.activeTexture(gl.TEXTURE0 + (unit || 0)); + //but for now we just have a single texture. + //@TODO need to extend this functionality + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, args[0].tex); + gl.uniform1i(gl.getUniformLocation(shaderProgram, 'isTexture'), true); + gl.uniform1i(gl.getUniformLocation(shaderProgram, 'uSampler'), 0); + return this; +}; + +/** + * Texture Util functions + */ +p5.RendererGL.prototype._bind = function(tex, data){ + var gl = this._renderer.GL; + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); + gl.texImage2D(gl.TEXTURE_2D, 0, + gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, data); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); + 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); +}; + +/** + * Checks whether val is a pot + * more info on power of 2 here: + * https://www.opengl.org/wiki/NPOT_Texture + * @param {Number} value + * @return {Boolean} + */ +// function _isPowerOf2 (value){ +// return (value & (value - 1)) === 0; +// } + +/** + * returns the next highest power of 2 value + * @param {Number} value [description] + * @return {Number} [description] + */ +// function _nextHighestPOT (value){ +// --value; +// for (var i = 1; i < 32; i <<= 1) { +// value = value | value >> i; +// } +// return value + 1; + +/** + * Ambient material for geometry with a given color. You can view all + * possible materials in this + * <a href="https://p5js.org/examples/examples/3D_Materials.php">example</a>. + * @method ambientMaterial + * @param {Number|Array|String|p5.Color} v1 gray value, + * red or hue value (depending on the current color mode), + * or color Array, or CSS color string + * @param {Number} [v2] optional: green or saturation value + * @param {Number} [v3] optional: blue or brightness value + * @param {Number} [a] optional: opacity +* @return {p5} the p5 object + * @example + * <div> + * <code> + * function setup(){ + * createCanvas(100, 100, WEBGL); + * } + * function draw(){ + * background(0); + * ambientLight(100); + * pointLight(250, 250, 250, 100, 100, 0); + * ambientMaterial(250); + * sphere(50); + * } + * </code> + * </div> + * + * @alt + * radiating light source from top right of canvas + * + */ +p5.prototype.ambientMaterial = function(v1, v2, v3, a) { + var gl = this._renderer.GL; + var shaderProgram = + this._renderer._getShader('lightVert', 'lightTextureFrag'); + + gl.useProgram(shaderProgram); + shaderProgram.uMaterialColor = gl.getUniformLocation( + shaderProgram, 'uMaterialColor' ); + var colors = this._renderer._applyColorBlend.apply(this._renderer, arguments); + + gl.uniform4f(shaderProgram.uMaterialColor, + colors[0], colors[1], colors[2], colors[3]); + + shaderProgram.uSpecular = gl.getUniformLocation( + shaderProgram, 'uSpecular' ); + gl.uniform1i(shaderProgram.uSpecular, false); + + gl.uniform1i(gl.getUniformLocation(shaderProgram, 'isTexture'), false); + + return this; +}; + +/** + * Specular material for geometry with a given color. You can view all + * possible materials in this + * <a href="https://p5js.org/examples/examples/3D_Materials.php">example</a>. + * @method specularMaterial + * @param {Number|Array|String|p5.Color} v1 gray value, + * red or hue value (depending on the current color mode), + * or color Array, or CSS color string + * @param {Number} [v2] optional: green or saturation value + * @param {Number} [v3] optional: blue or brightness value + * @param {Number} [a] optional: opacity + * @return {p5} the p5 object + * @example + * <div> + * <code> + * function setup(){ + * createCanvas(100, 100, WEBGL); + * } + * function draw(){ + * background(0); + * ambientLight(100); + * pointLight(250, 250, 250, 100, 100, 0); + * specularMaterial(250); + * sphere(50); + * } + * </code> + * </div> + * + * @alt + * diffused radiating light source from top right of canvas + * + */ +p5.prototype.specularMaterial = function(v1, v2, v3, a) { + var gl = this._renderer.GL; + var shaderProgram = + this._renderer._getShader('lightVert', 'lightTextureFrag'); + gl.useProgram(shaderProgram); + gl.uniform1i(gl.getUniformLocation(shaderProgram, 'isTexture'), false); + shaderProgram.uMaterialColor = gl.getUniformLocation( + shaderProgram, 'uMaterialColor' ); + var colors = this._renderer._applyColorBlend.apply(this._renderer, arguments); + gl.uniform4f(shaderProgram.uMaterialColor, + colors[0], colors[1], colors[2], colors[3]); + shaderProgram.uSpecular = gl.getUniformLocation( + shaderProgram, 'uSpecular' ); + gl.uniform1i(shaderProgram.uSpecular, true); + + return this; +}; + +/** + * @private blends colors according to color components. + * If alpha value is less than 1, we need to enable blending + * on our gl context. Otherwise opaque objects need to a depthMask. + * @param {Number} v1 [description] + * @param {Number} v2 [description] + * @param {Number} v3 [description] + * @param {Number} a [description] + * @return {[Number]} Normalized numbers array + */ +p5.RendererGL.prototype._applyColorBlend = function(v1,v2,v3,a){ + var gl = this.GL; + var color = this._pInst.color.apply( + this._pInst, arguments); + var colors = color._array; + if(colors[colors.length-1] < 1.0){ + gl.depthMask(false); + gl.enable(gl.BLEND); + gl.blendEquation( gl.FUNC_ADD ); + gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA ); + } else { + gl.depthMask(true); + gl.disable(gl.BLEND); + } + return colors; +}; + +module.exports = p5; + +},{"../core/core":37}],82:[function(_dereq_,module,exports){ +//some of the functions are adjusted from Three.js(http://threejs.org) + +'use strict'; + +var p5 = _dereq_('../core/core'); + +/** + * p5 Geometry class + * @constructor + * @param {Function | Object} vertData callback function or Object + * containing routine(s) for vertex data generation + * @param {Number} [detailX] number of vertices on horizontal surface + * @param {Number} [detailY] number of vertices on horizontal surface + * @param {Function} [callback] function to call upon object instantiation. + * + */ +p5.Geometry = function +(detailX, detailY, callback){ + //an array containing every vertex + //@type [p5.Vector] + this.vertices = []; + //an array containing 1 normal per vertex + //@type [p5.Vector] + //[p5.Vector, p5.Vector, p5.Vector,p5.Vector, p5.Vector, p5.Vector,...] + this.vertexNormals = []; + //an array containing each three vertex indices that form a face + //[[0, 1, 2], [2, 1, 3], ...] + this.faces = []; + //a 2D array containing uvs for every vertex + //[[0.0,0.0],[1.0,0.0], ...] + this.uvs = []; + this.detailX = (detailX !== undefined) ? detailX: 1; + this.detailY = (detailY !== undefined) ? detailY: 1; + if(callback instanceof Function){ + callback.call(this); + } + return this; +}; + +p5.Geometry.prototype.computeFaces = function(){ + var sliceCount = this.detailX + 1; + var a, b, c, d; + for (var i = 0; i < this.detailY; i++){ + for (var j = 0; j < this.detailX; j++){ + a = i * sliceCount + j;// + offset; + b = i * sliceCount + j + 1;// + offset; + c = (i + 1)* sliceCount + j + 1;// + offset; + d = (i + 1)* sliceCount + j;// + offset; + this.faces.push([a, b, d]); + this.faces.push([d, b, c]); + } + } + return this; +}; + +p5.Geometry.prototype._getFaceNormal = function(faceId,vertId){ + //This assumes that vA->vB->vC is a counter-clockwise ordering + var face = this.faces[faceId]; + var vA = this.vertices[face[vertId%3]]; + var vB = this.vertices[face[(vertId+1)%3]]; + var vC = this.vertices[face[(vertId+2)%3]]; + var n = p5.Vector.cross( + p5.Vector.sub(vB,vA), + p5.Vector.sub(vC,vA)); + var sinAlpha = p5.Vector.mag(n) / + (p5.Vector.mag(p5.Vector.sub(vB,vA))* + p5.Vector.mag(p5.Vector.sub(vC,vA))); + n = n.normalize(); + return n.mult(Math.asin(sinAlpha)); +}; +/** + * computes smooth normals per vertex as an average of each + * face. + */ +p5.Geometry.prototype.computeNormals = function (){ + for(var v=0; v < this.vertices.length; v++){ + var normal = new p5.Vector(); + for(var i=0; i < this.faces.length; i++){ + //if our face contains a given vertex + //calculate an average of the normals + //of the triangles adjacent to that vertex + if(this.faces[i][0] === v || + this.faces[i][1] === v || + this.faces[i][2] === v) + { + normal = normal.add(this._getFaceNormal(i, v)); + } + } + normal = normal.normalize(); + this.vertexNormals.push(normal); + } + return this; +}; + +/** + * Averages the vertex normals. Used in curved + * surfaces + * @return {p5.Geometry} + */ +p5.Geometry.prototype.averageNormals = function() { + + for(var i = 0; i <= this.detailY; i++){ + var offset = this.detailX + 1; + var temp = p5.Vector + .add(this.vertexNormals[i*offset], + this.vertexNormals[i*offset + this.detailX]); + temp = p5.Vector.div(temp, 2); + this.vertexNormals[i*offset] = temp; + this.vertexNormals[i*offset + this.detailX] = temp; + } + return this; +}; + +/** + * Averages pole normals. Used in spherical primitives + * @return {p5.Geometry} + */ +p5.Geometry.prototype.averagePoleNormals = function() { + + //average the north pole + var sum = new p5.Vector(0, 0, 0); + for(var i = 0; i < this.detailX; i++){ + sum.add(this.vertexNormals[i]); + } + sum = p5.Vector.div(sum, this.detailX); + + for(i = 0; i < this.detailX; i++){ + this.vertexNormals[i] = sum; + } + + //average the south pole + sum = new p5.Vector(0, 0, 0); + for(i = this.vertices.length - 1; + i > this.vertices.length - 1 - this.detailX; i--){ + sum.add(this.vertexNormals[i]); + } + sum = p5.Vector.div(sum, this.detailX); + + for(i = this.vertices.length - 1; + i > this.vertices.length - 1 - this.detailX; i--){ + this.vertexNormals[i] = sum; + } + return this; +}; + +/** + * Modifies all vertices to be centered within the range -100 to 100. + * @return {p5.Geometry} + */ +p5.Geometry.prototype.normalize = function() { + if(this.vertices.length > 0) { + // Find the corners of our bounding box + var maxPosition = this.vertices[0].copy(); + var minPosition = this.vertices[0].copy(); + + for(var i = 0; i < this.vertices.length; i++) { + maxPosition.x = Math.max(maxPosition.x, this.vertices[i].x); + minPosition.x = Math.min(minPosition.x, this.vertices[i].x); + maxPosition.y = Math.max(maxPosition.y, this.vertices[i].y); + minPosition.y = Math.min(minPosition.y, this.vertices[i].y); + maxPosition.z = Math.max(maxPosition.z, this.vertices[i].z); + minPosition.z = Math.min(minPosition.z, this.vertices[i].z); + } + + var center = p5.Vector.lerp(maxPosition, minPosition, 0.5); + var dist = p5.Vector.sub(maxPosition, minPosition); + var longestDist = Math.max(Math.max(dist.x, dist.y), dist.z); + var scale = 200 / longestDist; + + for(i = 0; i < this.vertices.length; i++) { + this.vertices[i].sub(center); + this.vertices[i].mult(scale); + } + } + return this; +}; + +module.exports = p5.Geometry; + +},{"../core/core":37}],83:[function(_dereq_,module,exports){ +/** +* @requires constants +* @todo see methods below needing further implementation. +* future consideration: implement SIMD optimizations +* when browser compatibility becomes available +* https://developer.mozilla.org/en-US/docs/Web/JavaScript/ +* Reference/Global_Objects/SIMD +*/ + +'use strict'; + +var p5 = _dereq_('../core/core'); +var polarGeometry = _dereq_('../math/polargeometry'); +var constants = _dereq_('../core/constants'); +var GLMAT_ARRAY_TYPE = ( + typeof Float32Array !== 'undefined') ? + Float32Array : Array; + +/** + * A class to describe a 4x4 matrix + * for model and view matrix manipulation in the p5js webgl renderer. + * class p5.Matrix + * @constructor + * @param {Array} [mat4] array literal of our 4x4 matrix + */ +p5.Matrix = function() { + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + // This is default behavior when object + // instantiated using createMatrix() + // @todo implement createMatrix() in core/math.js + if(args[0] instanceof p5) { + // save reference to p5 if passed in + this.p5 = args[0]; + if(args[1] === 'mat3'){ + this.mat3 = args[2] || new GLMAT_ARRAY_TYPE([ + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 + ]); + } + else { + this.mat4 = args[1] || new GLMAT_ARRAY_TYPE([ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + ]); + } + // default behavior when object + // instantiated using new p5.Matrix() + } else { + if(args[0] === 'mat3'){ + this.mat3 = args[1] || new GLMAT_ARRAY_TYPE([ + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 + ]); + } + else { + this.mat4 = args[0] || new GLMAT_ARRAY_TYPE([ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + ]); + } + } + return this; +}; + +/** + * Sets the x, y, and z component of the vector using two or three separate + * variables, the data from a p5.Matrix, or the values from a float array. + * + * @param {p5.Matrix|Array} [inMatrix] the input p5.Matrix or + * an Array of length 16 + */ +p5.Matrix.prototype.set = function (inMatrix) { + if (inMatrix instanceof p5.Matrix) { + this.mat4 = inMatrix.mat4; + return this; + } + else if (inMatrix instanceof GLMAT_ARRAY_TYPE) { + this.mat4 = inMatrix; + return this; + } + return this; +}; + +/** + * Gets a copy of the vector, returns a p5.Matrix object. + * + * @return {p5.Matrix} the copy of the p5.Matrix object + */ +p5.Matrix.prototype.get = function () { + return new p5.Matrix(this.mat4); +}; + +/** + * return a copy of a matrix + * @return {p5.Matrix} the result matrix + */ +p5.Matrix.prototype.copy = function(){ + var copied = new p5.Matrix(); + copied.mat4[0] = this.mat4[0]; + copied.mat4[1] = this.mat4[1]; + copied.mat4[2] = this.mat4[2]; + copied.mat4[3] = this.mat4[3]; + copied.mat4[4] = this.mat4[4]; + copied.mat4[5] = this.mat4[5]; + copied.mat4[6] = this.mat4[6]; + copied.mat4[7] = this.mat4[7]; + copied.mat4[8] = this.mat4[8]; + copied.mat4[9] = this.mat4[9]; + copied.mat4[10] = this.mat4[10]; + copied.mat4[11] = this.mat4[11]; + copied.mat4[12] = this.mat4[12]; + copied.mat4[13] = this.mat4[13]; + copied.mat4[14] = this.mat4[14]; + copied.mat4[15] = this.mat4[15]; + return copied; +}; + +/** + * return an identity matrix + * @return {p5.Matrix} the result matrix + */ +p5.Matrix.identity = function(){ + return new p5.Matrix(); +}; + +/** + * transpose according to a given matrix + * @param {p5.Matrix | Typed Array} a the matrix to be based on to transpose + * @return {p5.Matrix} this + */ +p5.Matrix.prototype.transpose = function(a){ + var a01, a02, a03, a12, a13, a23; + if(a instanceof p5.Matrix){ + a01 = a.mat4[1]; + a02 = a.mat4[2]; + a03 = a.mat4[3]; + a12 = a.mat4[6]; + a13 = a.mat4[7]; + a23 = a.mat4[11]; + + this.mat4[0] = a.mat4[0]; + this.mat4[1] = a.mat4[4]; + this.mat4[2] = a.mat4[8]; + this.mat4[3] = a.mat4[12]; + this.mat4[4] = a01; + this.mat4[5] = a.mat4[5]; + this.mat4[6] = a.mat4[9]; + this.mat4[7] = a.mat4[13]; + this.mat4[8] = a02; + this.mat4[9] = a12; + this.mat4[10] = a.mat4[10]; + this.mat4[11] = a.mat4[14]; + this.mat4[12] = a03; + this.mat4[13] = a13; + this.mat4[14] = a23; + this.mat4[15] = a.mat4[15]; + + }else if(a instanceof GLMAT_ARRAY_TYPE){ + a01 = a[1]; + a02 = a[2]; + a03 = a[3]; + a12 = a[6]; + a13 = a[7]; + a23 = a[11]; + + this.mat4[0] = a[0]; + this.mat4[1] = a[4]; + this.mat4[2] = a[8]; + this.mat4[3] = a[12]; + this.mat4[4] = a01; + this.mat4[5] = a[5]; + this.mat4[6] = a[9]; + this.mat4[7] = a[13]; + this.mat4[8] = a02; + this.mat4[9] = a12; + this.mat4[10] = a[10]; + this.mat4[11] = a[14]; + this.mat4[12] = a03; + this.mat4[13] = a13; + this.mat4[14] = a23; + this.mat4[15] = a[15]; + } + return this; +}; + +/** + * invert matrix according to a give matrix + * @param {p5.Matrix or Typed Array} a the matrix to be based on to invert + * @return {p5.Matrix} this + */ +p5.Matrix.prototype.invert = function(a){ + var a00, a01, a02, a03, a10, a11, a12, a13, + a20, a21, a22, a23, a30, a31, a32, a33; + if(a instanceof p5.Matrix){ + a00 = a.mat4[0]; + a01 = a.mat4[1]; + a02 = a.mat4[2]; + a03 = a.mat4[3]; + a10 = a.mat4[4]; + a11 = a.mat4[5]; + a12 = a.mat4[6]; + a13 = a.mat4[7]; + a20 = a.mat4[8]; + a21 = a.mat4[9]; + a22 = a.mat4[10]; + a23 = a.mat4[11]; + a30 = a.mat4[12]; + a31 = a.mat4[13]; + a32 = a.mat4[14]; + a33 = a.mat4[15]; + }else if(a instanceof GLMAT_ARRAY_TYPE){ + 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]; + } + var 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, + + // Calculate the determinant + det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - + b04 * b07 + b05 * b06; + + if (!det) { + return null; + } + det = 1.0 / det; + + this.mat4[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; + this.mat4[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; + this.mat4[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; + this.mat4[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; + this.mat4[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; + this.mat4[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; + this.mat4[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; + this.mat4[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; + this.mat4[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; + this.mat4[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; + this.mat4[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; + this.mat4[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; + this.mat4[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; + this.mat4[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; + this.mat4[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; + this.mat4[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; + + return this; +}; + +/** + * Inverts a 3x3 matrix + * @return {[type]} [description] + */ +p5.Matrix.prototype.invert3x3 = function (){ + var a00 = this.mat3[0], + a01 = this.mat3[1], + a02 = this.mat3[2], + a10 = this.mat3[3], + a11 = this.mat3[4], + a12 = this.mat3[5], + a20 = this.mat3[6], + a21 = this.mat3[7], + a22 = this.mat3[8], + b01 = a22 * a11 - a12 * a21, + b11 = -a22 * a10 + a12 * a20, + b21 = a21 * a10 - a11 * a20, + + // Calculate the determinant + det = a00 * b01 + a01 * b11 + a02 * b21; + if (!det) { + return null; + } + det = 1.0 / det; + this.mat3[0] = b01 * det; + this.mat3[1] = (-a22 * a01 + a02 * a21) * det; + this.mat3[2] = (a12 * a01 - a02 * a11) * det; + this.mat3[3] = b11 * det; + this.mat3[4] = (a22 * a00 - a02 * a20) * det; + this.mat3[5] = (-a12 * a00 + a02 * a10) * det; + this.mat3[6] = b21 * det; + this.mat3[7] = (-a21 * a00 + a01 * a20) * det; + this.mat3[8] = (a11 * a00 - a01 * a10) * det; + return this; +}; + +/** + * transposes a 3x3 p5.Matrix by a mat3 + * @param {[Number]} mat3 1-dimensional array + * @return {p5.Matrix} this + */ +p5.Matrix.prototype.transpose3x3 = function (mat3){ + var a01 = mat3[1], a02 = mat3[2], a12 = mat3[5]; + this.mat3[1] = mat3[3]; + this.mat3[2] = mat3[6]; + this.mat3[3] = a01; + this.mat3[5] = mat3[7]; + this.mat3[6] = a02; + this.mat3[7] = a12; + return this; +}; + +/** + * converts a 4x4 matrix to its 3x3 inverse tranform + * commonly used in MVMatrix to NMatrix conversions. + * @param {p5.Matrix} mat4 the matrix to be based on to invert + * @return {p5.Matrix} this with mat3 + * @todo finish implementation + */ +p5.Matrix.prototype.inverseTranspose = function (matrix){ + if(this.mat3 === undefined){ + console.error('sorry, this function only works with mat3'); + } + else { + //convert mat4 -> mat3 + this.mat3[0] = matrix.mat4[0]; + this.mat3[1] = matrix.mat4[1]; + this.mat3[2] = matrix.mat4[2]; + this.mat3[3] = matrix.mat4[4]; + this.mat3[4] = matrix.mat4[5]; + this.mat3[5] = matrix.mat4[6]; + this.mat3[6] = matrix.mat4[8]; + this.mat3[7] = matrix.mat4[9]; + this.mat3[8] = matrix.mat4[10]; + } + + this.invert3x3().transpose3x3(this.mat3); + return this; +}; + +/** + * inspired by Toji's mat4 determinant + * @return {Number} Determinant of our 4x4 matrix + */ +p5.Matrix.prototype.determinant = function(){ + var d00 = (this.mat4[0] * this.mat4[5]) - (this.mat4[1] * this.mat4[4]), + d01 = (this.mat4[0] * this.mat4[6]) - (this.mat4[2] * this.mat4[4]), + d02 = (this.mat4[0] * this.mat4[7]) - (this.mat4[3] * this.mat4[4]), + d03 = (this.mat4[1] * this.mat4[6]) - (this.mat4[2] * this.mat4[5]), + d04 = (this.mat4[1] * this.mat4[7]) - (this.mat4[3] * this.mat4[5]), + d05 = (this.mat4[2] * this.mat4[7]) - (this.mat4[3] * this.mat4[6]), + d06 = (this.mat4[8] * this.mat4[13]) - (this.mat4[9] * this.mat4[12]), + d07 = (this.mat4[8] * this.mat4[14]) - (this.mat4[10] * this.mat4[12]), + d08 = (this.mat4[8] * this.mat4[15]) - (this.mat4[11] * this.mat4[12]), + d09 = (this.mat4[9] * this.mat4[14]) - (this.mat4[10] * this.mat4[13]), + d10 = (this.mat4[9] * this.mat4[15]) - (this.mat4[11] * this.mat4[13]), + d11 = (this.mat4[10] * this.mat4[15]) - (this.mat4[11] * this.mat4[14]); + + // Calculate the determinant + return d00 * d11 - d01 * d10 + d02 * d09 + + d03 * d08 - d04 * d07 + d05 * d06; +}; + +/** + * multiply two mat4s + * @param {p5.Matrix | Array} multMatrix The matrix we want to multiply by + * @return {p5.Matrix} this + */ +p5.Matrix.prototype.mult = function(multMatrix){ + var _dest = new GLMAT_ARRAY_TYPE(16); + var _src = new GLMAT_ARRAY_TYPE(16); + + if(multMatrix instanceof p5.Matrix) { + _src = multMatrix.mat4; + } + else if(multMatrix instanceof GLMAT_ARRAY_TYPE){ + _src = multMatrix; + } + + // each row is used for the multiplier + var b0 = this.mat4[0], b1 = this.mat4[1], + b2 = this.mat4[2], b3 = this.mat4[3]; + _dest[0] = b0*_src[0] + b1*_src[4] + b2*_src[8] + b3*_src[12]; + _dest[1] = b0*_src[1] + b1*_src[5] + b2*_src[9] + b3*_src[13]; + _dest[2] = b0*_src[2] + b1*_src[6] + b2*_src[10] + b3*_src[14]; + _dest[3] = b0*_src[3] + b1*_src[7] + b2*_src[11] + b3*_src[15]; + + b0 = this.mat4[4]; + b1 = this.mat4[5]; + b2 = this.mat4[6]; + b3 = this.mat4[7]; + _dest[4] = b0*_src[0] + b1*_src[4] + b2*_src[8] + b3*_src[12]; + _dest[5] = b0*_src[1] + b1*_src[5] + b2*_src[9] + b3*_src[13]; + _dest[6] = b0*_src[2] + b1*_src[6] + b2*_src[10] + b3*_src[14]; + _dest[7] = b0*_src[3] + b1*_src[7] + b2*_src[11] + b3*_src[15]; + + b0 = this.mat4[8]; + b1 = this.mat4[9]; + b2 = this.mat4[10]; + b3 = this.mat4[11]; + _dest[8] = b0*_src[0] + b1*_src[4] + b2*_src[8] + b3*_src[12]; + _dest[9] = b0*_src[1] + b1*_src[5] + b2*_src[9] + b3*_src[13]; + _dest[10] = b0*_src[2] + b1*_src[6] + b2*_src[10] + b3*_src[14]; + _dest[11] = b0*_src[3] + b1*_src[7] + b2*_src[11] + b3*_src[15]; + + b0 = this.mat4[12]; + b1 = this.mat4[13]; + b2 = this.mat4[14]; + b3 = this.mat4[15]; + _dest[12] = b0*_src[0] + b1*_src[4] + b2*_src[8] + b3*_src[12]; + _dest[13] = b0*_src[1] + b1*_src[5] + b2*_src[9] + b3*_src[13]; + _dest[14] = b0*_src[2] + b1*_src[6] + b2*_src[10] + b3*_src[14]; + _dest[15] = b0*_src[3] + b1*_src[7] + b2*_src[11] + b3*_src[15]; + + this.mat4 = _dest; + + return this; +}; + +/** + * scales a p5.Matrix by scalars or a vector + * @param {p5.Vector | Array } + * vector to scale by + * @return {p5.Matrix} this + */ +p5.Matrix.prototype.scale = function() { + var x,y,z; + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + //if our 1st arg is a type p5.Vector + if (args[0] instanceof p5.Vector){ + x = args[0].x; + y = args[0].y; + z = args[0].z; + } + //otherwise if it's an array + else if (args[0] instanceof Array){ + x = args[0][0]; + y = args[0][1]; + z = args[0][2]; + } + var _dest = new GLMAT_ARRAY_TYPE(16); + _dest[0] = this.mat4[0] * x; + _dest[1] = this.mat4[1] * x; + _dest[2] = this.mat4[2] * x; + _dest[3] = this.mat4[3] * x; + _dest[4] = this.mat4[4] * y; + _dest[5] = this.mat4[5] * y; + _dest[6] = this.mat4[6] * y; + _dest[7] = this.mat4[7] * y; + _dest[8] = this.mat4[8] * z; + _dest[9] = this.mat4[9] * z; + _dest[10] = this.mat4[10] * z; + _dest[11] = this.mat4[11] * z; + _dest[12] = this.mat4[12]; + _dest[13] = this.mat4[13]; + _dest[14] = this.mat4[14]; + _dest[15] = this.mat4[15]; + + this.mat4 = _dest; + return this; +}; + +/** + * rotate our Matrix around an axis by the given angle. + * @param {Number} a The angle of rotation in radians + * @param {p5.Vector | Array} axis the axis(es) to rotate around + * @return {p5.Matrix} this + * inspired by Toji's gl-matrix lib, mat4 rotation + */ +p5.Matrix.prototype.rotate = function(a, axis){ + var x, y, z, _a, len; + + if (this.p5) { + if (this.p5._angleMode === constants.DEGREES) { + _a = polarGeometry.degreesToRadians(a); + } + } + else { + _a = a; + } + if (axis instanceof p5.Vector) { + x = axis.x; + y = axis.y; + z = axis.z; + } + else if (axis instanceof Array) { + x = axis[0]; + y = axis[1]; + z = axis[2]; + } + + len = Math.sqrt(x * x + y * y + z * z); + x *= (1/len); + y *= (1/len); + z *= (1/len); + + var a00 = this.mat4[0]; + var a01 = this.mat4[1]; + var a02 = this.mat4[2]; + var a03 = this.mat4[3]; + var a10 = this.mat4[4]; + var a11 = this.mat4[5]; + var a12 = this.mat4[6]; + var a13 = this.mat4[7]; + var a20 = this.mat4[8]; + var a21 = this.mat4[9]; + var a22 = this.mat4[10]; + var a23 = this.mat4[11]; + + //sin,cos, and tan of respective angle + var sA = Math.sin(_a); + var cA = Math.cos(_a); + var tA = 1 - cA; + // Construct the elements of the rotation matrix + var b00 = x * x * tA + cA; + var b01 = y * x * tA + z * sA; + var b02 = z * x * tA - y * sA; + var b10 = x * y * tA - z * sA; + var b11 = y * y * tA + cA; + var b12 = z * y * tA + x * sA; + var b20 = x * z * tA + y * sA; + var b21 = y * z * tA - x * sA; + var b22 = z * z * tA + cA; + + // rotation-specific matrix multiplication + this.mat4[0] = a00 * b00 + a10 * b01 + a20 * b02; + this.mat4[1] = a01 * b00 + a11 * b01 + a21 * b02; + this.mat4[2] = a02 * b00 + a12 * b01 + a22 * b02; + this.mat4[3] = a03 * b00 + a13 * b01 + a23 * b02; + this.mat4[4] = a00 * b10 + a10 * b11 + a20 * b12; + this.mat4[5] = a01 * b10 + a11 * b11 + a21 * b12; + this.mat4[6] = a02 * b10 + a12 * b11 + a22 * b12; + this.mat4[7] = a03 * b10 + a13 * b11 + a23 * b12; + this.mat4[8] = a00 * b20 + a10 * b21 + a20 * b22; + this.mat4[9] = a01 * b20 + a11 * b21 + a21 * b22; + this.mat4[10] = a02 * b20 + a12 * b21 + a22 * b22; + this.mat4[11] = a03 * b20 + a13 * b21 + a23 * b22; + + return this; +}; + +/** + * @todo finish implementing this method! + * translates + * @param {Array} v vector to translate by + * @return {p5.Matrix} this + */ +p5.Matrix.prototype.translate = function(v){ + var x = v[0], + y = v[1], + z = v[2] || 0; + this.mat4[12] = + this.mat4[0] * x +this.mat4[4] * y +this.mat4[8] * z +this.mat4[12]; + this.mat4[13] = + this.mat4[1] * x +this.mat4[5] * y +this.mat4[9] * z +this.mat4[13]; + this.mat4[14] = + this.mat4[2] * x +this.mat4[6] * y +this.mat4[10] * z +this.mat4[14]; + this.mat4[15] = + this.mat4[3] * x +this.mat4[7] * y +this.mat4[11] * z +this.mat4[15]; +}; + +p5.Matrix.prototype.rotateX = function(a){ + this.rotate(a, [1,0,0]); +}; +p5.Matrix.prototype.rotateY = function(a){ + this.rotate(a, [0,1,0]); +}; +p5.Matrix.prototype.rotateZ = function(a){ + this.rotate(a, [0,0,1]); +}; + +/** + * sets the perspective matrix + * @param {Number} fovy [description] + * @param {Number} aspect [description] + * @param {Number} near near clipping plane + * @param {Number} far far clipping plane + * @return {void} + */ +p5.Matrix.prototype.perspective = function(fovy,aspect,near,far){ + + var f = 1.0 / Math.tan(fovy / 2), + nf = 1 / (near - far); + + this.mat4[0] = f / aspect; + this.mat4[1] = 0; + this.mat4[2] = 0; + this.mat4[3] = 0; + this.mat4[4] = 0; + this.mat4[5] = f; + this.mat4[6] = 0; + this.mat4[7] = 0; + this.mat4[8] = 0; + this.mat4[9] = 0; + this.mat4[10] = (far + near) * nf; + this.mat4[11] = -1; + this.mat4[12] = 0; + this.mat4[13] = 0; + this.mat4[14] = (2 * far * near) * nf; + this.mat4[15] = 0; + + return this; + +}; + +/** + * sets the ortho matrix + * @param {Number} left [description] + * @param {Number} right [description] + * @param {Number} bottom [description] + * @param {Number} top [description] + * @param {Number} near near clipping plane + * @param {Number} far far clipping plane + * @return {void} + */ +p5.Matrix.prototype.ortho = function(left,right,bottom,top,near,far){ + + var lr = 1 / (left - right), + bt = 1 / (bottom - top), + nf = 1 / (near - far); + this.mat4[0] = -2 * lr; + this.mat4[1] = 0; + this.mat4[2] = 0; + this.mat4[3] = 0; + this.mat4[4] = 0; + this.mat4[5] = -2 * bt; + this.mat4[6] = 0; + this.mat4[7] = 0; + this.mat4[8] = 0; + this.mat4[9] = 0; + this.mat4[10] = 2 * nf; + this.mat4[11] = 0; + this.mat4[12] = (left + right) * lr; + this.mat4[13] = (top + bottom) * bt; + this.mat4[14] = (far + near) * nf; + this.mat4[15] = 1; + + return this; +}; + +/** + * PRIVATE + */ +// matrix methods adapted from: +// https://developer.mozilla.org/en-US/docs/Web/WebGL/ +// gluPerspective +// +// function _makePerspective(fovy, aspect, znear, zfar){ +// var ymax = znear * Math.tan(fovy * Math.PI / 360.0); +// var ymin = -ymax; +// var xmin = ymin * aspect; +// var xmax = ymax * aspect; +// return _makeFrustum(xmin, xmax, ymin, ymax, znear, zfar); +// } + +//// +//// glFrustum +//// +//function _makeFrustum(left, right, bottom, top, znear, zfar){ +// var X = 2*znear/(right-left); +// var Y = 2*znear/(top-bottom); +// var A = (right+left)/(right-left); +// var B = (top+bottom)/(top-bottom); +// var C = -(zfar+znear)/(zfar-znear); +// var D = -2*zfar*znear/(zfar-znear); +// var frustrumMatrix =[ +// X, 0, A, 0, +// 0, Y, B, 0, +// 0, 0, C, D, +// 0, 0, -1, 0 +//]; +//return frustrumMatrix; +// } + +// function _setMVPMatrices(){ +////an identity matrix +////@TODO use the p5.Matrix class to abstract away our MV matrices and +///other math +//var _mvMatrix = +//[ +// 1.0,0.0,0.0,0.0, +// 0.0,1.0,0.0,0.0, +// 0.0,0.0,1.0,0.0, +// 0.0,0.0,0.0,1.0 +//]; + +module.exports = p5.Matrix; + +},{"../core/constants":36,"../core/core":37,"../math/polargeometry":67}],84:[function(_dereq_,module,exports){ +/** + * Welcome to RendererGL Immediate Mode. + * Immediate mode is used for drawing custom shapes + * from a set of vertices. Immediate Mode is activated + * when you call beginShape() & de-activated when you call endShape(). + * Immediate mode is a style of programming borrowed + * from OpenGL's (now-deprecated) immediate mode. + * It differs from p5.js' default, Retained Mode, which caches + * geometries and buffers on the CPU to reduce the number of webgl + * draw calls. Retained mode is more efficient & performative, + * however, Immediate Mode is useful for sketching quick + * geometric ideas. + */ +'use strict'; + +var p5 = _dereq_('../core/core'); +var constants = _dereq_('../core/constants'); + +/** + * Begin shape drawing. This is a helpful way of generating + * custom shapes quickly. However in WEBGL mode, application + * performance will likely drop as a result of too many calls to + * beginShape() / endShape(). As a high performance alternative, + * please use p5.js geometry primitives. + * @param {Number} mode webgl primitives mode. beginShape supports the + * following modes: + * POINTS,LINES,LINE_STRIP,LINE_LOOP,TRIANGLES, + * TRIANGLE_STRIP,and TRIANGLE_FAN. + * @return {[type]} [description] + */ +p5.RendererGL.prototype.beginShape = function(mode){ + //default shape mode is line_strip + this.immediateMode.shapeMode = (mode !== undefined ) ? + mode : constants.LINE_STRIP; + //if we haven't yet initialized our + //immediateMode vertices & buffers, create them now! + if(this.immediateMode.vertexPositions === undefined){ + this.immediateMode.vertexPositions = []; + this.immediateMode.vertexColors = []; + this.immediateMode.vertexBuffer = this.GL.createBuffer(); + this.immediateMode.colorBuffer = this.GL.createBuffer(); + } else { + this.immediateMode.vertexPositions.length = 0; + this.immediateMode.vertexColors.length = 0; + } + this.isImmediateDrawing = true; + return this; +}; +/** + * adds a vertex to be drawn in a custom Shape. + * @param {Number} x x-coordinate of vertex + * @param {Number} y y-coordinate of vertex + * @param {Number} z z-coordinate of vertex + * @return {p5.RendererGL} [description] + * @TODO implement handling of p5.Vector args + */ +p5.RendererGL.prototype.vertex = function(x, y, z){ + this.immediateMode.vertexPositions.push(x, y, z); + var vertexColor = this.curFillColor || [0.5, 0.5, 0.5, 1.0]; + this.immediateMode.vertexColors.push( + vertexColor[0], + vertexColor[1], + vertexColor[2], + vertexColor[3]); + return this; +}; + +/** + * End shape drawing and render vertices to screen. + * @return {p5.RendererGL} [description] + */ +p5.RendererGL.prototype.endShape = +function(mode, isCurve, isBezier,isQuadratic, isContour, shapeKind){ + var gl = this.GL; + this._bindImmediateBuffers( + this.immediateMode.vertexPositions, + this.immediateMode.vertexColors); + if(mode){ + if(this.drawMode === 'fill'){ + switch(this.immediateMode.shapeMode){ + case constants.LINE_STRIP: + this.immediateMode.shapeMode = constants.TRIANGLE_FAN; + break; + case constants.LINES: + this.immediateMode.shapeMode = constants.TRIANGLE_FAN; + break; + case constants.TRIANGLES: + this.immediateMode.shapeMode = constants.TRIANGLE_FAN; + break; + } + } else { + switch(this.immediateMode.shapeMode){ + case constants.LINE_STRIP: + this.immediateMode.shapeMode = constants.LINE_LOOP; + break; + case constants.LINES: + this.immediateMode.shapeMode = constants.LINE_LOOP; + break; + } + } + } + //QUADS & QUAD_STRIP are not supported primitives modes + //in webgl. + if(this.immediateMode.shapeMode === constants.QUADS || + this.immediateMode.shapeMode === constants.QUAD_STRIP){ + throw new Error('sorry, ' + this.immediateMode.shapeMode+ + ' not yet implemented in webgl mode.'); + } + else { + gl.enable(gl.BLEND); + gl.drawArrays(this.immediateMode.shapeMode, 0, + this.immediateMode.vertexPositions.length / 3); + } + //clear out our vertexPositions & colors arrays + //after rendering + this.immediateMode.vertexPositions.length = 0; + this.immediateMode.vertexColors.length = 0; + this.isImmediateDrawing = false; + return this; +}; +/** + * Bind immediateMode buffers to data, + * then draw gl arrays + * @param {Array} vertices Numbers array representing + * vertex positions + * @return {p5.RendererGL} + */ +p5.RendererGL.prototype._bindImmediateBuffers = function(vertices, colors){ + this._setDefaultCamera(); + var gl = this.GL; + var shaderKey = this._getCurShaderId(); + var shaderProgram = this.mHash[shaderKey]; + //vertex position Attribute + shaderProgram.vertexPositionAttribute = + gl.getAttribLocation(shaderProgram, 'aPosition'); + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.bindBuffer(gl.ARRAY_BUFFER, this.immediateMode.vertexBuffer); + gl.bufferData( + gl.ARRAY_BUFFER, new Float32Array(vertices), gl.DYNAMIC_DRAW); + gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, + 3, gl.FLOAT, false, 0, 0); + + shaderProgram.vertexColorAttribute = + gl.getAttribLocation(shaderProgram, 'aVertexColor'); + gl.enableVertexAttribArray(shaderProgram.vertexColorAttribute); + gl.bindBuffer(gl.ARRAY_BUFFER, this.immediateMode.colorBuffer); + gl.bufferData(gl.ARRAY_BUFFER, + new Float32Array(colors),gl.DYNAMIC_DRAW); + gl.vertexAttribPointer(shaderProgram.vertexColorAttribute, + 4, gl.FLOAT, false, 0, 0); + //matrix + this._setMatrixUniforms(shaderKey); + //@todo implement in all shaders (not just immediateVert) + //set our default point size + // this._setUniform1f(shaderKey, + // 'uPointSize', + // this.pointSize); + return this; +}; + +////////////////////////////////////////////// +// COLOR +////////////////////////////////////////////// + +p5.RendererGL.prototype._getColorVertexShader = function(){ + var gl = this.GL; + var mId = 'immediateVert|vertexColorFrag'; + var shaderProgram; + + if(!this.materialInHash(mId)){ + shaderProgram = + this._initShaders('immediateVert', 'vertexColorFrag', true); + this.mHash[mId] = shaderProgram; + shaderProgram.vertexColorAttribute = + gl.getAttribLocation(shaderProgram, 'aVertexColor'); + gl.enableVertexAttribArray(shaderProgram.vertexColorAttribute); + }else{ + shaderProgram = this.mHash[mId]; + } + return shaderProgram; +}; + +module.exports = p5.RendererGL; +},{"../core/constants":36,"../core/core":37}],85:[function(_dereq_,module,exports){ +//Retained Mode. The default mode for rendering 3D primitives +//in WEBGL. +'use strict'; + +var p5 = _dereq_('../core/core'); +var hashCount = 0; +/** + * _initBufferDefaults + * @description initializes buffer defaults. runs each time a new geometry is + * registered + * @param {String} gId key of the geometry object + */ +p5.RendererGL.prototype._initBufferDefaults = function(gId) { + //@TODO remove this limit on hashes in gHash + hashCount ++; + if(hashCount > 1000){ + var key = Object.keys(this.gHash)[0]; + delete this.gHash[key]; + hashCount --; + } + + var gl = this.GL; + //create a new entry in our gHash + this.gHash[gId] = {}; + this.gHash[gId].vertexBuffer = gl.createBuffer(); + this.gHash[gId].normalBuffer = gl.createBuffer(); + this.gHash[gId].uvBuffer = gl.createBuffer(); + this.gHash[gId].indexBuffer = gl.createBuffer(); +}; +/** + * createBuffers description + * @param {String} gId key of the geometry object + * @param {p5.Geometry} obj contains geometry data + */ +p5.RendererGL.prototype.createBuffers = function(gId, obj) { + var gl = this.GL; + this._setDefaultCamera(); + //initialize the gl buffers for our geom groups + this._initBufferDefaults(gId); + //return the current shaderProgram from our material hash + var shaderProgram = this.mHash[this._getCurShaderId()]; + //@todo rename "numberOfItems" property to something more descriptive + //we mult the num geom faces by 3 + this.gHash[gId].numberOfItems = obj.faces.length * 3; + gl.bindBuffer(gl.ARRAY_BUFFER, this.gHash[gId].vertexBuffer); + gl.bufferData( + gl.ARRAY_BUFFER, + new Float32Array( vToNArray(obj.vertices) ), + gl.STATIC_DRAW); + //vertex position + shaderProgram.vertexPositionAttribute = + gl.getAttribLocation(shaderProgram, 'aPosition'); + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + + gl.vertexAttribPointer( + shaderProgram.vertexPositionAttribute, + 3, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.gHash[gId].normalBuffer); + gl.bufferData( + gl.ARRAY_BUFFER, + new Float32Array( vToNArray(obj.vertexNormals) ), + gl.STATIC_DRAW); + //vertex normal + shaderProgram.vertexNormalAttribute = + gl.getAttribLocation(shaderProgram, 'aNormal'); + gl.enableVertexAttribArray(shaderProgram.vertexNormalAttribute); + + gl.vertexAttribPointer( + shaderProgram.vertexNormalAttribute, + 3, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.gHash[gId].uvBuffer); + gl.bufferData( + gl.ARRAY_BUFFER, + new Float32Array( flatten(obj.uvs) ), + gl.STATIC_DRAW); + //texture coordinate Attribute + shaderProgram.textureCoordAttribute = + gl.getAttribLocation(shaderProgram, 'aTexCoord'); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.vertexAttribPointer( + shaderProgram.textureCoordAttribute, + 2, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.gHash[gId].indexBuffer); + gl.bufferData( + gl.ELEMENT_ARRAY_BUFFER, + new Uint16Array( flatten(obj.faces) ), + gl.STATIC_DRAW); +}; + +/** + * Draws buffers given a geometry key ID + * @param {String} gId ID in our geom hash + * @return {p5.RendererGL} this + */ +p5.RendererGL.prototype.drawBuffers = function(gId) { + this._setDefaultCamera(); + var gl = this.GL; + var shaderKey = this._getCurShaderId(); + var shaderProgram = this.mHash[shaderKey]; + //vertex position buffer + gl.bindBuffer(gl.ARRAY_BUFFER, this.gHash[gId].vertexBuffer); + gl.vertexAttribPointer( + shaderProgram.vertexPositionAttribute, + 3, gl.FLOAT, false, 0, 0); + //normal buffer + gl.bindBuffer(gl.ARRAY_BUFFER, this.gHash[gId].normalBuffer); + gl.vertexAttribPointer( + shaderProgram.vertexNormalAttribute, + 3, gl.FLOAT, false, 0, 0); + // uv buffer + gl.bindBuffer(gl.ARRAY_BUFFER, this.gHash[gId].uvBuffer); + gl.vertexAttribPointer( + shaderProgram.textureCoordAttribute, + 2, gl.FLOAT, false, 0, 0); + //vertex index buffer + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.gHash[gId].indexBuffer); + this._setMatrixUniforms(shaderKey); + gl.drawElements( + gl.TRIANGLES, this.gHash[gId].numberOfItems, + gl.UNSIGNED_SHORT, 0); + return this; +}; +/////////////////////////////// +//// UTILITY FUNCTIONS +////////////////////////////// +/** + * turn a two dimensional array into one dimensional array + * @param {Array} arr 2-dimensional array + * @return {Array} 1-dimensional array + * [[1, 2, 3],[4, 5, 6]] -> [1, 2, 3, 4, 5, 6] + */ +function flatten(arr){ + if (arr.length>0){ + return arr.reduce(function(a, b){ + return a.concat(b); + }); + } else { + return []; + } +} + +/** + * turn a p5.Vector Array into a one dimensional number array + * @param {Array} arr an array of p5.Vector + * @return {Array]} a one dimensional array of numbers + * [p5.Vector(1, 2, 3), p5.Vector(4, 5, 6)] -> + * [1, 2, 3, 4, 5, 6] + */ +function vToNArray(arr){ + return flatten(arr.map(function(item){ + return [item.x, item.y, item.z]; + })); +} +module.exports = p5.RendererGL; + +},{"../core/core":37}],86:[function(_dereq_,module,exports){ +'use strict'; + +var p5 = _dereq_('../core/core'); +var shader = _dereq_('./shader'); +_dereq_('../core/p5.Renderer'); +_dereq_('./p5.Matrix'); +var uMVMatrixStack = []; +var RESOLUTION = 1000; + +//@TODO should implement public method +//to override these attributes +var attributes = { + alpha: true, + depth: true, + stencil: true, + antialias: false, + premultipliedAlpha: false, + preserveDrawingBuffer: false +}; + +/** + * @class p5.RendererGL + * @constructor + * @extends p5.Renderer + * 3D graphics class. + * @todo extend class to include public method for offscreen + * rendering (FBO). + * + */ +p5.RendererGL = function(elt, pInst, isMainCanvas) { + p5.Renderer.call(this, elt, pInst, isMainCanvas); + this._initContext(); + + this.isP3D = true; //lets us know we're in 3d mode + this.GL = this.drawingContext; + //lights + this.ambientLightCount = 0; + this.directionalLightCount = 0; + this.pointLightCount = 0; + //camera + this._curCamera = null; + + /** + * model view, projection, & normal + * matrices + */ + this.uMVMatrix = new p5.Matrix(); + this.uPMatrix = new p5.Matrix(); + this.uNMatrix = new p5.Matrix('mat3'); + //Geometry & Material hashes + this.gHash = {}; + this.mHash = {}; + //Imediate Mode + //default drawing is done in Retained Mode + this.isImmediateDrawing = false; + this.immediateMode = {}; + this.curFillColor = [0.5,0.5,0.5,1.0]; + this.curStrokeColor = [0.5,0.5,0.5,1.0]; + this.pointSize = 5.0;//default point/stroke + return this; +}; + +p5.RendererGL.prototype = Object.create(p5.Renderer.prototype); + +////////////////////////////////////////////// +// Setting +////////////////////////////////////////////// + +p5.RendererGL.prototype._initContext = function() { + try { + this.drawingContext = this.canvas.getContext('webgl', attributes) || + this.canvas.getContext('experimental-webgl', attributes); + if (this.drawingContext === null) { + throw new Error('Error creating webgl context'); + } else { + console.log('p5.RendererGL: enabled webgl context'); + var gl = this.drawingContext; + gl.enable(gl.DEPTH_TEST); + gl.depthFunc(gl.LEQUAL); + gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); + } + } catch (er) { + throw new Error(er); + } +}; +//detect if user didn't set the camera +//then call this function below +p5.RendererGL.prototype._setDefaultCamera = function(){ + if(this._curCamera === null){ + var _w = this.width; + var _h = this.height; + this.uPMatrix = p5.Matrix.identity(); + this.uPMatrix.perspective(60 / 180 * Math.PI, _w / _h, 0.1, 100); + this._curCamera = 'default'; + } +}; + +p5.RendererGL.prototype._update = function() { + this.uMVMatrix = p5.Matrix.identity(); + this.translate(0, 0, -(this.height / 2) / Math.tan(Math.PI * 30 / 180)); + this.ambientLightCount = 0; + this.directionalLightCount = 0; + this.pointLightCount = 0; +}; + +/** + * [background description] + * @return {[type]} [description] + */ +p5.RendererGL.prototype.background = function() { + var gl = this.GL; + var _col = this._pInst.color.apply(this._pInst, arguments); + var _r = (_col.levels[0]) / 255; + var _g = (_col.levels[1]) / 255; + var _b = (_col.levels[2]) / 255; + var _a = (_col.levels[3]) / 255; + gl.clearColor(_r, _g, _b, _a); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); +}; + +//@TODO implement this +// p5.RendererGL.prototype.clear = function() { +//@TODO +// }; + +////////////////////////////////////////////// +// SHADER +////////////////////////////////////////////// + +/** + * [_initShaders description] + * @param {string} vertId [description] + * @param {string} fragId [description] + * @return {[type]} [description] + */ +p5.RendererGL.prototype._initShaders = +function(vertId, fragId, isImmediateMode) { + var gl = this.GL; + //set up our default shaders by: + // 1. create the shader, + // 2. load the shader source, + // 3. compile the shader + var _vertShader = gl.createShader(gl.VERTEX_SHADER); + //load in our default vertex shader + gl.shaderSource(_vertShader, shader[vertId]); + gl.compileShader(_vertShader); + // if our vertex shader failed compilation? + if (!gl.getShaderParameter(_vertShader, gl.COMPILE_STATUS)) { + alert('Yikes! An error occurred compiling the shaders:' + + gl.getShaderInfoLog(_vertShader)); + return null; + } + + var _fragShader = gl.createShader(gl.FRAGMENT_SHADER); + //load in our material frag shader + gl.shaderSource(_fragShader, shader[fragId]); + gl.compileShader(_fragShader); + // if our frag shader failed compilation? + if (!gl.getShaderParameter(_fragShader, gl.COMPILE_STATUS)) { + alert('Darn! An error occurred compiling the shaders:' + + gl.getShaderInfoLog(_fragShader)); + return null; + } + + var shaderProgram = gl.createProgram(); + gl.attachShader(shaderProgram, _vertShader); + gl.attachShader(shaderProgram, _fragShader); + gl.linkProgram(shaderProgram); + if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { + alert('Snap! Error linking shader program'); + } + //END SHADERS SETUP + + this._getLocation(shaderProgram, isImmediateMode); + + return shaderProgram; +}; + +p5.RendererGL.prototype._getLocation = +function(shaderProgram, isImmediateMode) { + var gl = this.GL; + gl.useProgram(shaderProgram); + shaderProgram.uResolution = + gl.getUniformLocation(shaderProgram, 'uResolution'); + gl.uniform1f(shaderProgram.uResolution, RESOLUTION); + + //projection Matrix uniform + shaderProgram.uPMatrixUniform = + gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'); + //model view Matrix uniform + shaderProgram.uMVMatrixUniform = + gl.getUniformLocation(shaderProgram, 'uModelViewMatrix'); + + //@TODO: figure out a better way instead of if statement + if(isImmediateMode === undefined){ + //normal Matrix uniform + shaderProgram.uNMatrixUniform = + gl.getUniformLocation(shaderProgram, 'uNormalMatrix'); + + shaderProgram.samplerUniform = + gl.getUniformLocation(shaderProgram, 'uSampler'); + } +}; + +/** + * Sets a shader uniform given a shaderProgram and uniform string + * @param {String} shaderKey key to material Hash. + * @param {String} uniform location in shader. + * @param { Number} data data to bind uniform. Float data type. + * @todo currently this function sets uniform1f data. + * Should generalize function to accept any uniform + * data type. + */ +p5.RendererGL.prototype._setUniform1f = function(shaderKey,uniform,data) +{ + var gl = this.GL; + var shaderProgram = this.mHash[shaderKey]; + gl.useProgram(shaderProgram); + shaderProgram[uniform] = gl.getUniformLocation(shaderProgram, uniform); + gl.uniform1f(shaderProgram[uniform], data); + return this; +}; + +p5.RendererGL.prototype._setMatrixUniforms = function(shaderKey) { + var gl = this.GL; + var shaderProgram = this.mHash[shaderKey]; + + gl.useProgram(shaderProgram); + + gl.uniformMatrix4fv( + shaderProgram.uPMatrixUniform, + false, this.uPMatrix.mat4); + + gl.uniformMatrix4fv( + shaderProgram.uMVMatrixUniform, + false, this.uMVMatrix.mat4); + + this.uNMatrix.inverseTranspose(this.uMVMatrix); + + gl.uniformMatrix3fv( + shaderProgram.uNMatrixUniform, + false, this.uNMatrix.mat3); +}; +////////////////////////////////////////////// +// GET CURRENT | for shader and color +////////////////////////////////////////////// +p5.RendererGL.prototype._getShader = function(vertId, fragId, isImmediateMode) { + var mId = vertId + '|' + fragId; + //create it and put it into hashTable + if(!this.materialInHash(mId)){ + var shaderProgram = this._initShaders(vertId, fragId, isImmediateMode); + this.mHash[mId] = shaderProgram; + } + this.curShaderId = mId; + + return this.mHash[this.curShaderId]; +}; + +p5.RendererGL.prototype._getCurShaderId = function(){ + //if the shader ID is not yet defined + var mId, shaderProgram; + if(this.drawMode !== 'fill' && this.curShaderId === undefined){ + //default shader: normalMaterial() + mId = 'normalVert|normalFrag'; + shaderProgram = this._initShaders('normalVert', 'normalFrag'); + this.mHash[mId] = shaderProgram; + this.curShaderId = mId; + } else if(this.isImmediateDrawing && this.drawMode === 'fill'){ + mId = 'immediateVert|vertexColorFrag'; + shaderProgram = this._initShaders('immediateVert', 'vertexColorFrag'); + this.mHash[mId] = shaderProgram; + this.curShaderId = mId; + } + return this.curShaderId; +}; + +////////////////////////////////////////////// +// COLOR +////////////////////////////////////////////// +/** + * Basic fill material for geometry with a given color + * @method fill + * @param {Number|Array|String|p5.Color} v1 gray value, + * red or hue value (depending on the current color mode), + * or color Array, or CSS color string + * @param {Number} [v2] optional: green or saturation value + * @param {Number} [v3] optional: blue or brightness value + * @param {Number} [a] optional: opacity + * @return {p5} the p5 object + * @example + * <div> + * <code> + * function setup(){ + * createCanvas(100, 100, WEBGL); + * } + * + * function draw(){ + * background(0); + * fill(250, 0, 0); + * rotateX(frameCount * 0.01); + * rotateY(frameCount * 0.01); + * rotateZ(frameCount * 0.01); + * box(200, 200, 200); + * } + * </code> + * </div> + * + * @alt + * red canvas + * + */ +p5.RendererGL.prototype.fill = function(v1, v2, v3, a) { + var gl = this.GL; + var shaderProgram; + //see material.js for more info on color blending in webgl + var colors = this._applyColorBlend.apply(this, arguments); + this.curFillColor = colors; + this.drawMode = 'fill'; + if(this.isImmediateDrawing){ + shaderProgram = + this._getShader('immediateVert','vertexColorFrag'); + gl.useProgram(shaderProgram); + } else { + shaderProgram = + this._getShader('normalVert', 'basicFrag'); + gl.useProgram(shaderProgram); + //RetainedMode uses a webgl uniform to pass color vals + //in ImmediateMode, we want access to each vertex so therefore + //we cannot use a uniform. + shaderProgram.uMaterialColor = gl.getUniformLocation( + shaderProgram, 'uMaterialColor' ); + gl.uniform4f( shaderProgram.uMaterialColor, + colors[0], + colors[1], + colors[2], + colors[3]); + } + return this; +}; +p5.RendererGL.prototype.stroke = function(r, g, b, a) { + var color = this._pInst.color.apply(this._pInst, arguments); + var colorNormalized = color._array; + this.curStrokeColor = colorNormalized; + this.drawMode = 'stroke'; + return this; +}; + +//@TODO +p5.RendererGL.prototype._strokeCheck = function(){ + if(this.drawMode === 'stroke'){ + throw new Error( + 'stroke for shapes in 3D not yet implemented, use fill for now :(' + ); + } +}; + +/** + * [strokeWeight description] + * @param {Number} pointSize stroke point size + * @return {[type]} [description] + * @todo strokeWeight currently works on points only. + * implement on all wireframes and strokes. + */ +p5.RendererGL.prototype.strokeWeight = function(pointSize) { + this.pointSize = pointSize; + return this; +}; +////////////////////////////////////////////// +// HASH | for material and geometry +////////////////////////////////////////////// + +p5.RendererGL.prototype.geometryInHash = function(gId){ + return this.gHash[gId] !== undefined; +}; + +p5.RendererGL.prototype.materialInHash = function(mId){ + return this.mHash[mId] !== undefined; +}; + +/** + * [resize description] + * @param {[type]} w [description] + * @param {[tyoe]} h [description] + * @return {[type]} [description] + */ +p5.RendererGL.prototype.resize = function(w,h) { + var gl = this.GL; + p5.Renderer.prototype.resize.call(this, w, h); + gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); + // If we're using the default camera, update the aspect ratio + if(this._curCamera === 'default') { + this._curCamera = null; + this._setDefaultCamera(); + } +}; + +/** + * clears color and depth buffers + * with r,g,b,a + * @param {Number} r normalized red val. + * @param {Number} g normalized green val. + * @param {Number} b normalized blue val. + * @param {Number} a normalized alpha val. + */ +p5.RendererGL.prototype.clear = function() { + var gl = this.GL; + gl.clearColor(arguments[0], + arguments[1], + arguments[2], + arguments[3]); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); +}; + +/** + * [translate description] + * @param {[type]} x [description] + * @param {[type]} y [description] + * @param {[type]} z [description] + * @return {[type]} [description] + * @todo implement handle for components or vector as args + */ +p5.RendererGL.prototype.translate = function(x, y, z) { + //@TODO: figure out how to fit the resolution + x = x / RESOLUTION; + y = -y / RESOLUTION; + z = z / RESOLUTION; + this.uMVMatrix.translate([x,y,z]); + return this; +}; + +/** + * Scales the Model View Matrix by a vector + * @param {Number | p5.Vector | Array} x [description] + * @param {Number} [y] y-axis scalar + * @param {Number} [z] z-axis scalar + * @return {this} [description] + */ +p5.RendererGL.prototype.scale = function(x,y,z) { + this.uMVMatrix.scale([x,y,z]); + return this; +}; + +p5.RendererGL.prototype.rotate = function(rad, axis){ + this.uMVMatrix.rotate(rad, axis); + return this; +}; + +p5.RendererGL.prototype.rotateX = function(rad) { + this.rotate(rad, [1,0,0]); + return this; +}; + +p5.RendererGL.prototype.rotateY = function(rad) { + this.rotate(rad, [0,1,0]); + return this; +}; + +p5.RendererGL.prototype.rotateZ = function(rad) { + this.rotate(rad, [0,0,1]); + return this; +}; + +/** + * pushes a copy of the model view matrix onto the + * MV Matrix stack. + */ +p5.RendererGL.prototype.push = function() { + uMVMatrixStack.push(this.uMVMatrix.copy()); +}; + +/** + * [pop description] + * @return {[type]} [description] + */ +p5.RendererGL.prototype.pop = function() { + if (uMVMatrixStack.length === 0) { + throw new Error('Invalid popMatrix!'); + } + this.uMVMatrix = uMVMatrixStack.pop(); +}; + +p5.RendererGL.prototype.resetMatrix = function() { + this.uMVMatrix = p5.Matrix.identity(); + this.translate(0, 0, -800); + return this; +}; + +// Text/Typography +// @TODO: +p5.RendererGL.prototype._applyTextProperties = function() { + //@TODO finish implementation + console.error('text commands not yet implemented in webgl'); +}; +module.exports = p5.RendererGL; + +},{"../core/core":37,"../core/p5.Renderer":43,"./p5.Matrix":83,"./shader":88}],87:[function(_dereq_,module,exports){ +/** + * @module Shape + * @submodule 3D Primitives + * @for p5 + * @requires core + * @requires p5.Geometry + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); +_dereq_('./p5.Geometry'); +/** + * Draw a plane with given a width and height + * @method plane + * @param {Number} width width of the plane + * @param {Number} height height of the plane + * @param {Number} [detailX] Optional number of triangle + * subdivisions in x-dimension + * @param {Number} [detailY] Optional number of triangle + * subdivisions in y-dimension + * @return {p5} the p5 object + * @example + * <div> + * <code> + * //draw a plane with width 200 and height 200 + * function setup(){ + * createCanvas(100, 100, WEBGL); + * } + * + * function draw(){ + * background(200); + * plane(200, 200); + * } + * </code> + * </div> + * + * @alt + * Nothing displayed on canvas + * Rotating interior view of a box with sides that change color. + * 3d red and green gradient. + * Rotating interior view of a cylinder with sides that change color. + * Rotating view of a cylinder with sides that change color. + * 3d red and green gradient. + * rotating view of a multi-colored cylinder with concave sides. + */ +p5.prototype.plane = function(){ + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + var width = args[0] || 50; + var height = args[1] || width; + var detailX = typeof args[2] === 'number' ? args[2] : 1; + var detailY = typeof args[3] === 'number' ? args[3] : 1; + + var gId = 'plane|'+width+'|'+height+'|'+detailX+'|'+detailY; + + if(!this._renderer.geometryInHash(gId)){ + var _plane = function(){ + var u,v,p; + for (var i = 0; i <= this.detailY; i++){ + v = i / this.detailY; + for (var j = 0; j <= this.detailX; j++){ + u = j / this.detailX; + p = new p5.Vector(width * u - width/2, + height * v - height/2, + 0); + this.vertices.push(p); + this.uvs.push([u,v]); + } + } + }; + var planeGeom = + new p5.Geometry(detailX, detailY, _plane); + planeGeom + .computeFaces() + .computeNormals(); + this._renderer.createBuffers(gId, planeGeom); + } + + this._renderer.drawBuffers(gId); + +}; + +/** + * Draw a box with given width, height and depth + * @method box + * @param {Number} width width of the box + * @param {Number} Height height of the box + * @param {Number} depth depth of the box + * @param {Number} [detailX] Optional number of triangle + * subdivisions in x-dimension + * @param {Number} [detailY] Optional number of triangle + * subdivisions in y-dimension + * @return {p5} the p5 object + * @example + * <div> + * <code> + * //draw a spinning box with width, height and depth 200 + * function setup(){ + * createCanvas(100, 100, WEBGL); + * } + * + * function draw(){ + * background(200); + * rotateX(frameCount * 0.01); + * rotateY(frameCount * 0.01); + * box(200, 200, 200); + * } + * </code> + * </div> + */ +p5.prototype.box = function(){ + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + var width = args[0] || 50; + var height = args[1] || width; + var depth = args[2] || width; + + var detailX = typeof args[3] === 'number' ? args[3] : 4; + var detailY = typeof args[4] === 'number' ? args[4] : 4; + var gId = 'box|'+width+'|'+height+'|'+depth+'|'+detailX+'|'+detailY; + + if(!this._renderer.geometryInHash(gId)){ + var _box = function(){ + var cubeIndices = [ + [0, 4, 2, 6],// -1, 0, 0],// -x + [1, 3, 5, 7],// +1, 0, 0],// +x + [0, 1, 4, 5],// 0, -1, 0],// -y + [2, 6, 3, 7],// 0, +1, 0],// +y + [0, 2, 1, 3],// 0, 0, -1],// -z + [4, 5, 6, 7]// 0, 0, +1] // +z + ]; + var id=0; + for (var i = 0; i < cubeIndices.length; i++) { + var cubeIndex = cubeIndices[i]; + var v = i * 4; + for (var j = 0; j < 4; j++) { + var d = cubeIndex[j]; + //inspired by lightgl: + //https://github.com/evanw/lightgl.js + //octants:https://en.wikipedia.org/wiki/Octant_(solid_geometry) + var octant = new p5.Vector( + ((d & 1) * 2 - 1)*width/2, + ((d & 2) - 1) *height/2, + ((d & 4) / 2 - 1) * depth/2); + this.vertices.push( octant ); + this.uvs.push([j & 1, (j & 2) / 2]); + id++; + } + this.faces.push([v, v + 1, v + 2]); + this.faces.push([v + 2, v + 1, v + 3]); + } + }; + var boxGeom = new p5.Geometry(detailX,detailY, _box); + boxGeom.computeNormals(); + //initialize our geometry buffer with + //the key val pair: + //geometry Id, Geom object + this._renderer.createBuffers(gId, boxGeom); + } + this._renderer.drawBuffers(gId); + + return this; + +}; + +/** + * Draw a sphere with given radius + * @method sphere + * @param {Number} radius radius of circle + * @param {Number} [detailX] optional: number of segments, + * the more segments the smoother geometry + * default is 24 + * @param {Number} [detailY] optional: number of segments, + * the more segments the smoother geometry + * default is 16 + * @return {p5} the p5 object + * @example + * <div> + * <code> + * // draw a sphere with radius 200 + * function setup(){ + * createCanvas(100, 100, WEBGL); + * } + * + * function draw(){ + * background(200); + * sphere(50); + * } + * </code> + * </div> + */ +p5.prototype.sphere = function(){ + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + //@todo validate params here + // + var radius = args[0] || 50; + var detailX = typeof args[1] === 'number' ? args[1] : 24; + var detailY = typeof args[2] === 'number' ? args[2] : 16; + var gId = 'sphere|'+radius+'|'+detailX+'|'+detailY; + if(!this._renderer.geometryInHash(gId)){ + var _sphere = function(){ + var u,v,p; + for (var i = 0; i <= this.detailY; i++){ + v = i / this.detailY; + for (var j = 0; j <= this.detailX; j++){ + u = j / this.detailX; + var theta = 2 * Math.PI * u; + var phi = Math.PI * v - Math.PI / 2; + p = new p5.Vector(radius * Math.cos(phi) * Math.sin(theta), + radius * Math.sin(phi), + radius * Math.cos(phi) * Math.cos(theta)); + this.vertices.push(p); + this.uvs.push([u,v]); + } + } + }; + var sphereGeom = new p5.Geometry(detailX, detailY, _sphere); + sphereGeom + .computeFaces() + .computeNormals() + .averageNormals() + .averagePoleNormals(); + this._renderer.createBuffers(gId, sphereGeom); + } + this._renderer.drawBuffers(gId); + + return this; +}; + + +/** +* @private +* helper function for creating both cones and cyllinders +*/ +var _truncatedCone = function( + bottomRadius, + topRadius, + height, + detailX, + detailY, + topCap, + bottomCap) { + detailX = (detailX < 3) ? 3 : detailX; + detailY = (detailY < 1) ? 1 : detailY; + topCap = (topCap === undefined) ? true : topCap; + bottomCap = (bottomCap === undefined) ? true : bottomCap; + var extra = (topCap ? 2 : 0) + (bottomCap ? 2 : 0); + var vertsAroundEdge = detailX + 1; + + // ensure constant slant + var slant = Math.atan2(bottomRadius - topRadius, height); + var start = topCap ? -2 : 0; + var end = detailY + (bottomCap ? 2 : 0); + var yy, ii; + for (yy = start; yy <= end; ++yy) { + var v = yy / detailY; + var y = height * v; + var ringRadius; + if (yy < 0) { + y = 0; + v = 1; + ringRadius = bottomRadius; + } else if (yy > detailY) { + y = height; + v = 1; + ringRadius = topRadius; + } else { + ringRadius = bottomRadius + + (topRadius - bottomRadius) * (yy / detailY); + } + if (yy === -2 || yy === detailY + 2) { + ringRadius = 0; + v = 0; + } + y -= height / 2; + for (ii = 0; ii < vertsAroundEdge; ++ii) { + //VERTICES + this.vertices.push( + new p5.Vector( + Math.sin(ii*Math.PI * 2 /detailX) * ringRadius, + y, + Math.cos(ii*Math.PI * 2 /detailX) * ringRadius) + ); + //VERTEX NORMALS + this.vertexNormals.push( + new p5.Vector( + (yy < 0 || yy > detailY) ? 0 : + (Math.sin(ii * Math.PI * 2 / detailX) * Math.cos(slant)), + (yy < 0) ? -1 : (yy > detailY ? 1 : Math.sin(slant)), + (yy < 0 || yy > detailY) ? 0 : + (Math.cos(ii * Math.PI * 2 / detailX) * Math.cos(slant))) + ); + //UVs + this.uvs.push([(ii / detailX), v]); + } + } + for (yy = 0; yy < detailY + extra; ++yy) { + for (ii = 0; ii < detailX; ++ii) { + this.faces.push([vertsAroundEdge * (yy + 0) + 0 + ii, + vertsAroundEdge * (yy + 0) + 1 + ii, + vertsAroundEdge * (yy + 1) + 1 + ii]); + this.faces.push([vertsAroundEdge * (yy + 0) + 0 + ii, + vertsAroundEdge * (yy + 1) + 1 + ii, + vertsAroundEdge * (yy + 1) + 0 + ii]); + } + } +}; + +/** + * Draw a cylinder with given radius and height + * @method cylinder + * @param {Number} radius radius of the surface + * @param {Number} height height of the cylinder + * @param {Number} [detailX] optional: number of segments, + * the more segments the smoother geometry + * default is 24 + * @param {Number} [detailY] optional: number of segments in y-dimension, + * the more segments the smoother geometry + * default is 16 + * @return {p5} the p5 object + * @example + * <div> + * <code> + * //draw a spinning cylinder with radius 200 and height 200 + * function setup(){ + * createCanvas(100, 100, WEBGL); + * } + * + * function draw(){ + * background(200); + * rotateX(frameCount * 0.01); + * rotateZ(frameCount * 0.01); + * cylinder(200, 200); + * } + * </code> + * </div> + */ +p5.prototype.cylinder = function(){ + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + var radius = args[0] || 50; + var height = args[1] || radius; + var detailX = typeof args[2] === 'number' ? args[2] : 24; + var detailY = typeof args[3] === 'number' ? args[3] : 16; + var gId = 'cylinder|'+radius+'|'+height+'|'+detailX+'|'+detailY; + if(!this._renderer.geometryInHash(gId)){ + var cylinderGeom = new p5.Geometry(detailX, detailY); + _truncatedCone.call( + cylinderGeom, + radius, + radius, + height, + detailX, + detailY, + true,true); + cylinderGeom.computeNormals(); + this._renderer.createBuffers(gId, cylinderGeom); + } + + this._renderer.drawBuffers(gId); + + return this; +}; + + +/** + * Draw a cone with given radius and height + * @method cone + * @param {Number} radius radius of the bottom surface + * @param {Number} height height of the cone + * @param {Number} [detailX] optional: number of segments, + * the more segments the smoother geometry + * default is 24 + * @param {Number} [detailY] optional: number of segments, + * the more segments the smoother geometry + * default is 16 + * @return {p5} the p5 object + * @example + * <div> + * <code> + * //draw a spinning cone with radius 200 and height 200 + * function setup(){ + * createCanvas(100, 100, WEBGL); + * } + * + * function draw(){ + * background(200); + * rotateX(frameCount * 0.01); + * rotateZ(frameCount * 0.01); + * cone(200, 200); + * } + * </code> + * </div> + */ +p5.prototype.cone = function(){ + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + var baseRadius = args[0] || 50; + var height = args[1] || baseRadius; + var detailX = typeof args[2] === 'number' ? args[2] : 24; + var detailY = typeof args[3] === 'number' ? args[3] : 16; + var gId = 'cone|'+baseRadius+'|'+height+'|'+detailX+'|'+detailY; + if(!this._renderer.geometryInHash(gId)){ + var coneGeom = new p5.Geometry(detailX, detailY); + _truncatedCone.call(coneGeom, + baseRadius, + 0,//top radius 0 + height, + detailX, + detailY, + true, + true); + //for cones we need to average Normals + coneGeom + .computeNormals(); + this._renderer.createBuffers(gId, coneGeom); + } + + this._renderer.drawBuffers(gId); + + return this; +}; + +/** + * Draw an ellipsoid with given raduis + * @method ellipsoid + * @param {Number} radiusx xradius of circle + * @param {Number} radiusy yradius of circle + * @param {Number} radiusz zradius of circle + * @param {Number} [detailX] optional: number of segments, + * the more segments the smoother geometry + * default is 24. Avoid detail number above + * 150, it may crash the browser. + * @param {Number} [detailY] optional: number of segments, + * the more segments the smoother geometry + * default is 16. Avoid detail number above + * 150, it may crash the browser. + * @return {p5} the p5 object + * @example + * <div> + * <code> + * // draw an ellipsoid with radius 20, 30 and 40. + * function setup(){ + * createCanvas(100, 100, WEBGL); + * } + * + * function draw(){ + * background(200); + * ellipsoid(20, 30, 40); + * } + * </code> + * </div> + */ +p5.prototype.ellipsoid = function(){ + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + var detailX = typeof args[3] === 'number' ? args[3] : 24; + var detailY = typeof args[4] === 'number' ? args[4] : 24; + var radiusX = args[0] || 50; + var radiusY = args[1] || radiusX; + var radiusZ = args[2] || radiusX; + + var gId = 'ellipsoid|'+radiusX+'|'+radiusY+ + '|'+radiusZ+'|'+detailX+'|'+detailY; + + + if(!this._renderer.geometryInHash(gId)){ + var _ellipsoid = function(){ + var u,v,p; + for (var i = 0; i <= this.detailY; i++){ + v = i / this.detailY; + for (var j = 0; j <= this.detailX; j++){ + u = j / this.detailX; + var theta = 2 * Math.PI * u; + var phi = Math.PI * v - Math.PI / 2; + p = new p5.Vector(radiusX * Math.cos(phi) * Math.sin(theta), + radiusY * Math.sin(phi), + radiusZ * Math.cos(phi) * Math.cos(theta)); + this.vertices.push(p); + this.uvs.push([u,v]); + } + } + }; + var ellipsoidGeom = new p5.Geometry(detailX, detailY,_ellipsoid); + ellipsoidGeom + .computeFaces() + .computeNormals(); + this._renderer.createBuffers(gId, ellipsoidGeom); + } + + this._renderer.drawBuffers(gId); + + return this; +}; + +/** + * Draw a torus with given radius and tube radius + * @method torus + * @param {Number} radius radius of the whole ring + * @param {Number} tubeRadius radius of the tube + * @param {Number} [detailX] optional: number of segments in x-dimension, + * the more segments the smoother geometry + * default is 24 + * @param {Number} [detailY] optional: number of segments in y-dimension, + * the more segments the smoother geometry + * default is 16 + * @return {p5} the p5 object + * @example + * <div> + * <code> + * //draw a spinning torus with radius 200 and tube radius 60 + * function setup(){ + * createCanvas(100, 100, WEBGL); + * } + * + * function draw(){ + * background(200); + * rotateX(frameCount * 0.01); + * rotateY(frameCount * 0.01); + * torus(200, 60); + * } + * </code> + * </div> + */ +p5.prototype.torus = function(){ + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + var detailX = typeof args[2] === 'number' ? args[2] : 24; + var detailY = typeof args[3] === 'number' ? args[3] : 16; + + var radius = args[0] || 50; + var tubeRadius = args[1] || 10; + + var gId = 'torus|'+radius+'|'+tubeRadius+'|'+detailX+'|'+detailY; + + if(!this._renderer.geometryInHash(gId)){ + var _torus = function(){ + var u,v,p; + for (var i = 0; i <= this.detailY; i++){ + v = i / this.detailY; + for (var j = 0; j <= this.detailX; j++){ + u = j / this.detailX; + var theta = 2 * Math.PI * u; + var phi = 2 * Math.PI * v; + p = new p5.Vector( + (radius + tubeRadius * Math.cos(phi)) * Math.cos(theta), + (radius + tubeRadius * Math.cos(phi)) * Math.sin(theta), + tubeRadius * Math.sin(phi)); + this.vertices.push(p); + this.uvs.push([u,v]); + } + } + }; + var torusGeom = new p5.Geometry(detailX, detailY, _torus); + torusGeom + .computeFaces() + .computeNormals() + .averageNormals(); + this._renderer.createBuffers(gId, torusGeom); + } + + this._renderer.drawBuffers(gId); + + return this; +}; + +/////////////////////// +/// 2D primitives +///////////////////////// + +//@TODO +p5.RendererGL.prototype.point = function(x, y, z){ + console.log('point not yet implemented in webgl'); + return this; +}; + +p5.RendererGL.prototype.triangle = function +(args){ + var x1=args[0], y1=args[1]; + var x2=args[2], y2=args[3]; + var x3=args[4], y3=args[5]; + var gId = 'tri|'+x1+'|'+y1+'|'+ + x2+'|'+y2+'|'+ + x3+'|'+y3; + if(!this.geometryInHash(gId)){ + var _triangle = function(){ + var vertices = []; + vertices.push(new p5.Vector(x1,y1,0)); + vertices.push(new p5.Vector(x2,y2,0)); + vertices.push(new p5.Vector(x3,y3,0)); + this.vertices = vertices; + this.faces = [[0,1,2]]; + this.uvs = [[0,0],[0,1],[1,1]]; + }; + var triGeom = new p5.Geometry(1,1,_triangle); + triGeom.computeNormals(); + this.createBuffers(gId, triGeom); + } + + this.drawBuffers(gId); + return this; +}; + +p5.RendererGL.prototype.ellipse = function +(args){ + var x = args[0]; + var y = args[1]; + var width = args[2]; + var height = args[3]; + //detailX and Y are optional 6th & 7th + //arguments + var detailX = args[4] || 24; + var detailY = args[5] || 16; + var gId = 'ellipse|'+args[0]+'|'+args[1]+'|'+args[2]+'|'+ + args[3]; + if(!this.geometryInHash(gId)){ + var _ellipse = function(){ + var u,v,p; + var centerX = x+width*0.5; + var centerY = y+height*0.5; + for (var i = 0; i <= this.detailY; i++){ + v = i / this.detailY; + for (var j = 0; j <= this.detailX; j++){ + u = j / this.detailX; + var theta = 2 * Math.PI * u; + if(v === 0){ + p = new p5.Vector(centerX, centerY, 0); + } + else{ + var _x = centerX + width*0.5 * Math.cos(theta); + var _y = centerY + height*0.5 * Math.sin(theta); + p = new p5.Vector(_x, _y, 0); + } + this.vertices.push(p); + this.uvs.push([u,v]); + } + } + }; + var ellipseGeom = new p5.Geometry(detailX,detailY,_ellipse); + ellipseGeom + .computeFaces() + .computeNormals(); + this.createBuffers(gId, ellipseGeom); + } + this.drawBuffers(gId); + return this; +}; + +p5.RendererGL.prototype.rect = function +(args){ + var gId = 'rect|'+args[0]+'|'+args[1]+'|'+args[2]+'|'+ + args[3]; + var x = args[0]; + var y = args[1]; + var width = args[2]; + var height = args[3]; + var detailX = args[4] || 24; + var detailY = args[5] || 16; + if(!this.geometryInHash(gId)){ + var _rect = function(){ + var u,v,p; + for (var i = 0; i <= this.detailY; i++){ + v = i / this.detailY; + for (var j = 0; j <= this.detailX; j++){ + u = j / this.detailX; + // var _x = x-width/2; + // var _y = y-height/2; + p = new p5.Vector( + x + (width*u), + y + (height*v), + 0 + ); + this.vertices.push(p); + this.uvs.push([u,v]); + } + } + }; + var rectGeom = new p5.Geometry(detailX,detailY,_rect); + rectGeom + .computeFaces() + .computeNormals(); + this.createBuffers(gId, rectGeom); + } + this.drawBuffers(gId); + return this; +}; + +p5.RendererGL.prototype.quad = function(){ + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + //@todo validate params here + // + var x1 = args[0], + y1 = args[1], + x2 = args[2], + y2 = args[3], + x3 = args[4], + y3 = args[5], + x4 = args[6], + y4 = args[7]; + var gId = 'quad|'+x1+'|'+y1+'|'+ + x2+'|'+y2+'|'+ + x3+'|'+y3+'|'+ + x4+'|'+y4; + if(!this.geometryInHash(gId)){ + var _quad = function(){ + this.vertices.push(new p5.Vector(x1,y1,0)); + this.vertices.push(new p5.Vector(x2,y2,0)); + this.vertices.push(new p5.Vector(x3,y3,0)); + this.vertices.push(new p5.Vector(x4,y4,0)); + this.uvs.push([0, 0], [1, 0], [1, 1], [0, 1]); + }; + var quadGeom = new p5.Geometry(2,2,_quad); + quadGeom.computeNormals(); + quadGeom.faces = [[0,1,2],[2,3,0]]; + this.createBuffers(gId, quadGeom); + } + this.drawBuffers(gId); + return this; +}; + +//this implementation of bezier curve +//is based on Bernstein polynomial +p5.RendererGL.prototype.bezier = function +(args){ + var bezierDetail=args[12] || 20;//value of Bezier detail + this.beginShape(); + var coeff=[0,0,0,0];// Bernstein polynomial coeffecients + var vertex=[0,0,0]; //(x,y,z) coordinates of points in bezier curve + for(var i=0; i<=bezierDetail; i++){ + coeff[0]=Math.pow(1-(i/bezierDetail),3); + coeff[1]=(3*(i/bezierDetail)) * (Math.pow(1-(i/bezierDetail),2)); + coeff[2]=(3*Math.pow(i/bezierDetail,2)) * (1-(i/bezierDetail)); + coeff[3]=Math.pow(i/bezierDetail,3); + vertex[0]=args[0]*coeff[0] + args[3]*coeff[1] + + args[6]*coeff[2] + args[9]*coeff[3]; + vertex[1]=args[1]*coeff[0] + args[4]*coeff[1] + + args[7]*coeff[2] + args[10]*coeff[3]; + vertex[2]=args[2]*coeff[0] + args[5]*coeff[1] + + args[8]*coeff[2] + args[11]*coeff[3]; + this.vertex(vertex[0],vertex[1],vertex[2]); + } + this.endShape(); + return this; +}; + +p5.RendererGL.prototype.curve=function +(args){ + var curveDetail=args[12]; + this.beginShape(); + var coeff=[0,0,0,0];//coeffecients of the equation + var vertex=[0,0,0]; //(x,y,z) coordinates of points in bezier curve + for(var i=0; i<=curveDetail; i++){ + coeff[0]=Math.pow((i/curveDetail),3) * 0.5; + coeff[1]=Math.pow((i/curveDetail),2) * 0.5; + coeff[2]=(i/curveDetail) * 0.5; + coeff[3]=0.5; + vertex[0]=coeff[0]*(-args[0] + (3*args[3]) - (3*args[6]) +args[9]) + + coeff[1]*((2*args[0]) - (5*args[3]) + (4*args[6]) - args[9]) + + coeff[2]*(-args[0] + args[6]) + + coeff[3]*(2*args[3]); + vertex[1]=coeff[0]*(-args[1] + (3*args[4]) - (3*args[7]) +args[10]) + + coeff[1]*((2*args[1]) - (5*args[4]) + (4*args[7]) - args[10]) + + coeff[2]*(-args[1] + args[7]) + + coeff[3]*(2*args[4]); + vertex[2]=coeff[0]*(-args[2] + (3*args[5]) - (3*args[8]) +args[11]) + + coeff[1]*((2*args[2]) - (5*args[5]) + (4*args[8]) - args[11]) + + coeff[2]*(-args[2] + args[8]) + + coeff[3]*(2*args[5]); + this.vertex(vertex[0],vertex[1],vertex[2]); + } + this.endShape(); + return this; +}; + +module.exports = p5; + +},{"../core/core":37,"./p5.Geometry":82}],88:[function(_dereq_,module,exports){ + + +module.exports = { + immediateVert: + "attribute vec3 aPosition;\nattribute vec4 aVertexColor;\n\nuniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\nuniform float uResolution;\nuniform float uPointSize;\n\nvarying vec4 vColor;\nvoid main(void) {\n vec4 positionVec4 = vec4(aPosition / uResolution *vec3(1.0, -1.0, 1.0), 1.0);\n gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;\n vColor = aVertexColor;\n gl_PointSize = uPointSize;\n}\n", + vertexColorVert: + "attribute vec3 aPosition;\nattribute vec4 aVertexColor;\n\nuniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\nuniform float uResolution;\n\nvarying vec4 vColor;\n\nvoid main(void) {\n vec4 positionVec4 = vec4(aPosition / uResolution * vec3(1.0, -1.0, 1.0), 1.0);\n gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;\n vColor = aVertexColor;\n}\n", + vertexColorFrag: + "precision mediump float;\nvarying vec4 vColor;\nvoid main(void) {\n gl_FragColor = vColor;\n}", + normalVert: + "attribute vec3 aPosition;\nattribute vec3 aNormal;\nattribute vec2 aTexCoord;\n\nuniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\nuniform mat3 uNormalMatrix;\nuniform float uResolution;\n\nvarying vec3 vVertexNormal;\nvarying highp vec2 vVertTexCoord;\n\nvoid main(void) {\n vec4 positionVec4 = vec4(aPosition / uResolution * vec3(1.0, -1.0, 1.0), 1.0);\n gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;\n vVertexNormal = vec3( uNormalMatrix * aNormal );\n vVertTexCoord = aTexCoord;\n}\n", + normalFrag: + "precision mediump float;\nvarying vec3 vVertexNormal;\nvoid main(void) {\n gl_FragColor = vec4(vVertexNormal, 1.0);\n}", + basicFrag: + "precision mediump float;\nvarying vec3 vVertexNormal;\nuniform vec4 uMaterialColor;\nvoid main(void) {\n gl_FragColor = uMaterialColor;\n}", + lightVert: + "attribute vec3 aPosition;\nattribute vec3 aNormal;\nattribute vec2 aTexCoord;\n\nuniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\nuniform mat3 uNormalMatrix;\nuniform float uResolution;\nuniform int uAmbientLightCount;\nuniform int uDirectionalLightCount;\nuniform int uPointLightCount;\n\nuniform vec3 uAmbientColor[8];\nuniform vec3 uLightingDirection[8];\nuniform vec3 uDirectionalColor[8];\nuniform vec3 uPointLightLocation[8];\nuniform vec3 uPointLightColor[8];\nuniform bool uSpecular;\n\nvarying vec3 vVertexNormal;\nvarying vec2 vVertTexCoord;\nvarying vec3 vLightWeighting;\n\nvec3 ambientLightFactor = vec3(0.0, 0.0, 0.0);\nvec3 directionalLightFactor = vec3(0.0, 0.0, 0.0);\nvec3 pointLightFactor = vec3(0.0, 0.0, 0.0);\nvec3 pointLightFactor2 = vec3(0.0, 0.0, 0.0);\n\nvoid main(void){\n\n vec4 positionVec4 = vec4(aPosition / uResolution, 1.0);\n gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;\n\n vec3 vertexNormal = vec3( uNormalMatrix * aNormal );\n vVertexNormal = vertexNormal;\n vVertTexCoord = aTexCoord;\n\n vec4 mvPosition = uModelViewMatrix * vec4(aPosition / uResolution, 1.0);\n vec3 eyeDirection = normalize(-mvPosition.xyz);\n\n float shininess = 32.0;\n float specularFactor = 2.0;\n float diffuseFactor = 0.3;\n\n for(int i = 0; i < 8; i++){\n if(uAmbientLightCount == i) break;\n ambientLightFactor += uAmbientColor[i];\n }\n\n for(int j = 0; j < 8; j++){\n if(uDirectionalLightCount == j) break;\n vec3 dir = uLightingDirection[j];\n float directionalLightWeighting = max(dot(vertexNormal, dir), 0.0);\n directionalLightFactor += uDirectionalColor[j] * directionalLightWeighting;\n }\n\n for(int k = 0; k < 8; k++){\n if(uPointLightCount == k) break;\n vec3 loc = uPointLightLocation[k];\n //loc = loc / uResolution;\n vec3 lightDirection = normalize(loc - mvPosition.xyz);\n\n float directionalLightWeighting = max(dot(vertexNormal, lightDirection), 0.0);\n pointLightFactor += uPointLightColor[k] * directionalLightWeighting;\n\n //factor2 for specular\n vec3 reflectionDirection = reflect(-lightDirection, vertexNormal);\n float specularLightWeighting = pow(max(dot(reflectionDirection, eyeDirection), 0.0), shininess);\n\n pointLightFactor2 += uPointLightColor[k] * (specularFactor * specularLightWeighting\n + directionalLightWeighting * diffuseFactor);\n }\n\n if(!uSpecular){\n vLightWeighting = ambientLightFactor + directionalLightFactor + pointLightFactor;\n }else{\n vLightWeighting = ambientLightFactor + directionalLightFactor + pointLightFactor2;\n }\n\n}\n", + lightTextureFrag: + "precision mediump float;\n\nuniform vec4 uMaterialColor;\nuniform sampler2D uSampler;\nuniform bool isTexture;\n\nvarying vec3 vLightWeighting;\nvarying highp vec2 vVertTexCoord;\n\nvoid main(void) {\n if(!isTexture){\n gl_FragColor = vec4(vec3(uMaterialColor.rgb * vLightWeighting), uMaterialColor.a);\n }else{\n vec4 textureColor = texture2D(uSampler, vVertTexCoord);\n if(vLightWeighting == vec3(0., 0., 0.)){\n gl_FragColor = textureColor;\n }else{\n gl_FragColor = vec4(vec3(textureColor.rgb * vLightWeighting), textureColor.a);\n }\n }\n}" +}; +},{}]},{},[28])(28) +}); \ No newline at end of file diff --git a/public/js/p5.min.js b/public/js/p5.min.js new file mode 100644 index 0000000000000000000000000000000000000000..4205309ed98336e90fa957bc1f5d251ec0ce5885 --- /dev/null +++ b/public/js/p5.min.js @@ -0,0 +1,9 @@ +/*! p5.js v0.5.4 October 01, 2016 */ !function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;b="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,b.p5=a()}}(function(){var define,module,exports;return function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c?c:a)},k,k.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g<d.length;g++)e(d[g]);return e}({1:[function(a,b,c){},{}],2:[function(a,b,c){"use strict";c.argument=function(a,b){if(!a)throw new Error(b)},c.assert=c.argument},{}],3:[function(a,b,c){"use strict";function d(a,b,c,d,e){a.beginPath(),a.moveTo(b,c),a.lineTo(d,e),a.stroke()}c.line=d},{}],4:[function(a,b,c){"use strict";function d(a){this.font=a}function e(a){this.cmap=a}function f(a,b){this.encoding=a,this.charset=b}function g(a){var b;switch(a.version){case 1:this.names=c.standardNames.slice();break;case 2:for(this.names=new Array(a.numberOfGlyphs),b=0;b<a.numberOfGlyphs;b++)a.glyphNameIndex[b]<c.standardNames.length?this.names[b]=c.standardNames[a.glyphNameIndex[b]]:this.names[b]=a.names[a.glyphNameIndex[b]-c.standardNames.length];break;case 2.5:for(this.names=new Array(a.numberOfGlyphs),b=0;b<a.numberOfGlyphs;b++)this.names[b]=c.standardNames[b+a.glyphNameIndex[b]];break;case 3:this.names=[]}}function h(a){for(var b,c=a.tables.cmap.glyphIndexMap,d=Object.keys(c),e=0;e<d.length;e+=1){var f=d[e],g=c[f];b=a.glyphs.get(g),b.addUnicode(parseInt(f))}for(e=0;e<a.glyphs.length;e+=1)b=a.glyphs.get(e),a.cffEncoding?b.name=a.cffEncoding.charset[e]:b.name=a.glyphNames.glyphIndexToName(e)}var i=[".notdef","space","exclam","quotedbl","numbersign","dollar","percent","ampersand","quoteright","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","quoteleft","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","braceleft","bar","braceright","asciitilde","exclamdown","cent","sterling","fraction","yen","florin","section","currency","quotesingle","quotedblleft","guillemotleft","guilsinglleft","guilsinglright","fi","fl","endash","dagger","daggerdbl","periodcentered","paragraph","bullet","quotesinglbase","quotedblbase","quotedblright","guillemotright","ellipsis","perthousand","questiondown","grave","acute","circumflex","tilde","macron","breve","dotaccent","dieresis","ring","cedilla","hungarumlaut","ogonek","caron","emdash","AE","ordfeminine","Lslash","Oslash","OE","ordmasculine","ae","dotlessi","lslash","oslash","oe","germandbls","onesuperior","logicalnot","mu","trademark","Eth","onehalf","plusminus","Thorn","onequarter","divide","brokenbar","degree","thorn","threequarters","twosuperior","registered","minus","eth","multiply","threesuperior","copyright","Aacute","Acircumflex","Adieresis","Agrave","Aring","Atilde","Ccedilla","Eacute","Ecircumflex","Edieresis","Egrave","Iacute","Icircumflex","Idieresis","Igrave","Ntilde","Oacute","Ocircumflex","Odieresis","Ograve","Otilde","Scaron","Uacute","Ucircumflex","Udieresis","Ugrave","Yacute","Ydieresis","Zcaron","aacute","acircumflex","adieresis","agrave","aring","atilde","ccedilla","eacute","ecircumflex","edieresis","egrave","iacute","icircumflex","idieresis","igrave","ntilde","oacute","ocircumflex","odieresis","ograve","otilde","scaron","uacute","ucircumflex","udieresis","ugrave","yacute","ydieresis","zcaron","exclamsmall","Hungarumlautsmall","dollaroldstyle","dollarsuperior","ampersandsmall","Acutesmall","parenleftsuperior","parenrightsuperior","266 ff","onedotenleader","zerooldstyle","oneoldstyle","twooldstyle","threeoldstyle","fouroldstyle","fiveoldstyle","sixoldstyle","sevenoldstyle","eightoldstyle","nineoldstyle","commasuperior","threequartersemdash","periodsuperior","questionsmall","asuperior","bsuperior","centsuperior","dsuperior","esuperior","isuperior","lsuperior","msuperior","nsuperior","osuperior","rsuperior","ssuperior","tsuperior","ff","ffi","ffl","parenleftinferior","parenrightinferior","Circumflexsmall","hyphensuperior","Gravesmall","Asmall","Bsmall","Csmall","Dsmall","Esmall","Fsmall","Gsmall","Hsmall","Ismall","Jsmall","Ksmall","Lsmall","Msmall","Nsmall","Osmall","Psmall","Qsmall","Rsmall","Ssmall","Tsmall","Usmall","Vsmall","Wsmall","Xsmall","Ysmall","Zsmall","colonmonetary","onefitted","rupiah","Tildesmall","exclamdownsmall","centoldstyle","Lslashsmall","Scaronsmall","Zcaronsmall","Dieresissmall","Brevesmall","Caronsmall","Dotaccentsmall","Macronsmall","figuredash","hypheninferior","Ogoneksmall","Ringsmall","Cedillasmall","questiondownsmall","oneeighth","threeeighths","fiveeighths","seveneighths","onethird","twothirds","zerosuperior","foursuperior","fivesuperior","sixsuperior","sevensuperior","eightsuperior","ninesuperior","zeroinferior","oneinferior","twoinferior","threeinferior","fourinferior","fiveinferior","sixinferior","seveninferior","eightinferior","nineinferior","centinferior","dollarinferior","periodinferior","commainferior","Agravesmall","Aacutesmall","Acircumflexsmall","Atildesmall","Adieresissmall","Aringsmall","AEsmall","Ccedillasmall","Egravesmall","Eacutesmall","Ecircumflexsmall","Edieresissmall","Igravesmall","Iacutesmall","Icircumflexsmall","Idieresissmall","Ethsmall","Ntildesmall","Ogravesmall","Oacutesmall","Ocircumflexsmall","Otildesmall","Odieresissmall","OEsmall","Oslashsmall","Ugravesmall","Uacutesmall","Ucircumflexsmall","Udieresissmall","Yacutesmall","Thornsmall","Ydieresissmall","001.000","001.001","001.002","001.003","Black","Bold","Book","Light","Medium","Regular","Roman","Semibold"],j=["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","space","exclam","quotedbl","numbersign","dollar","percent","ampersand","quoteright","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","quoteleft","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","braceleft","bar","braceright","asciitilde","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","exclamdown","cent","sterling","fraction","yen","florin","section","currency","quotesingle","quotedblleft","guillemotleft","guilsinglleft","guilsinglright","fi","fl","","endash","dagger","daggerdbl","periodcentered","","paragraph","bullet","quotesinglbase","quotedblbase","quotedblright","guillemotright","ellipsis","perthousand","","questiondown","","grave","acute","circumflex","tilde","macron","breve","dotaccent","dieresis","","ring","cedilla","","hungarumlaut","ogonek","caron","emdash","","","","","","","","","","","","","","","","","AE","","ordfeminine","","","","","Lslash","Oslash","OE","ordmasculine","","","","","","ae","","","","dotlessi","","","lslash","oslash","oe","germandbls"],k=["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","space","exclamsmall","Hungarumlautsmall","","dollaroldstyle","dollarsuperior","ampersandsmall","Acutesmall","parenleftsuperior","parenrightsuperior","twodotenleader","onedotenleader","comma","hyphen","period","fraction","zerooldstyle","oneoldstyle","twooldstyle","threeoldstyle","fouroldstyle","fiveoldstyle","sixoldstyle","sevenoldstyle","eightoldstyle","nineoldstyle","colon","semicolon","commasuperior","threequartersemdash","periodsuperior","questionsmall","","asuperior","bsuperior","centsuperior","dsuperior","esuperior","","","isuperior","","","lsuperior","msuperior","nsuperior","osuperior","","","rsuperior","ssuperior","tsuperior","","ff","fi","fl","ffi","ffl","parenleftinferior","","parenrightinferior","Circumflexsmall","hyphensuperior","Gravesmall","Asmall","Bsmall","Csmall","Dsmall","Esmall","Fsmall","Gsmall","Hsmall","Ismall","Jsmall","Ksmall","Lsmall","Msmall","Nsmall","Osmall","Psmall","Qsmall","Rsmall","Ssmall","Tsmall","Usmall","Vsmall","Wsmall","Xsmall","Ysmall","Zsmall","colonmonetary","onefitted","rupiah","Tildesmall","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","exclamdownsmall","centoldstyle","Lslashsmall","","","Scaronsmall","Zcaronsmall","Dieresissmall","Brevesmall","Caronsmall","","Dotaccentsmall","","","Macronsmall","","","figuredash","hypheninferior","","","Ogoneksmall","Ringsmall","Cedillasmall","","","","onequarter","onehalf","threequarters","questiondownsmall","oneeighth","threeeighths","fiveeighths","seveneighths","onethird","twothirds","","","zerosuperior","onesuperior","twosuperior","threesuperior","foursuperior","fivesuperior","sixsuperior","sevensuperior","eightsuperior","ninesuperior","zeroinferior","oneinferior","twoinferior","threeinferior","fourinferior","fiveinferior","sixinferior","seveninferior","eightinferior","nineinferior","centinferior","dollarinferior","periodinferior","commainferior","Agravesmall","Aacutesmall","Acircumflexsmall","Atildesmall","Adieresissmall","Aringsmall","AEsmall","Ccedillasmall","Egravesmall","Eacutesmall","Ecircumflexsmall","Edieresissmall","Igravesmall","Iacutesmall","Icircumflexsmall","Idieresissmall","Ethsmall","Ntildesmall","Ogravesmall","Oacutesmall","Ocircumflexsmall","Otildesmall","Odieresissmall","OEsmall","Oslashsmall","Ugravesmall","Uacutesmall","Ucircumflexsmall","Udieresissmall","Yacutesmall","Thornsmall","Ydieresissmall"],l=[".notdef",".null","nonmarkingreturn","space","exclam","quotedbl","numbersign","dollar","percent","ampersand","quotesingle","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","grave","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","braceleft","bar","braceright","asciitilde","Adieresis","Aring","Ccedilla","Eacute","Ntilde","Odieresis","Udieresis","aacute","agrave","acircumflex","adieresis","atilde","aring","ccedilla","eacute","egrave","ecircumflex","edieresis","iacute","igrave","icircumflex","idieresis","ntilde","oacute","ograve","ocircumflex","odieresis","otilde","uacute","ugrave","ucircumflex","udieresis","dagger","degree","cent","sterling","section","bullet","paragraph","germandbls","registered","copyright","trademark","acute","dieresis","notequal","AE","Oslash","infinity","plusminus","lessequal","greaterequal","yen","mu","partialdiff","summation","product","pi","integral","ordfeminine","ordmasculine","Omega","ae","oslash","questiondown","exclamdown","logicalnot","radical","florin","approxequal","Delta","guillemotleft","guillemotright","ellipsis","nonbreakingspace","Agrave","Atilde","Otilde","OE","oe","endash","emdash","quotedblleft","quotedblright","quoteleft","quoteright","divide","lozenge","ydieresis","Ydieresis","fraction","currency","guilsinglleft","guilsinglright","fi","fl","daggerdbl","periodcentered","quotesinglbase","quotedblbase","perthousand","Acircumflex","Ecircumflex","Aacute","Edieresis","Egrave","Iacute","Icircumflex","Idieresis","Igrave","Oacute","Ocircumflex","apple","Ograve","Uacute","Ucircumflex","Ugrave","dotlessi","circumflex","tilde","macron","breve","dotaccent","ring","cedilla","hungarumlaut","ogonek","caron","Lslash","lslash","Scaron","scaron","Zcaron","zcaron","brokenbar","Eth","eth","Yacute","yacute","Thorn","thorn","minus","multiply","onesuperior","twosuperior","threesuperior","onehalf","onequarter","threequarters","franc","Gbreve","gbreve","Idotaccent","Scedilla","scedilla","Cacute","cacute","Ccaron","ccaron","dcroat"];d.prototype.charToGlyphIndex=function(a){var b=a.charCodeAt(0),c=this.font.glyphs;if(!c)return null;for(var d=0;d<c.length;d+=1)for(var e=c.get(d),f=0;f<e.unicodes.length;f+=1)if(e.unicodes[f]===b)return d},e.prototype.charToGlyphIndex=function(a){return this.cmap.glyphIndexMap[a.charCodeAt(0)]||0},f.prototype.charToGlyphIndex=function(a){var b=a.charCodeAt(0),c=this.encoding[b];return this.charset.indexOf(c)},g.prototype.nameToGlyphIndex=function(a){return this.names.indexOf(a)},g.prototype.glyphIndexToName=function(a){return this.names[a]},c.cffStandardStrings=i,c.cffStandardEncoding=j,c.cffExpertEncoding=k,c.standardNames=l,c.DefaultEncoding=d,c.CmapEncoding=e,c.CffEncoding=f,c.GlyphNames=g,c.addGlyphNames=h},{}],5:[function(a,b,c){"use strict";function d(a){a=a||{},this.familyName=a.familyName||" ",this.styleName=a.styleName||" ",this.designer=a.designer||" ",this.designerURL=a.designerURL||" ",this.manufacturer=a.manufacturer||" ",this.manufacturerURL=a.manufacturerURL||" ",this.license=a.license||" ",this.licenseURL=a.licenseURL||" ",this.version=a.version||"Version 0.1",this.description=a.description||" ",this.copyright=a.copyright||" ",this.trademark=a.trademark||" ",this.unitsPerEm=a.unitsPerEm||1e3,this.ascender=a.ascender,this.descender=a.descender,this.supported=!0,this.glyphs=new h.GlyphSet(this,a.glyphs||[]),this.encoding=new g.DefaultEncoding(this),this.tables={}}var e=a("./path"),f=a("./tables/sfnt"),g=a("./encoding"),h=a("./glyphset");d.prototype.hasChar=function(a){return null!==this.encoding.charToGlyphIndex(a)},d.prototype.charToGlyphIndex=function(a){return this.encoding.charToGlyphIndex(a)},d.prototype.charToGlyph=function(a){var b=this.charToGlyphIndex(a),c=this.glyphs.get(b);return c||(c=this.glyphs.get(0)),c},d.prototype.stringToGlyphs=function(a){for(var b=[],c=0;c<a.length;c+=1){var d=a[c];b.push(this.charToGlyph(d))}return b},d.prototype.nameToGlyphIndex=function(a){return this.glyphNames.nameToGlyphIndex(a)},d.prototype.nameToGlyph=function(a){var b=this.nametoGlyphIndex(a),c=this.glyphs.get(b);return c||(c=this.glyphs.get(0)),c},d.prototype.glyphIndexToName=function(a){return this.glyphNames.glyphIndexToName?this.glyphNames.glyphIndexToName(a):""},d.prototype.getKerningValue=function(a,b){a=a.index||a,b=b.index||b;var c=this.getGposKerningValue;return c?c(a,b):this.kerningPairs[a+","+b]||0},d.prototype.forEachGlyph=function(a,b,c,d,e,f){if(this.supported){b=void 0!==b?b:0,c=void 0!==c?c:0,d=void 0!==d?d:72,e=e||{};for(var g=void 0===e.kerning?!0:e.kerning,h=1/this.unitsPerEm*d,i=this.stringToGlyphs(a),j=0;j<i.length;j+=1){var k=i[j];if(f(k,b,c,d,e),k.advanceWidth&&(b+=k.advanceWidth*h),g&&j<i.length-1){var l=this.getKerningValue(k,i[j+1]);b+=l*h}}}},d.prototype.getPath=function(a,b,c,d,f){var g=new e.Path;return this.forEachGlyph(a,b,c,d,f,function(a,b,c,d){var e=a.getPath(b,c,d);g.extend(e)}),g},d.prototype.draw=function(a,b,c,d,e,f){this.getPath(b,c,d,e,f).draw(a)},d.prototype.drawPoints=function(a,b,c,d,e,f){this.forEachGlyph(b,c,d,e,f,function(b,c,d,e){b.drawPoints(a,c,d,e)})},d.prototype.drawMetrics=function(a,b,c,d,e,f){this.forEachGlyph(b,c,d,e,f,function(b,c,d,e){b.drawMetrics(a,c,d,e)})},d.prototype.validate=function(){function a(a,b){a||c.push(b)}function b(b){a(d[b]&&d[b].trim().length>0,"No "+b+" specified.")}var c=[],d=this;b("familyName"),b("weightName"),b("manufacturer"),b("copyright"),b("version"),a(this.unitsPerEm>0,"No unitsPerEm specified.")},d.prototype.toTables=function(){return f.fontToTable(this)},d.prototype.toBuffer=function(){for(var a=this.toTables(),b=a.encode(),c=new ArrayBuffer(b.length),d=new Uint8Array(c),e=0;e<b.length;e++)d[e]=b[e];return c},d.prototype.download=function(){var a=this.familyName.replace(/\s/g,"")+"-"+this.styleName+".otf",b=this.toBuffer();window.requestFileSystem=window.requestFileSystem||window.webkitRequestFileSystem,window.requestFileSystem(window.TEMPORARY,b.byteLength,function(c){c.root.getFile(a,{create:!0},function(a){a.createWriter(function(c){var d=new DataView(b),e=new Blob([d],{type:"font/opentype"});c.write(e),c.addEventListener("writeend",function(){location.href=a.toURL()},!1)})})},function(a){throw a})},c.Font=d},{"./encoding":4,"./glyphset":7,"./path":10,"./tables/sfnt":25}],6:[function(a,b,c){"use strict";function d(a,b){var c=b||{commands:[]};return{configurable:!0,get:function(){return"function"==typeof c&&(c=c()),c},set:function(a){c=a}}}function e(a){this.bindConstructorValues(a)}var f=a("./check"),g=a("./draw"),h=a("./path");e.prototype.bindConstructorValues=function(a){this.index=a.index||0,this.name=a.name||null,this.unicode=a.unicode||void 0,this.unicodes=a.unicodes||void 0!==a.unicode?[a.unicode]:[],a.xMin&&(this.xMin=a.xMin),a.yMin&&(this.yMin=a.yMin),a.xMax&&(this.xMax=a.xMax),a.yMax&&(this.yMax=a.yMax),a.advanceWidth&&(this.advanceWidth=a.advanceWidth),Object.defineProperty(this,"path",d(this,a.path))},e.prototype.addUnicode=function(a){0===this.unicodes.length&&(this.unicode=a),this.unicodes.push(a)},e.prototype.getPath=function(a,b,c){a=void 0!==a?a:0,b=void 0!==b?b:0,c=void 0!==c?c:72;for(var d=1/this.path.unitsPerEm*c,e=new h.Path,f=this.path.commands,g=0;g<f.length;g+=1){var i=f[g];"M"===i.type?e.moveTo(a+i.x*d,b+-i.y*d):"L"===i.type?e.lineTo(a+i.x*d,b+-i.y*d):"Q"===i.type?e.quadraticCurveTo(a+i.x1*d,b+-i.y1*d,a+i.x*d,b+-i.y*d):"C"===i.type?e.curveTo(a+i.x1*d,b+-i.y1*d,a+i.x2*d,b+-i.y2*d,a+i.x*d,b+-i.y*d):"Z"===i.type&&e.closePath()}return e},e.prototype.getContours=function(){if(void 0===this.points)return[];for(var a=[],b=[],c=0;c<this.points.length;c+=1){var d=this.points[c];b.push(d),d.lastPointOfContour&&(a.push(b),b=[])}return f.argument(0===b.length,"There are still points left in the current contour."),a},e.prototype.getMetrics=function(){for(var a=this.path.commands,b=[],c=[],d=0;d<a.length;d+=1){var e=a[d];"Z"!==e.type&&(b.push(e.x),c.push(e.y)),("Q"===e.type||"C"===e.type)&&(b.push(e.x1),c.push(e.y1)),"C"===e.type&&(b.push(e.x2),c.push(e.y2))}var f={xMin:Math.min.apply(null,b),yMin:Math.min.apply(null,c),xMax:Math.max.apply(null,b),yMax:Math.max.apply(null,c),leftSideBearing:0};return f.rightSideBearing=this.advanceWidth-f.leftSideBearing-(f.xMax-f.xMin),f},e.prototype.draw=function(a,b,c,d){this.getPath(b,c,d).draw(a)},e.prototype.drawPoints=function(a,b,c,d){function e(b,c,d,e){var f=2*Math.PI;a.beginPath();for(var g=0;g<b.length;g+=1)a.moveTo(c+b[g].x*e,d+b[g].y*e),a.arc(c+b[g].x*e,d+b[g].y*e,2,0,f,!1);a.closePath(),a.fill()}b=void 0!==b?b:0,c=void 0!==c?c:0,d=void 0!==d?d:24;for(var f=1/this.path.unitsPerEm*d,g=[],h=[],i=this.path,j=0;j<i.commands.length;j+=1){var k=i.commands[j];void 0!==k.x&&g.push({x:k.x,y:-k.y}),void 0!==k.x1&&h.push({x:k.x1,y:-k.y1}),void 0!==k.x2&&h.push({x:k.x2,y:-k.y2})}a.fillStyle="blue",e(g,b,c,f),a.fillStyle="red",e(h,b,c,f)},e.prototype.drawMetrics=function(a,b,c,d){var e;b=void 0!==b?b:0,c=void 0!==c?c:0,d=void 0!==d?d:24,e=1/this.path.unitsPerEm*d,a.lineWidth=1,a.strokeStyle="black",g.line(a,b,-1e4,b,1e4),g.line(a,-1e4,c,1e4,c);var f=this.xMin||0,h=this.yMin||0,i=this.xMax||0,j=this.yMax||0,k=this.advanceWidth||0;a.strokeStyle="blue",g.line(a,b+f*e,-1e4,b+f*e,1e4),g.line(a,b+i*e,-1e4,b+i*e,1e4),g.line(a,-1e4,c+-h*e,1e4,c+-h*e),g.line(a,-1e4,c+-j*e,1e4,c+-j*e),a.strokeStyle="green",g.line(a,b+k*e,-1e4,b+k*e,1e4)},c.Glyph=e},{"./check":2,"./draw":3,"./path":10}],7:[function(a,b,c){"use strict";function d(a,b){if(this.font=a,this.glyphs={},Array.isArray(b))for(var c=0;c<b.length;c++)this.glyphs[c]=b[c];this.length=b&&b.length||0}function e(a,b){return new h.Glyph({index:b,font:a})}function f(a,b,c,d,e,f){return function(){var g=new h.Glyph({index:b,font:a});return g.path=function(){c(g,d,e);var b=f(a.glyphs,g);return b.unitsPerEm=a.unitsPerEm,b},g}}function g(a,b,c,d){return function(){var e=new h.Glyph({index:b,font:a});return e.path=function(){var b=c(a,e,d);return b.unitsPerEm=a.unitsPerEm,b},e}}var h=a("./glyph");d.prototype.get=function(a){return"function"==typeof this.glyphs[a]&&(this.glyphs[a]=this.glyphs[a]()),this.glyphs[a]},d.prototype.push=function(a,b){this.glyphs[a]=b,this.length++},c.GlyphSet=d,c.glyphLoader=e,c.ttfGlyphLoader=f,c.cffGlyphLoader=g},{"./glyph":6}],8:[function(a,b,c){"use strict";function d(a){for(var b=new ArrayBuffer(a.length),c=new Uint8Array(b),d=0;d<a.length;d+=1)c[d]=a[d];return b}function e(b,c){var e=a("fs");e.readFile(b,function(a,b){return a?c(a.message):void c(null,d(b))})}function f(a,b){var c=new XMLHttpRequest;c.open("get",a,!0),c.responseType="arraybuffer",c.onload=function(){return 200!==c.status?b("Font could not be loaded: "+c.statusText):b(null,c.response)},c.send()}function g(a){var b,c,d,e,f,g,h,k=new j.Font,m=new DataView(a,0),A=l.getFixed(m,0);if(1===A)k.outlinesFormat="truetype";else{if(A=l.getTag(m,0),"OTTO"!==A)throw new Error("Unsupported OpenType version "+A);k.outlinesFormat="cff"}for(var B=l.getUShort(m,4),C=12,D=0;B>D;D+=1){var E=l.getTag(m,C),F=l.getULong(m,C+8);switch(E){case"cmap":k.tables.cmap=n.parse(m,F),k.encoding=new i.CmapEncoding(k.tables.cmap),k.encoding||(k.supported=!1);break;case"head":k.tables.head=r.parse(m,F),k.unitsPerEm=k.tables.head.unitsPerEm,b=k.tables.head.indexToLocFormat;break;case"hhea":k.tables.hhea=s.parse(m,F),k.ascender=k.tables.hhea.ascender,k.descender=k.tables.hhea.descender,k.numberOfHMetrics=k.tables.hhea.numberOfHMetrics;break;case"hmtx":c=F;break;case"maxp":k.tables.maxp=w.parse(m,F),k.numGlyphs=k.tables.maxp.numGlyphs;break;case"name":k.tables.name=x.parse(m,F),k.familyName=k.tables.name.fontFamily,k.styleName=k.tables.name.fontSubfamily;break;case"OS/2":k.tables.os2=y.parse(m,F);break;case"post":k.tables.post=z.parse(m,F),k.glyphNames=new i.GlyphNames(k.tables.post);break;case"glyf":d=F;break;case"loca":e=F;break;case"CFF ":f=F;break;case"kern":g=F;break;case"GPOS":h=F}C+=16}if(d&&e){var G=0===b,H=v.parse(m,e,k.numGlyphs,G);k.glyphs=p.parse(m,d,H,k),t.parse(m,c,k.numberOfHMetrics,k.numGlyphs,k.glyphs),i.addGlyphNames(k)}else f?(o.parse(m,f,k),i.addGlyphNames(k)):k.supported=!1;return k.supported&&(g?k.kerningPairs=u.parse(m,g):k.kerningPairs={},h&&q.parse(m,h,k)),k}function h(a,b){var c="undefined"==typeof window,d=c?e:f;d(a,function(a,c){if(a)return b(a);var d=g(c);return d.supported?b(null,d):b("Font is not supported (is this a Postscript font?)")})}var i=a("./encoding"),j=a("./font"),k=a("./glyph"),l=a("./parse"),m=a("./path"),n=a("./tables/cmap"),o=a("./tables/cff"),p=a("./tables/glyf"),q=a("./tables/gpos"),r=a("./tables/head"),s=a("./tables/hhea"),t=a("./tables/hmtx"),u=a("./tables/kern"),v=a("./tables/loca"),w=a("./tables/maxp"),x=a("./tables/name"),y=a("./tables/os2"),z=a("./tables/post");c._parse=l,c.Font=j.Font,c.Glyph=k.Glyph,c.Path=m.Path,c.parse=g,c.load=h},{"./encoding":4,"./font":5,"./glyph":6,"./parse":9,"./path":10,"./tables/cff":12,"./tables/cmap":13,"./tables/glyf":14,"./tables/gpos":15,"./tables/head":16,"./tables/hhea":17,"./tables/hmtx":18,"./tables/kern":19,"./tables/loca":20,"./tables/maxp":21,"./tables/name":22,"./tables/os2":23,"./tables/post":24,fs:1}],9:[function(a,b,c){"use strict";function d(a,b){this.data=a,this.offset=b,this.relativeOffset=0}c.getByte=function(a,b){return a.getUint8(b)},c.getCard8=c.getByte,c.getUShort=function(a,b){return a.getUint16(b,!1)},c.getCard16=c.getUShort,c.getShort=function(a,b){return a.getInt16(b,!1)},c.getULong=function(a,b){return a.getUint32(b,!1)},c.getFixed=function(a,b){var c=a.getInt16(b,!1),d=a.getUint16(b+2,!1);return c+d/65535},c.getTag=function(a,b){for(var c="",d=b;b+4>d;d+=1)c+=String.fromCharCode(a.getInt8(d));return c},c.getOffset=function(a,b,c){for(var d=0,e=0;c>e;e+=1)d<<=8,d+=a.getUint8(b+e);return d},c.getBytes=function(a,b,c){for(var d=[],e=b;c>e;e+=1)d.push(a.getUint8(e));return d},c.bytesToString=function(a){for(var b="",c=0;c<a.length;c+=1)b+=String.fromCharCode(a[c]);return b};var e={"byte":1,uShort:2,"short":2,uLong:4,fixed:4,longDateTime:8,tag:4};d.prototype.parseByte=function(){var a=this.data.getUint8(this.offset+this.relativeOffset);return this.relativeOffset+=1,a},d.prototype.parseChar=function(){var a=this.data.getInt8(this.offset+this.relativeOffset);return this.relativeOffset+=1,a},d.prototype.parseCard8=d.prototype.parseByte,d.prototype.parseUShort=function(){var a=this.data.getUint16(this.offset+this.relativeOffset);return this.relativeOffset+=2,a},d.prototype.parseCard16=d.prototype.parseUShort,d.prototype.parseSID=d.prototype.parseUShort,d.prototype.parseOffset16=d.prototype.parseUShort,d.prototype.parseShort=function(){var a=this.data.getInt16(this.offset+this.relativeOffset);return this.relativeOffset+=2,a},d.prototype.parseF2Dot14=function(){var a=this.data.getInt16(this.offset+this.relativeOffset)/16384;return this.relativeOffset+=2,a},d.prototype.parseULong=function(){var a=c.getULong(this.data,this.offset+this.relativeOffset);return this.relativeOffset+=4,a},d.prototype.parseFixed=function(){var a=c.getFixed(this.data,this.offset+this.relativeOffset);return this.relativeOffset+=4,a},d.prototype.parseOffset16List=d.prototype.parseUShortList=function(a){for(var b=new Array(a),d=this.data,e=this.offset+this.relativeOffset,f=0;a>f;f++)b[f]=c.getUShort(d,e),e+=2;return this.relativeOffset+=2*a,b},d.prototype.parseString=function(a){var b=this.data,c=this.offset+this.relativeOffset,d="";this.relativeOffset+=a;for(var e=0;a>e;e++)d+=String.fromCharCode(b.getUint8(c+e));return d},d.prototype.parseTag=function(){return this.parseString(4)},d.prototype.parseLongDateTime=function(){var a=c.getULong(this.data,this.offset+this.relativeOffset+4);return this.relativeOffset+=8,a},d.prototype.parseFixed=function(){var a=c.getULong(this.data,this.offset+this.relativeOffset);return this.relativeOffset+=4,a/65536},d.prototype.parseVersion=function(){var a=c.getUShort(this.data,this.offset+this.relativeOffset),b=c.getUShort(this.data,this.offset+this.relativeOffset+2);return this.relativeOffset+=4,a+b/4096/10},d.prototype.skip=function(a,b){void 0===b&&(b=1),this.relativeOffset+=e[a]*b},c.Parser=d},{}],10:[function(a,b,c){"use strict";function d(){this.commands=[],this.fill="black",this.stroke=null,this.strokeWidth=1}d.prototype.moveTo=function(a,b){this.commands.push({type:"M",x:a,y:b})},d.prototype.lineTo=function(a,b){this.commands.push({type:"L",x:a,y:b})},d.prototype.curveTo=d.prototype.bezierCurveTo=function(a,b,c,d,e,f){this.commands.push({type:"C",x1:a,y1:b,x2:c,y2:d,x:e,y:f})},d.prototype.quadTo=d.prototype.quadraticCurveTo=function(a,b,c,d){this.commands.push({type:"Q",x1:a,y1:b,x:c,y:d})},d.prototype.close=d.prototype.closePath=function(){this.commands.push({type:"Z"})},d.prototype.extend=function(a){a.commands&&(a=a.commands),Array.prototype.push.apply(this.commands,a)},d.prototype.draw=function(a){a.beginPath();for(var b=0;b<this.commands.length;b+=1){var c=this.commands[b];"M"===c.type?a.moveTo(c.x,c.y):"L"===c.type?a.lineTo(c.x,c.y):"C"===c.type?a.bezierCurveTo(c.x1,c.y1,c.x2,c.y2,c.x,c.y):"Q"===c.type?a.quadraticCurveTo(c.x1,c.y1,c.x,c.y):"Z"===c.type&&a.closePath()}this.fill&&(a.fillStyle=this.fill,a.fill()),this.stroke&&(a.strokeStyle=this.stroke,a.lineWidth=this.strokeWidth,a.stroke())},d.prototype.toPathData=function(a){function b(b){return Math.round(b)===b?""+Math.round(b):b.toFixed(a)}function c(){for(var a="",c=0;c<arguments.length;c+=1){var d=arguments[c];d>=0&&c>0&&(a+=" "),a+=b(d)}return a}a=void 0!==a?a:2;for(var d="",e=0;e<this.commands.length;e+=1){var f=this.commands[e];"M"===f.type?d+="M"+c(f.x,f.y):"L"===f.type?d+="L"+c(f.x,f.y):"C"===f.type?d+="C"+c(f.x1,f.y1,f.x2,f.y2,f.x,f.y):"Q"===f.type?d+="Q"+c(f.x1,f.y1,f.x,f.y):"Z"===f.type&&(d+="Z")}return d},d.prototype.toSVG=function(a){var b='<path d="';return b+=this.toPathData(a),b+='"',this.fill&"black"!==this.fill&&(b+=null===this.fill?' fill="none"':' fill="'+this.fill+'"'),this.stroke&&(b+=' stroke="'+this.stroke+'" stroke-width="'+this.strokeWidth+'"'),b+="/>"},c.Path=d},{}],11:[function(a,b,c){"use strict";function d(a,b,c){var d;for(d=0;d<b.length;d+=1){var e=b[d];this[e.name]=e.value}if(this.tableName=a,this.fields=b,c){var f=Object.keys(c);for(d=0;d<f.length;d+=1){var g=f[d],h=c[g];void 0!==this[g]&&(this[g]=h)}}}var e=a("./check"),f=a("./types").encode,g=a("./types").sizeOf;d.prototype.sizeOf=function(){for(var a=0,b=0;b<this.fields.length;b+=1){var c=this.fields[b],d=this[c.name];if(void 0===d&&(d=c.value),"function"==typeof d.sizeOf)a+=d.sizeOf();else{var f=g[c.type];e.assert("function"==typeof f,"Could not find sizeOf function for field"+c.name),a+=f(d)}}return a},d.prototype.encode=function(){return f.TABLE(this)},c.Table=d},{"./check":2,"./types":26}],12:[function(a,b,c){"use strict";function d(a,b){if(a===b)return!0;if(Array.isArray(a)&&Array.isArray(b)){if(a.length!==b.length)return!1;for(var c=0;c<a.length;c+=1)if(!d(a[c],b[c]))return!1;return!0}return!1}function e(a,b,c){var d,e,f,g=[],h=[],i=J.getCard16(a,b);if(0!==i){var j=J.getByte(a,b+2);e=b+(i+1)*j+2;var k=b+3;for(d=0;i+1>d;d+=1)g.push(J.getOffset(a,k,j)),k+=j;f=e+g[i]}else f=b+2;for(d=0;d<g.length-1;d+=1){var l=J.getBytes(a,e+g[d],e+g[d+1]);c&&(l=c(l)),h.push(l)}return{objects:h,startOffset:b,endOffset:f}}function f(a){for(var b="",c=15,d=["0","1","2","3","4","5","6","7","8","9",".","E","E-",null,"-"];;){var e=a.parseByte(),f=e>>4,g=15&e;if(f===c)break;if(b+=d[f],g===c)break;b+=d[g]}return parseFloat(b)}function g(a,b){var c,d,e,g;if(28===b)return c=a.parseByte(),d=a.parseByte(),c<<8|d;if(29===b)return c=a.parseByte(),d=a.parseByte(),e=a.parseByte(),g=a.parseByte(),c<<24|d<<16|e<<8|g;if(30===b)return f(a);if(b>=32&&246>=b)return b-139;if(b>=247&&250>=b)return c=a.parseByte(),256*(b-247)+c+108;if(b>=251&&254>=b)return c=a.parseByte(),256*-(b-251)-c-108;throw new Error("Invalid b0 "+b)}function h(a){for(var b={},c=0;c<a.length;c+=1){var d,e=a[c][0],f=a[c][1];if(d=1===f.length?f[0]:f,b.hasOwnProperty(e))throw new Error("Object "+b+" already has key "+e);b[e]=d}return b}function i(a,b,c){b=void 0!==b?b:0;var d=new J.Parser(a,b),e=[],f=[];for(c=void 0!==c?c:a.length;d.relativeOffset<c;){var i=d.parseByte();21>=i?(12===i&&(i=1200+d.parseByte()),e.push([i,f]),f=[]):f.push(g(d,i))}return h(e)}function j(a,b){return b=390>=b?H.cffStandardStrings[b]:a[b-391]}function k(a,b,c){for(var d={},e=0;e<b.length;e+=1){var f=b[e],g=a[f.op];void 0===g&&(g=void 0!==f.value?f.value:null),"SID"===f.type&&(g=j(c,g)),d[f.name]=g}return d}function l(a,b){var c={};return c.formatMajor=J.getCard8(a,b),c.formatMinor=J.getCard8(a,b+1),c.size=J.getCard8(a,b+2),c.offsetSize=J.getCard8(a,b+3),c.startOffset=b,c.endOffset=b+4,c}function m(a,b){var c=i(a,0,a.byteLength);return k(c,M,b)}function n(a,b,c,d){var e=i(a,b,c);return k(e,N,d)}function o(a,b,c,d){var e,f,g,h=new J.Parser(a,b);c-=1;var i=[".notdef"],k=h.parseCard8();if(0===k)for(e=0;c>e;e+=1)f=h.parseSID(),i.push(j(d,f));else if(1===k)for(;i.length<=c;)for(f=h.parseSID(),g=h.parseCard8(),e=0;g>=e;e+=1)i.push(j(d,f)),f+=1;else{if(2!==k)throw new Error("Unknown charset format "+k);for(;i.length<=c;)for(f=h.parseSID(),g=h.parseCard16(),e=0;g>=e;e+=1)i.push(j(d,f)),f+=1}return i}function p(a,b,c){var d,e,f={},g=new J.Parser(a,b),h=g.parseCard8();if(0===h){var i=g.parseCard8();for(d=0;i>d;d+=1)e=g.parseCard8(),f[e]=d}else{if(1!==h)throw new Error("Unknown encoding format "+h);var j=g.parseCard8();for(e=1,d=0;j>d;d+=1)for(var k=g.parseCard8(),l=g.parseCard8(),m=k;k+l>=m;m+=1)f[m]=e,e+=1}return new H.CffEncoding(f,c)}function q(a,b,c){function d(a,b){p&&k.closePath(),k.moveTo(a,b),p=!0}function e(){ +var b;b=l.length%2!==0,b&&!n&&(o=l.shift()+a.nominalWidthX),m+=l.length>>1,l.length=0,n=!0}function f(c){for(var s,t,u,v,w,x,y,z,A,B,C,D,E=0;E<c.length;){var F=c[E];switch(E+=1,F){case 1:e();break;case 3:e();break;case 4:l.length>1&&!n&&(o=l.shift()+a.nominalWidthX,n=!0),r+=l.pop(),d(q,r);break;case 5:for(;l.length>0;)q+=l.shift(),r+=l.shift(),k.lineTo(q,r);break;case 6:for(;l.length>0&&(q+=l.shift(),k.lineTo(q,r),0!==l.length);)r+=l.shift(),k.lineTo(q,r);break;case 7:for(;l.length>0&&(r+=l.shift(),k.lineTo(q,r),0!==l.length);)q+=l.shift(),k.lineTo(q,r);break;case 8:for(;l.length>0;)g=q+l.shift(),h=r+l.shift(),i=g+l.shift(),j=h+l.shift(),q=i+l.shift(),r=j+l.shift(),k.curveTo(g,h,i,j,q,r);break;case 10:w=l.pop()+a.subrsBias,x=a.subrs[w],x&&f(x);break;case 11:return;case 12:switch(F=c[E],E+=1,F){case 35:g=q+l.shift(),h=r+l.shift(),i=g+l.shift(),j=h+l.shift(),y=i+l.shift(),z=j+l.shift(),A=y+l.shift(),B=z+l.shift(),C=A+l.shift(),D=B+l.shift(),q=C+l.shift(),r=D+l.shift(),l.shift(),k.curveTo(g,h,i,j,y,z),k.curveTo(A,B,C,D,q,r);break;case 34:g=q+l.shift(),h=r,i=g+l.shift(),j=h+l.shift(),y=i+l.shift(),z=j,A=y+l.shift(),B=j,C=A+l.shift(),D=r,q=C+l.shift(),k.curveTo(g,h,i,j,y,z),k.curveTo(A,B,C,D,q,r);break;case 36:g=q+l.shift(),h=r+l.shift(),i=g+l.shift(),j=h+l.shift(),y=i+l.shift(),z=j,A=y+l.shift(),B=j,C=A+l.shift(),D=B+l.shift(),q=C+l.shift(),k.curveTo(g,h,i,j,y,z),k.curveTo(A,B,C,D,q,r);break;case 37:g=q+l.shift(),h=r+l.shift(),i=g+l.shift(),j=h+l.shift(),y=i+l.shift(),z=j+l.shift(),A=y+l.shift(),B=z+l.shift(),C=A+l.shift(),D=B+l.shift(),Math.abs(C-q)>Math.abs(D-r)?q=C+l.shift():r=D+l.shift(),k.curveTo(g,h,i,j,y,z),k.curveTo(A,B,C,D,q,r);break;default:console.log("Glyph "+b.index+": unknown operator 1200"+F),l.length=0}break;case 14:l.length>0&&!n&&(o=l.shift()+a.nominalWidthX,n=!0),p&&(k.closePath(),p=!1);break;case 18:e();break;case 19:case 20:e(),E+=m+7>>3;break;case 21:l.length>2&&!n&&(o=l.shift()+a.nominalWidthX,n=!0),r+=l.pop(),q+=l.pop(),d(q,r);break;case 22:l.length>1&&!n&&(o=l.shift()+a.nominalWidthX,n=!0),q+=l.pop(),d(q,r);break;case 23:e();break;case 24:for(;l.length>2;)g=q+l.shift(),h=r+l.shift(),i=g+l.shift(),j=h+l.shift(),q=i+l.shift(),r=j+l.shift(),k.curveTo(g,h,i,j,q,r);q+=l.shift(),r+=l.shift(),k.lineTo(q,r);break;case 25:for(;l.length>6;)q+=l.shift(),r+=l.shift(),k.lineTo(q,r);g=q+l.shift(),h=r+l.shift(),i=g+l.shift(),j=h+l.shift(),q=i+l.shift(),r=j+l.shift(),k.curveTo(g,h,i,j,q,r);break;case 26:for(l.length%2&&(q+=l.shift());l.length>0;)g=q,h=r+l.shift(),i=g+l.shift(),j=h+l.shift(),q=i,r=j+l.shift(),k.curveTo(g,h,i,j,q,r);break;case 27:for(l.length%2&&(r+=l.shift());l.length>0;)g=q+l.shift(),h=r,i=g+l.shift(),j=h+l.shift(),q=i+l.shift(),r=j,k.curveTo(g,h,i,j,q,r);break;case 28:s=c[E],t=c[E+1],l.push((s<<24|t<<16)>>16),E+=2;break;case 29:w=l.pop()+a.gsubrsBias,x=a.gsubrs[w],x&&f(x);break;case 30:for(;l.length>0&&(g=q,h=r+l.shift(),i=g+l.shift(),j=h+l.shift(),q=i+l.shift(),r=j+(1===l.length?l.shift():0),k.curveTo(g,h,i,j,q,r),0!==l.length);)g=q+l.shift(),h=r,i=g+l.shift(),j=h+l.shift(),r=j+l.shift(),q=i+(1===l.length?l.shift():0),k.curveTo(g,h,i,j,q,r);break;case 31:for(;l.length>0&&(g=q+l.shift(),h=r,i=g+l.shift(),j=h+l.shift(),r=j+l.shift(),q=i+(1===l.length?l.shift():0),k.curveTo(g,h,i,j,q,r),0!==l.length);)g=q,h=r+l.shift(),i=g+l.shift(),j=h+l.shift(),q=i+l.shift(),r=j+(1===l.length?l.shift():0),k.curveTo(g,h,i,j,q,r);break;default:32>F?console.log("Glyph "+b.index+": unknown operator "+F):247>F?l.push(F-139):251>F?(s=c[E],E+=1,l.push(256*(F-247)+s+108)):255>F?(s=c[E],E+=1,l.push(256*-(F-251)-s-108)):(s=c[E],t=c[E+1],u=c[E+2],v=c[E+3],E+=4,l.push((s<<24|t<<16|u<<8|v)/65536))}}}var g,h,i,j,k=new K.Path,l=[],m=0,n=!1,o=a.defaultWidthX,p=!1,q=0,r=0;return f(c),b.advanceWidth=o,k}function r(a){var b;return b=a.length<1240?107:a.length<33900?1131:32768}function s(a,b,c){c.tables.cff={};var d=l(a,b),f=e(a,d.endOffset,J.bytesToString),g=e(a,f.endOffset),h=e(a,g.endOffset,J.bytesToString),i=e(a,h.endOffset);c.gsubrs=i.objects,c.gsubrsBias=r(c.gsubrs);var j=new DataView(new Uint8Array(g.objects[0]).buffer),k=m(j,h.objects);c.tables.cff.topDict=k;var s=b+k["private"][1],t=n(a,s,k["private"][0],h.objects);if(c.defaultWidthX=t.defaultWidthX,c.nominalWidthX=t.nominalWidthX,0!==t.subrs){var u=s+t.subrs,v=e(a,u);c.subrs=v.objects,c.subrsBias=r(c.subrs)}else c.subrs=[],c.subrsBias=0;var w=e(a,b+k.charStrings);c.nGlyphs=w.objects.length;var x=o(a,b+k.charset,c.nGlyphs,h.objects);0===k.encoding?c.cffEncoding=new H.CffEncoding(H.cffStandardEncoding,x):1===k.encoding?c.cffEncoding=new H.CffEncoding(H.cffExpertEncoding,x):c.cffEncoding=p(a,b+k.encoding,x),c.encoding=c.encoding||c.cffEncoding,c.glyphs=new I.GlyphSet(c);for(var y=0;y<c.nGlyphs;y+=1){var z=w.objects[y];c.glyphs.push(y,I.cffGlyphLoader(c,y,q,z))}}function t(a,b){var c,d=H.cffStandardStrings.indexOf(a);return d>=0&&(c=d),d=b.indexOf(a),d>=0?c=d+H.cffStandardStrings.length:(c=H.cffStandardStrings.length+b.length,b.push(a)),c}function u(){return new L.Table("Header",[{name:"major",type:"Card8",value:1},{name:"minor",type:"Card8",value:0},{name:"hdrSize",type:"Card8",value:4},{name:"major",type:"Card8",value:1}])}function v(a){var b=new L.Table("Name INDEX",[{name:"names",type:"INDEX",value:[]}]);b.names=[];for(var c=0;c<a.length;c+=1)b.names.push({name:"name_"+c,type:"NAME",value:a[c]});return b}function w(a,b,c){for(var e={},f=0;f<a.length;f+=1){var g=a[f],h=b[g.name];void 0===h||d(h,g.value)||("SID"===g.type&&(h=t(h,c)),e[g.op]={name:g.name,type:g.type,value:h})}return e}function x(a,b){var c=new L.Table("Top DICT",[{name:"dict",type:"DICT",value:{}}]);return c.dict=w(M,a,b),c}function y(a){var b=new L.Table("Top DICT INDEX",[{name:"topDicts",type:"INDEX",value:[]}]);return b.topDicts=[{name:"topDict_0",type:"TABLE",value:a}],b}function z(a){var b=new L.Table("String INDEX",[{name:"strings",type:"INDEX",value:[]}]);b.strings=[];for(var c=0;c<a.length;c+=1)b.strings.push({name:"string_"+c,type:"STRING",value:a[c]});return b}function A(){return new L.Table("Global Subr INDEX",[{name:"subrs",type:"INDEX",value:[]}])}function B(a,b){for(var c=new L.Table("Charsets",[{name:"format",type:"Card8",value:0}]),d=0;d<a.length;d+=1){var e=a[d],f=t(e,b);c.fields.push({name:"glyph_"+d,type:"SID",value:f})}return c}function C(a){var b=[],c=a.path;b.push({name:"width",type:"NUMBER",value:a.advanceWidth});for(var d=0,e=0,f=0;f<c.commands.length;f+=1){var g,h,i=c.commands[f];if("Q"===i.type){var j=1/3,k=2/3;i={type:"C",x:i.x,y:i.y,x1:j*d+k*i.x1,y1:j*e+k*i.y1,x2:j*i.x+k*i.x1,y2:j*i.y+k*i.y1}}if("M"===i.type)g=Math.round(i.x-d),h=Math.round(i.y-e),b.push({name:"dx",type:"NUMBER",value:g}),b.push({name:"dy",type:"NUMBER",value:h}),b.push({name:"rmoveto",type:"OP",value:21}),d=Math.round(i.x),e=Math.round(i.y);else if("L"===i.type)g=Math.round(i.x-d),h=Math.round(i.y-e),b.push({name:"dx",type:"NUMBER",value:g}),b.push({name:"dy",type:"NUMBER",value:h}),b.push({name:"rlineto",type:"OP",value:5}),d=Math.round(i.x),e=Math.round(i.y);else if("C"===i.type){var l=Math.round(i.x1-d),m=Math.round(i.y1-e),n=Math.round(i.x2-i.x1),o=Math.round(i.y2-i.y1);g=Math.round(i.x-i.x2),h=Math.round(i.y-i.y2),b.push({name:"dx1",type:"NUMBER",value:l}),b.push({name:"dy1",type:"NUMBER",value:m}),b.push({name:"dx2",type:"NUMBER",value:n}),b.push({name:"dy2",type:"NUMBER",value:o}),b.push({name:"dx",type:"NUMBER",value:g}),b.push({name:"dy",type:"NUMBER",value:h}),b.push({name:"rrcurveto",type:"OP",value:8}),d=Math.round(i.x),e=Math.round(i.y)}}return b.push({name:"endchar",type:"OP",value:14}),b}function D(a){for(var b=new L.Table("CharStrings INDEX",[{name:"charStrings",type:"INDEX",value:[]}]),c=0;c<a.length;c+=1){var d=a.get(c),e=C(d);b.charStrings.push({name:d.name,type:"CHARSTRING",value:e})}return b}function E(a,b){var c=new L.Table("Private DICT",[{name:"dict",type:"DICT",value:{}}]);return c.dict=w(N,a,b),c}function F(a){var b=new L.Table("Private DICT INDEX",[{name:"privateDicts",type:"INDEX",value:[]}]);return b.privateDicts=[{name:"privateDict_0",type:"TABLE",value:a}],b}function G(a,b){for(var c,d=new L.Table("CFF ",[{name:"header",type:"TABLE"},{name:"nameIndex",type:"TABLE"},{name:"topDictIndex",type:"TABLE"},{name:"stringIndex",type:"TABLE"},{name:"globalSubrIndex",type:"TABLE"},{name:"charsets",type:"TABLE"},{name:"charStringsIndex",type:"TABLE"},{name:"privateDictIndex",type:"TABLE"}]),e=1/b.unitsPerEm,f={version:b.version,fullName:b.fullName,familyName:b.familyName,weight:b.weightName,fontMatrix:[e,0,0,e,0,0],charset:999,encoding:0,charStrings:999,"private":[0,999]},g={},h=[],i=1;i<a.length;i+=1)c=a.get(i),h.push(c.name);var j=[];d.header=u(),d.nameIndex=v([b.postScriptName]);var k=x(f,j);d.topDictIndex=y(k),d.globalSubrIndex=A(),d.charsets=B(h,j),d.charStringsIndex=D(a);var l=E(g,j);d.privateDictIndex=F(l),d.stringIndex=z(j);var m=d.header.sizeOf()+d.nameIndex.sizeOf()+d.topDictIndex.sizeOf()+d.stringIndex.sizeOf()+d.globalSubrIndex.sizeOf();return f.charset=m,f.encoding=0,f.charStrings=f.charset+d.charsets.sizeOf(),f["private"][1]=f.charStrings+d.charStringsIndex.sizeOf(),k=x(f,j),d.topDictIndex=y(k),d}var H=a("../encoding"),I=a("../glyphset"),J=a("../parse"),K=a("../path"),L=a("../table"),M=[{name:"version",op:0,type:"SID"},{name:"notice",op:1,type:"SID"},{name:"copyright",op:1200,type:"SID"},{name:"fullName",op:2,type:"SID"},{name:"familyName",op:3,type:"SID"},{name:"weight",op:4,type:"SID"},{name:"isFixedPitch",op:1201,type:"number",value:0},{name:"italicAngle",op:1202,type:"number",value:0},{name:"underlinePosition",op:1203,type:"number",value:-100},{name:"underlineThickness",op:1204,type:"number",value:50},{name:"paintType",op:1205,type:"number",value:0},{name:"charstringType",op:1206,type:"number",value:2},{name:"fontMatrix",op:1207,type:["real","real","real","real","real","real"],value:[.001,0,0,.001,0,0]},{name:"uniqueId",op:13,type:"number"},{name:"fontBBox",op:5,type:["number","number","number","number"],value:[0,0,0,0]},{name:"strokeWidth",op:1208,type:"number",value:0},{name:"xuid",op:14,type:[],value:null},{name:"charset",op:15,type:"offset",value:0},{name:"encoding",op:16,type:"offset",value:0},{name:"charStrings",op:17,type:"offset",value:0},{name:"private",op:18,type:["number","offset"],value:[0,0]}],N=[{name:"subrs",op:19,type:"offset",value:0},{name:"defaultWidthX",op:20,type:"number",value:0},{name:"nominalWidthX",op:21,type:"number",value:0}];c.parse=s,c.make=G},{"../encoding":4,"../glyphset":7,"../parse":9,"../path":10,"../table":11}],13:[function(a,b,c){"use strict";function d(a,b){var c,d={};d.version=i.getUShort(a,b),h.argument(0===d.version,"cmap table version should be 0."),d.numTables=i.getUShort(a,b+2);var e=-1;for(c=0;c<d.numTables;c+=1){var f=i.getUShort(a,b+4+8*c),g=i.getUShort(a,b+4+8*c+2);if(3===f&&(1===g||0===g)){e=i.getULong(a,b+4+8*c+4);break}}if(-1===e)return null;var j=new i.Parser(a,b+e);d.format=j.parseUShort(),h.argument(4===d.format,"Only format 4 cmap tables are supported."),d.length=j.parseUShort(),d.language=j.parseUShort();var k;d.segCount=k=j.parseUShort()>>1,j.skip("uShort",3),d.glyphIndexMap={};var l=new i.Parser(a,b+e+14),m=new i.Parser(a,b+e+16+2*k),n=new i.Parser(a,b+e+16+4*k),o=new i.Parser(a,b+e+16+6*k),p=b+e+16+8*k;for(c=0;k-1>c;c+=1)for(var q,r=l.parseUShort(),s=m.parseUShort(),t=n.parseShort(),u=o.parseUShort(),v=s;r>=v;v+=1)0!==u?(p=o.offset+o.relativeOffset-2,p+=u,p+=2*(v-s),q=i.getUShort(a,p),0!==q&&(q=q+t&65535)):q=v+t&65535,d.glyphIndexMap[v]=q;return d}function e(a,b,c){a.segments.push({end:b,start:b,delta:-(b-c),offset:0})}function f(a){a.segments.push({end:65535,start:65535,delta:1,offset:0})}function g(a){var b,c=new j.Table("cmap",[{name:"version",type:"USHORT",value:0},{name:"numTables",type:"USHORT",value:1},{name:"platformID",type:"USHORT",value:3},{name:"encodingID",type:"USHORT",value:1},{name:"offset",type:"ULONG",value:12},{name:"format",type:"USHORT",value:4},{name:"length",type:"USHORT",value:0},{name:"language",type:"USHORT",value:0},{name:"segCountX2",type:"USHORT",value:0},{name:"searchRange",type:"USHORT",value:0},{name:"entrySelector",type:"USHORT",value:0},{name:"rangeShift",type:"USHORT",value:0}]);for(c.segments=[],b=0;b<a.length;b+=1){for(var d=a.get(b),g=0;g<d.unicodes.length;g+=1)e(c,d.unicodes[g],b);c.segments=c.segments.sort(function(a,b){return a.start-b.start})}f(c);var h;h=c.segments.length,c.segCountX2=2*h,c.searchRange=2*Math.pow(2,Math.floor(Math.log(h)/Math.log(2))),c.entrySelector=Math.log(c.searchRange/2)/Math.log(2),c.rangeShift=c.segCountX2-c.searchRange;var i=[],k=[],l=[],m=[],n=[];for(b=0;h>b;b+=1){var o=c.segments[b];i=i.concat({name:"end_"+b,type:"USHORT",value:o.end}),k=k.concat({name:"start_"+b,type:"USHORT",value:o.start}),l=l.concat({name:"idDelta_"+b,type:"SHORT",value:o.delta}),m=m.concat({name:"idRangeOffset_"+b,type:"USHORT",value:o.offset}),void 0!==o.glyphId&&(n=n.concat({name:"glyph_"+b,type:"USHORT",value:o.glyphId}))}return c.fields=c.fields.concat(i),c.fields.push({name:"reservedPad",type:"USHORT",value:0}),c.fields=c.fields.concat(k),c.fields=c.fields.concat(l),c.fields=c.fields.concat(m),c.fields=c.fields.concat(n),c.length=14+2*i.length+2+2*k.length+2*l.length+2*m.length+2*n.length,c}var h=a("../check"),i=a("../parse"),j=a("../table");c.parse=d,c.make=g},{"../check":2,"../parse":9,"../table":11}],14:[function(a,b,c){"use strict";function d(a,b,c,d,e){var f;return(b&d)>0?(f=a.parseByte(),0===(b&e)&&(f=-f),f=c+f):f=(b&e)>0?c:c+a.parseShort(),f}function e(a,b,c){var e=new m.Parser(b,c);a.numberOfContours=e.parseShort(),a.xMin=e.parseShort(),a.yMin=e.parseShort(),a.xMax=e.parseShort(),a.yMax=e.parseShort();var f,g;if(a.numberOfContours>0){var h,i=a.endPointIndices=[];for(h=0;h<a.numberOfContours;h+=1)i.push(e.parseUShort());for(a.instructionLength=e.parseUShort(),a.instructions=[],h=0;h<a.instructionLength;h+=1)a.instructions.push(e.parseByte());var j=i[i.length-1]+1;for(f=[],h=0;j>h;h+=1)if(g=e.parseByte(),f.push(g),(8&g)>0)for(var l=e.parseByte(),n=0;l>n;n+=1)f.push(g),h+=1;if(k.argument(f.length===j,"Bad flags."),i.length>0){var o,p=[];if(j>0){for(h=0;j>h;h+=1)g=f[h],o={},o.onCurve=!!(1&g),o.lastPointOfContour=i.indexOf(h)>=0,p.push(o);var q=0;for(h=0;j>h;h+=1)g=f[h],o=p[h],o.x=d(e,g,q,2,16),q=o.x;var r=0;for(h=0;j>h;h+=1)g=f[h],o=p[h],o.y=d(e,g,r,4,32),r=o.y}a.points=p}else a.points=[]}else if(0===a.numberOfContours)a.points=[];else{a.isComposite=!0,a.points=[],a.components=[];for(var s=!0;s;){f=e.parseUShort();var t={glyphIndex:e.parseUShort(),xScale:1,scale01:0,scale10:0,yScale:1,dx:0,dy:0};(1&f)>0?(t.dx=e.parseShort(),t.dy=e.parseShort()):(t.dx=e.parseChar(),t.dy=e.parseChar()),(8&f)>0?t.xScale=t.yScale=e.parseF2Dot14():(64&f)>0?(t.xScale=e.parseF2Dot14(),t.yScale=e.parseF2Dot14()):(128&f)>0&&(t.xScale=e.parseF2Dot14(),t.scale01=e.parseF2Dot14(),t.scale10=e.parseF2Dot14(),t.yScale=e.parseF2Dot14()),a.components.push(t),s=!!(32&f)}}}function f(a,b){for(var c=[],d=0;d<a.length;d+=1){var e=a[d],f={x:b.xScale*e.x+b.scale01*e.y+b.dx,y:b.scale10*e.x+b.yScale*e.y+b.dy,onCurve:e.onCurve,lastPointOfContour:e.lastPointOfContour};c.push(f)}return c}function g(a){for(var b=[],c=[],d=0;d<a.length;d+=1){var e=a[d];c.push(e),e.lastPointOfContour&&(b.push(c),c=[])}return k.argument(0===c.length,"There are still points left in the current contour."),b}function h(a){var b=new n.Path;if(!a)return b;for(var c=g(a),d=0;d<c.length;d+=1){var e,f,h=c[d],i=h[0],j=h[h.length-1];i.onCurve?(e=null,f=!0):(i=j.onCurve?j:{x:(i.x+j.x)/2,y:(i.y+j.y)/2},e=i,f=!1),b.moveTo(i.x,i.y);for(var k=f?1:0;k<h.length;k+=1){var l=h[k],m=0===k?i:h[k-1];if(m.onCurve&&l.onCurve)b.lineTo(l.x,l.y);else if(m.onCurve&&!l.onCurve)e=l;else if(m.onCurve||l.onCurve){if(m.onCurve||!l.onCurve)throw new Error("Invalid state.");b.quadraticCurveTo(e.x,e.y,l.x,l.y),e=null}else{var o={x:(m.x+l.x)/2,y:(m.y+l.y)/2};b.quadraticCurveTo(m.x,m.y,o.x,o.y),e=l}}i!==j&&(e?b.quadraticCurveTo(e.x,e.y,i.x,i.y):b.lineTo(i.x,i.y))}return b.closePath(),b}function i(a,b){if(b.isComposite)for(var c=0;c<b.components.length;c+=1){var d=b.components[c],e=a.get(d.glyphIndex);if(e.points){var g=f(e.points,d);b.points=b.points.concat(g)}}return h(b.points)}function j(a,b,c,d){var f,g=new l.GlyphSet(d);for(f=0;f<c.length-1;f+=1){var h=c[f],j=c[f+1];h!==j?g.push(f,l.ttfGlyphLoader(d,f,e,a,b+h,i)):g.push(f,l.glyphLoader(d,f))}return g}var k=a("../check"),l=a("../glyphset"),m=a("../parse"),n=a("../path");c.parse=j},{"../check":2,"../glyphset":7,"../parse":9,"../path":10}],15:[function(a,b,c){"use strict";function d(a,b){for(var c=new k.Parser(a,b),d=c.parseUShort(),e=[],f=0;d>f;f++)e[c.parseTag()]={offset:c.parseUShort()};return e}function e(a,b){var c=new k.Parser(a,b),d=c.parseUShort(),e=c.parseUShort();if(1===d)return c.parseUShortList(e);if(2===d){for(var f=[];e--;)for(var g=c.parseUShort(),h=c.parseUShort(),i=c.parseUShort(),j=g;h>=j;j++)f[i++]=j;return f}}function f(a,b){var c=new k.Parser(a,b),d=c.parseUShort();if(1===d){var e=c.parseUShort(),f=c.parseUShort(),g=c.parseUShortList(f);return function(a){return g[a-e]||0}}if(2===d){for(var h=c.parseUShort(),i=[],j=[],l=[],m=0;h>m;m++)i[m]=c.parseUShort(),j[m]=c.parseUShort(),l[m]=c.parseUShort();return function(a){for(var b=0,c=i.length-1;c>b;){var d=b+c+1>>1;a<i[d]?c=d-1:b=d}return i[b]<=a&&a<=j[b]?l[b]||0:0}}}function g(a,b){var c,d,g=new k.Parser(a,b),h=g.parseUShort(),i=g.parseUShort(),j=e(a,b+i),l=g.parseUShort(),m=g.parseUShort();if(4===l&&0===m){var n={};if(1===h){for(var o=g.parseUShort(),p=[],q=g.parseOffset16List(o),r=0;o>r;r++){var s=q[r],t=n[s];if(!t){t={},g.relativeOffset=s;for(var u=g.parseUShort();u--;){var v=g.parseUShort();l&&(c=g.parseShort()),m&&(d=g.parseShort()),t[v]=c}}p[j[r]]=t}return function(a,b){var c=p[a];return c?c[b]:void 0}}if(2===h){for(var w=g.parseUShort(),x=g.parseUShort(),y=g.parseUShort(),z=g.parseUShort(),A=f(a,b+w),B=f(a,b+x),C=[],D=0;y>D;D++)for(var E=C[D]=[],F=0;z>F;F++)l&&(c=g.parseShort()),m&&(d=g.parseShort()),E[F]=c;var G={};for(D=0;D<j.length;D++)G[j[D]]=1;return function(a,b){if(G[a]){var c=A(a),d=B(b),e=C[c];return e?e[d]:void 0}}}}}function h(a,b){var c=new k.Parser(a,b),d=c.parseUShort(),e=c.parseUShort(),f=16&e,h=c.parseUShort(),i=c.parseOffset16List(h),j={lookupType:d,lookupFlag:e,markFilteringSet:f?c.parseUShort():-1};if(2===d){for(var l=[],m=0;h>m;m++)l.push(g(a,b+i[m]));j.getKerningValue=function(a,b){for(var c=l.length;c--;){var d=l[c](a,b);if(void 0!==d)return d}return 0}}return j}function i(a,b,c){var e=new k.Parser(a,b),f=e.parseFixed();j.argument(1===f,"Unsupported GPOS table version."),d(a,b+e.parseUShort()),d(a,b+e.parseUShort());var g=e.parseUShort();e.relativeOffset=g;for(var i=e.parseUShort(),l=e.parseOffset16List(i),m=b+g,n=0;i>n;n++){var o=h(a,m+l[n]);2!==o.lookupType||c.getGposKerningValue||(c.getGposKerningValue=o.getKerningValue)}}var j=a("../check"),k=a("../parse");c.parse=i},{"../check":2,"../parse":9}],16:[function(a,b,c){"use strict";function d(a,b){var c={},d=new g.Parser(a,b);return c.version=d.parseVersion(),c.fontRevision=Math.round(1e3*d.parseFixed())/1e3,c.checkSumAdjustment=d.parseULong(),c.magicNumber=d.parseULong(),f.argument(1594834165===c.magicNumber,"Font header has wrong magic number."),c.flags=d.parseUShort(),c.unitsPerEm=d.parseUShort(),c.created=d.parseLongDateTime(),c.modified=d.parseLongDateTime(),c.xMin=d.parseShort(),c.yMin=d.parseShort(),c.xMax=d.parseShort(),c.yMax=d.parseShort(),c.macStyle=d.parseUShort(),c.lowestRecPPEM=d.parseUShort(),c.fontDirectionHint=d.parseShort(),c.indexToLocFormat=d.parseShort(),c.glyphDataFormat=d.parseShort(),c}function e(a){return new h.Table("head",[{name:"version",type:"FIXED",value:65536},{name:"fontRevision",type:"FIXED",value:65536},{name:"checkSumAdjustment",type:"ULONG",value:0},{name:"magicNumber",type:"ULONG",value:1594834165},{name:"flags",type:"USHORT",value:0},{name:"unitsPerEm",type:"USHORT",value:1e3},{name:"created",type:"LONGDATETIME",value:0},{name:"modified",type:"LONGDATETIME",value:0},{name:"xMin",type:"SHORT",value:0},{name:"yMin",type:"SHORT",value:0},{name:"xMax",type:"SHORT",value:0},{name:"yMax",type:"SHORT",value:0},{name:"macStyle",type:"USHORT",value:0},{name:"lowestRecPPEM",type:"USHORT",value:0},{name:"fontDirectionHint",type:"SHORT",value:2},{name:"indexToLocFormat",type:"SHORT",value:0},{name:"glyphDataFormat",type:"SHORT",value:0}],a)}var f=a("../check"),g=a("../parse"),h=a("../table");c.parse=d,c.make=e},{"../check":2,"../parse":9,"../table":11}],17:[function(a,b,c){"use strict";function d(a,b){var c={},d=new f.Parser(a,b);return c.version=d.parseVersion(),c.ascender=d.parseShort(),c.descender=d.parseShort(),c.lineGap=d.parseShort(),c.advanceWidthMax=d.parseUShort(),c.minLeftSideBearing=d.parseShort(),c.minRightSideBearing=d.parseShort(),c.xMaxExtent=d.parseShort(),c.caretSlopeRise=d.parseShort(),c.caretSlopeRun=d.parseShort(),c.caretOffset=d.parseShort(),d.relativeOffset+=8,c.metricDataFormat=d.parseShort(),c.numberOfHMetrics=d.parseUShort(),c}function e(a){return new g.Table("hhea",[{name:"version",type:"FIXED",value:65536},{name:"ascender",type:"FWORD",value:0},{name:"descender",type:"FWORD",value:0},{name:"lineGap",type:"FWORD",value:0},{name:"advanceWidthMax",type:"UFWORD",value:0},{name:"minLeftSideBearing",type:"FWORD",value:0},{name:"minRightSideBearing",type:"FWORD",value:0},{name:"xMaxExtent",type:"FWORD",value:0},{name:"caretSlopeRise",type:"SHORT",value:1},{name:"caretSlopeRun",type:"SHORT",value:0},{name:"caretOffset",type:"SHORT",value:0},{name:"reserved1",type:"SHORT",value:0},{name:"reserved2",type:"SHORT",value:0},{name:"reserved3",type:"SHORT",value:0},{name:"reserved4",type:"SHORT",value:0},{name:"metricDataFormat",type:"SHORT",value:0},{name:"numberOfHMetrics",type:"USHORT",value:0}],a)}var f=a("../parse"),g=a("../table");c.parse=d,c.make=e},{"../parse":9,"../table":11}],18:[function(a,b,c){"use strict";function d(a,b,c,d,e){for(var g,h,i=new f.Parser(a,b),j=0;d>j;j+=1){c>j&&(g=i.parseUShort(),h=i.parseShort());var k=e.get(j);k.advanceWidth=g,k.leftSideBearing=h}}function e(a){for(var b=new g.Table("hmtx",[]),c=0;c<a.length;c+=1){var d=a.get(c),e=d.advanceWidth||0,f=d.leftSideBearing||0;b.fields.push({name:"advanceWidth_"+c,type:"USHORT",value:e}),b.fields.push({name:"leftSideBearing_"+c,type:"SHORT",value:f})}return b}var f=a("../parse"),g=a("../table");c.parse=d,c.make=e},{"../parse":9,"../table":11}],19:[function(a,b,c){"use strict";function d(a,b){var c={},d=new f.Parser(a,b),g=d.parseUShort();e.argument(0===g,"Unsupported kern table version."),d.skip("uShort",1);var h=d.parseUShort();e.argument(0===h,"Unsupported kern sub-table version."),d.skip("uShort",2);var i=d.parseUShort();d.skip("uShort",3);for(var j=0;i>j;j+=1){var k=d.parseUShort(),l=d.parseUShort(),m=d.parseShort();c[k+","+l]=m}return c}var e=a("../check"),f=a("../parse");c.parse=d},{"../check":2,"../parse":9}],20:[function(a,b,c){"use strict";function d(a,b,c,d){for(var f=new e.Parser(a,b),g=d?f.parseUShort:f.parseULong,h=[],i=0;c+1>i;i+=1){var j=g.call(f);d&&(j*=2),h.push(j)}return h}var e=a("../parse");c.parse=d},{"../parse":9}],21:[function(a,b,c){"use strict";function d(a,b){var c={},d=new f.Parser(a,b);return c.version=d.parseVersion(),c.numGlyphs=d.parseUShort(),1===c.version&&(c.maxPoints=d.parseUShort(),c.maxContours=d.parseUShort(),c.maxCompositePoints=d.parseUShort(),c.maxCompositeContours=d.parseUShort(),c.maxZones=d.parseUShort(),c.maxTwilightPoints=d.parseUShort(),c.maxStorage=d.parseUShort(),c.maxFunctionDefs=d.parseUShort(),c.maxInstructionDefs=d.parseUShort(),c.maxStackElements=d.parseUShort(),c.maxSizeOfInstructions=d.parseUShort(),c.maxComponentElements=d.parseUShort(),c.maxComponentDepth=d.parseUShort()),c}function e(a){return new g.Table("maxp",[{name:"version",type:"FIXED",value:20480},{name:"numGlyphs",type:"USHORT",value:a}])}var f=a("../parse"),g=a("../table");c.parse=d,c.make=e},{"../parse":9,"../table":11}],22:[function(a,b,c){"use strict";function d(a,b){var c={},d=new j.Parser(a,b);c.format=d.parseUShort();for(var e=d.parseUShort(),f=d.offset+d.parseUShort(),g=0,h=0;e>h;h++){var i=d.parseUShort(),k=d.parseUShort(),m=d.parseUShort(),n=d.parseUShort(),o=l[n],p=d.parseUShort(),q=d.parseUShort();if(3===i&&1===k&&1033===m){for(var r=[],s=p/2,t=0;s>t;t++,q+=2)r[t]=j.getShort(a,f+q);var u=String.fromCharCode.apply(null,r);o?c[o]=u:(g++,c["unknown"+g]=u)}}return 1===c.format&&(c.langTagCount=d.parseUShort()),c}function e(a,b,c,d,e,f){return new k.Table("NameRecord",[{name:"platformID",type:"USHORT",value:a},{name:"encodingID",type:"USHORT",value:b},{name:"languageID",type:"USHORT",value:c},{name:"nameID",type:"USHORT",value:d},{name:"length",type:"USHORT",value:e},{name:"offset",type:"USHORT",value:f}])}function f(a,b,c,d){var f=i.STRING(c);return a.records.push(e(1,0,0,b,f.length,d)),a.strings.push(f),d+=f.length}function g(a,b,c,d){var f=i.UTF16(c);return a.records.push(e(3,1,1033,b,f.length,d)),a.strings.push(f),d+=f.length}function h(a){var b=new k.Table("name",[{name:"format",type:"USHORT",value:0},{name:"count",type:"USHORT",value:0},{name:"stringOffset",type:"USHORT",value:0}]);b.records=[],b.strings=[];var c,d,e=0;for(c=0;c<l.length;c+=1)void 0!==a[l[c]]&&(d=a[l[c]],e=f(b,c,d,e));for(c=0;c<l.length;c+=1)void 0!==a[l[c]]&&(d=a[l[c]],e=g(b,c,d,e));for(b.count=b.records.length,b.stringOffset=6+12*b.count,c=0;c<b.records.length;c+=1)b.fields.push({name:"record_"+c,type:"TABLE",value:b.records[c]});for(c=0;c<b.strings.length;c+=1)b.fields.push({name:"string_"+c,type:"LITERAL",value:b.strings[c]});return b}var i=a("../types").encode,j=a("../parse"),k=a("../table"),l=["copyright","fontFamily","fontSubfamily","uniqueID","fullName","version","postScriptName","trademark","manufacturer","designer","description","manufacturerURL","designerURL","licence","licenceURL","reserved","preferredFamily","preferredSubfamily","compatibleFullName","sampleText","postScriptFindFontName","wwsFamily","wwsSubfamily"];c.parse=d,c.make=h},{"../parse":9,"../table":11,"../types":26}],23:[function(a,b,c){"use strict";function d(a){for(var b=0;b<i.length;b+=1){var c=i[b];if(a>=c.begin&&a<c.end)return b}return-1}function e(a,b){var c={},d=new g.Parser(a,b);c.version=d.parseUShort(),c.xAvgCharWidth=d.parseShort(),c.usWeightClass=d.parseUShort(),c.usWidthClass=d.parseUShort(),c.fsType=d.parseUShort(),c.ySubscriptXSize=d.parseShort(),c.ySubscriptYSize=d.parseShort(),c.ySubscriptXOffset=d.parseShort(),c.ySubscriptYOffset=d.parseShort(),c.ySuperscriptXSize=d.parseShort(),c.ySuperscriptYSize=d.parseShort(),c.ySuperscriptXOffset=d.parseShort(),c.ySuperscriptYOffset=d.parseShort(),c.yStrikeoutSize=d.parseShort(),c.yStrikeoutPosition=d.parseShort(),c.sFamilyClass=d.parseShort(),c.panose=[];for(var e=0;10>e;e++)c.panose[e]=d.parseByte();return c.ulUnicodeRange1=d.parseULong(),c.ulUnicodeRange2=d.parseULong(),c.ulUnicodeRange3=d.parseULong(),c.ulUnicodeRange4=d.parseULong(),c.achVendID=String.fromCharCode(d.parseByte(),d.parseByte(),d.parseByte(),d.parseByte()),c.fsSelection=d.parseUShort(),c.usFirstCharIndex=d.parseUShort(),c.usLastCharIndex=d.parseUShort(),c.sTypoAscender=d.parseShort(),c.sTypoDescender=d.parseShort(),c.sTypoLineGap=d.parseShort(),c.usWinAscent=d.parseUShort(),c.usWinDescent=d.parseUShort(),c.version>=1&&(c.ulCodePageRange1=d.parseULong(),c.ulCodePageRange2=d.parseULong()),c.version>=2&&(c.sxHeight=d.parseShort(),c.sCapHeight=d.parseShort(),c.usDefaultChar=d.parseUShort(),c.usBreakChar=d.parseUShort(),c.usMaxContent=d.parseUShort()),c}function f(a){return new h.Table("OS/2",[{name:"version",type:"USHORT",value:3},{name:"xAvgCharWidth",type:"SHORT",value:0},{name:"usWeightClass",type:"USHORT",value:0},{name:"usWidthClass",type:"USHORT",value:0},{name:"fsType",type:"USHORT",value:0},{name:"ySubscriptXSize",type:"SHORT",value:650},{name:"ySubscriptYSize",type:"SHORT",value:699},{name:"ySubscriptXOffset",type:"SHORT",value:0},{name:"ySubscriptYOffset",type:"SHORT",value:140},{name:"ySuperscriptXSize",type:"SHORT",value:650},{name:"ySuperscriptYSize",type:"SHORT",value:699},{name:"ySuperscriptXOffset",type:"SHORT",value:0},{name:"ySuperscriptYOffset",type:"SHORT",value:479},{name:"yStrikeoutSize",type:"SHORT",value:49},{name:"yStrikeoutPosition",type:"SHORT",value:258},{name:"sFamilyClass",type:"SHORT",value:0},{name:"bFamilyType",type:"BYTE",value:0},{name:"bSerifStyle",type:"BYTE",value:0},{name:"bWeight",type:"BYTE",value:0},{name:"bProportion",type:"BYTE",value:0},{name:"bContrast",type:"BYTE",value:0},{name:"bStrokeVariation",type:"BYTE",value:0},{name:"bArmStyle",type:"BYTE",value:0},{name:"bLetterform",type:"BYTE",value:0},{name:"bMidline",type:"BYTE",value:0},{name:"bXHeight",type:"BYTE",value:0},{name:"ulUnicodeRange1",type:"ULONG",value:0},{name:"ulUnicodeRange2",type:"ULONG",value:0},{name:"ulUnicodeRange3",type:"ULONG",value:0},{name:"ulUnicodeRange4",type:"ULONG",value:0},{name:"achVendID",type:"CHARARRAY",value:"XXXX"},{name:"fsSelection",type:"USHORT",value:0},{name:"usFirstCharIndex",type:"USHORT",value:0},{name:"usLastCharIndex",type:"USHORT",value:0},{name:"sTypoAscender",type:"SHORT",value:0},{name:"sTypoDescender",type:"SHORT",value:0},{name:"sTypoLineGap",type:"SHORT",value:0},{name:"usWinAscent",type:"USHORT",value:0},{name:"usWinDescent",type:"USHORT",value:0},{name:"ulCodePageRange1",type:"ULONG",value:0},{name:"ulCodePageRange2",type:"ULONG",value:0},{name:"sxHeight",type:"SHORT",value:0},{name:"sCapHeight",type:"SHORT",value:0},{name:"usDefaultChar",type:"USHORT",value:0},{name:"usBreakChar",type:"USHORT",value:0},{name:"usMaxContext",type:"USHORT",value:0}],a)}var g=a("../parse"),h=a("../table"),i=[{begin:0,end:127},{begin:128,end:255},{begin:256,end:383},{begin:384,end:591},{begin:592,end:687},{begin:688,end:767},{begin:768,end:879},{begin:880,end:1023},{begin:11392,end:11519},{begin:1024,end:1279},{begin:1328,end:1423},{begin:1424,end:1535},{begin:42240,end:42559},{begin:1536,end:1791},{begin:1984,end:2047},{begin:2304,end:2431},{begin:2432,end:2559},{begin:2560,end:2687},{begin:2688,end:2815},{begin:2816,end:2943},{begin:2944,end:3071},{begin:3072,end:3199},{begin:3200,end:3327},{begin:3328,end:3455},{begin:3584,end:3711},{begin:3712,end:3839},{begin:4256,end:4351},{begin:6912,end:7039},{begin:4352,end:4607},{begin:7680,end:7935},{begin:7936,end:8191},{begin:8192,end:8303},{begin:8304,end:8351},{begin:8352,end:8399},{begin:8400,end:8447},{begin:8448,end:8527},{begin:8528,end:8591},{begin:8592,end:8703},{begin:8704,end:8959},{begin:8960,end:9215},{begin:9216,end:9279},{begin:9280,end:9311},{begin:9312,end:9471},{begin:9472,end:9599},{begin:9600,end:9631},{begin:9632,end:9727},{begin:9728,end:9983},{begin:9984,end:10175},{begin:12288,end:12351},{begin:12352,end:12447},{begin:12448,end:12543},{begin:12544,end:12591},{begin:12592,end:12687},{begin:43072,end:43135},{begin:12800,end:13055},{begin:13056,end:13311},{begin:44032,end:55215},{begin:55296,end:57343},{begin:67840,end:67871},{begin:19968,end:40959},{begin:57344,end:63743},{begin:12736,end:12783},{begin:64256,end:64335},{begin:64336,end:65023},{begin:65056,end:65071},{begin:65040,end:65055},{begin:65104,end:65135},{begin:65136,end:65279},{begin:65280,end:65519},{begin:65520,end:65535},{begin:3840,end:4095},{begin:1792,end:1871},{begin:1920,end:1983},{begin:3456,end:3583},{begin:4096,end:4255},{begin:4608,end:4991},{begin:5024,end:5119},{begin:5120,end:5759},{begin:5760,end:5791},{begin:5792,end:5887},{begin:6016,end:6143},{begin:6144,end:6319},{begin:10240,end:10495},{begin:40960,end:42127},{begin:5888,end:5919},{begin:66304,end:66351},{begin:66352,end:66383},{begin:66560,end:66639},{begin:118784,end:119039},{begin:119808,end:120831},{begin:1044480,end:1048573},{begin:65024,end:65039},{begin:917504,end:917631},{begin:6400,end:6479},{begin:6480,end:6527},{begin:6528,end:6623},{begin:6656,end:6687},{begin:11264,end:11359},{begin:11568,end:11647},{begin:19904,end:19967},{begin:43008,end:43055},{begin:65536,end:65663},{begin:65856,end:65935},{begin:66432,end:66463},{begin:66464,end:66527},{begin:66640,end:66687},{begin:66688,end:66735},{begin:67584, +end:67647},{begin:68096,end:68191},{begin:119552,end:119647},{begin:73728,end:74751},{begin:119648,end:119679},{begin:7040,end:7103},{begin:7168,end:7247},{begin:7248,end:7295},{begin:43136,end:43231},{begin:43264,end:43311},{begin:43312,end:43359},{begin:43520,end:43615},{begin:65936,end:65999},{begin:66e3,end:66047},{begin:66208,end:66271},{begin:127024,end:127135}];c.unicodeRanges=i,c.getUnicodeRange=d,c.parse=e,c.make=f},{"../parse":9,"../table":11}],24:[function(a,b,c){"use strict";function d(a,b){var c,d={},e=new g.Parser(a,b);switch(d.version=e.parseVersion(),d.italicAngle=e.parseFixed(),d.underlinePosition=e.parseShort(),d.underlineThickness=e.parseShort(),d.isFixedPitch=e.parseULong(),d.minMemType42=e.parseULong(),d.maxMemType42=e.parseULong(),d.minMemType1=e.parseULong(),d.maxMemType1=e.parseULong(),d.version){case 1:d.names=f.standardNames.slice();break;case 2:for(d.numberOfGlyphs=e.parseUShort(),d.glyphNameIndex=new Array(d.numberOfGlyphs),c=0;c<d.numberOfGlyphs;c++)d.glyphNameIndex[c]=e.parseUShort();for(d.names=[],c=0;c<d.numberOfGlyphs;c++)if(d.glyphNameIndex[c]>=f.standardNames.length){var h=e.parseChar();d.names.push(e.parseString(h))}break;case 2.5:for(d.numberOfGlyphs=e.parseUShort(),d.offset=new Array(d.numberOfGlyphs),c=0;c<d.numberOfGlyphs;c++)d.offset[c]=e.parseChar()}return d}function e(){return new h.Table("post",[{name:"version",type:"FIXED",value:196608},{name:"italicAngle",type:"FIXED",value:0},{name:"underlinePosition",type:"FWORD",value:0},{name:"underlineThickness",type:"FWORD",value:0},{name:"isFixedPitch",type:"ULONG",value:0},{name:"minMemType42",type:"ULONG",value:0},{name:"maxMemType42",type:"ULONG",value:0},{name:"minMemType1",type:"ULONG",value:0},{name:"maxMemType1",type:"ULONG",value:0}])}var f=a("../encoding"),g=a("../parse"),h=a("../table");c.parse=d,c.make=e},{"../encoding":4,"../parse":9,"../table":11}],25:[function(a,b,c){"use strict";function d(a){return Math.log(a)/Math.log(2)|0}function e(a){for(;a.length%4!==0;)a.push(0);for(var b=0,c=0;c<a.length;c+=4)b+=(a[c]<<24)+(a[c+1]<<16)+(a[c+2]<<8)+a[c+3];return b%=Math.pow(2,32)}function f(a,b,c,d){return new l.Table("Table Record",[{name:"tag",type:"TAG",value:void 0!==a?a:""},{name:"checkSum",type:"ULONG",value:void 0!==b?b:0},{name:"offset",type:"ULONG",value:void 0!==c?c:0},{name:"length",type:"ULONG",value:void 0!==d?d:0}])}function g(a){var b=new l.Table("sfnt",[{name:"version",type:"TAG",value:"OTTO"},{name:"numTables",type:"USHORT",value:0},{name:"searchRange",type:"USHORT",value:0},{name:"entrySelector",type:"USHORT",value:0},{name:"rangeShift",type:"USHORT",value:0}]);b.tables=a,b.numTables=a.length;var c=Math.pow(2,d(b.numTables));b.searchRange=16*c,b.entrySelector=d(c),b.rangeShift=16*b.numTables-b.searchRange;for(var g=[],h=[],i=b.sizeOf()+f().sizeOf()*b.numTables;i%4!==0;)i+=1,h.push({name:"padding",type:"BYTE",value:0});for(var j=0;j<a.length;j+=1){var m=a[j];k.argument(4===m.tableName.length,"Table name"+m.tableName+" is invalid.");var n=m.sizeOf(),o=f(m.tableName,e(m.encode()),i,n);for(g.push({name:o.tag+" Table Record",type:"TABLE",value:o}),h.push({name:m.tableName+" table",type:"TABLE",value:m}),i+=n,k.argument(!isNaN(i),"Something went wrong calculating the offset.");i%4!==0;)i+=1,h.push({name:"padding",type:"BYTE",value:0})}return g.sort(function(a,b){return a.value.tag>b.value.tag?1:-1}),b.fields=b.fields.concat(g),b.fields=b.fields.concat(h),b}function h(a,b,c){for(var d=0;d<b.length;d+=1){var e=a.charToGlyphIndex(b[d]);if(e>0){var f=a.glyphs.get(e);return f.getMetrics()}}return c}function i(a){for(var b=0,c=0;c<a.length;c+=1)b+=a[c];return b/a.length}function j(a){for(var b,c=[],d=[],f=[],j=[],k=[],l=[],v=[],w=0,x=0,y=0,z=0,A=0,B=0;B<a.glyphs.length;B+=1){var C=a.glyphs.get(B),D=0|C.unicode;(b>D||null===b)&&(b=D),D>w&&(w=D);var E=t.getUnicodeRange(D);if(32>E)x|=1<<E;else if(64>E)y|=1<<E-32;else if(96>E)z|=1<<E-64;else{if(!(123>E))throw new Error("Unicode ranges bits > 123 are reserved for internal usage");A|=1<<E-96}if(".notdef"!==C.name){var F=C.getMetrics();c.push(F.xMin),d.push(F.yMin),f.push(F.xMax),j.push(F.yMax),l.push(F.leftSideBearing),v.push(F.rightSideBearing),k.push(C.advanceWidth)}}var G={xMin:Math.min.apply(null,c),yMin:Math.min.apply(null,d),xMax:Math.max.apply(null,f),yMax:Math.max.apply(null,j),advanceWidthMax:Math.max.apply(null,k),advanceWidthAvg:i(k),minLeftSideBearing:Math.min.apply(null,l),maxLeftSideBearing:Math.max.apply(null,l),minRightSideBearing:Math.min.apply(null,v)};G.ascender=void 0!==a.ascender?a.ascender:G.yMax,G.descender=void 0!==a.descender?a.descender:G.yMin;var H=o.make({unitsPerEm:a.unitsPerEm,xMin:G.xMin,yMin:G.yMin,xMax:G.xMax,yMax:G.yMax}),I=p.make({ascender:G.ascender,descender:G.descender,advanceWidthMax:G.advanceWidthMax,minLeftSideBearing:G.minLeftSideBearing,minRightSideBearing:G.minRightSideBearing,xMaxExtent:G.maxLeftSideBearing+(G.xMax-G.xMin),numberOfHMetrics:a.glyphs.length}),J=r.make(a.glyphs.length),K=t.make({xAvgCharWidth:Math.round(G.advanceWidthAvg),usWeightClass:500,usWidthClass:5,usFirstCharIndex:b,usLastCharIndex:w,ulUnicodeRange1:x,ulUnicodeRange2:y,ulUnicodeRange3:z,ulUnicodeRange4:A,sTypoAscender:G.ascender,sTypoDescender:G.descender,sTypoLineGap:0,usWinAscent:G.ascender,usWinDescent:-G.descender,sxHeight:h(a,"xyvw",{yMax:0}).yMax,sCapHeight:h(a,"HIKLEFJMNTZBDPRAGOQSUVWXY",G).yMax,usBreakChar:a.hasChar(" ")?32:0}),L=q.make(a.glyphs),M=m.make(a.glyphs),N=a.familyName+" "+a.styleName,O=a.familyName.replace(/\s/g,"")+"-"+a.styleName,P=s.make({copyright:a.copyright,fontFamily:a.familyName,fontSubfamily:a.styleName,uniqueID:a.manufacturer+":"+N,fullName:N,version:a.version,postScriptName:O,trademark:a.trademark,manufacturer:a.manufacturer,designer:a.designer,description:a.description,manufacturerURL:a.manufacturerURL,designerURL:a.designerURL,license:a.license,licenseURL:a.licenseURL,preferredFamily:a.familyName,preferredSubfamily:a.styleName}),Q=u.make(),R=n.make(a.glyphs,{version:a.version,fullName:N,familyName:a.familyName,weightName:a.styleName,postScriptName:O,unitsPerEm:a.unitsPerEm}),S=[H,I,J,K,P,M,Q,R,L],T=g(S),U=T.encode(),V=e(U),W=T.fields,X=!1;for(B=0;B<W.length;B+=1)if("head table"===W[B].name){W[B].value.checkSumAdjustment=2981146554-V,X=!0;break}if(!X)throw new Error("Could not find head table with checkSum to adjust.");return T}var k=a("../check"),l=a("../table"),m=a("./cmap"),n=a("./cff"),o=a("./head"),p=a("./hhea"),q=a("./hmtx"),r=a("./maxp"),s=a("./name"),t=a("./os2"),u=a("./post");c.computeCheckSum=e,c.make=g,c.fontToTable=j},{"../check":2,"../table":11,"./cff":12,"./cmap":13,"./head":16,"./hhea":17,"./hmtx":18,"./maxp":21,"./name":22,"./os2":23,"./post":24}],26:[function(a,b,c){"use strict";function d(a){return function(){return a}}var e=a("./check"),f=32768,g=2147483648,h={},i={},j={};i.BYTE=function(a){return e.argument(a>=0&&255>=a,"Byte value should be between 0 and 255."),[a]},j.BYTE=d(1),i.CHAR=function(a){return[a.charCodeAt(0)]},j.BYTE=d(1),i.CHARARRAY=function(a){for(var b=[],c=0;c<a.length;c+=1)b.push(a.charCodeAt(c));return b},j.CHARARRAY=function(a){return a.length},i.USHORT=function(a){return[a>>8&255,255&a]},j.USHORT=d(2),i.SHORT=function(a){return a>=f&&(a=-(2*f-a)),[a>>8&255,255&a]},j.SHORT=d(2),i.UINT24=function(a){return[a>>16&255,a>>8&255,255&a]},j.UINT24=d(3),i.ULONG=function(a){return[a>>24&255,a>>16&255,a>>8&255,255&a]},j.ULONG=d(4),i.LONG=function(a){return a>=g&&(a=-(2*g-a)),[a>>24&255,a>>16&255,a>>8&255,255&a]},j.LONG=d(4),i.FIXED=i.ULONG,j.FIXED=j.ULONG,i.FWORD=i.SHORT,j.FWORD=j.SHORT,i.UFWORD=i.USHORT,j.UFWORD=j.USHORT,i.LONGDATETIME=function(){return[0,0,0,0,0,0,0,0]},j.LONGDATETIME=d(8),i.TAG=function(a){return e.argument(4===a.length,"Tag should be exactly 4 ASCII characters."),[a.charCodeAt(0),a.charCodeAt(1),a.charCodeAt(2),a.charCodeAt(3)]},j.TAG=d(4),i.Card8=i.BYTE,j.Card8=j.BYTE,i.Card16=i.USHORT,j.Card16=j.USHORT,i.OffSize=i.BYTE,j.OffSize=j.BYTE,i.SID=i.USHORT,j.SID=j.USHORT,i.NUMBER=function(a){return a>=-107&&107>=a?[a+139]:a>=108&&1131>=a?(a-=108,[(a>>8)+247,255&a]):a>=-1131&&-108>=a?(a=-a-108,[(a>>8)+251,255&a]):a>=-32768&&32767>=a?i.NUMBER16(a):i.NUMBER32(a)},j.NUMBER=function(a){return i.NUMBER(a).length},i.NUMBER16=function(a){return[28,a>>8&255,255&a]},j.NUMBER16=d(2),i.NUMBER32=function(a){return[29,a>>24&255,a>>16&255,a>>8&255,255&a]},j.NUMBER32=d(4),i.REAL=function(a){var b=a.toString(),c=/\.(\d*?)(?:9{5,20}|0{5,20})\d{0,2}(?:e(.+)|$)/.exec(b);if(c){var d=parseFloat("1e"+((c[2]?+c[2]:0)+c[1].length));b=(Math.round(a*d)/d).toString()}var e,f,g="";for(e=0,f=b.length;f>e;e+=1){var h=b[e];g+="e"===h?"-"===b[++e]?"c":"b":"."===h?"a":"-"===h?"e":h}g+=1&g.length?"f":"ff";var i=[30];for(e=0,f=g.length;f>e;e+=2)i.push(parseInt(g.substr(e,2),16));return i},j.REAL=function(a){return i.REAL(a).length},i.NAME=i.CHARARRAY,j.NAME=j.CHARARRAY,i.STRING=i.CHARARRAY,j.STRING=j.CHARARRAY,i.UTF16=function(a){for(var b=[],c=0;c<a.length;c+=1)b.push(0),b.push(a.charCodeAt(c));return b},j.UTF16=function(a){return 2*a.length},i.INDEX=function(a){var b,c=1,d=[c],e=[],f=0;for(b=0;b<a.length;b+=1){var g=i.OBJECT(a[b]);Array.prototype.push.apply(e,g),f+=g.length,c+=g.length,d.push(c)}if(0===e.length)return[0,0];var h=[],j=1+Math.floor(Math.log(f)/Math.log(2))/8|0,k=[void 0,i.BYTE,i.USHORT,i.UINT24,i.ULONG][j];for(b=0;b<d.length;b+=1){var l=k(d[b]);Array.prototype.push.apply(h,l)}return Array.prototype.concat(i.Card16(a.length),i.OffSize(j),h,e)},j.INDEX=function(a){return i.INDEX(a).length},i.DICT=function(a){for(var b=[],c=Object.keys(a),d=c.length,e=0;d>e;e+=1){var f=parseInt(c[e],0),g=a[f];b=b.concat(i.OPERAND(g.value,g.type)),b=b.concat(i.OPERATOR(f))}return b},j.DICT=function(a){return i.DICT(a).length},i.OPERATOR=function(a){return 1200>a?[a]:[12,a-1200]},i.OPERAND=function(a,b){var c=[];if(Array.isArray(b))for(var d=0;d<b.length;d+=1)e.argument(a.length===b.length,"Not enough arguments given for type"+b),c=c.concat(i.OPERAND(a[d],b[d]));else if("SID"===b)c=c.concat(i.NUMBER(a));else if("offset"===b)c=c.concat(i.NUMBER32(a));else if("number"===b)c=c.concat(i.NUMBER(a));else{if("real"!==b)throw new Error("Unknown operand type "+b);c=c.concat(i.REAL(a))}return c},i.OP=i.BYTE,j.OP=j.BYTE;var k="function"==typeof WeakMap&&new WeakMap;i.CHARSTRING=function(a){if(k&&k.has(a))return k.get(a);for(var b=[],c=a.length,d=0;c>d;d+=1){var e=a[d];b=b.concat(i[e.type](e.value))}return k&&k.set(a,b),b},j.CHARSTRING=function(a){return i.CHARSTRING(a).length},i.OBJECT=function(a){var b=i[a.type];return e.argument(void 0!==b,"No encoding function for type "+a.type),b(a.value)},i.TABLE=function(a){for(var b=[],c=a.fields.length,d=0;c>d;d+=1){var f=a.fields[d],g=i[f.type];e.argument(void 0!==g,"No encoding function for field type "+f.type);var h=a[f.name];void 0===h&&(h=f.value);var j=g(h);b=b.concat(j)}return b},i.LITERAL=function(a){return a},j.LITERAL=function(a){return a.length},c.decode=h,c.encode=i,c.sizeOf=j},{"./check":2}],27:[function(_dereq_,module,exports){!function(a,b,c){"undefined"!=typeof module&&module.exports?module.exports=c():"function"==typeof define&&define.amd?define(c):b[a]=c()}("reqwest",this,function(){function succeed(a){var b=protocolRe.exec(a.url);return b=b&&b[1]||window.location.protocol,httpsRe.test(b)?twoHundo.test(a.request.status):!!a.request.response}function handleReadyState(a,b,c){return function(){return a._aborted?c(a.request):a._timedOut?c(a.request,"Request is aborted: timeout"):void(a.request&&4==a.request[readyState]&&(a.request.onreadystatechange=noop,succeed(a)?b(a.request):c(a.request)))}}function setHeaders(a,b){var c,d=b.headers||{};d.Accept=d.Accept||defaultHeaders.accept[b.type]||defaultHeaders.accept["*"];var e="function"==typeof FormData&&b.data instanceof FormData;b.crossOrigin||d[requestedWith]||(d[requestedWith]=defaultHeaders.requestedWith),d[contentType]||e||(d[contentType]=b.contentType||defaultHeaders.contentType);for(c in d)d.hasOwnProperty(c)&&"setRequestHeader"in a&&a.setRequestHeader(c,d[c])}function setCredentials(a,b){"undefined"!=typeof b.withCredentials&&"undefined"!=typeof a.withCredentials&&(a.withCredentials=!!b.withCredentials)}function generalCallback(a){lastValue=a}function urlappend(a,b){return a+(/\?/.test(a)?"&":"?")+b}function handleJsonp(a,b,c,d){var e=uniqid++,f=a.jsonpCallback||"callback",g=a.jsonpCallbackName||reqwest.getcallbackPrefix(e),h=new RegExp("((^|\\?|&)"+f+")=([^&]+)"),i=d.match(h),j=doc.createElement("script"),k=0,l=-1!==navigator.userAgent.indexOf("MSIE 10.0");return i?"?"===i[3]?d=d.replace(h,"$1="+g):g=i[3]:d=urlappend(d,f+"="+g),win[g]=generalCallback,j.type="text/javascript",j.src=d,j.async=!0,"undefined"==typeof j.onreadystatechange||l||(j.htmlFor=j.id="_reqwest_"+e),j.onload=j.onreadystatechange=function(){return j[readyState]&&"complete"!==j[readyState]&&"loaded"!==j[readyState]||k?!1:(j.onload=j.onreadystatechange=null,j.onclick&&j.onclick(),b(lastValue),lastValue=void 0,head.removeChild(j),void(k=1))},head.appendChild(j),{abort:function(){j.onload=j.onreadystatechange=null,c({},"Request is aborted: timeout",{}),lastValue=void 0,head.removeChild(j),k=1}}}function getRequest(a,b){var c,d=this.o,e=(d.method||"GET").toUpperCase(),f="string"==typeof d?d:d.url,g=d.processData!==!1&&d.data&&"string"!=typeof d.data?reqwest.toQueryString(d.data):d.data||null,h=!1;return"jsonp"!=d.type&&"GET"!=e||!g||(f=urlappend(f,g),g=null),"jsonp"==d.type?handleJsonp(d,a,b,f):(c=d.xhr&&d.xhr(d)||xhr(d),c.open(e,f,d.async===!1?!1:!0),setHeaders(c,d),setCredentials(c,d),win[xDomainRequest]&&c instanceof win[xDomainRequest]?(c.onload=a,c.onerror=b,c.onprogress=function(){},h=!0):c.onreadystatechange=handleReadyState(this,a,b),d.before&&d.before(c),h?setTimeout(function(){c.send(g)},200):c.send(g),c)}function Reqwest(a,b){this.o=a,this.fn=b,init.apply(this,arguments)}function setType(a){return a.match("json")?"json":a.match("javascript")?"js":a.match("text")?"html":a.match("xml")?"xml":void 0}function init(o,fn){function complete(a){for(o.timeout&&clearTimeout(self.timeout),self.timeout=null;self._completeHandlers.length>0;)self._completeHandlers.shift()(a)}function success(resp){var type=o.type||resp&&setType(resp.getResponseHeader("Content-Type"));resp="jsonp"!==type?self.request:resp;var filteredResponse=globalSetupOptions.dataFilter(resp.responseText,type),r=filteredResponse;try{resp.responseText=r}catch(e){}if(r)switch(type){case"json":try{resp=win.JSON?win.JSON.parse(r):eval("("+r+")")}catch(err){return error(resp,"Could not parse JSON in response",err)}break;case"js":resp=eval(r);break;case"html":resp=r;break;case"xml":resp=resp.responseXML&&resp.responseXML.parseError&&resp.responseXML.parseError.errorCode&&resp.responseXML.parseError.reason?null:resp.responseXML}for(self._responseArgs.resp=resp,self._fulfilled=!0,fn(resp),self._successHandler(resp);self._fulfillmentHandlers.length>0;)resp=self._fulfillmentHandlers.shift()(resp);complete(resp)}function timedOut(){self._timedOut=!0,self.request.abort()}function error(a,b,c){for(a=self.request,self._responseArgs.resp=a,self._responseArgs.msg=b,self._responseArgs.t=c,self._erred=!0;self._errorHandlers.length>0;)self._errorHandlers.shift()(a,b,c);complete(a)}this.url="string"==typeof o?o:o.url,this.timeout=null,this._fulfilled=!1,this._successHandler=function(){},this._fulfillmentHandlers=[],this._errorHandlers=[],this._completeHandlers=[],this._erred=!1,this._responseArgs={};var self=this;fn=fn||function(){},o.timeout&&(this.timeout=setTimeout(function(){timedOut()},o.timeout)),o.success&&(this._successHandler=function(){o.success.apply(o,arguments)}),o.error&&this._errorHandlers.push(function(){o.error.apply(o,arguments)}),o.complete&&this._completeHandlers.push(function(){o.complete.apply(o,arguments)}),this.request=getRequest.call(this,success,error)}function reqwest(a,b){return new Reqwest(a,b)}function normalize(a){return a?a.replace(/\r?\n/g,"\r\n"):""}function serial(a,b){var c,d,e,f,g=a.name,h=a.tagName.toLowerCase(),i=function(a){a&&!a.disabled&&b(g,normalize(a.attributes.value&&a.attributes.value.specified?a.value:a.text))};if(!a.disabled&&g)switch(h){case"input":/reset|button|image|file/i.test(a.type)||(c=/checkbox/i.test(a.type),d=/radio/i.test(a.type),e=a.value,(!(c||d)||a.checked)&&b(g,normalize(c&&""===e?"on":e)));break;case"textarea":b(g,normalize(a.value));break;case"select":if("select-one"===a.type.toLowerCase())i(a.selectedIndex>=0?a.options[a.selectedIndex]:null);else for(f=0;a.length&&f<a.length;f++)a.options[f].selected&&i(a.options[f])}}function eachFormElement(){var a,b,c=this,d=function(a,b){var d,e,f;for(d=0;d<b.length;d++)for(f=a[byTag](b[d]),e=0;e<f.length;e++)serial(f[e],c)};for(b=0;b<arguments.length;b++)a=arguments[b],/input|select|textarea/i.test(a.tagName)&&serial(a,c),d(a,["input","select","textarea"])}function serializeQueryString(){return reqwest.toQueryString(reqwest.serializeArray.apply(null,arguments))}function serializeHash(){var a={};return eachFormElement.apply(function(b,c){b in a?(a[b]&&!isArray(a[b])&&(a[b]=[a[b]]),a[b].push(c)):a[b]=c},arguments),a}function buildParams(a,b,c,d){var e,f,g,h=/\[\]$/;if(isArray(b))for(f=0;b&&f<b.length;f++)g=b[f],c||h.test(a)?d(a,g):buildParams(a+"["+("object"==typeof g?f:"")+"]",g,c,d);else if(b&&"[object Object]"===b.toString())for(e in b)buildParams(a+"["+e+"]",b[e],c,d);else d(a,b)}var win=window,doc=document,httpsRe=/^http/,protocolRe=/(^\w+):\/\//,twoHundo=/^(20\d|1223)$/,byTag="getElementsByTagName",readyState="readyState",contentType="Content-Type",requestedWith="X-Requested-With",head=doc[byTag]("head")[0],uniqid=0,callbackPrefix="reqwest_"+ +new Date,lastValue,xmlHttpRequest="XMLHttpRequest",xDomainRequest="XDomainRequest",noop=function(){},isArray="function"==typeof Array.isArray?Array.isArray:function(a){return a instanceof Array},defaultHeaders={contentType:"application/x-www-form-urlencoded",requestedWith:xmlHttpRequest,accept:{"*":"text/javascript, text/html, application/xml, text/xml, */*",xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript",js:"application/javascript, text/javascript"}},xhr=function(a){if(a.crossOrigin===!0){var b=win[xmlHttpRequest]?new XMLHttpRequest:null;if(b&&"withCredentials"in b)return b;if(win[xDomainRequest])return new XDomainRequest;throw new Error("Browser does not support cross-origin requests")}return win[xmlHttpRequest]?new XMLHttpRequest:new ActiveXObject("Microsoft.XMLHTTP")},globalSetupOptions={dataFilter:function(a){return a}};return Reqwest.prototype={abort:function(){this._aborted=!0,this.request.abort()},retry:function(){init.call(this,this.o,this.fn)},then:function(a,b){return a=a||function(){},b=b||function(){},this._fulfilled?this._responseArgs.resp=a(this._responseArgs.resp):this._erred?b(this._responseArgs.resp,this._responseArgs.msg,this._responseArgs.t):(this._fulfillmentHandlers.push(a),this._errorHandlers.push(b)),this},always:function(a){return this._fulfilled||this._erred?a(this._responseArgs.resp):this._completeHandlers.push(a),this},fail:function(a){return this._erred?a(this._responseArgs.resp,this._responseArgs.msg,this._responseArgs.t):this._errorHandlers.push(a),this},"catch":function(a){return this.fail(a)}},reqwest.serializeArray=function(){var a=[];return eachFormElement.apply(function(b,c){a.push({name:b,value:c})},arguments),a},reqwest.serialize=function(){if(0===arguments.length)return"";var a,b,c=Array.prototype.slice.call(arguments,0);return a=c.pop(),a&&a.nodeType&&c.push(a)&&(a=null),a&&(a=a.type),b="map"==a?serializeHash:"array"==a?reqwest.serializeArray:serializeQueryString,b.apply(null,c)},reqwest.toQueryString=function(a,b){var c,d,e=b||!1,f=[],g=encodeURIComponent,h=function(a,b){b="function"==typeof b?b():null==b?"":b,f[f.length]=g(a)+"="+g(b)};if(isArray(a))for(d=0;a&&d<a.length;d++)h(a[d].name,a[d].value);else for(c in a)a.hasOwnProperty(c)&&buildParams(c,a[c],e,h);return f.join("&").replace(/%20/g,"+")},reqwest.getcallbackPrefix=function(){return callbackPrefix},reqwest.compat=function(a,b){return a&&(a.type&&(a.method=a.type)&&delete a.type,a.dataType&&(a.type=a.dataType),a.jsonpCallback&&(a.jsonpCallbackName=a.jsonpCallback)&&delete a.jsonpCallback,a.jsonp&&(a.jsonpCallback=a.jsonp)),new Reqwest(a,b)},reqwest.ajaxSetup=function(a){a=a||{};for(var b in a)globalSetupOptions[b]=a[b]},reqwest})},{}],28:[function(a,b,c){"use strict";var d=a("./core/core");a("./color/p5.Color"),a("./core/p5.Element"),a("./typography/p5.Font"),a("./core/p5.Graphics"),a("./core/p5.Renderer2D"),a("./image/p5.Image"),a("./math/p5.Vector"),a("./io/p5.TableRow"),a("./io/p5.Table"),a("./io/p5.XML"),a("./color/creating_reading"),a("./color/setting"),a("./core/constants"),a("./utilities/conversion"),a("./utilities/array_functions"),a("./utilities/string_functions"),a("./core/environment"),a("./image/image"),a("./image/loading_displaying"),a("./image/pixels"),a("./io/files"),a("./events/keyboard"),a("./events/acceleration"),a("./events/mouse"),a("./utilities/time_date"),a("./events/touch"),a("./math/math"),a("./math/calculation"),a("./math/random"),a("./math/noise"),a("./math/trigonometry"),a("./core/rendering"),a("./core/2d_primitives"),a("./core/attributes"),a("./core/curves"),a("./core/vertex"),a("./core/structure"),a("./core/transform"),a("./typography/attributes"),a("./typography/loading_displaying"),a("./webgl/p5.RendererGL"),a("./webgl/p5.Geometry"),a("./webgl/p5.RendererGL.Retained"),a("./webgl/p5.RendererGL.Immediate"),a("./webgl/primitives"),a("./webgl/loading"),a("./webgl/p5.Matrix"),a("./webgl/material"),a("./webgl/light"),a("./webgl/shader"),a("./webgl/camera"),a("./webgl/interaction");var e=function(){window.PHANTOMJS||window.mocha||(window.setup&&"function"==typeof window.setup||window.draw&&"function"==typeof window.draw)&&!d.instance&&new d};"complete"===document.readyState?e():window.addEventListener("load",e,!1),b.exports=d},{"./color/creating_reading":30,"./color/p5.Color":31,"./color/setting":32,"./core/2d_primitives":33,"./core/attributes":34,"./core/constants":36,"./core/core":37,"./core/curves":38,"./core/environment":39,"./core/p5.Element":41,"./core/p5.Graphics":42,"./core/p5.Renderer2D":44,"./core/rendering":45,"./core/structure":47,"./core/transform":48,"./core/vertex":49,"./events/acceleration":50,"./events/keyboard":51,"./events/mouse":52,"./events/touch":53,"./image/image":55,"./image/loading_displaying":56,"./image/p5.Image":57,"./image/pixels":58,"./io/files":59,"./io/p5.Table":60,"./io/p5.TableRow":61,"./io/p5.XML":62,"./math/calculation":63,"./math/math":64,"./math/noise":65,"./math/p5.Vector":66,"./math/random":68,"./math/trigonometry":69,"./typography/attributes":70,"./typography/loading_displaying":71,"./typography/p5.Font":72,"./utilities/array_functions":73,"./utilities/conversion":74,"./utilities/string_functions":75,"./utilities/time_date":76,"./webgl/camera":77,"./webgl/interaction":78,"./webgl/light":79,"./webgl/loading":80,"./webgl/material":81,"./webgl/p5.Geometry":82,"./webgl/p5.Matrix":83,"./webgl/p5.RendererGL":86,"./webgl/p5.RendererGL.Immediate":84,"./webgl/p5.RendererGL.Retained":85,"./webgl/primitives":87,"./webgl/shader":88}],29:[function(a,b,c){"use strict";var d=a("../core/core");d.ColorConversion={},d.ColorConversion._hsbaToHSLA=function(a){var b=a[0],c=a[1],d=a[2],e=(2-c)*d/2;return 0!==e&&(1===e?c=0:.5>e?c/=2-c:c=c*d/(2-2*e)),[b,c,e,a[3]]},d.ColorConversion._hsbaToRGBA=function(a){var b=6*a[0],c=a[1],d=a[2],e=[];if(0===c)e=[d,d,d,a[3]];else{var f,g,h,i=Math.floor(b),j=d*(1-c),k=d*(1-c*(b-i)),l=d*(1-c*(1+i-b));1===i?(f=k,g=d,h=j):2===i?(f=j,g=d,h=l):3===i?(f=j,g=k,h=d):4===i?(f=l,g=j,h=d):5===i?(f=d,g=j,h=k):(f=d,g=l,h=j),e=[f,g,h,a[3]]}return e},d.ColorConversion._hslaToHSBA=function(a){var b,c=a[0],d=a[1],e=a[2];return b=.5>e?(1+d)*e:e+d-e*d,d=2*(b-e)/b,[c,d,b,a[3]]},d.ColorConversion._hslaToRGBA=function(a){var b=6*a[0],c=a[1],d=a[2],e=[];if(0===c)e=[d,d,d,a[3]];else{var f;f=.5>d?(1+c)*d:d+c-d*c;var g=2*d-f,h=function(a,b,c){return 0>a?a+=6:a>=6&&(a-=6),1>a?b+(c-b)*a:3>a?c:4>a?b+(c-b)*(4-a):b};e=[h(b+2,g,f),h(b,g,f),h(b-2,g,f),a[3]]}return e},d.ColorConversion._rgbaToHSBA=function(a){var b,c,d=a[0],e=a[1],f=a[2],g=Math.max(d,e,f),h=g-Math.min(d,e,f);return 0===h?(b=0,c=0):(c=h/g,d===g?b=(e-f)/h:e===g?b=2+(f-d)/h:f===g&&(b=4+(d-e)/h),0>b?b+=6:b>=6&&(b-=6)),[b/6,c,g,a[3]]},d.ColorConversion._rgbaToHSLA=function(a){var b,c,d=a[0],e=a[1],f=a[2],g=Math.max(d,e,f),h=Math.min(d,e,f),i=g+h,j=g-h;return 0===j?(b=0,c=0):(c=1>i?j/i:j/(2-j),d===g?b=(e-f)/j:e===g?b=2+(f-d)/j:f===g&&(b=4+(d-e)/j),0>b?b+=6:b>=6&&(b-=6)),[b/6,c,i/2,a[3]]},b.exports=d.ColorConversion},{"../core/core":37}],30:[function(a,b,c){"use strict";var d=a("../core/core"),e=a("../core/constants");a("./p5.Color"),d.prototype.alpha=function(a){if(a instanceof d.Color||a instanceof Array)return this.color(a)._getAlpha();throw new Error("Needs p5.Color or pixel array as argument.")},d.prototype.blue=function(a){if(a instanceof d.Color||a instanceof Array)return this.color(a)._getBlue();throw new Error("Needs p5.Color or pixel array as argument.")},d.prototype.brightness=function(a){if(a instanceof d.Color||a instanceof Array)return this.color(a)._getBrightness();throw new Error("Needs p5.Color or pixel array as argument.")},d.prototype.color=function(){return arguments[0]instanceof d.Color?arguments[0]:arguments[0]instanceof Array?this instanceof d.Renderer?new d.Color(this,arguments[0]):new d.Color(this._renderer,arguments[0]):this instanceof d.Renderer?new d.Color(this,arguments):new d.Color(this._renderer,arguments)},d.prototype.green=function(a){if(a instanceof d.Color||a instanceof Array)return this.color(a)._getGreen();throw new Error("Needs p5.Color or pixel array as argument.")},d.prototype.hue=function(a){if(a instanceof d.Color||a instanceof Array)return this.color(a)._getHue();throw new Error("Needs p5.Color or pixel array as argument.")},d.prototype.lerpColor=function(a,b,c){var d,f,g,h,i,j,k=this._renderer._colorMode,l=this._renderer._colorMaxes;if(k===e.RGB)i=a.levels.map(function(a){return a/255}),j=b.levels.map(function(a){return a/255});else if(k===e.HSB)a._getBrightness(),b._getBrightness(),i=a.hsba,j=b.hsba;else{if(k!==e.HSL)throw new Error(k+"cannot be used for interpolation.");a._getLightness(),b._getLightness(),i=a.hsla,j=b.hsla}return c=Math.max(Math.min(c,1),0),d=this.lerp(i[0],j[0],c),f=this.lerp(i[1],j[1],c),g=this.lerp(i[2],j[2],c),h=this.lerp(i[3],j[3],c),d*=l[k][0],f*=l[k][1],g*=l[k][2],h*=l[k][3],this.color(d,f,g,h)},d.prototype.lightness=function(a){if(a instanceof d.Color||a instanceof Array)return this.color(a)._getLightness();throw new Error("Needs p5.Color or pixel array as argument.")},d.prototype.red=function(a){if(a instanceof d.Color||a instanceof Array)return this.color(a)._getRed();throw new Error("Needs p5.Color or pixel array as argument.")},d.prototype.saturation=function(a){if(a instanceof d.Color||a instanceof Array)return this.color(a)._getSaturation();throw new Error("Needs p5.Color or pixel array as argument.")},b.exports=d},{"../core/constants":36,"../core/core":37,"./p5.Color":31}],31:[function(a,b,c){var d=a("../core/core"),e=a("../core/constants"),f=a("./color_conversion");d.Color=function(a,b){if(this.mode=a._colorMode,this.maxes=a._colorMaxes,this.mode!==e.RGB&&this.mode!==e.HSL&&this.mode!==e.HSB)throw new Error(this.mode+" is an invalid colorMode.");return this._array=d.Color._parseInputs.apply(a,b),this.levels=this._array.map(function(a){return Math.round(255*a)}),this},d.Color.prototype.toString=function(){var a=this.levels,b=this._array[3];return"rgba("+a[0]+","+a[1]+","+a[2]+","+b+")"},d.Color.prototype._getAlpha=function(){return this._array[3]*this.maxes[this.mode][3]},d.Color.prototype._getBlue=function(){return this._array[2]*this.maxes[e.RGB][2]},d.Color.prototype._getBrightness=function(){return this.hsba||(this.hsba=f._rgbaToHSBA(this._array)),this.hsba[2]*this.maxes[e.HSB][2]},d.Color.prototype._getGreen=function(){return this._array[1]*this.maxes[e.RGB][1]},d.Color.prototype._getHue=function(){return this.mode===e.HSB?(this.hsba||(this.hsba=f._rgbaToHSBA(this._array)),this.hsba[0]*this.maxes[e.HSB][0]):(this.hsla||(this.hsla=f._rgbaToHSLA(this._array)),this.hsla[0]*this.maxes[e.HSL][0])},d.Color.prototype._getLightness=function(){return this.hsla||(this.hsla=f._rgbaToHSLA(this._array)),this.hsla[2]*this.maxes[e.HSL][2]},d.Color.prototype._getRed=function(){return this._array[0]*this.maxes[e.RGB][0]},d.Color.prototype._getSaturation=function(){return this.mode===e.HSB?(this.hsba||(this.hsba=f._rgbaToHSBA(this._array)),this.hsba[1]*this.maxes[e.HSB][1]):(this.hsla||(this.hsla=f._rgbaToHSLA(this._array)),this.hsla[1]*this.maxes[e.HSL][1])};var g={aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyan:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgreen:"#006400",darkgrey:"#a9a9a9",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkslategrey:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc",ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",green:"#008000",greenyellow:"#adff2f",grey:"#808080",honeydew:"#f0fff0",hotpink:"#ff69b4",indianred:"#cd5c5c",indigo:"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgray:"#d3d3d3",lightgreen:"#90ee90",lightgrey:"#d3d3d3",lightpink:"#ffb6c1",lightsalmon:"#ffa07a",lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370db",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#db7093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57",seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",slategrey:"#708090",snow:"#fffafa",springgreen:"#00ff7f",steelblue:"#4682b4",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",tomato:"#ff6347",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32"},h=/\s*/,i=/(\d{1,3})/,j=/((?:\d+(?:\.\d+)?)|(?:\.\d+))/,k=new RegExp(j.source+"%"),l={HEX3:/^#([a-f0-9])([a-f0-9])([a-f0-9])$/i,HEX6:/^#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})$/i,RGB:new RegExp(["^rgb\\(",i.source,",",i.source,",",i.source,"\\)$"].join(h.source),"i"),RGB_PERCENT:new RegExp(["^rgb\\(",k.source,",",k.source,",",k.source,"\\)$"].join(h.source),"i"),RGBA:new RegExp(["^rgba\\(",i.source,",",i.source,",",i.source,",",j.source,"\\)$"].join(h.source),"i"), +RGBA_PERCENT:new RegExp(["^rgba\\(",k.source,",",k.source,",",k.source,",",j.source,"\\)$"].join(h.source),"i"),HSL:new RegExp(["^hsl\\(",i.source,",",k.source,",",k.source,"\\)$"].join(h.source),"i"),HSLA:new RegExp(["^hsla\\(",i.source,",",k.source,",",k.source,",",j.source,"\\)$"].join(h.source),"i"),HSB:new RegExp(["^hsb\\(",i.source,",",k.source,",",k.source,"\\)$"].join(h.source),"i"),HSBA:new RegExp(["^hsba\\(",i.source,",",k.source,",",k.source,",",j.source,"\\)$"].join(h.source),"i")};d.Color._parseInputs=function(){var a=arguments.length,b=this._colorMode,c=this._colorMaxes,h=[];if(a>=3)return h[0]=arguments[0]/c[b][0],h[1]=arguments[1]/c[b][1],h[2]=arguments[2]/c[b][2],"number"==typeof arguments[3]?h[3]=arguments[3]/c[b][3]:h[3]=1,h=h.map(function(a){return Math.max(Math.min(a,1),0)}),b===e.HSL?f._hslaToRGBA(h):b===e.HSB?f._hsbaToRGBA(h):h;if(1===a&&"string"==typeof arguments[0]){var i=arguments[0].trim().toLowerCase();if(g[i])return d.Color._parseInputs.apply(this,[g[i]]);if(l.HEX3.test(i))return h=l.HEX3.exec(i).slice(1).map(function(a){return parseInt(a+a,16)/255}),h[3]=1,h;if(l.HEX6.test(i))return h=l.HEX6.exec(i).slice(1).map(function(a){return parseInt(a,16)/255}),h[3]=1,h;if(l.RGB.test(i))return h=l.RGB.exec(i).slice(1).map(function(a){return a/255}),h[3]=1,h;if(l.RGB_PERCENT.test(i))return h=l.RGB_PERCENT.exec(i).slice(1).map(function(a){return parseFloat(a)/100}),h[3]=1,h;if(l.RGBA.test(i))return h=l.RGBA.exec(i).slice(1).map(function(a,b){return 3===b?parseFloat(a):a/255});if(l.RGBA_PERCENT.test(i))return h=l.RGBA_PERCENT.exec(i).slice(1).map(function(a,b){return 3===b?parseFloat(a):parseFloat(a)/100});if(l.HSL.test(i)?(h=l.HSL.exec(i).slice(1).map(function(a,b){return 0===b?parseInt(a,10)/360:parseInt(a,10)/100}),h[3]=1):l.HSLA.test(i)&&(h=l.HSLA.exec(i).slice(1).map(function(a,b){return 0===b?parseInt(a,10)/360:3===b?parseFloat(a):parseInt(a,10)/100})),h.length)return f._hslaToRGBA(h);if(l.HSB.test(i)?(h=l.HSB.exec(i).slice(1).map(function(a,b){return 0===b?parseInt(a,10)/360:parseInt(a,10)/100}),h[3]=1):l.HSBA.test(i)&&(h=l.HSBA.exec(i).slice(1).map(function(a,b){return 0===b?parseInt(a,10)/360:3===b?parseFloat(a):parseInt(a,10)/100})),h.length)return f._hsbaToRGBA(h);h=[1,1,1,1]}else{if(1!==a&&2!==a||"number"!=typeof arguments[0])throw new Error(arguments+"is not a valid color representation.");h[0]=arguments[0]/c[b][2],h[1]=arguments[0]/c[b][2],h[2]=arguments[0]/c[b][2],"number"==typeof arguments[1]?h[3]=arguments[1]/c[b][3]:h[3]=1,h=h.map(function(a){return Math.max(Math.min(a,1),0)})}return h},b.exports=d.Color},{"../core/constants":36,"../core/core":37,"./color_conversion":29}],32:[function(a,b,c){"use strict";var d=a("../core/core"),e=a("../core/constants");a("./p5.Color"),d.prototype.background=function(){return arguments[0]instanceof d.Image?this.image(arguments[0],0,0,this.width,this.height):this._renderer.background.apply(this._renderer,arguments),this},d.prototype.clear=function(){return this._renderer.clear(),this},d.prototype.colorMode=function(){if(arguments[0]===e.RGB||arguments[0]===e.HSB||arguments[0]===e.HSL){this._renderer._colorMode=arguments[0];var a=this._renderer._colorMaxes[this._renderer._colorMode];2===arguments.length?(a[0]=arguments[1],a[1]=arguments[1],a[2]=arguments[1],a[3]=arguments[1]):4===arguments.length?(a[0]=arguments[1],a[1]=arguments[2],a[2]=arguments[3]):5===arguments.length&&(a[0]=arguments[1],a[1]=arguments[2],a[2]=arguments[3],a[3]=arguments[4])}return this},d.prototype.fill=function(){return this._renderer._setProperty("_fillSet",!0),this._renderer._setProperty("_doFill",!0),this._renderer.fill.apply(this._renderer,arguments),this},d.prototype.noFill=function(){return this._renderer._setProperty("_doFill",!1),this},d.prototype.noStroke=function(){return this._renderer._setProperty("_doStroke",!1),this},d.prototype.stroke=function(){return this._renderer._setProperty("_strokeSet",!0),this._renderer._setProperty("_doStroke",!0),this._renderer.stroke.apply(this._renderer,arguments),this},b.exports=d},{"../core/constants":36,"../core/core":37,"./p5.Color":31}],33:[function(a,b,c){"use strict";var d=a("./core"),e=a("./constants"),f=a("./canvas");a("./error_helpers"),d.prototype.arc=function(a,b,c,d,f,g,h){for(var i=new Array(arguments.length),j=0;j<i.length;++j)i[j]=arguments[j];if(!this._renderer._doStroke&&!this._renderer._doFill)return this;for(this._angleMode===e.DEGREES&&(f=this.radians(f),g=this.radians(g));0>f;)f+=e.TWO_PI;for(;0>g;)g+=e.TWO_PI;return f%=e.TWO_PI,g%=e.TWO_PI,g===f&&(g+=e.TWO_PI),f=f<=e.HALF_PI?Math.atan(c/d*Math.tan(f)):f>e.HALF_PI&&f<=3*e.HALF_PI?Math.atan(c/d*Math.tan(f))+e.PI:Math.atan(c/d*Math.tan(f))+e.TWO_PI,g=g<=e.HALF_PI?Math.atan(c/d*Math.tan(g)):g>e.HALF_PI&&g<=3*e.HALF_PI?Math.atan(c/d*Math.tan(g))+e.PI:Math.atan(c/d*Math.tan(g))+e.TWO_PI,f>g&&(g+=e.TWO_PI),c=Math.abs(c),d=Math.abs(d),this._renderer.arc(a,b,c,d,f,g,h),this},d.prototype.ellipse=function(){for(var a=new Array(arguments.length),b=0;b<a.length;++b)a[b]=arguments[b];if(3===a.length&&a.push(a[2]),a[2]<0&&(a[2]=Math.abs(a[2])),a[3]<0&&(a[3]=Math.abs(a[3])),!this._renderer._doStroke&&!this._renderer._doFill)return this;var c=f.modeAdjust(a[0],a[1],a[2],a[3],this._renderer._ellipseMode);return a[0]=c.x,a[1]=c.y,a[2]=c.w,a[3]=c.h,this._renderer.ellipse(a),this},d.prototype.line=function(){if(!this._renderer._doStroke)return this;for(var a=new Array(arguments.length),b=0;b<a.length;++b)a[b]=arguments[b];return this._renderer.isP3D?this._renderer.line(a[0],a[1],a[2],a[3],a[4],a[5]):this._renderer.line(a[0],a[1],a[2],a[3]),this},d.prototype.point=function(){if(!this._renderer._doStroke)return this;for(var a=new Array(arguments.length),b=0;b<a.length;++b)a[b]=arguments[b];return this._renderer.isP3D?this._renderer.point(a[0],a[1],a[2]):this._renderer.point(a[0],a[1]),this},d.prototype.quad=function(){if(!this._renderer._doStroke&&!this._renderer._doFill)return this;for(var a=new Array(arguments.length),b=0;b<a.length;++b)a[b]=arguments[b];return this._renderer.isP3D?this._renderer.quad(a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8],a[9],a[10],a[11]):this._renderer.quad(a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7]),this},d.prototype.rect=function(){for(var a=new Array(arguments.length),b=0;b<a.length;++b)a[b]=arguments[b];if(this._renderer._doStroke||this._renderer._doFill){var c=f.modeAdjust(a[0],a[1],a[2],a[3],this._renderer._rectMode);return a[0]=c.x,a[1]=c.y,a[2]=c.w,a[3]=c.h,this._renderer.rect(a),this}},d.prototype.triangle=function(){if(!this._renderer._doStroke&&!this._renderer._doFill)return this;for(var a=new Array(arguments.length),b=0;b<a.length;++b)a[b]=arguments[b];return this._renderer.triangle(a),this},b.exports=d},{"./canvas":35,"./constants":36,"./core":37,"./error_helpers":40}],34:[function(a,b,c){"use strict";var d=a("./core"),e=a("./constants");d.prototype.ellipseMode=function(a){return(a===e.CORNER||a===e.CORNERS||a===e.RADIUS||a===e.CENTER)&&(this._renderer._ellipseMode=a),this},d.prototype.noSmooth=function(){return this._renderer.noSmooth(),this},d.prototype.rectMode=function(a){return(a===e.CORNER||a===e.CORNERS||a===e.RADIUS||a===e.CENTER)&&(this._renderer._rectMode=a),this},d.prototype.smooth=function(){return this._renderer.smooth(),this},d.prototype.strokeCap=function(a){return(a===e.ROUND||a===e.SQUARE||a===e.PROJECT)&&this._renderer.strokeCap(a),this},d.prototype.strokeJoin=function(a){return(a===e.ROUND||a===e.BEVEL||a===e.MITER)&&this._renderer.strokeJoin(a),this},d.prototype.strokeWeight=function(a){return this._renderer.strokeWeight(a),this},b.exports=d},{"./constants":36,"./core":37}],35:[function(a,b,c){var d=a("./constants");b.exports={modeAdjust:function(a,b,c,e,f){return f===d.CORNER?{x:a,y:b,w:c,h:e}:f===d.CORNERS?{x:a,y:b,w:c-a,h:e-b}:f===d.RADIUS?{x:a-c,y:b-e,w:2*c,h:2*e}:f===d.CENTER?{x:a-.5*c,y:b-.5*e,w:c,h:e}:void 0},arcModeAdjust:function(a,b,c,e,f){return f===d.CORNER?{x:a+.5*c,y:b+.5*e,w:c,h:e}:f===d.CORNERS?{x:a,y:b,w:c+a,h:e+b}:f===d.RADIUS?{x:a,y:b,w:2*c,h:2*e}:f===d.CENTER?{x:a,y:b,w:c,h:e}:void 0}}},{"./constants":36}],36:[function(a,b,c){var d=Math.PI;b.exports={P2D:"p2d",WEBGL:"webgl",ARROW:"default",CROSS:"crosshair",HAND:"pointer",MOVE:"move",TEXT:"text",WAIT:"wait",HALF_PI:d/2,PI:d,QUARTER_PI:d/4,TAU:2*d,TWO_PI:2*d,DEGREES:"degrees",RADIANS:"radians",CORNER:"corner",CORNERS:"corners",RADIUS:"radius",RIGHT:"right",LEFT:"left",CENTER:"center",TOP:"top",BOTTOM:"bottom",BASELINE:"alphabetic",POINTS:0,LINES:1,LINE_STRIP:3,LINE_LOOP:2,TRIANGLES:4,TRIANGLE_FAN:6,TRIANGLE_STRIP:5,QUADS:"quads",QUAD_STRIP:"quad_strip",CLOSE:"close",OPEN:"open",CHORD:"chord",PIE:"pie",PROJECT:"square",SQUARE:"butt",ROUND:"round",BEVEL:"bevel",MITER:"miter",RGB:"rgb",HSB:"hsb",HSL:"hsl",AUTO:"auto",ALT:18,BACKSPACE:8,CONTROL:17,DELETE:46,DOWN_ARROW:40,ENTER:13,ESCAPE:27,LEFT_ARROW:37,OPTION:18,RETURN:13,RIGHT_ARROW:39,SHIFT:16,TAB:9,UP_ARROW:38,BLEND:"normal",ADD:"lighter",DARKEST:"darken",LIGHTEST:"lighten",DIFFERENCE:"difference",EXCLUSION:"exclusion",MULTIPLY:"multiply",SCREEN:"screen",REPLACE:"source-over",OVERLAY:"overlay",HARD_LIGHT:"hard-light",SOFT_LIGHT:"soft-light",DODGE:"color-dodge",BURN:"color-burn",THRESHOLD:"threshold",GRAY:"gray",OPAQUE:"opaque",INVERT:"invert",POSTERIZE:"posterize",DILATE:"dilate",ERODE:"erode",BLUR:"blur",NORMAL:"normal",ITALIC:"italic",BOLD:"bold",_DEFAULT_TEXT_FILL:"#000000",_DEFAULT_LEADMULT:1.25,_CTX_MIDDLE:"middle",LINEAR:"linear",QUADRATIC:"quadratic",BEZIER:"bezier",CURVE:"curve",_DEFAULT_STROKE:"#000000",_DEFAULT_FILL:"#FFFFFF"}},{}],37:[function(a,b,c){"use strict";a("./shim");var d=a("./constants"),e=function(a,b,c){2===arguments.length&&"boolean"==typeof b&&(c=b,b=void 0),this._setupDone=!1,this._pixelDensity=Math.ceil(window.devicePixelRatio)||1,this._userNode=b,this._curElement=null,this._elements=[],this._requestAnimId=0,this._preloadCount=0,this._isGlobal=!1,this._loop=!0,this._styles=[],this._defaultCanvasSize={width:100,height:100},this._events={mousemove:null,mousedown:null,mouseup:null,dragend:null,dragover:null,click:null,mouseover:null,mouseout:null,keydown:null,keyup:null,keypress:null,touchstart:null,touchmove:null,touchend:null,resize:null,blur:null},this._events.wheel=null,this._loadingScreenId="p5_loading",window.DeviceOrientationEvent&&(this._events.deviceorientation=null),window.DeviceMotionEvent&&!window._isNodeWebkit&&(this._events.devicemotion=null),this._start=function(){this._userNode&&"string"==typeof this._userNode&&(this._userNode=document.getElementById(this._userNode)),this.createCanvas(this._defaultCanvasSize.width,this._defaultCanvasSize.height,"p2d",!0);var a=this.preload||window.preload;if(a){var b=document.getElementById(this._loadingScreenId);if(!b){b=document.createElement("div"),b.innerHTML="Loading...",b.style.position="absolute",b.id=this._loadingScreenId;var c=this._userNode||document.body;c.appendChild(b)}for(var d in this._preloadMethods){this._preloadMethods[d]=this._preloadMethods[d]||e;var f=this._preloadMethods[d];(f===e.prototype||f===e)&&(f=this._isGlobal?window:this),this._registeredPreloadMethods[d]=f[d],f[d]=this._wrapPreload(f,d)}a(),this._runIfPreloadsAreDone()}else this._setup(),this._runFrames(),this._draw()}.bind(this),this._runIfPreloadsAreDone=function(){var a=this._isGlobal?window:this;if(0===a._preloadCount){var b=document.getElementById(a._loadingScreenId);b&&b.parentNode.removeChild(b),a._setup(),a._runFrames(),a._draw()}},this._decrementPreload=function(){var a=this._isGlobal?window:this;a._setProperty("_preloadCount",a._preloadCount-1),a._runIfPreloadsAreDone()},this._wrapPreload=function(a,b){return function(){this._incrementPreload();for(var c=new Array(arguments.length),d=0;d<c.length;++d)c[d]=arguments[d];return c.push(this._decrementPreload.bind(this)),this._registeredPreloadMethods[b].apply(a,c)}.bind(this)},this._incrementPreload=function(){var a=this._isGlobal?window:this;a._setProperty("_preloadCount",a._preloadCount+1)},this._setup=function(){var a=this._isGlobal?window:this;if("function"==typeof a.preload)for(var b in this._preloadMethods)a[b]=this._preloadMethods[b][b],a[b]&&this&&(a[b]=a[b].bind(this));"function"==typeof a.setup&&a.setup();for(var c=document.getElementsByTagName("canvas"),d=0;d<c.length;d++){var e=c[d];"true"===e.dataset.hidden&&(e.style.visibility="",delete e.dataset.hidden)}this._setupDone=!0}.bind(this),this._draw=function(){var a=window.performance.now(),b=a-this._lastFrameTime,c=1e3/this._targetFrameRate,d=5;(!this._loop||b>=c-d)&&(this._setProperty("frameCount",this.frameCount+1),this.redraw(),this._updateMouseCoords(),this._updateTouchCoords(),this._frameRate=1e3/(a-this._lastFrameTime),this._lastFrameTime=a),this._loop&&(this._requestAnimId=window.requestAnimationFrame(this._draw))}.bind(this),this._runFrames=function(){this._updateInterval&&clearInterval(this._updateInterval)}.bind(this),this._setProperty=function(a,b){this[a]=b,this._isGlobal&&(window[a]=b)}.bind(this),this.remove=function(){if(this._curElement){this._loop=!1,this._requestAnimId&&window.cancelAnimationFrame(this._requestAnimId);for(var a in this._events)window.removeEventListener(a,this._events[a]);for(var b=0;b<this._elements.length;b++){var c=this._elements[b];c.elt.parentNode&&c.elt.parentNode.removeChild(c.elt);for(var d in c._events)c.elt.removeEventListener(d,c._events[d])}var f=this;if(this._registeredMethods.remove.forEach(function(a){"undefined"!=typeof a&&a.call(f)}),this._isGlobal){for(var g in e.prototype)try{delete window[g]}catch(h){window[g]=void 0}for(var i in this)if(this.hasOwnProperty(i))try{delete window[i]}catch(h){window[i]=void 0}}}}.bind(this),this._registeredMethods.init.forEach(function(a){"undefined"!=typeof a&&a.call(this)},this);var d=this._createFriendlyGlobalFunctionBinder();if(a)a(this);else{this._isGlobal=!0,e.instance=this;for(var f in e.prototype)if("function"==typeof e.prototype[f]){var g=f.substring(2);this._events.hasOwnProperty(g)||(Math.hasOwnProperty(f)&&Math[f]===e.prototype[f]?d(f,e.prototype[f]):d(f,e.prototype[f].bind(this)))}else d(f,e.prototype[f]);for(var h in this)this.hasOwnProperty(h)&&d(h,this[h])}for(var i in this._events){var j=this["_on"+i];if(j){var k=j.bind(this);window.addEventListener(i,k),this._events[i]=k}}var l=function(){this._setProperty("focused",!0)}.bind(this),m=function(){this._setProperty("focused",!1)}.bind(this);window.addEventListener("focus",l),window.addEventListener("blur",m),this.registerMethod("remove",function(){window.removeEventListener("focus",l),window.removeEventListener("blur",m)}),c?this._start():"complete"===document.readyState?this._start():window.addEventListener("load",this._start.bind(this),!1)};e.instance=null,e.disableFriendlyErrors=!1;for(var f in d)e.prototype[f]=d[f];e.prototype._preloadMethods={loadJSON:e.prototype,loadImage:e.prototype,loadStrings:e.prototype,loadXML:e.prototype,loadShape:e.prototype,loadTable:e.prototype,loadFont:e.prototype,loadModel:e.prototype},e.prototype._registeredMethods={init:[],pre:[],post:[],remove:[]},e.prototype._registeredPreloadMethods={},e.prototype.registerPreloadMethod=function(a,b){e.prototype._preloadMethods.hasOwnProperty(a)||(e.prototype._preloadMethods[a]=b)},e.prototype.registerMethod=function(a,b){e.prototype._registeredMethods.hasOwnProperty(a)||(e.prototype._registeredMethods[a]=[]),e.prototype._registeredMethods[a].push(b)},e.prototype._createFriendlyGlobalFunctionBinder=function(a){a=a||{};var b=a.globalObject||window,c=a.log||console.log.bind(console),d={print:!0};return function(a,f){if(!e.disableFriendlyErrors,1)b[a]=f;else try{if(a in b&&!(a in d))throw new Error('global "'+a+'" already exists');Object.defineProperty(b,a,{configurable:!0,enumerable:!0,get:function(){return f},set:function(d){Object.defineProperty(b,a,{configurable:!0,enumerable:!0,value:d,writable:!0}),c('You just changed the value of "'+a+"\", which was a p5 function. This could cause problems later if you're not careful.")}})}catch(g){c('p5 had problems creating the global function "'+a+'", possibly because your code is already using that name as a variable. You may want to rename your variable to something else.'),b[a]=f}}},b.exports=e},{"./constants":36,"./shim":46}],38:[function(a,b,c){"use strict";var d=a("./core");a("./error_helpers");var e=20,f=20;d.prototype.bezier=function(){for(var a=new Array(arguments.length),b=0;b<a.length;++b)a[b]=arguments[b];return this._renderer.isP3D?this._validateParameters("bezier",a,["Number","Number","Number","Number","Number","Number","Number","Number","Number","Number","Number","Number"]):this._validateParameters("bezier",a,["Number","Number","Number","Number","Number","Number","Number","Number"]),this._renderer._doStroke?(this._renderer.isP3D?(a.push(e),this._renderer.bezier(a)):this._renderer.bezier(a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7]),this):this},d.prototype.bezierDetail=function(a){return e=a,this},d.prototype.bezierPoint=function(a,b,c,d,e){var f=1-e;return Math.pow(f,3)*a+3*Math.pow(f,2)*e*b+3*f*Math.pow(e,2)*c+Math.pow(e,3)*d},d.prototype.bezierTangent=function(a,b,c,d,e){var f=1-e;return 3*d*Math.pow(e,2)-3*c*Math.pow(e,2)+6*c*f*e-6*b*f*e+3*b*Math.pow(f,2)-3*a*Math.pow(f,2)},d.prototype.curve=function(){for(var a=new Array(arguments.length),b=0;b<a.length;++b)a[b]=arguments[b];return this._renderer.isP3D?this._validateParameters("curve",a,["Number","Number","Number","Number","Number","Number","Number","Number","Number","Number","Number","Number"]):this._validateParameters("curve",a,["Number","Number","Number","Number","Number","Number","Number","Number"]),this._renderer._doStroke?(this._renderer.isP3D?(a.push(f),this._renderer.curve(a)):this._renderer.curve(a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7]),this):this},d.prototype.curveDetail=function(a){return f=a,this},d.prototype.curveTightness=function(a){this._renderer._curveTightness=a},d.prototype.curvePoint=function(a,b,c,d,e){var f=e*e*e,g=e*e,h=-.5*f+g-.5*e,i=1.5*f-2.5*g+1,j=-1.5*f+2*g+.5*e,k=.5*f-.5*g;return a*h+b*i+c*j+d*k},d.prototype.curveTangent=function(a,b,c,d,e){var f=e*e,g=-3*f/2+2*e-.5,h=9*f/2-5*e,i=-9*f/2+4*e+.5,j=3*f/2-e;return a*g+b*h+c*i+d*j},b.exports=d},{"./core":37,"./error_helpers":40}],39:[function(a,b,c){"use strict";function d(){return window.innerWidth||document.documentElement&&document.documentElement.clientWidth||document.body&&document.body.clientWidth||0}function e(){return window.innerHeight||document.documentElement&&document.documentElement.clientHeight||document.body&&document.body.clientHeight||0}function f(a){var b=document.fullscreenEnabled||document.webkitFullscreenEnabled||document.mozFullScreenEnabled||document.msFullscreenEnabled;if(!b)throw new Error("Fullscreen not enabled in this browser.");a.requestFullscreen?a.requestFullscreen():a.mozRequestFullScreen?a.mozRequestFullScreen():a.webkitRequestFullscreen?a.webkitRequestFullscreen():a.msRequestFullscreen&&a.msRequestFullscreen()}function g(){document.exitFullscreen?document.exitFullscreen():document.mozCancelFullScreen?document.mozCancelFullScreen():document.webkitExitFullscreen?document.webkitExitFullscreen():document.msExitFullscreen&&document.msExitFullscreen()}var h=a("./core"),i=a("./constants"),j=[i.ARROW,i.CROSS,i.HAND,i.MOVE,i.TEXT,i.WAIT];h.prototype._frameRate=0,h.prototype._lastFrameTime=window.performance.now(),h.prototype._targetFrameRate=60;var k=window.print;window.console&&console.log?h.prototype.print=function(a){try{if(0===arguments.length)k();else if(arguments.length>1)console.log.apply(console,arguments);else{var b=JSON.parse(JSON.stringify(a));console.log(b)}}catch(c){console.log(a)}}:h.prototype.print=function(){},h.prototype.frameCount=0,h.prototype.focused=document.hasFocus(),h.prototype.cursor=function(a,b,c){var d="auto",e=this._curElement.elt;if(j.indexOf(a)>-1)d=a;else if("string"==typeof a){var f="";b&&c&&"number"==typeof b&&"number"==typeof c&&(f=b+" "+c),d="http://"!==a.substring(0,6)?"url("+a+") "+f+", auto":/\.(cur|jpg|jpeg|gif|png|CUR|JPG|JPEG|GIF|PNG)$/.test(a)?"url("+a+") "+f+", auto":a}e.style.cursor=d},h.prototype.frameRate=function(a){return"number"!=typeof a||0>=a?this._frameRate:(this._setProperty("_targetFrameRate",a),this._runFrames(),this)},h.prototype.getFrameRate=function(){return this.frameRate()},h.prototype.setFrameRate=function(a){return this.frameRate(a)},h.prototype.noCursor=function(){this._curElement.elt.style.cursor="none"},h.prototype.displayWidth=screen.width,h.prototype.displayHeight=screen.height,h.prototype.windowWidth=d(),h.prototype.windowHeight=e(),h.prototype._onresize=function(a){this._setProperty("windowWidth",d()),this._setProperty("windowHeight",e());var b,c=this._isGlobal?window:this;"function"==typeof c.windowResized&&(b=c.windowResized(a),void 0===b||b||a.preventDefault())},h.prototype.width=0,h.prototype.height=0,h.prototype.fullscreen=function(a){return"undefined"==typeof a?document.fullscreenElement||document.webkitFullscreenElement||document.mozFullScreenElement||document.msFullscreenElement:void(a?f(document.documentElement):g())},h.prototype.pixelDensity=function(a){return"number"!=typeof a?this._pixelDensity:(this._pixelDensity=a,void this.resizeCanvas(this.width,this.height,!0))},h.prototype.displayDensity=function(){return window.devicePixelRatio},h.prototype.getURL=function(){return location.href},h.prototype.getURLPath=function(){return location.pathname.split("/").filter(function(a){return""!==a})},h.prototype.getURLParams=function(){for(var a,b=/[?&]([^&=]+)(?:[&=])([^&=]+)/gim,c={};null!=(a=b.exec(location.search));)a.index===b.lastIndex&&b.lastIndex++,c[a[1]]=a[2];return c},b.exports=h},{"./constants":36,"./core":37}],40:[function(a,b,c){"use strict";function d(a,b,c){if(a.match(/^p5\./)){var d=a.split(".");return c instanceof i[d[1]]}return"Boolean"===a||a.toLowerCase()===b||r.indexOf(a)>-1&&q(c)}function e(a,b,c){j&&(f(),j=!1),"undefined"===o(c)?c="#B40033":"number"===o(c)&&(c=w[c])}function f(){var a="transparent",b="#ED225D",c="#ED225D",d="white";console.log("%c _ \n /\\| |/\\ \n \\ ` ' / \n / , . \\ \n \\/|_|\\/ \n\n%c> p5.js says: Welcome! This is your friendly debugger. To turn me off switch to using “p5.min.js”.","background-color:"+a+";color:"+b+";","background-color:"+c+";color:"+d+";")}function g(){var b={},c=function(a){return Object.getOwnPropertyNames(a).filter(function(a){return"_"===a[0]?!1:a in b?!1:(b[a]=!0,!0)}).map(function(b){var c;return c="function"==typeof a[b]?"function":b===b.toUpperCase()?"constant":"variable",{name:b,type:c}})};y=[].concat(c(i.prototype),c(a("./constants"))),y.sort(function(a,b){return b.name.length-a.name.length})}function h(a,b){b||(b=console.log.bind(console)),y||g(),y.some(function(c){return a.message&&-1!==a.message.indexOf(c.name)?(b("%cDid you just try to use p5.js's "+c.name+("function"===c.type?"() ":" ")+c.type+"? If so, you may want to move it into your sketch's setup() function.\n\nFor more details, see: "+z,"color: #B40033"),!0):void 0})}for(var i=a("./core"),j=!1,k={},l=k.toString,m=["Boolean","Number","String","Function","Array","Date","RegExp","Object","Error"],n=0;n<m.length;n++)k["[object "+m[n]+"]"]=m[n].toLowerCase();var o=function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?k[l.call(a)]||"object":typeof a},p=Array.isArray||function(a){return"array"===o(a)},q=function(a){return!p(a)&&a-parseFloat(a)+1>=0},r=["Number","Integer","Number/Constant"],s=0,t=1,u=2,v=3,w=["#2D7BB6","#EE9900","#4DB200","#C83C00"];i.prototype._validateParameters=function(a,b,c){p(c[0])||(c=[c]);for(var f,g=Math.abs(b.length-c[0].length),h=0,i=1,j=c.length;j>i;i++){var k=Math.abs(b.length-c[i].length);g>=k&&(h=i,g=k)}var l="X";g>0&&(f="You wrote "+a+"(",b.length>0&&(f+=l+Array(b.length).join(","+l)),f+="). "+a+" was expecting "+c[h].length+" parameters. Try "+a+"(",c[h].length>0&&(f+=l+Array(c[h].length).join(","+l)),f+=").",c.length>1&&(f+=" "+a+" takes different numbers of parameters depending on what you want to do. Click this link to learn more: "),e(f,a,s));for(var m=0;m<c.length;m++)for(var n=0;n<c[m].length&&n<b.length;n++){var q=c[m][n],r=o(b[n]);"undefined"===r||null===r?e("It looks like "+a+" received an empty variable in spot #"+(n+1)+". If not intentional, this is often a problem with scope: [link to scope].",a,t):"*"===q||d(q,r,b[n])||(f=a+" was expecting a "+q.toLowerCase()+" for parameter #"+(n+1)+", received ",f+="string"===r?'"'+b[n]+'"':b[n],f+=" instead.",c.length>1&&(f+=" "+a+" takes different numbers of parameters depending on what you want to do. Click this link to learn more:"),e(f,a,u))}},i.prototype._validateParameters=function(){return!0};var x={0:{fileType:"image",method:"loadImage",message:" hosting the image online,"},1:{fileType:"XML file",method:"loadXML"},2:{fileType:"table file",method:"loadTable"},3:{fileType:"text file",method:"loadStrings"},4:{fileType:"font",method:"loadFont",message:" hosting the font online,"}};i._friendlyFileLoadError=function(a,b){var c=x[a],d="It looks like there was a problem loading your "+c.fileType+". Try checking if the file path%c ["+b+"] %cis correct,"+(c.message||"")+" or running a local server.";e(d,c.method,v)};var y=null,z="https://github.com/processing/p5.js/wiki/Frequently-Asked-Questions#why-cant-i-assign-variables-using-p5-functions-and-variables-before-setup";i.prototype._helpForMisusedAtTopLevelCode=h,"complete"!==document.readyState&&(window.addEventListener("error",h,!1),window.addEventListener("load",function(){window.removeEventListener("error",h,!1)})),b.exports=i},{"./constants":36,"./core":37}],41:[function(a,b,c){function d(a,b,c){var d=b.bind(c);c.elt.addEventListener(a,d,!1),c._events[a]=d}var e=a("./core");e.Element=function(a,b){this.elt=a,this._pInst=b,this._events={},this.width=this.elt.offsetWidth,this.height=this.elt.offsetHeight},e.Element.prototype.parent=function(a){return 0===arguments.length?this.elt.parentNode:("string"==typeof a?("#"===a[0]&&(a=a.substring(1)),a=document.getElementById(a)):a instanceof e.Element&&(a=a.elt),a.appendChild(this.elt),this)},e.Element.prototype.id=function(a){return 0===arguments.length?this.elt.id:(this.elt.id=a,this.width=this.elt.offsetWidth,this.height=this.elt.offsetHeight,this)},e.Element.prototype["class"]=function(a){return 0===arguments.length?this.elt.className:(this.elt.className=a,this)},e.Element.prototype.mousePressed=function(a){return d("mousedown",a,this),d("touchstart",a,this),this},e.Element.prototype.mouseWheel=function(a){return d("wheel",a,this),this},e.Element.prototype.mouseReleased=function(a){return d("mouseup",a,this),d("touchend",a,this),this},e.Element.prototype.mouseClicked=function(a){return d("click",a,this),this},e.Element.prototype.mouseMoved=function(a){return d("mousemove",a,this),d("touchmove",a,this),this},e.Element.prototype.mouseOver=function(a){return d("mouseover",a,this),this},e.Element.prototype.changed=function(a){return d("change",a,this),this},e.Element.prototype.input=function(a){return d("input",a,this),this},e.Element.prototype.mouseOut=function(a){return d("mouseout",a,this),this},e.Element.prototype.touchStarted=function(a){return d("touchstart",a,this),d("mousedown",a,this),this},e.Element.prototype.touchMoved=function(a){return d("touchmove",a,this),d("mousemove",a,this),this},e.Element.prototype.touchEnded=function(a){return d("touchend",a,this),d("mouseup",a,this),this},e.Element.prototype.dragOver=function(a){return d("dragover",a,this),this},e.Element.prototype.dragLeave=function(a){return d("dragleave",a,this),this},e.Element.prototype.drop=function(a,b){function c(b){var c=new e.File(b);return function(b){c.data=b.target.result,a(c)}}return window.File&&window.FileReader&&window.FileList&&window.Blob?(d("dragover",function(a){a.stopPropagation(),a.preventDefault()},this),d("dragleave",function(a){a.stopPropagation(),a.preventDefault()},this),arguments.length>1&&d("drop",b,this),d("drop",function(a){a.stopPropagation(),a.preventDefault();for(var b=a.dataTransfer.files,d=0;d<b.length;d++){var e=b[d],f=new FileReader;f.onload=c(e),e.type.indexOf("text")>-1?f.readAsText(e):f.readAsDataURL(e)}},this)):console.log("The File APIs are not fully supported in this browser."),this},e.Element.prototype._setProperty=function(a,b){this[a]=b},b.exports=e.Element},{"./core":37}],42:[function(a,b,c){var d=a("./core"),e=a("./constants");d.Graphics=function(a,b,c,f){var g=c||e.P2D,h=document.createElement("canvas"),i=this._userNode||document.body;i.appendChild(h),d.Element.call(this,h,f,!1),this._styles=[],this.width=a,this.height=b,this._pixelDensity=f._pixelDensity,g===e.WEBGL?this._renderer=new d.RendererGL(h,this,!1):this._renderer=new d.Renderer2D(h,this,!1),this._renderer.resize(a,b),this._renderer._applyDefaults(),f._elements.push(this);for(var j in d.prototype)this[j]||("function"==typeof d.prototype[j]?this[j]=d.prototype[j].bind(this):this[j]=d.prototype[j]);return this},d.Graphics.prototype=Object.create(d.Element.prototype),b.exports=d.Graphics},{"./constants":36,"./core":37}],43:[function(a,b,c){function d(a){var b=0,c=0;if(a.offsetParent){do b+=a.offsetLeft,c+=a.offsetTop;while(a=a.offsetParent)}else b+=a.offsetLeft,c+=a.offsetTop;return[b,c]}var e=a("./core"),f=a("../core/constants");e.Renderer=function(a,b,c){e.Element.call(this,a,b),this.canvas=a,this._pInst=b,c?(this._isMainCanvas=!0,this._pInst._setProperty("_curElement",this),this._pInst._setProperty("canvas",this.canvas),this._pInst._setProperty("width",this.width),this._pInst._setProperty("height",this.height)):(this.canvas.style.display="none",this._styles=[]),this._textSize=12,this._textLeading=15,this._textFont="sans-serif",this._textStyle=f.NORMAL,this._textAscent=null,this._textDescent=null,this._rectMode=f.CORNER,this._ellipseMode=f.CENTER,this._curveTightness=0,this._imageMode=f.CORNER,this._tint=null,this._doStroke=!0,this._doFill=!0,this._strokeSet=!1,this._fillSet=!1,this._colorMode=f.RGB,this._colorMaxes={rgb:[255,255,255,255],hsb:[360,100,100,1],hsl:[360,100,100,1]}},e.Renderer.prototype=Object.create(e.Element.prototype),e.Renderer.prototype.resize=function(a,b){this.width=a,this.height=b,this.elt.width=a*this._pInst._pixelDensity,this.elt.height=b*this._pInst._pixelDensity,this.elt.style.width=a+"px",this.elt.style.height=b+"px",this._isMainCanvas&&(this._pInst._setProperty("width",this.width),this._pInst._setProperty("height",this.height))},e.Renderer.prototype.textLeading=function(a){return arguments.length&&arguments[0]?(this._setProperty("_textLeading",a),this):this._textLeading},e.Renderer.prototype.textSize=function(a){return arguments.length&&arguments[0]?(this._setProperty("_textSize",a),this._setProperty("_textLeading",a*f._DEFAULT_LEADMULT),this._applyTextProperties()):this._textSize},e.Renderer.prototype.textStyle=function(a){return arguments.length&&arguments[0]?((a===f.NORMAL||a===f.ITALIC||a===f.BOLD)&&this._setProperty("_textStyle",a),this._applyTextProperties()):this._textStyle},e.Renderer.prototype.textAscent=function(){return null===this._textAscent&&this._updateTextMetrics(),this._textAscent},e.Renderer.prototype.textDescent=function(){return null===this._textDescent&&this._updateTextMetrics(),this._textDescent},e.Renderer.prototype._applyDefaults=function(){return this},e.Renderer.prototype._isOpenType=function(a){return a=a||this._textFont,"object"==typeof a&&a.font&&a.font.supported},e.Renderer.prototype._updateTextMetrics=function(){if(this._isOpenType())return this._setProperty("_textAscent",this._textFont._textAscent()),this._setProperty("_textDescent",this._textFont._textDescent()),this;var a=document.createElement("span");a.style.fontFamily=this._textFont,a.style.fontSize=this._textSize+"px",a.innerHTML="ABCjgq|";var b=document.createElement("div");b.style.display="inline-block",b.style.width="1px",b.style.height="0px";var c=document.createElement("div");c.appendChild(a),c.appendChild(b),c.style.height="0px",c.style.overflow="hidden",document.body.appendChild(c),b.style.verticalAlign="baseline";var e=d(b),f=d(a),g=e[1]-f[1];b.style.verticalAlign="bottom",e=d(b),f=d(a);var h=e[1]-f[1],i=h-g; +return document.body.removeChild(c),this._setProperty("_textAscent",g),this._setProperty("_textDescent",i),this},b.exports=e.Renderer},{"../core/constants":36,"./core":37}],44:[function(a,b,c){var d=a("./core"),e=a("./canvas"),f=a("./constants"),g=a("../image/filters");a("./p5.Renderer");var h="rgba(0,0,0,0)";d.Renderer2D=function(a,b,c){return d.Renderer.call(this,a,b,c),this.drawingContext=this.canvas.getContext("2d"),this._pInst._setProperty("drawingContext",this.drawingContext),this},d.Renderer2D.prototype=Object.create(d.Renderer.prototype),d.Renderer2D.prototype._applyDefaults=function(){this.drawingContext.fillStyle=f._DEFAULT_FILL,this.drawingContext.strokeStyle=f._DEFAULT_STROKE,this.drawingContext.lineCap=f.ROUND,this.drawingContext.font="normal 12px sans-serif"},d.Renderer2D.prototype.resize=function(a,b){d.Renderer.prototype.resize.call(this,a,b),this.drawingContext.scale(this._pInst._pixelDensity,this._pInst._pixelDensity)},d.Renderer2D.prototype.background=function(){if(this.drawingContext.save(),this.drawingContext.setTransform(1,0,0,1,0,0),this.drawingContext.scale(this._pInst._pixelDensity,this._pInst._pixelDensity),arguments[0]instanceof d.Image)this._pInst.image(arguments[0],0,0,this.width,this.height);else{var a=this.drawingContext.fillStyle,b=this._pInst.color.apply(this,arguments),c=b.toString();this.drawingContext.fillStyle=c,this.drawingContext.fillRect(0,0,this.width,this.height),this.drawingContext.fillStyle=a}this.drawingContext.restore()},d.Renderer2D.prototype.clear=function(){this.drawingContext.clearRect(0,0,this.width,this.height)},d.Renderer2D.prototype.fill=function(){var a=this.drawingContext,b=this._pInst.color.apply(this,arguments);a.fillStyle=b.toString()},d.Renderer2D.prototype.stroke=function(){var a=this.drawingContext,b=this._pInst.color.apply(this,arguments);a.strokeStyle=b.toString()},d.Renderer2D.prototype.image=function(a,b,c,e,f,g,h,i,j){var k;try{this._tint&&(d.MediaElement&&a instanceof d.MediaElement&&a.loadPixels(),a.canvas&&(k=this._getTintedImageCanvas(a))),k||(k=a.canvas||a.elt),this.drawingContext.drawImage(k,b,c,e,f,g,h,i,j)}catch(l){if("NS_ERROR_NOT_AVAILABLE"!==l.name)throw l}},d.Renderer2D.prototype._getTintedImageCanvas=function(a){if(!a.canvas)return a;var b=g._toPixels(a.canvas),c=document.createElement("canvas");c.width=a.canvas.width,c.height=a.canvas.height;for(var d=c.getContext("2d"),e=d.createImageData(a.canvas.width,a.canvas.height),f=e.data,h=0;h<b.length;h+=4){var i=b[h],j=b[h+1],k=b[h+2],l=b[h+3];f[h]=i*this._tint[0]/255,f[h+1]=j*this._tint[1]/255,f[h+2]=k*this._tint[2]/255,f[h+3]=l*this._tint[3]/255}return d.putImageData(e,0,0),c},d.Renderer2D.prototype.blendMode=function(a){this.drawingContext.globalCompositeOperation=a},d.Renderer2D.prototype.blend=function(){var a=this.drawingContext.globalCompositeOperation,b=arguments[arguments.length-1],c=Array.prototype.slice.call(arguments,0,arguments.length-1);this.drawingContext.globalCompositeOperation=b,this._pInst?this._pInst.copy.apply(this._pInst,c):this.copy.apply(this,c),this.drawingContext.globalCompositeOperation=a},d.Renderer2D.prototype.copy=function(){var a,b,c,e,f,g,h,i,j;if(9===arguments.length)a=arguments[0],b=arguments[1],c=arguments[2],e=arguments[3],f=arguments[4],g=arguments[5],h=arguments[6],i=arguments[7],j=arguments[8];else{if(8!==arguments.length)throw new Error("Signature not supported");a=this._pInst,b=arguments[0],c=arguments[1],e=arguments[2],f=arguments[3],g=arguments[4],h=arguments[5],i=arguments[6],j=arguments[7]}d.Renderer2D._copyHelper(a,b,c,e,f,g,h,i,j)},d.Renderer2D._copyHelper=function(a,b,c,d,e,f,g,h,i){a.canvas||a.loadPixels();var j=a.canvas.width/a.width;this.drawingContext.drawImage(a.canvas,j*b,j*c,j*d,j*e,f,g,h,i)},d.Renderer2D.prototype.get=function(a,b,c,e){if(void 0===a&&void 0===b&&void 0===c&&void 0===e?(a=0,b=0,c=this.width,e=this.height):void 0===c&&void 0===e&&(c=1,e=1),0>a+c||0>b+e||a>this.width||b>this.height)return[0,0,0,255];var f=this._pInst||this,g=f._pixelDensity;a=Math.floor(a),b=Math.floor(b);var h=a*g,i=b*g;if(1===c&&1===e){var j=this.drawingContext.getImageData(h,i,1,1).data;return[j[0],j[1],j[2],j[3]]}var k=Math.min(c,f.width),l=Math.min(e,f.height),m=k*g,n=l*g,o=new d.Image(k,l);return o.canvas.getContext("2d").drawImage(this.canvas,h,i,m,n,0,0,k,l),o},d.Renderer2D.prototype.loadPixels=function(){var a=this._pixelDensity||this._pInst._pixelDensity,b=this.width*a,c=this.height*a,d=this.drawingContext.getImageData(0,0,b,c);this._pInst?(this._pInst._setProperty("imageData",d),this._pInst._setProperty("pixels",d.data)):(this._setProperty("imageData",d),this._setProperty("pixels",d.data))},d.Renderer2D.prototype.set=function(a,b,c){if(a=Math.floor(a),b=Math.floor(b),c instanceof d.Image)this.drawingContext.save(),this.drawingContext.setTransform(1,0,0,1,0,0),this.drawingContext.scale(this._pInst._pixelDensity,this._pInst._pixelDensity),this.drawingContext.drawImage(c.canvas,a,b),this.loadPixels.call(this._pInst),this.drawingContext.restore();else{var e=this._pInst||this,f=0,g=0,h=0,i=0,j=4*(b*e._pixelDensity*this.width*e._pixelDensity+a*e._pixelDensity);if(e.imageData||e.loadPixels.call(e),"number"==typeof c)j<e.pixels.length&&(f=c,g=c,h=c,i=255);else if(c instanceof Array){if(c.length<4)throw new Error("pixel array must be of the form [R, G, B, A]");j<e.pixels.length&&(f=c[0],g=c[1],h=c[2],i=c[3])}else c instanceof d.Color&&j<e.pixels.length&&(f=c.levels[0],g=c.levels[1],h=c.levels[2],i=c.levels[3]);for(var k=0;k<e._pixelDensity;k++)for(var l=0;l<e._pixelDensity;l++)j=4*((b*e._pixelDensity+l)*this.width*e._pixelDensity+(a*e._pixelDensity+k)),e.pixels[j]=f,e.pixels[j+1]=g,e.pixels[j+2]=h,e.pixels[j+3]=i}},d.Renderer2D.prototype.updatePixels=function(a,b,c,d){var e=this._pixelDensity||this._pInst._pixelDensity;void 0===a&&void 0===b&&void 0===c&&void 0===d&&(a=0,b=0,c=this.width,d=this.height),c*=e,d*=e,this._pInst?this.drawingContext.putImageData(this._pInst.imageData,a,b,0,0,c,d):this.drawingContext.putImageData(this.imageData,a,b,0,0,c,d)},d.Renderer2D.prototype._acuteArcToBezier=function(a,b){var c=b/2,d=Math.cos(c),e=Math.sin(c),f=1/Math.tan(c),g=a+c,h=Math.cos(g),i=Math.sin(g),j=(4-d)/3,k=e+(d-j)*f;return{ax:Math.cos(a),ay:Math.sin(a),bx:j*h+k*i,by:j*i-k*h,cx:j*h-k*i,cy:j*i+k*h,dx:Math.cos(a+b),dy:Math.sin(a+b)}},d.Renderer2D.prototype.arc=function(a,b,c,d,g,h,i){for(var j=this.drawingContext,k=e.arcModeAdjust(a,b,c,d,this._ellipseMode),l=k.w/2,m=k.h/2,n=1e-5,o=0,p=[];h-g>n;)o=Math.min(h-g,f.HALF_PI),p.push(this._acuteArcToBezier(g,o)),g+=o;return this._doFill&&(j.beginPath(),p.forEach(function(a,b){0===b&&j.moveTo(k.x+a.ax*l,k.y+a.ay*m),j.bezierCurveTo(k.x+a.bx*l,k.y+a.by*m,k.x+a.cx*l,k.y+a.cy*m,k.x+a.dx*l,k.y+a.dy*m)}),(i===f.PIE||null==i)&&j.lineTo(k.x,k.y),j.closePath(),j.fill()),this._doStroke&&(j.beginPath(),p.forEach(function(a,b){0===b&&j.moveTo(k.x+a.ax*l,k.y+a.ay*m),j.bezierCurveTo(k.x+a.bx*l,k.y+a.by*m,k.x+a.cx*l,k.y+a.cy*m,k.x+a.dx*l,k.y+a.dy*m)}),i===f.PIE?(j.lineTo(k.x,k.y),j.closePath()):i===f.CHORD&&j.closePath(),j.stroke()),this},d.Renderer2D.prototype.ellipse=function(a){var b=this.drawingContext,c=this._doFill,d=this._doStroke,e=a[0],f=a[1],g=a[2],i=a[3];if(c&&!d){if(b.fillStyle===h)return this}else if(!c&&d&&b.strokeStyle===h)return this;var j=.5522847498,k=g/2*j,l=i/2*j,m=e+g,n=f+i,o=e+g/2,p=f+i/2;b.beginPath(),b.moveTo(e,p),b.bezierCurveTo(e,p-l,o-k,f,o,f),b.bezierCurveTo(o+k,f,m,p-l,m,p),b.bezierCurveTo(m,p+l,o+k,n,o,n),b.bezierCurveTo(o-k,n,e,p+l,e,p),b.closePath(),c&&b.fill(),d&&b.stroke()},d.Renderer2D.prototype.line=function(a,b,c,d){var e=this.drawingContext;return this._doStroke?e.strokeStyle===h?this:(e.lineWidth%2===1&&e.translate(.5,.5),e.beginPath(),e.moveTo(a,b),e.lineTo(c,d),e.stroke(),e.lineWidth%2===1&&e.translate(-.5,-.5),this):this},d.Renderer2D.prototype.point=function(a,b){var c=this.drawingContext,d=c.strokeStyle,e=c.fillStyle;return this._doStroke?c.strokeStyle===h?this:(a=Math.round(a),b=Math.round(b),c.fillStyle=d,c.lineWidth>1?(c.beginPath(),c.arc(a,b,c.lineWidth/2,0,f.TWO_PI,!1),c.fill()):c.fillRect(a,b,1,1),void(c.fillStyle=e)):this},d.Renderer2D.prototype.quad=function(a,b,c,d,e,f,g,i){var j=this.drawingContext,k=this._doFill,l=this._doStroke;if(k&&!l){if(j.fillStyle===h)return this}else if(!k&&l&&j.strokeStyle===h)return this;return j.beginPath(),j.moveTo(a,b),j.lineTo(c,d),j.lineTo(e,f),j.lineTo(g,i),j.closePath(),k&&j.fill(),l&&j.stroke(),this},d.Renderer2D.prototype.rect=function(a){var b=a[0],c=a[1],d=a[2],e=a[3],f=a[4],g=a[5],i=a[6],j=a[7],k=this.drawingContext,l=this._doFill,m=this._doStroke;if(l&&!m){if(k.fillStyle===h)return this}else if(!l&&m&&k.strokeStyle===h)return this;if(this._doStroke&&k.lineWidth%2===1&&k.translate(.5,.5),k.beginPath(),"undefined"==typeof f)k.rect(b,c,d,e);else{"undefined"==typeof g&&(g=f),"undefined"==typeof i&&(i=g),"undefined"==typeof j&&(j=i);var n=d/2,o=e/2;2*f>d&&(f=n),2*f>e&&(f=o),2*g>d&&(g=n),2*g>e&&(g=o),2*i>d&&(i=n),2*i>e&&(i=o),2*j>d&&(j=n),2*j>e&&(j=o),k.beginPath(),k.moveTo(b+f,c),k.arcTo(b+d,c,b+d,c+e,g),k.arcTo(b+d,c+e,b,c+e,i),k.arcTo(b,c+e,b,c,j),k.arcTo(b,c,b+d,c,f),k.closePath()}return this._doFill&&k.fill(),this._doStroke&&k.stroke(),this._doStroke&&k.lineWidth%2===1&&k.translate(-.5,-.5),this},d.Renderer2D.prototype.triangle=function(a){var b=this.drawingContext,c=this._doFill,d=this._doStroke,e=a[0],f=a[1],g=a[2],i=a[3],j=a[4],k=a[5];if(c&&!d){if(b.fillStyle===h)return this}else if(!c&&d&&b.strokeStyle===h)return this;b.beginPath(),b.moveTo(e,f),b.lineTo(g,i),b.lineTo(j,k),b.closePath(),c&&b.fill(),d&&b.stroke()},d.Renderer2D.prototype.endShape=function(a,b,c,d,e,g,h){if(0===b.length)return this;if(!this._doStroke&&!this._doFill)return this;var i,j=a===f.CLOSE;j&&!g&&b.push(b[0]);var k,l,m=b.length;if(!c||h!==f.POLYGON&&null!==h)if(!d||h!==f.POLYGON&&null!==h)if(!e||h!==f.POLYGON&&null!==h)if(h===f.POINTS)for(k=0;m>k;k++)i=b[k],this._doStroke&&this._pInst.stroke(i[6]),this._pInst.point(i[0],i[1]);else if(h===f.LINES)for(k=0;m>k+1;k+=2)i=b[k],this._doStroke&&this._pInst.stroke(b[k+1][6]),this._pInst.line(i[0],i[1],b[k+1][0],b[k+1][1]);else if(h===f.TRIANGLES)for(k=0;m>k+2;k+=3)i=b[k],this.drawingContext.beginPath(),this.drawingContext.moveTo(i[0],i[1]),this.drawingContext.lineTo(b[k+1][0],b[k+1][1]),this.drawingContext.lineTo(b[k+2][0],b[k+2][1]),this.drawingContext.lineTo(i[0],i[1]),this._doFill&&(this._pInst.fill(b[k+2][5]),this.drawingContext.fill()),this._doStroke&&(this._pInst.stroke(b[k+2][6]),this.drawingContext.stroke()),this.drawingContext.closePath();else if(h===f.TRIANGLE_STRIP)for(k=0;m>k+1;k++)i=b[k],this.drawingContext.beginPath(),this.drawingContext.moveTo(b[k+1][0],b[k+1][1]),this.drawingContext.lineTo(i[0],i[1]),this._doStroke&&this._pInst.stroke(b[k+1][6]),this._doFill&&this._pInst.fill(b[k+1][5]),m>k+2&&(this.drawingContext.lineTo(b[k+2][0],b[k+2][1]),this._doStroke&&this._pInst.stroke(b[k+2][6]),this._doFill&&this._pInst.fill(b[k+2][5])),this._doFillStrokeClose();else if(h===f.TRIANGLE_FAN){if(m>2)for(this.drawingContext.beginPath(),this.drawingContext.moveTo(b[0][0],b[0][1]),this.drawingContext.lineTo(b[1][0],b[1][1]),this.drawingContext.lineTo(b[2][0],b[2][1]),this._doFill&&this._pInst.fill(b[2][5]),this._doStroke&&this._pInst.stroke(b[2][6]),this._doFillStrokeClose(),k=3;m>k;k++)i=b[k],this.drawingContext.beginPath(),this.drawingContext.moveTo(b[0][0],b[0][1]),this.drawingContext.lineTo(b[k-1][0],b[k-1][1]),this.drawingContext.lineTo(i[0],i[1]),this._doFill&&this._pInst.fill(i[5]),this._doStroke&&this._pInst.stroke(i[6]),this._doFillStrokeClose()}else if(h===f.QUADS)for(k=0;m>k+3;k+=4){for(i=b[k],this.drawingContext.beginPath(),this.drawingContext.moveTo(i[0],i[1]),l=1;4>l;l++)this.drawingContext.lineTo(b[k+l][0],b[k+l][1]);this.drawingContext.lineTo(i[0],i[1]),this._doFill&&this._pInst.fill(b[k+3][5]),this._doStroke&&this._pInst.stroke(b[k+3][6]),this._doFillStrokeClose()}else if(h===f.QUAD_STRIP){if(m>3)for(k=0;m>k+1;k+=2)i=b[k],this.drawingContext.beginPath(),m>k+3?(this.drawingContext.moveTo(b[k+2][0],b[k+2][1]),this.drawingContext.lineTo(i[0],i[1]),this.drawingContext.lineTo(b[k+1][0],b[k+1][1]),this.drawingContext.lineTo(b[k+3][0],b[k+3][1]),this._doFill&&this._pInst.fill(b[k+3][5]),this._doStroke&&this._pInst.stroke(b[k+3][6])):(this.drawingContext.moveTo(i[0],i[1]),this.drawingContext.lineTo(b[k+1][0],b[k+1][1])),this._doFillStrokeClose()}else{for(this.drawingContext.beginPath(),this.drawingContext.moveTo(b[0][0],b[0][1]),k=1;m>k;k++)i=b[k],i.isVert&&(i.moveTo?this.drawingContext.moveTo(i[0],i[1]):this.drawingContext.lineTo(i[0],i[1]));this._doFillStrokeClose()}else{for(this.drawingContext.beginPath(),k=0;m>k;k++)b[k].isVert?b[k].moveTo?this.drawingContext.moveTo([0],b[k][1]):this.drawingContext.lineTo(b[k][0],b[k][1]):this.drawingContext.quadraticCurveTo(b[k][0],b[k][1],b[k][2],b[k][3]);this._doFillStrokeClose()}else{for(this.drawingContext.beginPath(),k=0;m>k;k++)b[k].isVert?b[k].moveTo?this.drawingContext.moveTo(b[k][0],b[k][1]):this.drawingContext.lineTo(b[k][0],b[k][1]):this.drawingContext.bezierCurveTo(b[k][0],b[k][1],b[k][2],b[k][3],b[k][4],b[k][5]);this._doFillStrokeClose()}else if(m>3){var n=[],o=1-this._curveTightness;for(this.drawingContext.beginPath(),this.drawingContext.moveTo(b[1][0],b[1][1]),k=1;m>k+2;k++)i=b[k],n[0]=[i[0],i[1]],n[1]=[i[0]+(o*b[k+1][0]-o*b[k-1][0])/6,i[1]+(o*b[k+1][1]-o*b[k-1][1])/6],n[2]=[b[k+1][0]+(o*b[k][0]-o*b[k+2][0])/6,b[k+1][1]+(o*b[k][1]-o*b[k+2][1])/6],n[3]=[b[k+1][0],b[k+1][1]],this.drawingContext.bezierCurveTo(n[1][0],n[1][1],n[2][0],n[2][1],n[3][0],n[3][1]);j&&this.drawingContext.lineTo(b[k+1][0],b[k+1][1]),this._doFillStrokeClose()}return c=!1,d=!1,e=!1,g=!1,j&&b.pop(),this},d.Renderer2D.prototype.noSmooth=function(){return"imageSmoothingEnabled"in this.drawingContext?this.drawingContext.imageSmoothingEnabled=!1:"mozImageSmoothingEnabled"in this.drawingContext?this.drawingContext.mozImageSmoothingEnabled=!1:"webkitImageSmoothingEnabled"in this.drawingContext?this.drawingContext.webkitImageSmoothingEnabled=!1:"msImageSmoothingEnabled"in this.drawingContext&&(this.drawingContext.msImageSmoothingEnabled=!1),this},d.Renderer2D.prototype.smooth=function(){return"imageSmoothingEnabled"in this.drawingContext?this.drawingContext.imageSmoothingEnabled=!0:"mozImageSmoothingEnabled"in this.drawingContext?this.drawingContext.mozImageSmoothingEnabled=!0:"webkitImageSmoothingEnabled"in this.drawingContext?this.drawingContext.webkitImageSmoothingEnabled=!0:"msImageSmoothingEnabled"in this.drawingContext&&(this.drawingContext.msImageSmoothingEnabled=!0),this},d.Renderer2D.prototype.strokeCap=function(a){return(a===f.ROUND||a===f.SQUARE||a===f.PROJECT)&&(this.drawingContext.lineCap=a),this},d.Renderer2D.prototype.strokeJoin=function(a){return(a===f.ROUND||a===f.BEVEL||a===f.MITER)&&(this.drawingContext.lineJoin=a),this},d.Renderer2D.prototype.strokeWeight=function(a){return"undefined"==typeof a||0===a?this.drawingContext.lineWidth=1e-4:this.drawingContext.lineWidth=a,this},d.Renderer2D.prototype._getFill=function(){return this.drawingContext.fillStyle},d.Renderer2D.prototype._getStroke=function(){return this.drawingContext.strokeStyle},d.Renderer2D.prototype.bezier=function(a,b,c,d,e,f,g,h){return this._pInst.beginShape(),this._pInst.vertex(a,b),this._pInst.bezierVertex(c,d,e,f,g,h),this._pInst.endShape(),this},d.Renderer2D.prototype.curve=function(a,b,c,d,e,f,g,h){return this._pInst.beginShape(),this._pInst.curveVertex(a,b),this._pInst.curveVertex(c,d),this._pInst.curveVertex(e,f),this._pInst.curveVertex(g,h),this._pInst.endShape(),this},d.Renderer2D.prototype._doFillStrokeClose=function(){this._doFill&&this.drawingContext.fill(),this._doStroke&&this.drawingContext.stroke(),this.drawingContext.closePath()},d.Renderer2D.prototype.applyMatrix=function(a,b,c,d,e,f){this.drawingContext.transform(a,b,c,d,e,f)},d.Renderer2D.prototype.resetMatrix=function(){return this.drawingContext.setTransform(1,0,0,1,0,0),this.drawingContext.scale(this._pInst._pixelDensity,this._pInst._pixelDensity),this},d.Renderer2D.prototype.rotate=function(a){this.drawingContext.rotate(a)},d.Renderer2D.prototype.scale=function(a,b){return this.drawingContext.scale(a,b),this},d.Renderer2D.prototype.shearX=function(a){return this._pInst._angleMode===f.DEGREES&&(a=this._pInst.degrees(a)),this.drawingContext.transform(1,0,this._pInst.tan(a),1,0,0),this},d.Renderer2D.prototype.shearY=function(a){return this._pInst._angleMode===f.DEGREES&&(a=this._pInst.degrees(a)),this.drawingContext.transform(1,this._pInst.tan(a),0,1,0,0),this},d.Renderer2D.prototype.translate=function(a,b){return this.drawingContext.translate(a,b),this},d.Renderer2D.prototype.text=function(a,b,c,d,e){var g,h,i,j,k,l,m,n,o,p,q=this._pInst,r=Number.MAX_VALUE;if(this._doFill||this._doStroke){if("string"!=typeof a&&(a=a.toString()),a=a.replace(/(\t)/g," "),g=a.split("\n"),"undefined"!=typeof d){for(o=0,i=0;i<g.length;i++)for(k="",n=g[i].split(" "),h=0;h<n.length;h++)l=k+n[h]+" ",m=this.textWidth(l),m>d?(k=n[h]+" ",o+=q.textLeading()):k=l;switch(this._rectMode===f.CENTER&&(b-=d/2,c-=e/2),this.drawingContext.textAlign){case f.CENTER:b+=d/2;break;case f.RIGHT:b+=d}if("undefined"!=typeof e){switch(this.drawingContext.textBaseline){case f.BOTTOM:c+=e-o;break;case f._CTX_MIDDLE:c+=(e-o)/2;break;case f.BASELINE:p=!0,this.drawingContext.textBaseline=f.TOP}r=c+e-q.textAscent()}for(i=0;i<g.length;i++){for(k="",n=g[i].split(" "),h=0;h<n.length;h++)l=k+n[h]+" ",m=this.textWidth(l),m>d&&k.length>0?(this._renderText(q,k,b,c,r),k=n[h]+" ",c+=q.textLeading()):k=l;this._renderText(q,k,b,c,r),c+=q.textLeading()}}else{var s=0,t=q.textAlign().vertical;for(t===f.CENTER?s=(g.length-1)*q.textLeading()/2:t===f.BOTTOM&&(s=(g.length-1)*q.textLeading()),j=0;j<g.length;j++)this._renderText(q,g[j],b,c-s,r),c+=q.textLeading()}return p&&(this.drawingContext.textBaseline=f.BASELINE),q}},d.Renderer2D.prototype._renderText=function(a,b,c,d,e){return d>=e?void 0:(a.push(),this._isOpenType()?this._textFont._renderPath(b,c,d,{renderer:this}):(this._doStroke&&this._strokeSet&&this.drawingContext.strokeText(b,c,d),this._doFill&&(this.drawingContext.fillStyle=this._fillSet?this.drawingContext.fillStyle:f._DEFAULT_TEXT_FILL,this.drawingContext.fillText(b,c,d))),a.pop(),a)},d.Renderer2D.prototype.textWidth=function(a){return this._isOpenType()?this._textFont._textWidth(a,this._textSize):this.drawingContext.measureText(a).width},d.Renderer2D.prototype.textAlign=function(a,b){if(arguments.length)return(a===f.LEFT||a===f.RIGHT||a===f.CENTER)&&(this.drawingContext.textAlign=a),(b===f.TOP||b===f.BOTTOM||b===f.CENTER||b===f.BASELINE)&&(b===f.CENTER?this.drawingContext.textBaseline=f._CTX_MIDDLE:this.drawingContext.textBaseline=b),this._pInst;var c=this.drawingContext.textBaseline;return c===f._CTX_MIDDLE&&(c=f.CENTER),{horizontal:this.drawingContext.textAlign,vertical:c}},d.Renderer2D.prototype._applyTextProperties=function(){var a,b=this._pInst;return this._setProperty("_textAscent",null),this._setProperty("_textDescent",null),a=this._textFont,this._isOpenType()&&(a=this._textFont.font.familyName,this._setProperty("_textStyle",this._textFont.font.styleName)),this.drawingContext.font=this._textStyle+" "+this._textSize+"px "+a,b},d.Renderer2D.prototype.push=function(){this.drawingContext.save()},d.Renderer2D.prototype.pop=function(){this.drawingContext.restore()},b.exports=d.Renderer2D},{"../image/filters":54,"./canvas":35,"./constants":36,"./core":37,"./p5.Renderer":43}],45:[function(a,b,c){var d=a("./core"),e=a("./constants");a("./p5.Graphics"),a("./p5.Renderer2D"),a("../webgl/p5.RendererGL");var f="defaultCanvas0";d.prototype.createCanvas=function(a,b,c){var g,h,i=c||e.P2D;if(arguments[3]&&(g="boolean"==typeof arguments[3]?arguments[3]:!1),i===e.WEBGL)h=document.getElementById(f),h&&h.parentNode.removeChild(h),h=document.createElement("canvas"),h.id=f;else if(g){h=document.createElement("canvas");for(var j=0;document.getElementById("defaultCanvas"+j);)j++;f="defaultCanvas"+j,h.id=f}else h=this.canvas;return this._setupDone||(h.dataset.hidden=!0,h.style.visibility="hidden"),this._userNode?this._userNode.appendChild(h):document.body.appendChild(h),i===e.WEBGL?(this._setProperty("_renderer",new d.RendererGL(h,this,!0)),this._isdefaultGraphics=!0):this._isdefaultGraphics||(this._setProperty("_renderer",new d.Renderer2D(h,this,!0)),this._isdefaultGraphics=!0),this._renderer.resize(a,b),this._renderer._applyDefaults(),g&&this._elements.push(this._renderer),this._renderer},d.prototype.resizeCanvas=function(a,b,c){if(this._renderer){var d={};for(var e in this.drawingContext){var f=this.drawingContext[e];"object"!=typeof f&&"function"!=typeof f&&(d[e]=f)}this._renderer.resize(a,b);for(var g in d)this.drawingContext[g]=d[g];c||this.redraw()}},d.prototype.noCanvas=function(){this.canvas&&this.canvas.parentNode.removeChild(this.canvas)},d.prototype.createGraphics=function(a,b,c){return new d.Graphics(a,b,c,this)},d.prototype.blendMode=function(a){if(a!==e.BLEND&&a!==e.DARKEST&&a!==e.LIGHTEST&&a!==e.DIFFERENCE&&a!==e.MULTIPLY&&a!==e.EXCLUSION&&a!==e.SCREEN&&a!==e.REPLACE&&a!==e.OVERLAY&&a!==e.HARD_LIGHT&&a!==e.SOFT_LIGHT&&a!==e.DODGE&&a!==e.BURN&&a!==e.ADD&&a!==e.NORMAL)throw new Error("Mode "+a+" not recognized.");this._renderer.blendMode(a)},b.exports=d},{"../webgl/p5.RendererGL":86,"./constants":36,"./core":37,"./p5.Graphics":42,"./p5.Renderer2D":44}],46:[function(a,b,c){window.requestAnimationFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(a,b){window.setTimeout(a,1e3/60)}}(),window.performance=window.performance||{},window.performance.now=function(){var a=Date.now();return window.performance.now||window.performance.mozNow||window.performance.msNow||window.performance.oNow||window.performance.webkitNow||function(){return Date.now()-a}}(),function(){"use strict";"undefined"==typeof Uint8ClampedArray||Uint8ClampedArray.prototype.slice||Object.defineProperty(Uint8ClampedArray.prototype,"slice",{value:Array.prototype.slice,writable:!0,configurable:!0,enumerable:!1})}()},{}],47:[function(a,b,c){"use strict";var d=a("./core");d.prototype.exit=function(){throw"exit() not implemented, see remove()"},d.prototype.noLoop=function(){this._loop=!1},d.prototype.loop=function(){this._loop=!0,this._draw()},d.prototype.push=function(){this._renderer.push(),this._styles.push({_doStroke:this._renderer._doStroke,_strokeSet:this._renderer._strokeSet,_doFill:this._renderer._doFill,_fillSet:this._renderer._fillSet,_tint:this._renderer._tint,_imageMode:this._renderer._imageMode,_rectMode:this._renderer._rectMode,_ellipseMode:this._renderer._ellipseMode,_colorMode:this._renderer._colorMode,_textFont:this._renderer._textFont,_textLeading:this._renderer._textLeading,_textSize:this._renderer._textSize,_textStyle:this._renderer._textStyle})},d.prototype.pop=function(){this._renderer.pop();var a=this._styles.pop();for(var b in a)this._renderer[b]=a[b]},d.prototype.pushStyle=function(){throw new Error("pushStyle() not used, see push()")},d.prototype.popStyle=function(){throw new Error("popStyle() not used, see pop()")},d.prototype.redraw=function(){this.resetMatrix(),this._renderer.isP3D&&this._renderer._update();var a=1;if(1===arguments.length)try{parseInt(arguments[0])>1&&(a=parseInt(arguments[0]))}catch(b){}var c=this.setup||window.setup,d=this.draw||window.draw;if("function"==typeof d){"undefined"==typeof c&&this.scale(this._pixelDensity,this._pixelDensity);for(var e=this,f=function(a){a.call(e)},g=0;a>g;g++)this._registeredMethods.pre.forEach(f),d(),this._registeredMethods.post.forEach(f)}},d.prototype.size=function(){var a="size() is not a valid p5 function, to set the size of the ";throw a+="drawing canvas, please use createCanvas() instead"},b.exports=d},{"./core":37}],48:[function(a,b,c){"use strict";var d=a("./core"),e=a("./constants");d.prototype.applyMatrix=function(a,b,c,d,e,f){return this._renderer.applyMatrix(a,b,c,d,e,f),this},d.prototype.popMatrix=function(){throw new Error("popMatrix() not used, see pop()")},d.prototype.printMatrix=function(){throw new Error("printMatrix() not implemented")},d.prototype.pushMatrix=function(){throw new Error("pushMatrix() not used, see push()")},d.prototype.resetMatrix=function(){return this._renderer.resetMatrix(),this},d.prototype.rotate=function(){for(var a,b=new Array(arguments.length),c=0;c<b.length;++c)b[c]=arguments[c];return this._angleMode===e.DEGREES?a=this.radians(b[0]):this._angleMode===e.RADIANS&&(a=b[0]),b.length>1?this._renderer.rotate(a,b[1]):this._renderer.rotate(a),this},d.prototype.rotateX=function(a){for(var b=new Array(arguments.length),c=0;c<b.length;++c)b[c]=arguments[c];if(!this._renderer.isP3D)throw"not supported in p2d. Please use webgl mode";return this._validateParameters("rotateX",b,[["Number"]]),this._renderer.rotateX(a),this},d.prototype.rotateY=function(a){if(!this._renderer.isP3D)throw"not supported in p2d. Please use webgl mode";for(var b=new Array(arguments.length),c=0;c<b.length;++c)b[c]=arguments[c];return this._validateParameters("rotateY",b,[["Number"]]),this._renderer.rotateY(a),this},d.prototype.rotateZ=function(a){if(!this._renderer.isP3D)throw"not supported in p2d. Please use webgl mode";for(var b=new Array(arguments.length),c=0;c<b.length;++c)b[c]=arguments[c];return this._validateParameters("rotateZ",b,[["Number"]]),this._renderer.rotateZ(a),this},d.prototype.scale=function(){for(var a,b,c,e=new Array(arguments.length),f=0;f<e.length;f++)e[f]=arguments[f];return e[0]instanceof d.Vector?(a=e[0].x,b=e[0].y,c=e[0].z):e[0]instanceof Array?(a=e[0][0],b=e[0][1],c=e[0][2]||1):1===e.length?a=b=c=e[0]:(a=e[0],b=e[1],c=e[2]||1),this._renderer.isP3D?this._renderer.scale.call(this._renderer,a,b,c):this._renderer.scale.call(this._renderer,a,b),this},d.prototype.shearX=function(a){return this._angleMode===e.DEGREES&&(a=this.radians(a)),this._renderer.shearX(a),this},d.prototype.shearY=function(a){return this._angleMode===e.DEGREES&&(a=this.radians(a)),this._renderer.shearY(a),this},d.prototype.translate=function(a,b,c){for(var d=new Array(arguments.length),e=0;e<d.length;++e)d[e]=arguments[e];return this._renderer.isP3D?(this._validateParameters("translate",d,[["Number","Number","Number"]]),this._renderer.translate(a,b,c)):(this._validateParameters("translate",d,[["Number","Number"]]),this._renderer.translate(a,b)),this},b.exports=d},{"./constants":36,"./core":37}],49:[function(a,b,c){"use strict";var d=a("./core"),e=a("./constants"),f=null,g=[],h=[],i=!1,j=!1,k=!1,l=!1,m=!0;d.prototype.beginContour=function(){return h=[],l=!0,this},d.prototype.beginShape=function(a){return f=a===e.POINTS||a===e.LINES||a===e.TRIANGLES||a===e.TRIANGLE_FAN||a===e.TRIANGLE_STRIP||a===e.QUADS||a===e.QUAD_STRIP?a:null,this._renderer.isP3D?this._renderer.beginShape(a):(g=[],h=[]),this},d.prototype.bezierVertex=function(a,b,c,d,e,f){if(0===g.length)throw"vertex() must be used once before calling bezierVertex()";i=!0;for(var j=[],k=0;k<arguments.length;k++)j[k]=arguments[k];return j.isVert=!1,l?h.push(j):g.push(j),this},d.prototype.curveVertex=function(a,b){return j=!0,this.vertex(a,b),this},d.prototype.endContour=function(){var a=h[0].slice();a.isVert=h[0].isVert,a.moveTo=!1,h.push(a),m&&(g.push(g[0]),m=!1);for(var b=0;b<h.length;b++)g.push(h[b]);return this},d.prototype.endShape=function(a){if(this._renderer.isP3D)this._renderer.endShape(a,j,i,k,l,f);else{if(0===g.length)return this;if(!this._renderer._doStroke&&!this._renderer._doFill)return this;var b=a===e.CLOSE;b&&!l&&g.push(g[0]),this._renderer.endShape(a,g,j,i,k,l,f),j=!1,i=!1,k=!1,l=!1,m=!0,b&&g.pop()}return this},d.prototype.quadraticVertex=function(a,b,c,d){if(this._contourInited){var f={};return f.x=a,f.y=b,f.x3=c,f.y3=d,f.type=e.QUADRATIC,this._contourVertices.push(f),this}if(!(g.length>0))throw"vertex() must be used once before calling quadraticVertex()";k=!0;for(var i=[],j=0;j<arguments.length;j++)i[j]=arguments[j];return i.isVert=!1,l?h.push(i):g.push(i),this},d.prototype.vertex=function(a,b,c){for(var d=new Array(arguments.length),e=0;e<d.length;++e)d[e]=arguments[e];if(this._renderer.isP3D)this._validateParameters("vertex",d,[["Number","Number","Number"]]),this._renderer.vertex(arguments[0],arguments[1],arguments[2]);else{this._validateParameters("vertex",d,[["Number","Number"],["Number","Number","Number"]]);var f=[];f.isVert=!0,f[0]=a,f[1]=b,f[2]=0,f[3]=0,f[4]=0,f[5]=this._renderer._getFill(),f[6]=this._renderer._getStroke(),c&&(f.moveTo=c),l?(0===h.length&&(f.moveTo=!0),h.push(f)):g.push(f)}return this},b.exports=d},{"./constants":36,"./core":37}],50:[function(a,b,c){"use strict";var d=a("../core/core");d.prototype.deviceOrientation=void 0,d.prototype.accelerationX=0,d.prototype.accelerationY=0,d.prototype.accelerationZ=0,d.prototype.pAccelerationX=0,d.prototype.pAccelerationY=0,d.prototype.pAccelerationZ=0,d.prototype._updatePAccelerations=function(){this._setProperty("pAccelerationX",this.accelerationX),this._setProperty("pAccelerationY",this.accelerationY),this._setProperty("pAccelerationZ",this.accelerationZ)},d.prototype.rotationX=0,d.prototype.rotationY=0,d.prototype.rotationZ=0,d.prototype.pRotationX=0,d.prototype.pRotationY=0,d.prototype.pRotationZ=0;var e,f,g,h=0,i=0,j=0,k="clockwise",l="clockwise",m="clockwise";d.prototype._updatePRotations=function(){this._setProperty("pRotationX",this.rotationX),this._setProperty("pRotationY",this.rotationY),this._setProperty("pRotationZ",this.rotationZ)},d.prototype.turnAxis=void 0;var n=.5,o=30;d.prototype.setMoveThreshold=function(a){"number"==typeof a&&(n=a)},d.prototype.setShakeThreshold=function(a){"number"==typeof a&&(o=a)},d.prototype._ondeviceorientation=function(a){this._updatePRotations(),this._setProperty("rotationX",a.beta),this._setProperty("rotationY",a.gamma),this._setProperty("rotationZ",a.alpha),this._handleMotion()},d.prototype._ondevicemotion=function(a){this._updatePAccelerations(),this._setProperty("accelerationX",2*a.acceleration.x),this._setProperty("accelerationY",2*a.acceleration.y),this._setProperty("accelerationZ",2*a.acceleration.z),this._handleMotion()},d.prototype._handleMotion=function(){90===window.orientation||-90===window.orientation?this._setProperty("deviceOrientation","landscape"):0===window.orientation?this._setProperty("deviceOrientation","portrait"):void 0===window.orientation&&this._setProperty("deviceOrientation","undefined");var a=this.deviceMoved||window.deviceMoved;"function"==typeof a&&(Math.abs(this.accelerationX-this.pAccelerationX)>n||Math.abs(this.accelerationY-this.pAccelerationY)>n||Math.abs(this.accelerationZ-this.pAccelerationZ)>n)&&a();var b=this.deviceTurned||window.deviceTurned;if("function"==typeof b){var c=this.rotationX+180,d=this.pRotationX+180,p=h+180;c-d>0&&270>c-d||-270>c-d?k="clockwise":(0>c-d||c-d>270)&&(k="counter-clockwise"),k!==e&&(p=c),Math.abs(c-p)>90&&Math.abs(c-p)<270&&(p=c,this._setProperty("turnAxis","X"),b()),e=k,h=p-180;var q=this.rotationY+180,r=this.pRotationY+180,s=i+180;q-r>0&&270>q-r||-270>q-r?l="clockwise":(0>q-r||q-this.pRotationY>270)&&(l="counter-clockwise"),l!==f&&(s=q),Math.abs(q-s)>90&&Math.abs(q-s)<270&&(s=q,this._setProperty("turnAxis","Y"),b()),f=l,i=s-180,this.rotationZ-this.pRotationZ>0&&this.rotationZ-this.pRotationZ<270||this.rotationZ-this.pRotationZ<-270?m="clockwise":(this.rotationZ-this.pRotationZ<0||this.rotationZ-this.pRotationZ>270)&&(m="counter-clockwise"),m!==g&&(j=this.rotationZ),Math.abs(this.rotationZ-j)>90&&Math.abs(this.rotationZ-j)<270&&(j=this.rotationZ,this._setProperty("turnAxis","Z"),b()),g=m,this._setProperty("turnAxis",void 0)}var t=this.deviceShaken||window.deviceShaken;if("function"==typeof t){var u,v;null!==this.pAccelerationX&&(u=Math.abs(this.accelerationX-this.pAccelerationX),v=Math.abs(this.accelerationY-this.pAccelerationY)),u+v>o&&t()}},b.exports=d},{"../core/core":37}],51:[function(a,b,c){"use strict";var d=a("../core/core"),e={};d.prototype.isKeyPressed=!1, +d.prototype.keyIsPressed=!1,d.prototype.key="",d.prototype.keyCode=0,d.prototype._onkeydown=function(a){if(!e[a.which]){this._setProperty("isKeyPressed",!0),this._setProperty("keyIsPressed",!0),this._setProperty("keyCode",a.which),e[a.which]=!0;var b=String.fromCharCode(a.which);b||(b=a.which),this._setProperty("key",b);var c=this.keyPressed||window.keyPressed;if("function"==typeof c&&!a.charCode){var d=c(a);d===!1&&a.preventDefault()}}},d.prototype._onkeyup=function(a){var b=this.keyReleased||window.keyReleased;this._setProperty("isKeyPressed",!1),this._setProperty("keyIsPressed",!1),this._setProperty("_lastKeyCodeTyped",null),e[a.which]=!1;var c=String.fromCharCode(a.which);if(c||(c=a.which),this._setProperty("key",c),this._setProperty("keyCode",a.which),"function"==typeof b){var d=b(a);d===!1&&a.preventDefault()}},d.prototype._onkeypress=function(a){if(a.which!==this._lastKeyCodeTyped){this._setProperty("keyCode",a.which),this._setProperty("_lastKeyCodeTyped",a.which),this._setProperty("key",String.fromCharCode(a.which));var b=this.keyTyped||window.keyTyped;if("function"==typeof b){var c=b(a);c===!1&&a.preventDefault()}}},d.prototype._onblur=function(a){e={}},d.prototype.keyIsDown=function(a){return e[a]},b.exports=d},{"../core/core":37}],52:[function(a,b,c){"use strict";function d(a,b){var c=a.getBoundingClientRect();return{x:b.clientX-c.left,y:b.clientY-c.top,winX:b.clientX,winY:b.clientY}}var e=a("../core/core"),f=a("../core/constants");e.prototype._hasMouseInteracted=!1,e.prototype.mouseX=0,e.prototype.mouseY=0,e.prototype.pmouseX=0,e.prototype.pmouseY=0,e.prototype.winMouseX=0,e.prototype.winMouseY=0,e.prototype.pwinMouseX=0,e.prototype.pwinMouseY=0,e.prototype.mouseButton=0,e.prototype.mouseIsPressed=!1,e.prototype.isMousePressed=!1,e.prototype._updateNextMouseCoords=function(a){var b=this.mouseX,c=this.mouseY,e=this.winMouseX,f=this.winMouseY;if("touchstart"===a.type||"touchmove"===a.type||"touchend"===a.type||a.touches)b=this.touchX,c=this.touchY,e=this.winTouchX,f=this.winTouchY;else if(null!==this._curElement){var g=d(this._curElement.elt,a);b=g.x,c=g.y,e=g.winX,f=g.winY}this._setProperty("mouseX",b),this._setProperty("mouseY",c),this._setProperty("winMouseX",e),this._setProperty("winMouseY",f),this._hasMouseInteracted||(this._updateMouseCoords(),this._setProperty("_hasMouseInteracted",!0))},e.prototype._updateMouseCoords=function(){this._setProperty("pmouseX",this.mouseX),this._setProperty("pmouseY",this.mouseY),this._setProperty("pwinMouseX",this.winMouseX),this._setProperty("pwinMouseY",this.winMouseY)},e.prototype._setMouseButton=function(a){1===a.button?this._setProperty("mouseButton",f.CENTER):2===a.button?this._setProperty("mouseButton",f.RIGHT):this._setProperty("mouseButton",f.LEFT)},e.prototype._onmousemove=function(a){var b,c=this._isGlobal?window:this;this._updateNextMouseCoords(a),this._updateNextTouchCoords(a),this.isMousePressed?"function"==typeof c.mouseDragged?(b=c.mouseDragged(a),b===!1&&a.preventDefault()):"function"==typeof c.touchMoved&&(b=c.touchMoved(a),b===!1&&a.preventDefault()):"function"==typeof c.mouseMoved&&(b=c.mouseMoved(a),b===!1&&a.preventDefault())},e.prototype._onmousedown=function(a){var b,c=this._isGlobal?window:this;this._setProperty("isMousePressed",!0),this._setProperty("mouseIsPressed",!0),this._setMouseButton(a),this._updateNextMouseCoords(a),this._updateNextTouchCoords(a),"function"==typeof c.mousePressed?(b=c.mousePressed(a),b===!1&&a.preventDefault()):"function"==typeof c.touchStarted&&(b=c.touchStarted(a),b===!1&&a.preventDefault())},e.prototype._onmouseup=function(a){var b,c=this._isGlobal?window:this;this._setProperty("isMousePressed",!1),this._setProperty("mouseIsPressed",!1),"function"==typeof c.mouseReleased?(b=c.mouseReleased(a),b===!1&&a.preventDefault()):"function"==typeof c.touchEnded&&(b=c.touchEnded(a),b===!1&&a.preventDefault())},e.prototype._ondragend=e.prototype._onmouseup,e.prototype._ondragover=e.prototype._onmousemove,e.prototype._onclick=function(a){var b=this._isGlobal?window:this;if("function"==typeof b.mouseClicked){var c=b.mouseClicked(a);c===!1&&a.preventDefault()}},e.prototype._onwheel=function(a){var b=this._isGlobal?window:this;if("function"==typeof b.mouseWheel){a.delta=a.deltaY;var c=b.mouseWheel(a);c===!1&&a.preventDefault()}},b.exports=e},{"../core/constants":36,"../core/core":37}],53:[function(a,b,c){"use strict";function d(a,b,c){c=c||0;var d=a.getBoundingClientRect(),e=b.touches[c]||b.changedTouches[c];return{x:e.clientX-d.left,y:e.clientY-d.top,winX:e.clientX,winY:e.clientY,id:e.identifier}}var e=a("../core/core");e.prototype._hasTouchInteracted=!1,e.prototype.touchX=0,e.prototype.touchY=0,e.prototype.ptouchX=0,e.prototype.ptouchY=0,e.prototype.winTouchX=0,e.prototype.winTouchY=0,e.prototype.pwinTouchX=0,e.prototype.pwinTouchY=0,e.prototype.touches=[],e.prototype.touchIsDown=!1,e.prototype._updateNextTouchCoords=function(a){var b=this.touchX,c=this.touchY,e=this.winTouchX,f=this.winTouchY;if("mousedown"!==a.type&&"mousemove"!==a.type&&"mouseup"!==a.type&&a.touches){if(null!==this._curElement){var g=d(this._curElement.elt,a,0);b=g.x,c=g.y,e=g.winX,f=g.winY;for(var h=[],i=0;i<a.touches.length;i++)h[i]=d(this._curElement.elt,a,i);this._setProperty("touches",h)}}else b=this.mouseX,c=this.mouseY,e=this.winMouseX,f=this.winMouseY;this._setProperty("touchX",b),this._setProperty("touchY",c),this._setProperty("winTouchX",e),this._setProperty("winTouchY",f),this._hasTouchInteracted||(this._updateTouchCoords(),this._setProperty("_hasTouchInteracted",!0))},e.prototype._updateTouchCoords=function(){this._setProperty("ptouchX",this.touchX),this._setProperty("ptouchY",this.touchY),this._setProperty("pwinTouchX",this.winTouchX),this._setProperty("pwinTouchY",this.winTouchY)},e.prototype._ontouchstart=function(a){var b,c=this._isGlobal?window:this;this._updateNextTouchCoords(a),this._updateNextMouseCoords(a),this._setProperty("touchIsDown",!0),"function"==typeof c.touchStarted?(b=c.touchStarted(a),b===!1&&a.preventDefault()):"function"==typeof c.mousePressed&&(b=c.mousePressed(a),b===!1&&a.preventDefault())},e.prototype._ontouchmove=function(a){var b,c=this._isGlobal?window:this;this._updateNextTouchCoords(a),this._updateNextMouseCoords(a),"function"==typeof c.touchMoved?(b=c.touchMoved(a),b===!1&&a.preventDefault()):"function"==typeof c.mouseDragged&&(b=c.mouseDragged(a),b===!1&&a.preventDefault())},e.prototype._ontouchend=function(a){this._updateNextTouchCoords(a),this._updateNextMouseCoords(a),0===this.touches.length&&this._setProperty("touchIsDown",!1);var b,c=this._isGlobal?window:this;"function"==typeof c.touchEnded?(b=c.touchEnded(a),b===!1&&a.preventDefault()):"function"==typeof c.mouseReleased&&(b=c.mouseReleased(a),b===!1&&a.preventDefault())},b.exports=e},{"../core/core":37}],54:[function(a,b,c){"use strict";function d(a){var b=3.5*a|0;if(b=1>b?1:248>b?b:248,g!==b){g=b,h=1+g<<1,i=new Int32Array(h),j=new Array(h);for(var c=0;h>c;c++)j[c]=new Int32Array(256);for(var d,e,f,k,l=1,m=b-1;b>l;l++){i[b+l]=i[m]=e=m*m,f=j[b+l],k=j[m--];for(var n=0;256>n;n++)f[n]=k[n]=e*n}d=i[b]=b*b,f=j[b];for(var o=0;256>o;o++)f[o]=d*o}}function e(a,b){for(var c=f._toPixels(a),e=a.width,k=a.height,l=e*k,m=new Int32Array(l),n=0;l>n;n++)m[n]=f._getARGB(c,n);var o,p,q,r,s,t,u,v,w,x,y=new Int32Array(l),z=new Int32Array(l),A=new Int32Array(l),B=new Int32Array(l),C=0;d(b);var D,E,F,G;for(E=0;k>E;E++){for(D=0;e>D;D++){if(r=q=p=s=o=0,t=D-g,0>t)x=-t,t=0;else{if(t>=e)break;x=0}for(F=x;h>F&&!(t>=e);F++){var H=m[t+C];G=j[F],s+=G[(-16777216&H)>>>24],p+=G[(16711680&H)>>16],q+=G[(65280&H)>>8],r+=G[255&H],o+=i[F],t++}u=C+D,y[u]=s/o,z[u]=p/o,A[u]=q/o,B[u]=r/o}C+=e}for(C=0,v=-g,w=v*e,E=0;k>E;E++){for(D=0;e>D;D++){if(r=q=p=s=o=0,0>v)x=u=-v,t=D;else{if(v>=k)break;x=0,u=v,t=D+w}for(F=x;h>F&&!(u>=k);F++)G=j[F],s+=G[y[t]],p+=G[z[t]],q+=G[A[t]],r+=G[B[t]],o+=i[F],u++,t+=e;m[D+C]=s/o<<24|p/o<<16|q/o<<8|r/o}C+=e,w+=e,v++}f._setPixels(c,m)}var f={};f._toPixels=function(a){return a instanceof ImageData?a.data:a.getContext("2d").getImageData(0,0,a.width,a.height).data},f._getARGB=function(a,b){var c=4*b;return a[c+3]<<24&4278190080|a[c]<<16&16711680|a[c+1]<<8&65280|255&a[c+2]},f._setPixels=function(a,b){for(var c=0,d=0,e=a.length;e>d;d++)c=4*d,a[c+0]=(16711680&b[d])>>>16,a[c+1]=(65280&b[d])>>>8,a[c+2]=255&b[d],a[c+3]=(4278190080&b[d])>>>24},f._toImageData=function(a){return a instanceof ImageData?a:a.getContext("2d").getImageData(0,0,a.width,a.height)},f._createImageData=function(a,b){return f._tmpCanvas=document.createElement("canvas"),f._tmpCtx=f._tmpCanvas.getContext("2d"),this._tmpCtx.createImageData(a,b)},f.apply=function(a,b,c){var d=a.getContext("2d"),e=d.getImageData(0,0,a.width,a.height),f=b(e,c);f instanceof ImageData?d.putImageData(f,0,0,0,0,a.width,a.height):d.putImageData(e,0,0,0,0,a.width,a.height)},f.threshold=function(a,b){var c=f._toPixels(a);void 0===b&&(b=.5);for(var d=Math.floor(255*b),e=0;e<c.length;e+=4){var g,h=c[e],i=c[e+1],j=c[e+2],k=.2126*h+.7152*i+.0722*j;g=k>=d?255:0,c[e]=c[e+1]=c[e+2]=g}},f.gray=function(a){for(var b=f._toPixels(a),c=0;c<b.length;c+=4){var d=b[c],e=b[c+1],g=b[c+2],h=.2126*d+.7152*e+.0722*g;b[c]=b[c+1]=b[c+2]=h}},f.opaque=function(a){for(var b=f._toPixels(a),c=0;c<b.length;c+=4)b[c+3]=255;return b},f.invert=function(a){for(var b=f._toPixels(a),c=0;c<b.length;c+=4)b[c]=255-b[c],b[c+1]=255-b[c+1],b[c+2]=255-b[c+2]},f.posterize=function(a,b){var c=f._toPixels(a);if(2>b||b>255)throw new Error("Level must be greater than 2 and less than 255 for posterize");for(var d=b-1,e=0;e<c.length;e+=4){var g=c[e],h=c[e+1],i=c[e+2];c[e]=255*(g*b>>8)/d,c[e+1]=255*(h*b>>8)/d,c[e+2]=255*(i*b>>8)/d}},f.dilate=function(a){for(var b,c,d,e,g,h,i,j,k,l,m,n,o,p,q,r,s,t=f._toPixels(a),u=0,v=t.length?t.length/4:0,w=new Int32Array(v);v>u;)for(b=u,c=u+a.width;c>u;)d=e=f._getARGB(t,u),i=u-1,h=u+1,j=u-a.width,k=u+a.width,b>i&&(i=u),h>=c&&(h=u),0>j&&(j=0),k>=v&&(k=u),n=f._getARGB(t,j),m=f._getARGB(t,i),o=f._getARGB(t,k),l=f._getARGB(t,h),g=77*(d>>16&255)+151*(d>>8&255)+28*(255&d),q=77*(m>>16&255)+151*(m>>8&255)+28*(255&m),p=77*(l>>16&255)+151*(l>>8&255)+28*(255&l),r=77*(n>>16&255)+151*(n>>8&255)+28*(255&n),s=77*(o>>16&255)+151*(o>>8&255)+28*(255&o),q>g&&(e=m,g=q),p>g&&(e=l,g=p),r>g&&(e=n,g=r),s>g&&(e=o,g=s),w[u++]=e;f._setPixels(t,w)},f.erode=function(a){for(var b,c,d,e,g,h,i,j,k,l,m,n,o,p,q,r,s,t=f._toPixels(a),u=0,v=t.length?t.length/4:0,w=new Int32Array(v);v>u;)for(b=u,c=u+a.width;c>u;)d=e=f._getARGB(t,u),i=u-1,h=u+1,j=u-a.width,k=u+a.width,b>i&&(i=u),h>=c&&(h=u),0>j&&(j=0),k>=v&&(k=u),n=f._getARGB(t,j),m=f._getARGB(t,i),o=f._getARGB(t,k),l=f._getARGB(t,h),g=77*(d>>16&255)+151*(d>>8&255)+28*(255&d),q=77*(m>>16&255)+151*(m>>8&255)+28*(255&m),p=77*(l>>16&255)+151*(l>>8&255)+28*(255&l),r=77*(n>>16&255)+151*(n>>8&255)+28*(255&n),s=77*(o>>16&255)+151*(o>>8&255)+28*(255&o),g>q&&(e=m,g=q),g>p&&(e=l,g=p),g>r&&(e=n,g=r),g>s&&(e=o,g=s),w[u++]=e;f._setPixels(t,w)};var g,h,i,j;f.blur=function(a,b){e(a,b)},b.exports=f},{}],55:[function(a,b,c){"use strict";var d=a("../core/core"),e=[];d.prototype.createImage=function(a,b){return new d.Image(a,b)},d.prototype.saveCanvas=function(){var a,b,c;if(3===arguments.length?(a=arguments[0],b=arguments[1],c=arguments[2]):2===arguments.length?"object"==typeof arguments[0]?(a=arguments[0],b=arguments[1]):(b=arguments[0],c=arguments[1]):1===arguments.length&&("object"==typeof arguments[0]?a=arguments[0]:b=arguments[0]),a instanceof d.Element&&(a=a.elt),a instanceof HTMLCanvasElement||(a=null),c||(c=d.prototype._checkFileExtension(b,c)[1],""===c&&(c="png")),a||this._curElement&&this._curElement.elt&&(a=this._curElement.elt),d.prototype._isSafari()){var e="Hello, Safari user!\n";e+="Now capturing a screenshot...\n",e+="To save this image,\n",e+="go to File --> Save As.\n",alert(e),window.location.href=a.toDataURL()}else{var f;if("undefined"==typeof c)c="png",f="image/png";else switch(c){case"png":f="image/png";break;case"jpeg":f="image/jpeg";break;case"jpg":f="image/jpeg";break;default:f="image/png"}var g="image/octet-stream",h=a.toDataURL(f);h=h.replace(f,g),d.prototype.downloadFile(h,b,c)}},d.prototype.saveFrames=function(a,b,c,f,g){var h=c||3;h=d.prototype.constrain(h,0,15),h=1e3*h;var i=f||15;i=d.prototype.constrain(i,0,22);var j=0,k=d.prototype._makeFrame,l=this._curElement.elt,m=setInterval(function(){k(a+j,b,l),j++},1e3/i);setTimeout(function(){if(clearInterval(m),g)g(e);else for(var a=0;a<e.length;a++){var b=e[a];d.prototype.downloadFile(b.imageData,b.filename,b.ext)}e=[]},h+.01)},d.prototype._makeFrame=function(a,b,c){var d;d=this?this._curElement.elt:c;var f;if(b)switch(b.toLowerCase()){case"png":f="image/png";break;case"jpeg":f="image/jpeg";break;case"jpg":f="image/jpeg";break;default:f="image/png"}else b="png",f="image/png";var g="image/octet-stream",h=d.toDataURL(f);h=h.replace(f,g);var i={};i.imageData=h,i.filename=a,i.ext=b,e.push(i)},b.exports=d},{"../core/core":37}],56:[function(a,b,c){"use strict";function d(a,b){return a>0&&b>a?a:b}var e=a("../core/core"),f=a("./filters"),g=a("../core/canvas"),h=a("../core/constants");a("../core/error_helpers"),e.prototype.loadImage=function(a,b,c){var d=new Image,f=new e.Image(1,1,this),g=e._getDecrementPreload.apply(this,arguments);return d.onload=function(){f.width=f.canvas.width=d.width,f.height=f.canvas.height=d.height,f.drawingContext.drawImage(d,0,0),"function"==typeof b&&b(f),g&&b!==g&&g()},d.onerror=function(a){e._friendlyFileLoadError(0,d.src),"function"==typeof c&&c!==g&&c(a)},0!==a.indexOf("data:image/")&&(d.crossOrigin="Anonymous"),d.src=a,f},e.prototype.image=function(a,b,c,e,f,h,i,j,k){if(arguments.length<=5)if(h=b||0,i=c||0,b=0,c=0,a.elt&&a.elt.videoWidth&&!a.canvas){var l=a.elt.videoWidth,m=a.elt.videoHeight;j=e||a.elt.width,k=f||a.elt.width*m/l,e=l,f=m}else j=e||a.width,k=f||a.height,e=a.width,f=a.height;else{if(9!==arguments.length)throw"Wrong number of arguments to image()";b=b||0,c=c||0,e=d(e,a.width),f=d(f,a.height),h=h||0,i=i||0,j=j||a.width,k=k||a.height}var n=g.modeAdjust(h,i,j,k,this._renderer._imageMode);this._renderer.image(a,b,c,e,f,n.x,n.y,n.w,n.h)},e.prototype.tint=function(){var a=this.color.apply(this,arguments);this._renderer._tint=a.levels},e.prototype.noTint=function(){this._renderer._tint=null},e.prototype._getTintedImageCanvas=function(a){if(!a.canvas)return a;var b=f._toPixels(a.canvas),c=document.createElement("canvas");c.width=a.canvas.width,c.height=a.canvas.height;for(var d=c.getContext("2d"),e=d.createImageData(a.canvas.width,a.canvas.height),g=e.data,h=0;h<b.length;h+=4){var i=b[h],j=b[h+1],k=b[h+2],l=b[h+3];g[h]=i*this._renderer._tint[0]/255,g[h+1]=j*this._renderer._tint[1]/255,g[h+2]=k*this._renderer._tint[2]/255,g[h+3]=l*this._renderer._tint[3]/255}return d.putImageData(e,0,0),c},e.prototype.imageMode=function(a){(a===h.CORNER||a===h.CORNERS||a===h.CENTER)&&(this._renderer._imageMode=a)},b.exports=e},{"../core/canvas":35,"../core/constants":36,"../core/core":37,"../core/error_helpers":40,"./filters":54}],57:[function(a,b,c){"use strict";var d=a("../core/core"),e=a("./filters");d.Image=function(a,b){this.width=a,this.height=b,this.canvas=document.createElement("canvas"),this.canvas.width=this.width,this.canvas.height=this.height,this.drawingContext=this.canvas.getContext("2d"),this._pixelDensity=1,this.isTexture=!1,this.pixels=[]},d.Image.prototype._setProperty=function(a,b){this[a]=b},d.Image.prototype.loadPixels=function(){d.Renderer2D.prototype.loadPixels.call(this)},d.Image.prototype.updatePixels=function(a,b,c,e){d.Renderer2D.prototype.updatePixels.call(this,a,b,c,e)},d.Image.prototype.get=function(a,b,c,e){return d.Renderer2D.prototype.get.call(this,a,b,c,e)},d.Image.prototype.set=function(a,b,c){d.Renderer2D.prototype.set.call(this,a,b,c)},d.Image.prototype.resize=function(a,b){0===a&&0===b?(a=this.canvas.width,b=this.canvas.height):0===a?a=this.canvas.width*b/this.canvas.height:0===b&&(b=this.canvas.height*a/this.canvas.width),a=Math.floor(a),b=Math.floor(b);var c=document.createElement("canvas");c.width=a,c.height=b,c.getContext("2d").drawImage(this.canvas,0,0,this.canvas.width,this.canvas.height,0,0,c.width,c.height),this.canvas.width=this.width=a,this.canvas.height=this.height=b,this.drawingContext.drawImage(c,0,0,a,b,0,0,a,b),this.pixels.length>0&&this.loadPixels()},d.Image.prototype.copy=function(){d.prototype.copy.apply(this,arguments)},d.Image.prototype.mask=function(a){void 0===a&&(a=this);var b=this.drawingContext.globalCompositeOperation,c=1;a instanceof d.Renderer&&(c=a._pInst._pixelDensity);var e=[a,0,0,c*a.width,c*a.height,0,0,this.width,this.height];this.drawingContext.globalCompositeOperation="destination-in",d.Image.prototype.copy.apply(this,e),this.drawingContext.globalCompositeOperation=b},d.Image.prototype.filter=function(a,b){e.apply(this.canvas,e[a.toLowerCase()],b)},d.Image.prototype.blend=function(){d.prototype.blend.apply(this,arguments)},d.Image.prototype.save=function(a,b){var c;if(b)switch(b.toLowerCase()){case"png":c="image/png";break;case"jpeg":c="image/jpeg";break;case"jpg":c="image/jpeg";break;default:c="image/png"}else b="png",c="image/png";var e="image/octet-stream",f=this.canvas.toDataURL(c);f=f.replace(c,e),d.prototype.downloadFile(f,a,b)},b.exports=d.Image},{"../core/core":37,"./filters":54}],58:[function(a,b,c){"use strict";var d=a("../core/core"),e=a("./filters");a("../color/p5.Color"),d.prototype.pixels=[],d.prototype.blend=function(){this._renderer?this._renderer.blend.apply(this._renderer,arguments):d.Renderer2D.prototype.blend.apply(this,arguments)},d.prototype.copy=function(){d.Renderer2D._copyHelper.apply(this,arguments)},d.prototype.filter=function(a,b){e.apply(this.canvas,e[a.toLowerCase()],b)},d.prototype.get=function(a,b,c,d){return this._renderer.get(a,b,c,d)},d.prototype.loadPixels=function(){this._renderer.loadPixels()},d.prototype.set=function(a,b,c){this._renderer.set(a,b,c)},d.prototype.updatePixels=function(a,b,c,d){0!==this.pixels.length&&this._renderer.updatePixels(a,b,c,d)},b.exports=d},{"../color/p5.Color":31,"../core/core":37,"./filters":54}],59:[function(a,b,c){"use strict";function d(a,b){var c={};if(b=b||[],"undefined"==typeof b)for(var d=0;d<a.length;d++)b[d.toString()]=d;for(var e=0;e<b.length;e++){var f=b[e],g=a[e];c[f]=g}return c}function e(a){return a.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function f(a,b){b&&b!==!0&&"true"!==b||(b=""),a||(a="untitled");var c="";return a&&a.indexOf(".")>-1&&(c=a.split(".").pop()),b&&c!==b&&(c=b,a=a+"."+c),[a,c]}function g(a){document.body.removeChild(a.target)}var h=a("../core/core"),i=a("reqwest"),j=a("opentype.js");a("../core/error_helpers"),h._getDecrementPreload=function(){var a=arguments[arguments.length-1];return(window.preload||this&&this.preload)&&"function"==typeof a?a:null},h.prototype.loadFont=function(a,b,c){var d=new h.Font(this),e=h._getDecrementPreload.apply(this,arguments);return j.load(a,function(f,g){if(f)return"undefined"!=typeof c&&c!==e?c(f):(h._friendlyFileLoadError(4,a),void console.error(f,a));d.font=g,"undefined"!=typeof b&&b(d),e&&b!==e&&e();var i,j,k=["ttf","otf","woff","woff2"],l=a.split("\\").pop().split("/").pop(),m=l.lastIndexOf("."),n=1>m?null:l.substr(m+1);k.indexOf(n)>-1&&(i=l.substr(0,m),j=document.createElement("style"),j.appendChild(document.createTextNode("\n@font-face {\nfont-family: "+i+";\nsrc: url("+a+");\n}\n")),document.head.appendChild(j))}),d},h.prototype.createInput=function(){throw"not yet implemented"},h.prototype.createReader=function(){throw"not yet implemented"},h.prototype.loadBytes=function(){throw"not yet implemented"},h.prototype.loadJSON=function(){for(var a,b=arguments[0],c=arguments[1],d=h._getDecrementPreload.apply(this,arguments),e={},f="json",g=2;g<arguments.length;g++){var j=arguments[g];"string"==typeof j?("jsonp"===j||"json"===j)&&(f=j):"function"==typeof j&&(a=j)}return i({url:b,type:f,crossOrigin:!0,error:function(b){a?a(b):console.log(b.statusText)},success:function(a){for(var b in a)e[b]=a[b];"undefined"!=typeof c&&c(a),d&&c!==d&&d()}}),e},h.prototype.loadStrings=function(a,b,c){var d=[],e=new XMLHttpRequest,f=h._getDecrementPreload.apply(this,arguments);return e.addEventListener("error",function(a){c?c(a):console.log(a.responseText)}),e.open("GET",a,!0),e.onreadystatechange=function(){if(4===e.readyState)if(200===e.status){var a=e.responseText.match(/[^\r\n]+/g);for(var g in a)d[g]=a[g];"undefined"!=typeof b&&b(d),f&&b!==f&&f()}else c?c(e):console.log(e.statusText)},e.send(null),d},h.prototype.loadTable=function(a){for(var b=null,c=[],e=!1,f=",",g=!1,j=h._getDecrementPreload.apply(this,arguments),k=1;k<arguments.length;k++)if("function"==typeof arguments[k]&&arguments[k]!==j)b=arguments[k];else if("string"==typeof arguments[k])if(c.push(arguments[k]),"header"===arguments[k]&&(e=!0),"csv"===arguments[k]){if(g)throw new Error("Cannot set multiple separator types.");f=",",g=!0}else if("tsv"===arguments[k]){if(g)throw new Error("Cannot set multiple separator types.");f=" ",g=!0}var l=new h.Table;return i({url:a,crossOrigin:!0,type:"csv"}).then(function(a){a=a.responseText;for(var c,g={},i=0,m=1,n=2,o=4,p='"',q="\r",r="\n",s=[],t=0,u=null,v=function(){g.escaped=!1,u=[],x()},w=function(){g.currentState=o,s.push(u),u=null},x=function(){g.currentState=i,g.token=""},y=function(){u.push(g.token),x()};;){if(c=a[t++],null==c){if(g.escaped)throw new Error("Unclosed quote in file.");if(u){y(),w();break}}if(null===u&&v(),g.currentState===i){if(c===p){g.escaped=!0,g.currentState=m;continue}g.currentState=m}g.currentState===m&&g.escaped?c===p?a[t]===p?(g.token+=p,t++):(g.escaped=!1,g.currentState=n):g.token+=c:c===q?(a[t]===r&&t++,y(),w()):c===r?(y(),w()):c===f?y():g.currentState===m&&(g.token+=c)}if(e)l.columns=s.shift();else for(k=0;k<s[0].length;k++)l.columns[k]="null";var z;for(k=0;k<s.length&&(k!==s.length-1||1!==s[k].length||"undefined"!==s[k][0]);k++)z=new h.TableRow,z.arr=s[k],z.obj=d(s[k],l.columns),l.addRow(z);null!==b&&b(l),j&&b!==j&&j()}).fail(function(c,d){h._friendlyFileLoadError(2,a),"function"==typeof b&&b!==j&&b(!1)}),l},h.prototype.parseXML=function(a){var b,c=new h.XML;if(a.children.length){for(b=0;b<a.children.length;b++){var d=parseXML(a.children[b]);c.addChild(d)}c.setName(a.nodeName),c._setCont(a.textContent),c._setAttributes(a);for(var e=0;e<c.children.length;e++)c.children[e].parent=c;return c}return c.setName(a.nodeName),c._setCont(a.textContent),c._setAttributes(a),c},h.prototype.loadXML=function(a,b,c){var d={},e=h._getDecrementPreload.apply(this,arguments);return i({url:a,type:"xml",crossOrigin:!0,error:function(a){c?c(a):console.log(a.statusText)}}).then(function(a){var c=parseXML(a.documentElement);for(var f in c)d[f]=c[f];"undefined"!=typeof b&&b(d),e&&b!==e&&e()}),d},h.prototype.selectFolder=function(){throw"not yet implemented"},h.prototype.selectInput=function(){throw"not yet implemented"},h.prototype.httpGet=function(){for(var a=new Array(arguments.length),b=0;b<a.length;++b)a[b]=arguments[b];a.push("GET"),h.prototype.httpDo.apply(this,a)},h.prototype.httpPost=function(){for(var a=new Array(arguments.length),b=0;b<a.length;++b)a[b]=arguments[b];a.push("POST"),h.prototype.httpDo.apply(this,a)},h.prototype.httpDo=function(){if("object"==typeof arguments[0])i(arguments[0]);else{for(var a,b,c="GET",d=arguments[0],e={},f="",g=1;g<arguments.length;g++){var h=arguments[g];"string"==typeof h?"GET"===h||"POST"===h||"PUT"===h?c=h:f=h:"object"==typeof h?e=h:"function"==typeof h&&(a?b=h:a=h)}""===f&&(f=-1!==d.indexOf("json")?"json":-1!==d.indexOf("xml")?"xml":"text"),i({url:d,method:c,data:e,type:f,crossOrigin:!0,success:function(b){"undefined"!=typeof a&&a("text"===f?b.response:b)},error:function(a){b?b(a):console.log(a.statusText)}})}},window.URL=window.URL||window.webkitURL,h.prototype._pWriters=[],h.prototype.beginRaw=function(){throw"not yet implemented"},h.prototype.beginRecord=function(){throw"not yet implemented"},h.prototype.createOutput=function(){throw"not yet implemented"},h.prototype.createWriter=function(a,b){var c;for(var d in h.prototype._pWriters)if(h.prototype._pWriters[d].name===a)return c=new h.PrintWriter(a+window.millis(),b),h.prototype._pWriters.push(c),c;return c=new h.PrintWriter(a,b),h.prototype._pWriters.push(c),c},h.prototype.endRaw=function(){throw"not yet implemented"},h.prototype.endRecord=function(){throw"not yet implemented"},h.PrintWriter=function(a,b){var c=this;this.name=a,this.content="",this.print=function(a){this.content+=a},this.print=function(a){this.content+=a+"\n"},this.flush=function(){this.content=""},this.close=function(){var d=[];d.push(this.content),h.prototype.writeFile(d,a,b);for(var e in h.prototype._pWriters)h.prototype._pWriters[e].name===this.name&&h.prototype._pWriters.splice(e,1);c.flush(),c={}}},h.prototype.saveBytes=function(){throw"not yet implemented"},h.prototype.save=function(a,b,c){var d=arguments,e=this._curElement.elt;if(0===d.length)return void h.prototype.saveCanvas(e);if(d[0]instanceof h.Renderer||d[0]instanceof h.Graphics)return void h.prototype.saveCanvas(d[0].elt,d[1],d[2]);if(1===d.length&&"string"==typeof d[0])h.prototype.saveCanvas(e,d[0]);else{var g=f(d[1],d[2])[1];switch(g){case"json":return void h.prototype.saveJSON(d[0],d[1],d[2]);case"txt":return void h.prototype.saveStrings(d[0],d[1],d[2]);default:d[0]instanceof Array?h.prototype.saveStrings(d[0],d[1],d[2]):d[0]instanceof h.Table?h.prototype.saveTable(d[0],d[1],d[2],d[3]):d[0]instanceof h.Image?h.prototype.saveCanvas(d[0].canvas,d[1]):d[0]instanceof h.SoundFile&&h.prototype.saveSound(d[0],d[1],d[2],d[3])}}},h.prototype.saveJSON=function(a,b,c){var d;d=c?JSON.stringify(a):JSON.stringify(a,void 0,2),console.log(d),this.saveStrings(d.split("\n"),b,"json")},h.prototype.saveJSONObject=h.prototype.saveJSON,h.prototype.saveJSONArray=h.prototype.saveJSON,h.prototype.saveStream=function(){throw"not yet implemented"},h.prototype.saveStrings=function(a,b,c){for(var d=c||"txt",e=this.createWriter(b,d),f=0;f<a.length;f++)f<a.length-1?e.print(a[f]):e.print(a[f]);e.close(),e.flush()},h.prototype.saveXML=function(){throw"not yet implemented"},h.prototype.selectOutput=function(){throw"not yet implemented"},h.prototype.saveTable=function(a,b,c){var d=this.createWriter(b,c),f=a.columns,g=",";if("tsv"===c&&(g=" "),"html"!==c){if("0"!==f[0])for(var h=0;h<f.length;h++)h<f.length-1?d.print(f[h]+g):d.print(f[h]);for(var i=0;i<a.rows.length;i++){var j;for(j=0;j<a.rows[i].arr.length;j++)j<a.rows[i].arr.length-1?d.print(a.rows[i].arr[j]+g):i<a.rows.length-1?d.print(a.rows[i].arr[j]):d.print(a.rows[i].arr[j])}}else{d.print("<html>"),d.print("<head>");var k=' <meta http-equiv="content-type" content';if(k+='="text/html;charset=utf-8" />',d.print(k),d.print("</head>"),d.print("<body>"),d.print(" <table>"),"0"!==f[0]){d.print(" <tr>");for(var l=0;l<f.length;l++){var m=e(f[l]);d.print(" <td>"+m),d.print(" </td>")}d.print(" </tr>")}for(var n=0;n<a.rows.length;n++){d.print(" <tr>");for(var o=0;o<a.columns.length;o++){var p=a.rows[n].getString(o),q=e(p);d.print(" <td>"+q),d.print(" </td>")}d.print(" </tr>")}d.print(" </table>"),d.print("</body>"),d.print("</html>")}d.close(),d.flush()},h.prototype.writeFile=function(a,b,c){var d="application/octet-stream";h.prototype._isSafari()&&(d="text/plain");var e=new Blob(a,{type:d}),f=window.URL.createObjectURL(e);h.prototype.downloadFile(f,b,c)},h.prototype.downloadFile=function(a,b,c){var d=f(b,c),e=d[0],i=d[1],j=document.createElement("a");if(j.href=a,j.download=e,j.onclick=g,j.style.display="none",document.body.appendChild(j),h.prototype._isSafari()){var k="Hello, Safari user! To download this file...\n";k+="1. Go to File --> Save As.\n",k+='2. Choose "Page Source" as the Format.\n',k+='3. Name it with this extension: ."'+i+'"',alert(k)}j.click(),a=null},h.prototype._checkFileExtension=f,h.prototype._isSafari=function(){var a=Object.prototype.toString.call(window.HTMLElement);return a.indexOf("Constructor")>0},b.exports=h},{"../core/core":37,"../core/error_helpers":40,"opentype.js":8,reqwest:27}],60:[function(a,b,c){"use strict";var d=a("../core/core");d.Table=function(a){this.columns=[],this.rows=[]},d.Table.prototype.addRow=function(a){var b=a||new d.TableRow;if("undefined"==typeof b.arr||"undefined"==typeof b.obj)throw"invalid TableRow: "+b;return b.table=this,this.rows.push(b),b},d.Table.prototype.removeRow=function(a){this.rows[a].table=null;var b=this.rows.splice(a+1,this.rows.length);this.rows.pop(),this.rows=this.rows.concat(b)},d.Table.prototype.getRow=function(a){return this.rows[a]},d.Table.prototype.getRows=function(){return this.rows},d.Table.prototype.findRow=function(a,b){if("string"==typeof b){for(var c=0;c<this.rows.length;c++)if(this.rows[c].obj[b]===a)return this.rows[c]}else for(var d=0;d<this.rows.length;d++)if(this.rows[d].arr[b]===a)return this.rows[d];return null},d.Table.prototype.findRows=function(a,b){var c=[];if("string"==typeof b)for(var d=0;d<this.rows.length;d++)this.rows[d].obj[b]===a&&c.push(this.rows[d]);else for(var e=0;e<this.rows.length;e++)this.rows[e].arr[b]===a&&c.push(this.rows[e]);return c},d.Table.prototype.matchRow=function(a,b){if("number"==typeof b){for(var c=0;c<this.rows.length;c++)if(this.rows[c].arr[b].match(a))return this.rows[c]}else for(var d=0;d<this.rows.length;d++)if(this.rows[d].obj[b].match(a))return this.rows[d];return null},d.Table.prototype.matchRows=function(a,b){var c=[];if("number"==typeof b)for(var d=0;d<this.rows.length;d++)this.rows[d].arr[b].match(a)&&c.push(this.rows[d]);else for(var e=0;e<this.rows.length;e++)this.rows[e].obj[b].match(a)&&c.push(this.rows[e]);return c},d.Table.prototype.getColumn=function(a){var b=[];if("string"==typeof a)for(var c=0;c<this.rows.length;c++)b.push(this.rows[c].obj[a]);else for(var d=0;d<this.rows.length;d++)b.push(this.rows[d].arr[a]);return b},d.Table.prototype.clearRows=function(){delete this.rows,this.rows=[]},d.Table.prototype.addColumn=function(a){var b=a||null;this.columns.push(b)},d.Table.prototype.getColumnCount=function(){return this.columns.length},d.Table.prototype.getRowCount=function(){return this.rows.length},d.Table.prototype.removeTokens=function(a,b){for(var c=function(a){return a.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")},d=[],e=0;e<a.length;e++)d.push(c(a.charAt(e)));var f=new RegExp(d.join("|"),"g");if("undefined"==typeof b)for(var g=0;g<this.columns.length;g++)for(var h=0;h<this.rows.length;h++){var i=this.rows[h].arr[g];i=i.replace(f,""),this.rows[h].arr[g]=i,this.rows[h].obj[this.columns[g]]=i}else if("string"==typeof b)for(var j=0;j<this.rows.length;j++){var k=this.rows[j].obj[b];k=k.replace(f,""),this.rows[j].obj[b]=k;var l=this.columns.indexOf(b);this.rows[j].arr[l]=k}else for(var m=0;m<this.rows.length;m++){var n=this.rows[m].arr[b];n=n.replace(f,""),this.rows[m].arr[b]=n,this.rows[m].obj[this.columns[b]]=n}},d.Table.prototype.trim=function(a){var b=new RegExp(" ","g");if("undefined"==typeof a)for(var c=0;c<this.columns.length;c++)for(var d=0;d<this.rows.length;d++){var e=this.rows[d].arr[c];e=e.replace(b,""),this.rows[d].arr[c]=e,this.rows[d].obj[this.columns[c]]=e}else if("string"==typeof a)for(var f=0;f<this.rows.length;f++){var g=this.rows[f].obj[a];g=g.replace(b,""),this.rows[f].obj[a]=g;var h=this.columns.indexOf(a);this.rows[f].arr[h]=g}else for(var i=0;i<this.rows.length;i++){var j=this.rows[i].arr[a];j=j.replace(b,""),this.rows[i].arr[a]=j,this.rows[i].obj[this.columns[a]]=j}},d.Table.prototype.removeColumn=function(a){var b,c;"string"==typeof a?(b=a,c=this.columns.indexOf(a),console.log("string")):(c=a,b=this.columns[a]);var d=this.columns.splice(c+1,this.columns.length);this.columns.pop(),this.columns=this.columns.concat(d);for(var e=0;e<this.rows.length;e++){var f=this.rows[e].arr,g=f.splice(c+1,f.length);f.pop(),this.rows[e].arr=f.concat(g),delete this.rows[e].obj[b]}},d.Table.prototype.set=function(a,b,c){this.rows[a].set(b,c)},d.Table.prototype.setNum=function(a,b,c){this.rows[a].setNum(b,c)},d.Table.prototype.setString=function(a,b,c){this.rows[a].setString(b,c); +},d.Table.prototype.get=function(a,b){return this.rows[a].get(b)},d.Table.prototype.getNum=function(a,b){return this.rows[a].getNum(b)},d.Table.prototype.getString=function(a,b){return this.rows[a].getString(b)},d.Table.prototype.getObject=function(a){for(var b,c,d,e={},f=0;f<this.rows.length;f++)if(b=this.rows[f].obj,"string"==typeof a){if(c=this.columns.indexOf(a),!(c>=0))throw'This table has no column named "'+a+'"';d=b[a],e[d]=b}else e[f]=this.rows[f].obj;return e},d.Table.prototype.getArray=function(){for(var a=[],b=0;b<this.rows.length;b++)a.push(this.rows[b].arr);return a},b.exports=d.Table},{"../core/core":37}],61:[function(a,b,c){"use strict";var d=a("../core/core");d.TableRow=function(a,b){var c=[],d={};a&&(b=b||",",c=a.split(b));for(var e=0;e<c.length;e++){var f=e,g=c[e];d[f]=g}this.arr=c,this.obj=d,this.table=null},d.TableRow.prototype.set=function(a,b){if("string"==typeof a){var c=this.table.columns.indexOf(a);if(!(c>=0))throw'This table has no column named "'+a+'"';this.obj[a]=b,this.arr[c]=b}else{if(!(a<this.table.columns.length))throw"Column #"+a+" is out of the range of this table";this.arr[a]=b;var d=this.table.columns[a];this.obj[d]=b}},d.TableRow.prototype.setNum=function(a,b){var c=parseFloat(b,10);this.set(a,c)},d.TableRow.prototype.setString=function(a,b){var c=b.toString();this.set(a,c)},d.TableRow.prototype.get=function(a){return"string"==typeof a?this.obj[a]:this.arr[a]},d.TableRow.prototype.getNum=function(a){var b;if(b="string"==typeof a?parseFloat(this.obj[a],10):parseFloat(this.arr[a],10),"NaN"===b.toString())throw"Error: "+this.obj[a]+" is NaN (Not a Number)";return b},d.TableRow.prototype.getString=function(a){return"string"==typeof a?this.obj[a].toString():this.arr[a].toString()},b.exports=d.TableRow},{"../core/core":37}],62:[function(a,b,c){"use strict";var d=a("../core/core");d.XML=function(){this.name=null,this.attributes={},this.children=[],this.parent=null,this.content=null},d.XML.prototype.getParent=function(){return this.parent},d.XML.prototype.getName=function(){return this.name},d.XML.prototype.setName=function(a){this.name=a},d.XML.prototype.hasChildren=function(){return this.children.length>0},d.XML.prototype.listChildren=function(){return this.children.map(function(a){return a.name})},d.XML.prototype.getChildren=function(a){return a?this.children.filter(function(b){return b.name===a}):this.children},d.XML.prototype.getChild=function(a){return"string"==typeof a?this.children.find(function(b){return b.name===a}):this.children[a]},d.XML.prototype.addChild=function(a){a instanceof d.XML&&this.children.push(a)},d.XML.prototype.removeChild=function(a){var b=-1;if("string"==typeof a){for(var c=0;c<this.children.length;c++)if(this.children[c].name===a){b=c;break}}else b=a;-1!==b&&this.children.splice(b,1)},d.XML.prototype.getAttributeCount=function(){return Object.keys(this.attributes).length},d.XML.prototype.listAttributes=function(){return Object.keys(this.attributes)},d.XML.prototype.hasAttribute=function(a){return this.attributes[a]?!0:!1},d.XML.prototype.getNumber=function(a,b){return Number(this.attributes[a])||b||0},d.XML.prototype.getString=function(a,b){return String(this.attributes[a])||b||null},d.XML.prototype.setAttribute=function(a,b){this.attributes[a]&&(this.attributes[a]=b)},d.XML.prototype.getContent=function(a){return this.content||a||null},d.XML.prototype.setContent=function(a){this.children.length||(this.content=a)},d.XML.prototype._setCont=function(a){var b;b=a,b=b.replace(/\s\s+/g,","),this.content=b},d.XML.prototype._setAttributes=function(a){var b,c={};for(b=0;b<a.attributes.length;b++)c[a.attributes[b].nodeName]=a.attributes[b].nodeValue;this.attributes=c},b.exports=d.XML},{"../core/core":37}],63:[function(a,b,c){"use strict";var d=a("../core/core");d.prototype.abs=Math.abs,d.prototype.ceil=Math.ceil,d.prototype.constrain=function(a,b,c){return Math.max(Math.min(a,c),b)},d.prototype.dist=function(a,b,c,d,e,f){return 4===arguments.length?Math.sqrt((c-a)*(c-a)+(d-b)*(d-b)):6===arguments.length?Math.sqrt((d-a)*(d-a)+(e-b)*(e-b)+(f-c)*(f-c)):void 0},d.prototype.exp=Math.exp,d.prototype.floor=Math.floor,d.prototype.lerp=function(a,b,c){return c*(b-a)+a},d.prototype.log=Math.log,d.prototype.mag=function(a,b){return Math.sqrt(a*a+b*b)},d.prototype.map=function(a,b,c,d,e){return(a-b)/(c-b)*(e-d)+d},d.prototype.max=function(){return arguments[0]instanceof Array?Math.max.apply(null,arguments[0]):Math.max.apply(null,arguments)},d.prototype.min=function(){return arguments[0]instanceof Array?Math.min.apply(null,arguments[0]):Math.min.apply(null,arguments)},d.prototype.norm=function(a,b,c){return this.map(a,b,c,0,1)},d.prototype.pow=Math.pow,d.prototype.round=Math.round,d.prototype.sq=function(a){return a*a},d.prototype.sqrt=Math.sqrt,b.exports=d},{"../core/core":37}],64:[function(a,b,c){"use strict";var d=a("../core/core");d.prototype.createVector=function(a,b,c){return this instanceof d?new d.Vector(this,arguments):new d.Vector(a,b,c)},b.exports=d},{"../core/core":37}],65:[function(a,b,c){"use strict";var d,e=a("../core/core"),f=4,g=1<<f,h=8,i=1<<h,j=4095,k=4,l=.5,m=function(a){return.5*(1-Math.cos(a*Math.PI))};e.prototype.noise=function(a,b,c){if(b=b||0,c=c||0,null==d){d=new Array(j+1);for(var e=0;j+1>e;e++)d[e]=Math.random()}0>a&&(a=-a),0>b&&(b=-b),0>c&&(c=-c);for(var n,o,p,q,r,s=Math.floor(a),t=Math.floor(b),u=Math.floor(c),v=a-s,w=b-t,x=c-u,y=0,z=.5,A=0;k>A;A++){var B=s+(t<<f)+(u<<h);n=m(v),o=m(w),p=d[B&j],p+=n*(d[B+1&j]-p),q=d[B+g&j],q+=n*(d[B+g+1&j]-q),p+=o*(q-p),B+=i,q=d[B&j],q+=n*(d[B+1&j]-q),r=d[B+g&j],r+=n*(d[B+g+1&j]-r),q+=o*(r-q),p+=m(x)*(q-p),y+=p*z,z*=l,s<<=1,v*=2,t<<=1,w*=2,u<<=1,x*=2,v>=1&&(s++,v--),w>=1&&(t++,w--),x>=1&&(u++,x--)}return y},e.prototype.noiseDetail=function(a,b){a>0&&(k=a),b>0&&(l=b)},e.prototype.noiseSeed=function(a){var b=function(){var a,b,c=4294967296,d=1664525,e=1013904223;return{setSeed:function(d){b=a=(null==d?Math.random()*c:d)>>>0},getSeed:function(){return a},rand:function(){return b=(d*b+e)%c,b/c}}}();b.setSeed(a),d=new Array(j+1);for(var c=0;j+1>c;c++)d[c]=b.rand()},b.exports=e},{"../core/core":37}],66:[function(a,b,c){"use strict";var d=a("../core/core"),e=a("./polargeometry"),f=a("../core/constants");d.Vector=function(){var a,b,c;arguments[0]instanceof d?(this.p5=arguments[0],a=arguments[1][0]||0,b=arguments[1][1]||0,c=arguments[1][2]||0):(a=arguments[0]||0,b=arguments[1]||0,c=arguments[2]||0),this.x=a,this.y=b,this.z=c},d.Vector.prototype.toString=function(){return"p5.Vector Object : ["+this.x+", "+this.y+", "+this.z+"]"},d.Vector.prototype.set=function(a,b,c){return a instanceof d.Vector?(this.x=a.x||0,this.y=a.y||0,this.z=a.z||0,this):a instanceof Array?(this.x=a[0]||0,this.y=a[1]||0,this.z=a[2]||0,this):(this.x=a||0,this.y=b||0,this.z=c||0,this)},d.Vector.prototype.copy=function(){return this.p5?new d.Vector(this.p5,[this.x,this.y,this.z]):new d.Vector(this.x,this.y,this.z)},d.Vector.prototype.add=function(a,b,c){return a instanceof d.Vector?(this.x+=a.x||0,this.y+=a.y||0,this.z+=a.z||0,this):a instanceof Array?(this.x+=a[0]||0,this.y+=a[1]||0,this.z+=a[2]||0,this):(this.x+=a||0,this.y+=b||0,this.z+=c||0,this)},d.Vector.prototype.sub=function(a,b,c){return a instanceof d.Vector?(this.x-=a.x||0,this.y-=a.y||0,this.z-=a.z||0,this):a instanceof Array?(this.x-=a[0]||0,this.y-=a[1]||0,this.z-=a[2]||0,this):(this.x-=a||0,this.y-=b||0,this.z-=c||0,this)},d.Vector.prototype.mult=function(a){return this.x*=a||0,this.y*=a||0,this.z*=a||0,this},d.Vector.prototype.div=function(a){return this.x/=a,this.y/=a,this.z/=a,this},d.Vector.prototype.mag=function(){return Math.sqrt(this.magSq())},d.Vector.prototype.magSq=function(){var a=this.x,b=this.y,c=this.z;return a*a+b*b+c*c},d.Vector.prototype.dot=function(a,b,c){return a instanceof d.Vector?this.dot(a.x,a.y,a.z):this.x*(a||0)+this.y*(b||0)+this.z*(c||0)},d.Vector.prototype.cross=function(a){var b=this.y*a.z-this.z*a.y,c=this.z*a.x-this.x*a.z,e=this.x*a.y-this.y*a.x;return this.p5?new d.Vector(this.p5,[b,c,e]):new d.Vector(b,c,e)},d.Vector.prototype.dist=function(a){var b=a.copy().sub(this);return b.mag()},d.Vector.prototype.normalize=function(){return 0===this.mag()?this:this.div(this.mag())},d.Vector.prototype.limit=function(a){var b=this.magSq();return b>a*a&&(this.div(Math.sqrt(b)),this.mult(a)),this},d.Vector.prototype.setMag=function(a){return this.normalize().mult(a)},d.Vector.prototype.heading=function(){var a=Math.atan2(this.y,this.x);return this.p5?this.p5._angleMode===f.RADIANS?a:e.radiansToDegrees(a):a},d.Vector.prototype.rotate=function(a){this.p5&&this.p5._angleMode===f.DEGREES&&(a=e.degreesToRadians(a));var b=this.heading()+a,c=this.mag();return this.x=Math.cos(b)*c,this.y=Math.sin(b)*c,this},d.Vector.prototype.lerp=function(a,b,c,e){return a instanceof d.Vector?this.lerp(a.x,a.y,a.z,b):(this.x+=(a-this.x)*e||0,this.y+=(b-this.y)*e||0,this.z+=(c-this.z)*e||0,this)},d.Vector.prototype.array=function(){return[this.x||0,this.y||0,this.z||0]},d.Vector.prototype.equals=function(a,b,c){var e,f,g;return a instanceof d.Vector?(e=a.x||0,f=a.y||0,g=a.z||0):a instanceof Array?(e=a[0]||0,f=a[1]||0,g=a[2]||0):(e=a||0,f=b||0,g=c||0),this.x===e&&this.y===f&&this.z===g},d.Vector.fromAngle=function(a){return this.p5&&this.p5._angleMode===f.DEGREES&&(a=e.degreesToRadians(a)),this.p5?new d.Vector(this.p5,[Math.cos(a),Math.sin(a),0]):new d.Vector(Math.cos(a),Math.sin(a),0)},d.Vector.random2D=function(){var a;return a=this.p5?this.p5._angleMode===f.DEGREES?this.p5.random(360):this.p5.random(f.TWO_PI):Math.random()*Math.PI*2,this.fromAngle(a)},d.Vector.random3D=function(){var a,b;this.p5?(a=this.p5.random(0,f.TWO_PI),b=this.p5.random(-1,1)):(a=Math.random()*Math.PI*2,b=2*Math.random()-1);var c=Math.sqrt(1-b*b)*Math.cos(a),e=Math.sqrt(1-b*b)*Math.sin(a);return this.p5?new d.Vector(this.p5,[c,e,b]):new d.Vector(c,e,b)},d.Vector.add=function(a,b,c){return c?c.set(a):c=a.copy(),c.add(b),c},d.Vector.sub=function(a,b,c){return c?c.set(a):c=a.copy(),c.sub(b),c},d.Vector.mult=function(a,b,c){return c?c.set(a):c=a.copy(),c.mult(b),c},d.Vector.div=function(a,b,c){return c?c.set(a):c=a.copy(),c.div(b),c},d.Vector.dot=function(a,b){return a.dot(b)},d.Vector.cross=function(a,b){return a.cross(b)},d.Vector.dist=function(a,b){return a.dist(b)},d.Vector.lerp=function(a,b,c,d){return d?d.set(a):d=a.copy(),d.lerp(b,c),d},d.Vector.angleBetween=function(a,b){var c=Math.acos(a.dot(b)/(a.mag()*b.mag()));return this.p5&&this.p5._angleMode===f.DEGREES&&(c=e.radiansToDegrees(c)),c},d.Vector.mag=function(a){var b=a.x,c=a.y,d=a.z,e=b*b+c*c+d*d;return Math.sqrt(e)},b.exports=d.Vector},{"../core/constants":36,"../core/core":37,"./polargeometry":67}],67:[function(a,b,c){b.exports={degreesToRadians:function(a){return 2*Math.PI*a/360},radiansToDegrees:function(a){return 360*a/(2*Math.PI)}}},{}],68:[function(a,b,c){"use strict";var d=a("../core/core"),e=!1,f=function(){var a,b,c=4294967296,d=1664525,e=1013904223;return{setSeed:function(d){b=a=(null==d?Math.random()*c:d)>>>0},getSeed:function(){return a},rand:function(){return b=(d*b+e)%c,b/c}}}();d.prototype.randomSeed=function(a){f.setSeed(a),e=!0},d.prototype.random=function(a,b){var c;if(c=e?f.rand():Math.random(),"undefined"==typeof a)return c;if("undefined"==typeof b)return a instanceof Array?a[Math.floor(c*a.length)]:c*a;if(a>b){var d=a;a=b,b=d}return c*(b-a)+a};var g,h=!1;d.prototype.randomGaussian=function(a,b){var c,d,e,f;if(h)c=g,h=!1;else{do d=this.random(2)-1,e=this.random(2)-1,f=d*d+e*e;while(f>=1);f=Math.sqrt(-2*Math.log(f)/f),c=d*f,g=e*f,h=!0}var i=a||0,j=b||1;return c*j+i},b.exports=d},{"../core/core":37}],69:[function(a,b,c){"use strict";var d=a("../core/core"),e=a("./polargeometry"),f=a("../core/constants");d.prototype._angleMode=f.RADIANS,d.prototype.acos=function(a){return this._angleMode===f.RADIANS?Math.acos(a):e.radiansToDegrees(Math.acos(a))},d.prototype.asin=function(a){return this._angleMode===f.RADIANS?Math.asin(a):e.radiansToDegrees(Math.asin(a))},d.prototype.atan=function(a){return this._angleMode===f.RADIANS?Math.atan(a):e.radiansToDegrees(Math.atan(a))},d.prototype.atan2=function(a,b){return this._angleMode===f.RADIANS?Math.atan2(a,b):e.radiansToDegrees(Math.atan2(a,b))},d.prototype.cos=function(a){return this._angleMode===f.RADIANS?Math.cos(a):Math.cos(this.radians(a))},d.prototype.sin=function(a){return this._angleMode===f.RADIANS?Math.sin(a):Math.sin(this.radians(a))},d.prototype.tan=function(a){return this._angleMode===f.RADIANS?Math.tan(a):Math.tan(this.radians(a))},d.prototype.degrees=function(a){return e.radiansToDegrees(a)},d.prototype.radians=function(a){return e.degreesToRadians(a)},d.prototype.angleMode=function(a){(a===f.DEGREES||a===f.RADIANS)&&(this._angleMode=a)},b.exports=d},{"../core/constants":36,"../core/core":37,"./polargeometry":67}],70:[function(a,b,c){"use strict";var d=a("../core/core");d.prototype.textAlign=function(a,b){return this._renderer.textAlign.apply(this._renderer,arguments)},d.prototype.textLeading=function(a){return this._renderer.textLeading.apply(this._renderer,arguments)},d.prototype.textSize=function(a){return this._renderer.textSize.apply(this._renderer,arguments)},d.prototype.textStyle=function(a){return this._renderer.textStyle.apply(this._renderer,arguments)},d.prototype.textWidth=function(a){return 0===a.length?0:this._renderer.textWidth.apply(this._renderer,arguments)},d.prototype.textAscent=function(){return this._renderer.textAscent()},d.prototype.textDescent=function(){return this._renderer.textDescent()},d.prototype._updateTextMetrics=function(){return this._renderer._updateTextMetrics()},b.exports=d},{"../core/core":37}],71:[function(a,b,c){"use strict";var d=a("../core/core"),e=a("../core/constants");a("../core/error_helpers"),d.prototype.text=function(a,b,c,d,e){for(var f=new Array(arguments.length),g=0;g<f.length;++g)f[g]=arguments[g];return this._validateParameters("text",f,[["*","Number","Number"],["*","Number","Number","Number","Number"]]),this._renderer._doFill||this._renderer._doStroke?this._renderer.text.apply(this._renderer,arguments):this},d.prototype.textFont=function(a,b){if(arguments.length){if(!a)throw Error("null font passed to textFont");return this._renderer._setProperty("_textFont",a),b&&(this._renderer._setProperty("_textSize",b),this._renderer._setProperty("_textLeading",b*e._DEFAULT_LEADMULT)),this._renderer._applyTextProperties()}return this},b.exports=d},{"../core/constants":36,"../core/core":37,"../core/error_helpers":40}],72:[function(a,b,c){"use strict";function d(a,b){for(var c=h(b,{sampleFactor:.1,simplifyThreshold:0}),d=n(a,0,1),f=d/(d*c.sampleFactor),g=[],i=0;d>i;i+=f)g.push(n(a,i));return c.simplifyThreshold&&e(g,c.simplifyThreshold),g}function e(a,b){b="undefined"==typeof b?0:b;for(var c=0,d=a.length-1;a.length>3&&d>=0;--d)j(i(a,d-1),i(a,d),i(a,d+1),b)&&(a.splice(d%a.length,1),c++);return c}function f(a){for(var b,c=[],d=0;d<a.length;d++)"M"===a[d].type&&(b&&c.push(b),b=[]),b.push(g(a[d]));return c.push(b),c}function g(a){var b=[a.type];return"M"===a.type||"L"===a.type?b.push(a.x,a.y):"C"===a.type?b.push(a.x1,a.y1,a.x2,a.y2,a.x,a.y):"Q"===a.type&&b.push(a.x1,a.y1,a.x,a.y),b}function h(a,b){if("object"!=typeof a)a=b;else for(var c in b)"undefined"==typeof a[c]&&(a[c]=b[c]);return a}function i(a,b){var c=a.length;return a[0>b?b%c+c:b%c]}function j(a,b,c,d){if(!d)return 0===k(a,b,c);"undefined"==typeof j.tmpPoint1&&(j.tmpPoint1=[],j.tmpPoint2=[]);var e=j.tmpPoint1,f=j.tmpPoint2;e.x=b.x-a.x,e.y=b.y-a.y,f.x=c.x-b.x,f.y=c.y-b.y;var g=e.x*f.x+e.y*f.y,h=Math.sqrt(e.x*e.x+e.y*e.y),i=Math.sqrt(f.x*f.x+f.y*f.y),l=Math.acos(g/(h*i));return d>l}function k(a,b,c){return(b[0]-a[0])*(c[1]-a[1])-(c[0]-a[0])*(b[1]-a[1])}function l(a,b,c,d,e,f,g,h,i){var j=1-i,k=Math.pow(j,3),l=Math.pow(j,2),m=i*i,n=m*i,o=k*a+3*l*i*c+3*j*i*i*e+n*g,p=k*b+3*l*i*d+3*j*i*i*f+n*h,q=a+2*i*(c-a)+m*(e-2*c+a),r=b+2*i*(d-b)+m*(f-2*d+b),s=c+2*i*(e-c)+m*(g-2*e+c),t=d+2*i*(f-d)+m*(h-2*f+d),u=j*a+i*c,v=j*b+i*d,w=j*e+i*g,x=j*f+i*h,y=90-180*Math.atan2(q-s,r-t)/Math.PI;return(q>s||t>r)&&(y+=180),{x:o,y:p,m:{x:q,y:r},n:{x:s,y:t},start:{x:u,y:v},end:{x:w,y:x},alpha:y}}function m(a,b,c,d,e,f,g,h,i){return null==i?u(a,b,c,d,e,f,g,h):l(a,b,c,d,e,f,g,h,v(a,b,c,d,e,f,g,h,i))}function n(a,b,c){a=p(a);for(var d,e,f,g,h,i="",j={},k=0,n=0,o=a.length;o>n;n++){if(f=a[n],"M"===f[0])d=+f[1],e=+f[2];else{if(g=m(d,e,f[1],f[2],f[3],f[4],f[5],f[6]),k+g>b&&!c)return h=m(d,e,f[1],f[2],f[3],f[4],f[5],f[6],b-k),{x:h.x,y:h.y,alpha:h.alpha};k+=g,d=+f[5],e=+f[6]}i+=f.shift()+f}return j.end=i,h=c?k:l(d,e,f[0],f[1],f[2],f[3],f[4],f[5],1),h.alpha&&(h={x:h.x,y:h.y,alpha:h.alpha}),h}function o(a){var b=[],c=0,d=0,e=0,f=0,g=0;"M"===a[0][0]&&(c=+a[0][1],d=+a[0][2],e=c,f=d,g++,b[0]=["M",c,d]);for(var h,i,j,k=3===a.length&&"M"===a[0][0]&&"R"===a[1][0].toUpperCase()&&"Z"===a[2][0].toUpperCase(),l=g,m=a.length;m>l;l++){if(b.push(i=[]),j=a[l],j[0]!==String.prototype.toUpperCase.call(j[0]))switch(i[0]=String.prototype.toUpperCase.call(j[0]),i[0]){case"A":i[1]=j[1],i[2]=j[2],i[3]=j[3],i[4]=j[4],i[5]=j[5],i[6]=+(j[6]+c),i[7]=+(j[7]+d);break;case"V":i[1]=+j[1]+d;break;case"H":i[1]=+j[1]+c;break;case"R":h=[c,d].concat(j.slice(1));for(var n=2,o=h.length;o>n;n++)h[n]=+h[n]+c,h[++n]=+h[n]+d;b.pop(),b=b.concat(r(h,k));break;case"M":e=+j[1]+c,f=+j[2]+d;break;default:for(n=1,o=j.length;o>n;n++)i[n]=+j[n]+(n%2?c:d)}else if("R"===j[0])h=[c,d].concat(j.slice(1)),b.pop(),b=b.concat(r(h,k)),i=["R"].concat(j.slice(-2));else for(var p=0,q=j.length;q>p;p++)i[p]=j[p];switch(i[0]){case"Z":c=e,d=f;break;case"H":c=i[1];break;case"V":d=i[1];break;case"M":e=i[i.length-2],f=i[i.length-1];break;default:c=i[i.length-2],d=i[i.length-1]}}return b}function p(a,b){for(var c=o(a),d=b&&o(b),e={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},f={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},g=(function(a,b,c){var d,e,f={T:1,Q:1};if(!a)return["C",b.x,b.y,b.x,b.y,b.x,b.y];switch(a[0]in f||(b.qx=b.qy=null),a[0]){case"M":b.X=a[1],b.Y=a[2];break;case"A":a=["C"].concat(q.apply(0,[b.x,b.y].concat(a.slice(1))));break;case"S":"C"===c||"S"===c?(d=2*b.x-b.bx,e=2*b.y-b.by):(d=b.x,e=b.y),a=["C",d,e].concat(a.slice(1));break;case"T":"Q"===c||"T"===c?(b.qx=2*b.x-b.qx,b.qy=2*b.y-b.qy):(b.qx=b.x,b.qy=b.y),a=["C"].concat(t(b.x,b.y,b.qx,b.qy,a[1],a[2]));break;case"Q":b.qx=a[1],b.qy=a[2],a=["C"].concat(t(b.x,b.y,a[1],a[2],a[3],a[4]));break;case"L":a=["C"].concat(s(b.x,b.y,a[1],a[2]));break;case"H":a=["C"].concat(s(b.x,b.y,a[1],b.y));break;case"V":a=["C"].concat(s(b.x,b.y,b.x,a[1]));break;case"Z":a=["C"].concat(s(b.x,b.y,b.X,b.Y))}return a}),h=function(a,b){if(a[b].length>7){a[b].shift();for(var e=a[b];e.length;)j[b]="A",d&&(k[b]="A"),a.splice(b++,0,["C"].concat(e.splice(0,6)));a.splice(b,1),p=Math.max(c.length,d&&d.length||0)}},i=function(a,b,e,f,g){a&&b&&"M"===a[g][0]&&"M"!==b[g][0]&&(b.splice(g,0,["M",f.x,f.y]),e.bx=0,e.by=0,e.x=a[g][1],e.y=a[g][2],p=Math.max(c.length,d&&d.length||0))},j=[],k=[],l="",m="",n=0,p=Math.max(c.length,d&&d.length||0);p>n;n++){c[n]&&(l=c[n][0]),"C"!==l&&(j[n]=l,n&&(m=j[n-1])),c[n]=g(c[n],e,m),"A"!==j[n]&&"C"===l&&(j[n]="C"),h(c,n),d&&(d[n]&&(l=d[n][0]),"C"!==l&&(k[n]=l,n&&(m=k[n-1])),d[n]=g(d[n],f,m),"A"!==k[n]&&"C"===l&&(k[n]="C"),h(d,n)),i(c,d,e,f,n),i(d,c,f,e,n);var r=c[n],u=d&&d[n],v=r.length,w=d&&u.length;e.x=r[v-2],e.y=r[v-1],e.bx=parseFloat(r[v-4])||e.x,e.by=parseFloat(r[v-3])||e.y,f.bx=d&&(parseFloat(u[w-4])||f.x),f.by=d&&(parseFloat(u[w-3])||f.y),f.x=d&&u[w-2],f.y=d&&u[w-1]}return d?[c,d]:c}function q(a,b,c,d,e,f,g,h,i,j){var k,l,m,n,o,p=Math.PI,r=120*p/180,s=p/180*(+e||0),t=[],u=function(a,b,c){var d=a*Math.cos(c)-b*Math.sin(c),e=a*Math.sin(c)+b*Math.cos(c);return{x:d,y:e}};if(j)k=j[0],l=j[1],m=j[2],n=j[3];else{o=u(a,b,-s),a=o.x,b=o.y,o=u(h,i,-s),h=o.x,i=o.y;var v=(a-h)/2,w=(b-i)/2,x=v*v/(c*c)+w*w/(d*d);x>1&&(x=Math.sqrt(x),c=x*c,d=x*d);var y=c*c,z=d*d,A=(f===g?-1:1)*Math.sqrt(Math.abs((y*z-y*w*w-z*v*v)/(y*w*w+z*v*v)));m=A*c*w/d+(a+h)/2,n=A*-d*v/c+(b+i)/2,k=Math.asin(((b-n)/d).toFixed(9)),l=Math.asin(((i-n)/d).toFixed(9)),k=m>a?p-k:k,l=m>h?p-l:l,0>k&&(k=2*p+k),0>l&&(l=2*p+l),g&&k>l&&(k-=2*p),!g&&l>k&&(l-=2*p)}var B=l-k;if(Math.abs(B)>r){var C=l,D=h,E=i;l=k+r*(g&&l>k?1:-1),h=m+c*Math.cos(l),i=n+d*Math.sin(l),t=q(h,i,c,d,e,0,g,D,E,[l,C,m,n])}B=l-k;var F=Math.cos(k),G=Math.sin(k),H=Math.cos(l),I=Math.sin(l),J=Math.tan(B/4),K=4/3*c*J,L=4/3*d*J,M=[a,b],N=[a+K*G,b-L*F],O=[h+K*I,i-L*H],P=[h,i];if(N[0]=2*M[0]-N[0],N[1]=2*M[1]-N[1],j)return[N,O,P].concat(t);t=[N,O,P].concat(t).join().split(",");for(var Q=[],R=0,S=t.length;S>R;R++)Q[R]=R%2?u(t[R-1],t[R],s).y:u(t[R],t[R+1],s).x;return Q}function r(a,b){for(var c=[],d=0,e=a.length;e-2*!b>d;d+=2){var f=[{x:+a[d-2],y:+a[d-1]},{x:+a[d],y:+a[d+1]},{x:+a[d+2],y:+a[d+3]},{x:+a[d+4],y:+a[d+5]}];b?d?e-4===d?f[3]={x:+a[0],y:+a[1]}:e-2===d&&(f[2]={x:+a[0],y:+a[1]},f[3]={x:+a[2],y:+a[3]}):f[0]={x:+a[e-2],y:+a[e-1]}:e-4===d?f[3]=f[2]:d||(f[0]={x:+a[d],y:+a[d+1]}),c.push(["C",(-f[0].x+6*f[1].x+f[2].x)/6,(-f[0].y+6*f[1].y+f[2].y)/6,(f[1].x+6*f[2].x-f[3].x)/6,(f[1].y+6*f[2].y-f[3].y)/6,f[2].x,f[2].y])}return c}function s(a,b,c,d){return[a,b,c,d,c,d]}function t(a,b,c,d,e,f){var g=1/3,h=2/3;return[g*a+h*c,g*b+h*d,g*e+h*c,g*f+h*d,e,f]}function u(a,b,c,d,e,f,g,h,i){null==i&&(i=1),i=i>1?1:0>i?0:i;for(var j=i/2,k=12,l=[-.1252,.1252,-.3678,.3678,-.5873,.5873,-.7699,.7699,-.9041,.9041,-.9816,.9816],m=0,n=[.2491,.2491,.2335,.2335,.2032,.2032,.1601,.1601,.1069,.1069,.0472,.0472],o=0;k>o;o++){var p=j*l[o]+j,q=w(p,a,c,e,g),r=w(p,b,d,f,h),s=q*q+r*r;m+=n[o]*Math.sqrt(s)}return j*m}function v(a,b,c,d,e,f,g,h,i){if(!(0>i||u(a,b,c,d,e,f,g,h)<i)){var j,k=1,l=k/2,m=k-l,n=.01;for(j=u(a,b,c,d,e,f,g,h,m);Math.abs(j-i)>n;)l/=2,m+=(i>j?1:-1)*l,j=u(a,b,c,d,e,f,g,h,m);return m}}function w(a,b,c,d,e){var f=-3*b+9*c-9*d+3*e,g=a*f+6*b-12*c+6*d;return a*g-3*b+3*c}function x(){for(var a=new Array(arguments.length),b=0;b<a.length;++b)a[b]=arguments[b];b=a.length;for(var c="";b--;)c+=a[b]===Object(a[b])?JSON.stringify(a[b]):a[b];return c}var y=a("../core/core"),z=a("../core/constants");y.Font=function(a){this.parent=a,this.cache={},this.font=void 0},y.Font.prototype.list=function(){throw"not yet implemented"},y.Font.prototype.textBounds=function(a,b,c,d,e){b=void 0!==b?b:0,c=void 0!==c?c:0,d=d||this.parent._renderer._textSize;var f=e&&e.renderer&&e.renderer._pInst||this.parent,g=f._renderer.drawingContext,h=g.textAlign||z.LEFT,i=g.textBaseline||z.BASELINE,j=this.cache[x("textBounds",a,b,c,d,h,i)];if(!j){var k,l,m,n,o=[],p=[],q=this,r=this._scale(d);this.font.forEachGlyph(a,b,c,d,e,function(a,b,c,e){o.push(b),p.push(c);var f=a.getMetrics();"space"!==a.name?(o.push(b+f.xMax*r),p.push(c+-f.yMin*r),p.push(c+-f.yMax*r)):o.push(b+q.font.charToGlyph(" ").advanceWidth*q._scale(d))}),k=Math.min.apply(null,o),l=Math.min.apply(null,p),m=Math.max.apply(null,o),n=Math.max.apply(null,p),j={x:k,y:l,h:n-l,w:m-k,advance:k-b};var s=j.w+j.advance,t=this._handleAlignment(f,g,a,j.x,j.y,s);j.x=t.x,j.y=t.y,this.cache[x("textBounds",a,b,c,d,h,i)]=j}return j},y.Font.prototype.textToPoints=function(a,b,c,e,g){var h=0,i=[],j=this._getGlyphs(a);e=e||this.parent._renderer._textSize;for(var k=0;k<j.length;k++){for(var l=j[k].getPath(b,c,e),m=f(l.commands),n=0;n<m.length;n++)for(var o=d(m[n],g),p=0;p<o.length;p++)o[p].x+=h,i.push(o[p]);h+=j[k].advanceWidth*this._scale(e)}return i},y.Font.prototype._getGlyphs=function(a){return this.font.stringToGlyphs(a)},y.Font.prototype._getPath=function(a,b,c,d){var e=d&&d.renderer&&d.renderer._pInst||this.parent,f=e._renderer.drawingContext,g=this._handleAlignment(e,f,a,b,c);return this.font.getPath(a,g.x,g.y,e._renderer._textSize,d)},y.Font.prototype._getPathData=function(a,b,c,d){var e=3;return"string"==typeof a&&arguments.length>2?a=this._getPath(a,b,c,d):"object"==typeof b&&(d=b),d&&"number"==typeof d.decimals&&(e=d.decimals),a.toPathData(e)},y.Font.prototype._getSVG=function(a,b,c,d){var e=3;return"string"==typeof a&&arguments.length>2?a=this._getPath(a,b,c,d):"object"==typeof b&&(d=b),d&&("number"==typeof d.decimals&&(e=d.decimals),"number"==typeof d.strokeWidth&&(a.strokeWidth=d.strokeWidth),"undefined"!=typeof d.fill&&(a.fill=d.fill),"undefined"!=typeof d.stroke&&(a.stroke=d.stroke)),a.toSVG(e)},y.Font.prototype._renderPath=function(a,b,c,d){var e,f=d&&d.renderer||this.parent._renderer,g=f.drawingContext;e="object"==typeof a&&a.commands?a.commands:this._getPath(a,b,c,d).commands,g.beginPath();for(var h=0;h<e.length;h+=1){var i=e[h];"M"===i.type?g.moveTo(i.x,i.y):"L"===i.type?g.lineTo(i.x,i.y):"C"===i.type?g.bezierCurveTo(i.x1,i.y1,i.x2,i.y2,i.x,i.y):"Q"===i.type?g.quadraticCurveTo(i.x1,i.y1,i.x,i.y):"Z"===i.type&&g.closePath()}return f._doStroke&&f._strokeSet&&g.stroke(),f._doFill&&(g.fillStyle=f._fillSet?g.fillStyle:z._DEFAULT_TEXT_FILL,g.fill()),this},y.Font.prototype._textWidth=function(a,b){if(" "===a)return this.font.charToGlyph(" ").advanceWidth*this._scale(b);var c=this.textBounds(a,0,0,b);return c.w+c.advance},y.Font.prototype._textAscent=function(a){return this.font.ascender*this._scale(a)},y.Font.prototype._textDescent=function(a){return-this.font.descender*this._scale(a)},y.Font.prototype._scale=function(a){return 1/this.font.unitsPerEm*(a||this.parent._renderer._textSize)},y.Font.prototype._handleAlignment=function(a,b,c,d,e,f){var g=a._renderer._textSize,h=this._textAscent(g),i=this._textDescent(g);return f=void 0!==f?f:this._textWidth(c,g),b.textAlign===z.CENTER?d-=f/2:b.textAlign===z.RIGHT&&(d-=f),b.textBaseline===z.TOP?e+=h:b.textBaseline===z._CTX_MIDDLE?e+=h/2:b.textBaseline===z.BOTTOM&&(e-=i),{x:d,y:e}},b.exports=y.Font},{"../core/constants":36,"../core/core":37}],73:[function(a,b,c){"use strict";var d=a("../core/core");d.prototype.append=function(a,b){return a.push(b),a},d.prototype.arrayCopy=function(a,b,c,d,e){var f,g;"undefined"!=typeof e?(g=Math.min(e,a.length),f=d,a=a.slice(b,g+b)):("undefined"!=typeof c?(g=c,g=Math.min(g,a.length)):g=a.length,f=0,c=b,a=a.slice(0,g)),Array.prototype.splice.apply(c,[f,g].concat(a))},d.prototype.concat=function(a,b){return a.concat(b)},d.prototype.reverse=function(a){return a.reverse()},d.prototype.shorten=function(a){return a.pop(),a},d.prototype.shuffle=function(a,b){var c=ArrayBuffer&&ArrayBuffer.isView&&ArrayBuffer.isView(a);a=b||c?a:a.slice();for(var d,e,f=a.length;f>1;)d=Math.random()*f|0,e=a[--f],a[f]=a[d],a[d]=e;return a},d.prototype.sort=function(a,b){var c=b?a.slice(0,Math.min(b,a.length)):a,d=b?a.slice(Math.min(b,a.length)):[];return c="string"==typeof c[0]?c.sort():c.sort(function(a,b){return a-b}),c.concat(d)},d.prototype.splice=function(a,b,c){return Array.prototype.splice.apply(a,[c,0].concat(b)),a},d.prototype.subset=function(a,b,c){return"undefined"!=typeof c?a.slice(b,b+c):a.slice(b,a.length)},b.exports=d},{"../core/core":37}],74:[function(a,b,c){"use strict";var d=a("../core/core");d.prototype["float"]=function(a){return parseFloat(a)},d.prototype["int"]=function(a,b){return"string"==typeof a?(b=b||10,parseInt(a,b)):"number"==typeof a?0|a:"boolean"==typeof a?a?1:0:a instanceof Array?a.map(function(a){return d.prototype["int"](a,b)}):void 0},d.prototype.str=function(a){return a instanceof Array?a.map(d.prototype.str):String(a)},d.prototype["boolean"]=function(a){return"number"==typeof a?0!==a:"string"==typeof a?"true"===a.toLowerCase():"boolean"==typeof a?a:a instanceof Array?a.map(d.prototype["boolean"]):void 0},d.prototype["byte"]=function(a){var b=d.prototype["int"](a,10);return"number"==typeof b?(b+128)%256-128:b instanceof Array?b.map(d.prototype["byte"]):void 0},d.prototype["char"]=function(a){return"number"!=typeof a||isNaN(a)?a instanceof Array?a.map(d.prototype["char"]):"string"==typeof a?d.prototype["char"](parseInt(a,10)):void 0:String.fromCharCode(a)},d.prototype.unchar=function(a){return"string"==typeof a&&1===a.length?a.charCodeAt(0):a instanceof Array?a.map(d.prototype.unchar):void 0},d.prototype.hex=function(a,b){if(b=void 0===b||null===b?b=8:b,a instanceof Array)return a.map(function(a){return d.prototype.hex(a,b)});if("number"==typeof a){0>a&&(a=4294967295+a+1);for(var c=Number(a).toString(16).toUpperCase();c.length<b;)c="0"+c;return c.length>=b&&(c=c.substring(c.length-b,c.length)),c}},d.prototype.unhex=function(a){return a instanceof Array?a.map(d.prototype.unhex):parseInt("0x"+a,16)},b.exports=d},{"../core/core":37}],75:[function(a,b,c){"use strict";function d(){var a=arguments[0],b=0>a,c=b?a.toString().substring(1):a.toString(),d=c.indexOf("."),e=-1!==d?c.substring(0,d):c,f=-1!==d?c.substring(d+1):"",g=b?"-":"";if(3===arguments.length){var h="";(-1!==d||arguments[2]-f.length>0)&&(h="."),f.length>arguments[2]&&(f=f.substring(0,arguments[2]));for(var i=0;i<arguments[1]-e.length;i++)g+="0";g+=e,g+=h,g+=f;for(var j=0;j<arguments[2]-f.length;j++)g+="0";return g}for(var k=0;k<Math.max(arguments[1]-e.length,0);k++)g+="0";return g+=c}function e(){var a=arguments[0].toString(),b=a.indexOf("."),c=-1!==b?a.substring(b):"",d=-1!==b?a.substring(0,b):a;if(d=d.toString().replace(/\B(?=(\d{3})+(?!\d))/g,","),0===arguments[1])c="";else if(void 0!==arguments[1])if(arguments[1]>c.length){c+=-1===b?".":"";for(var e=arguments[1]-c.length+1,f=0;e>f;f++)c+="0"}else c=c.substring(0,arguments[1]+1);return d+c}function f(){return parseFloat(arguments[0])>0?"+"+arguments[0].toString():arguments[0].toString()}function g(){return parseFloat(arguments[0])>0?" "+arguments[0].toString():arguments[0].toString()}var h=a("../core/core");h.prototype.join=function(a,b){return a.join(b)},h.prototype.match=function(a,b){return a.match(b)},h.prototype.matchAll=function(a,b){for(var c=new RegExp(b,"g"),d=c.exec(a),e=[];null!==d;)e.push(d),d=c.exec(a);return e},h.prototype.nf=function(){if(arguments[0]instanceof Array){var a=arguments[1],b=arguments[2];return arguments[0].map(function(c){return d(c,a,b)})}var c=Object.prototype.toString.call(arguments[0]);return"[object Arguments]"===c?3===arguments[0].length?this.nf(arguments[0][0],arguments[0][1],arguments[0][2]):2===arguments[0].length?this.nf(arguments[0][0],arguments[0][1]):this.nf(arguments[0][0]):d.apply(this,arguments)},h.prototype.nfc=function(){if(arguments[0]instanceof Array){var a=arguments[1];return arguments[0].map(function(b){return e(b,a)})}return e.apply(this,arguments)},h.prototype.nfp=function(){var a=this.nf.apply(this,arguments);return a instanceof Array?a.map(f):f(a)},h.prototype.nfs=function(){var a=this.nf.apply(this,arguments);return a instanceof Array?a.map(g):g(a)},h.prototype.split=function(a,b){return a.split(b)},h.prototype.splitTokens=function(){var a,b,c,d;return d=arguments[1],arguments.length>1?(c=/\]/g.exec(d),b=/\[/g.exec(d),b&&c?(d=d.slice(0,c.index)+d.slice(c.index+1),b=/\[/g.exec(d),d=d.slice(0,b.index)+d.slice(b.index+1),a=new RegExp("[\\["+d+"\\]]","g")):c?(d=d.slice(0,c.index)+d.slice(c.index+1),a=new RegExp("["+d+"\\]]","g")):b?(d=d.slice(0,b.index)+d.slice(b.index+1),a=new RegExp("["+d+"\\[]","g")):a=new RegExp("["+d+"]","g")):a=/\s/g,arguments[0].split(a).filter(function(a){return a})},h.prototype.trim=function(a){return a instanceof Array?a.map(this.trim):a.trim()},b.exports=h},{"../core/core":37}],76:[function(a,b,c){"use strict";var d=a("../core/core");d.prototype.day=function(){return(new Date).getDate()},d.prototype.hour=function(){return(new Date).getHours()},d.prototype.minute=function(){return(new Date).getMinutes()},d.prototype.millis=function(){return window.performance.now()},d.prototype.month=function(){return(new Date).getMonth()+1},d.prototype.second=function(){return(new Date).getSeconds()},d.prototype.year=function(){return(new Date).getFullYear()},b.exports=d},{"../core/core":37}],77:[function(a,b,c){"use strict";var d=a("../core/core");d.prototype.camera=function(a,b,c){for(var d=new Array(arguments.length),e=0;e<d.length;++e)d[e]=arguments[e];this._validateParameters("camera",d,["Number","Number","Number"]),this._renderer.translate(-a,-b,-c)},d.prototype.perspective=function(a,b,c,e){for(var f=new Array(arguments.length),g=0;g<f.length;++g)f[g]=arguments[g];this._validateParameters("perspective",f,["Number","Number","Number","Number"]),this._renderer.uPMatrix=d.Matrix.identity(), +this._renderer.uPMatrix.perspective(a,b,c,e),this._renderer._curCamera="custom"},d.prototype.ortho=function(a,b,c,e,f,g){for(var h=new Array(arguments.length),i=0;i<h.length;++i)h[i]=arguments[i];this._validateParameters("ortho",h,["Number","Number","Number","Number","Number","Number"]),a/=this.width,b/=this.width,e/=this.height,c/=this.height,this._renderer.uPMatrix=d.Matrix.identity(),this._renderer.uPMatrix.ortho(a,b,c,e,f,g),this._renderer._curCamera="custom"},b.exports=d},{"../core/core":37}],78:[function(a,b,c){"use strict";var d=a("../core/core");d.prototype.orbitControl=function(){return this.mouseIsPressed&&(this.rotateY((this.mouseX-this.width/2)/(this.width/2)),this.rotateX((this.mouseY-this.height/2)/(this.width/2))),this},b.exports=d},{"../core/core":37}],79:[function(a,b,c){"use strict";var d=a("../core/core");d.prototype.ambientLight=function(a,b,c,d){var e=this._renderer.GL,f=this._renderer._getShader("lightVert","lightTextureFrag");e.useProgram(f),f.uAmbientColor=e.getUniformLocation(f,"uAmbientColor["+this._renderer.ambientLightCount+"]");var g=this._renderer._pInst.color.apply(this._renderer._pInst,arguments),h=g._array;return e.uniform3f(f.uAmbientColor,h[0],h[1],h[2]),f.uMaterialColor=e.getUniformLocation(f,"uMaterialColor"),e.uniform4f(f.uMaterialColor,1,1,1,1),this._renderer.ambientLightCount++,f.uAmbientLightCount=e.getUniformLocation(f,"uAmbientLightCount"),e.uniform1i(f.uAmbientLightCount,this._renderer.ambientLightCount),this},d.prototype.directionalLight=function(a,b,c,d,e,f,g){var h=this._renderer.GL,i=this._renderer._getShader("lightVert","lightTextureFrag");h.useProgram(i),i.uDirectionalColor=h.getUniformLocation(i,"uDirectionalColor["+this._renderer.directionalLightCount+"]");var j=this._renderer._pInst.color.apply(this._renderer._pInst,[a,b,c]),k=j._array;h.uniform3f(i.uDirectionalColor,k[0],k[1],k[2]);for(var l,m,n,o=new Array(arguments.length),p=0;p<o.length;++p)o[p]=arguments[p];if("number"==typeof o[o.length-1])l=o[o.length-3],m=o[o.length-2],n=o[o.length-1];else try{l=o[o.length-1].x,m=o[o.length-1].y,n=o[o.length-1].z}catch(q){throw q}return i.uLightingDirection=h.getUniformLocation(i,"uLightingDirection["+this._renderer.directionalLightCount+"]"),h.uniform3f(i.uLightingDirection,l,m,n),i.uMaterialColor=h.getUniformLocation(i,"uMaterialColor"),h.uniform4f(i.uMaterialColor,1,1,1,1),this._renderer.directionalLightCount++,i.uDirectionalLightCount=h.getUniformLocation(i,"uDirectionalLightCount"),h.uniform1i(i.uDirectionalLightCount,this._renderer.directionalLightCount),this},d.prototype.pointLight=function(a,b,c,d,e,f,g){var h=this._renderer.GL,i=this._renderer._getShader("lightVert","lightTextureFrag");h.useProgram(i),i.uPointLightColor=h.getUniformLocation(i,"uPointLightColor["+this._renderer.pointLightCount+"]");var j=this._renderer._pInst.color.apply(this._renderer._pInst,[a,b,c]),k=j._array;h.uniform3f(i.uPointLightColor,k[0],k[1],k[2]);for(var l,m,n,o=new Array(arguments.length),p=0;p<o.length;++p)o[p]=arguments[p];if("number"==typeof o[o.length-1])l=o[o.length-3],m=o[o.length-2],n=o[o.length-1];else try{l=o[o.length-1].x,m=o[o.length-1].y,n=o[o.length-1].z}catch(q){throw q}return i.uPointLightLocation=h.getUniformLocation(i,"uPointLightLocation["+this._renderer.pointLightCount+"]"),h.uniform3f(i.uPointLightLocation,l,m,n),i.uMaterialColor=h.getUniformLocation(i,"uMaterialColor"),h.uniform4f(i.uMaterialColor,1,1,1,1),this._renderer.pointLightCount++,i.uPointLightCount=h.getUniformLocation(i,"uPointLightCount"),h.uniform1i(i.uPointLightCount,this._renderer.pointLightCount),this},b.exports=d},{"../core/core":37}],80:[function(a,b,c){"use strict";function d(a,b){for(var c={v:[],vt:[],vn:[]},d={},f=0;f<b.length;++f){var g=b[f].trim().split(/\b\s+/);if(g.length>0)if("v"===g[0]||"vn"===g[0]){var h=new e.Vector(parseFloat(g[1]),parseFloat(g[2]),parseFloat(g[3]));c[g[0]].push(h)}else if("vt"===g[0]){var i=[parseFloat(g[1]),parseFloat(g[2])];c[g[0]].push(i)}else if("f"===g[0])for(var j=3;j<g.length;++j){for(var k=[],l=[1,j-1,j],m=0;m<l.length;++m){var n=g[l[m]],o=0;if(void 0!==d[n])o=d[n];else{for(var p=n.split("/"),q=0;q<p.length;q++)p[q]=parseInt(p[q])-1;o=d[n]=a.vertices.length,a.vertices.push(c.v[p[0]].copy()),c.vt[p[1]]?a.uvs.push(c.vt[p[1]].slice()):a.uvs.push([0,0]),c.vn[p[2]]&&a.vertexNormals.push(c.vn[p[2]].copy())}k.push(o)}a.faces.push(k)}}return 0===a.vertexNormals.length&&a.computeNormals(),a}var e=a("../core/core");a("./p5.Geometry"),e.prototype.loadModel=function(){var a,b,c,f=arguments[0];"boolean"==typeof arguments[1]?(a=arguments[1],b=arguments[2],c=arguments[3]):(a=!1,b=arguments[1],c=arguments[2]);var g=new e.Geometry;return g.gid=f+"|"+a,this.loadStrings(f,function(c){d(g,c),a&&g.normalize(),"function"==typeof b&&b(g)}.bind(this),c),g},e.prototype.model=function(a){a.vertices.length>0&&(this._renderer.geometryInHash(a.gid)||this._renderer.createBuffers(a.gid,a),this._renderer.drawBuffers(a.gid))},b.exports=e},{"../core/core":37,"./p5.Geometry":82}],81:[function(a,b,c){"use strict";var d=a("../core/core");d.prototype.normalMaterial=function(){return this._renderer._getShader("normalVert","normalFrag"),this},d.prototype.texture=function(){for(var a=new Array(arguments.length),b=0;b<a.length;++b)a[b]=arguments[b];var c=this._renderer.GL,e=this._renderer._getShader("lightVert","lightTextureFrag");c.useProgram(e);var f;if(a[0].isTexture)a[0]instanceof d.Graphics||"undefined"!=typeof d.MediaElement&&a[0]instanceof d.MediaElement?f=a[0].elt:a[0]instanceof d.Image&&(f=a[0].canvas),this._renderer._bind.call(this,a[0].tex,f);else{if(a[0]instanceof d.Image)f=a[0].canvas;else if("undefined"!=typeof d.MediaElement&&a[0]instanceof d.MediaElement){if(!a[0].loadedmetadata)return;f=a[0].elt}else a[0]instanceof d.Graphics&&(f=a[0].elt);var g=c.createTexture();a[0]._setProperty("tex",g),a[0]._setProperty("isTexture",!0),this._renderer._bind.call(this,g,f)}return c.activeTexture(c.TEXTURE0),c.bindTexture(c.TEXTURE_2D,a[0].tex),c.uniform1i(c.getUniformLocation(e,"isTexture"),!0),c.uniform1i(c.getUniformLocation(e,"uSampler"),0),this},d.RendererGL.prototype._bind=function(a,b){var c=this._renderer.GL;c.bindTexture(c.TEXTURE_2D,a),c.pixelStorei(c.UNPACK_FLIP_Y_WEBGL,!0),c.texImage2D(c.TEXTURE_2D,0,c.RGBA,c.RGBA,c.UNSIGNED_BYTE,b),c.pixelStorei(c.UNPACK_FLIP_Y_WEBGL,!0),c.texParameteri(c.TEXTURE_2D,c.TEXTURE_MAG_FILTER,c.LINEAR),c.texParameteri(c.TEXTURE_2D,c.TEXTURE_MIN_FILTER,c.LINEAR),c.texParameteri(c.TEXTURE_2D,c.TEXTURE_WRAP_S,c.CLAMP_TO_EDGE),c.texParameteri(c.TEXTURE_2D,c.TEXTURE_WRAP_T,c.CLAMP_TO_EDGE),c.bindTexture(c.TEXTURE_2D,null)},d.prototype.ambientMaterial=function(a,b,c,d){var e=this._renderer.GL,f=this._renderer._getShader("lightVert","lightTextureFrag");e.useProgram(f),f.uMaterialColor=e.getUniformLocation(f,"uMaterialColor");var g=this._renderer._applyColorBlend.apply(this._renderer,arguments);return e.uniform4f(f.uMaterialColor,g[0],g[1],g[2],g[3]),f.uSpecular=e.getUniformLocation(f,"uSpecular"),e.uniform1i(f.uSpecular,!1),e.uniform1i(e.getUniformLocation(f,"isTexture"),!1),this},d.prototype.specularMaterial=function(a,b,c,d){var e=this._renderer.GL,f=this._renderer._getShader("lightVert","lightTextureFrag");e.useProgram(f),e.uniform1i(e.getUniformLocation(f,"isTexture"),!1),f.uMaterialColor=e.getUniformLocation(f,"uMaterialColor");var g=this._renderer._applyColorBlend.apply(this._renderer,arguments);return e.uniform4f(f.uMaterialColor,g[0],g[1],g[2],g[3]),f.uSpecular=e.getUniformLocation(f,"uSpecular"),e.uniform1i(f.uSpecular,!0),this},d.RendererGL.prototype._applyColorBlend=function(a,b,c,d){var e=this.GL,f=this._pInst.color.apply(this._pInst,arguments),g=f._array;return g[g.length-1]<1?(e.depthMask(!1),e.enable(e.BLEND),e.blendEquation(e.FUNC_ADD),e.blendFunc(e.SRC_ALPHA,e.ONE_MINUS_SRC_ALPHA)):(e.depthMask(!0),e.disable(e.BLEND)),g},b.exports=d},{"../core/core":37}],82:[function(a,b,c){"use strict";var d=a("../core/core");d.Geometry=function(a,b,c){return this.vertices=[],this.vertexNormals=[],this.faces=[],this.uvs=[],this.detailX=void 0!==a?a:1,this.detailY=void 0!==b?b:1,c instanceof Function&&c.call(this),this},d.Geometry.prototype.computeFaces=function(){for(var a,b,c,d,e=this.detailX+1,f=0;f<this.detailY;f++)for(var g=0;g<this.detailX;g++)a=f*e+g,b=f*e+g+1,c=(f+1)*e+g+1,d=(f+1)*e+g,this.faces.push([a,b,d]),this.faces.push([d,b,c]);return this},d.Geometry.prototype._getFaceNormal=function(a,b){var c=this.faces[a],e=this.vertices[c[b%3]],f=this.vertices[c[(b+1)%3]],g=this.vertices[c[(b+2)%3]],h=d.Vector.cross(d.Vector.sub(f,e),d.Vector.sub(g,e)),i=d.Vector.mag(h)/(d.Vector.mag(d.Vector.sub(f,e))*d.Vector.mag(d.Vector.sub(g,e)));return h=h.normalize(),h.mult(Math.asin(i))},d.Geometry.prototype.computeNormals=function(){for(var a=0;a<this.vertices.length;a++){for(var b=new d.Vector,c=0;c<this.faces.length;c++)(this.faces[c][0]===a||this.faces[c][1]===a||this.faces[c][2]===a)&&(b=b.add(this._getFaceNormal(c,a)));b=b.normalize(),this.vertexNormals.push(b)}return this},d.Geometry.prototype.averageNormals=function(){for(var a=0;a<=this.detailY;a++){var b=this.detailX+1,c=d.Vector.add(this.vertexNormals[a*b],this.vertexNormals[a*b+this.detailX]);c=d.Vector.div(c,2),this.vertexNormals[a*b]=c,this.vertexNormals[a*b+this.detailX]=c}return this},d.Geometry.prototype.averagePoleNormals=function(){for(var a=new d.Vector(0,0,0),b=0;b<this.detailX;b++)a.add(this.vertexNormals[b]);for(a=d.Vector.div(a,this.detailX),b=0;b<this.detailX;b++)this.vertexNormals[b]=a;for(a=new d.Vector(0,0,0),b=this.vertices.length-1;b>this.vertices.length-1-this.detailX;b--)a.add(this.vertexNormals[b]);for(a=d.Vector.div(a,this.detailX),b=this.vertices.length-1;b>this.vertices.length-1-this.detailX;b--)this.vertexNormals[b]=a;return this},d.Geometry.prototype.normalize=function(){if(this.vertices.length>0){for(var a=this.vertices[0].copy(),b=this.vertices[0].copy(),c=0;c<this.vertices.length;c++)a.x=Math.max(a.x,this.vertices[c].x),b.x=Math.min(b.x,this.vertices[c].x),a.y=Math.max(a.y,this.vertices[c].y),b.y=Math.min(b.y,this.vertices[c].y),a.z=Math.max(a.z,this.vertices[c].z),b.z=Math.min(b.z,this.vertices[c].z);var e=d.Vector.lerp(a,b,.5),f=d.Vector.sub(a,b),g=Math.max(Math.max(f.x,f.y),f.z),h=200/g;for(c=0;c<this.vertices.length;c++)this.vertices[c].sub(e),this.vertices[c].mult(h)}return this},b.exports=d.Geometry},{"../core/core":37}],83:[function(a,b,c){"use strict";var d=a("../core/core"),e=a("../math/polargeometry"),f=a("../core/constants"),g="undefined"!=typeof Float32Array?Float32Array:Array;d.Matrix=function(){for(var a=new Array(arguments.length),b=0;b<a.length;++b)a[b]=arguments[b];return a[0]instanceof d?(this.p5=a[0],"mat3"===a[1]?this.mat3=a[2]||new g([1,0,0,0,1,0,0,0,1]):this.mat4=a[1]||new g([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1])):"mat3"===a[0]?this.mat3=a[1]||new g([1,0,0,0,1,0,0,0,1]):this.mat4=a[0]||new g([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]),this},d.Matrix.prototype.set=function(a){return a instanceof d.Matrix?(this.mat4=a.mat4,this):a instanceof g?(this.mat4=a,this):this},d.Matrix.prototype.get=function(){return new d.Matrix(this.mat4)},d.Matrix.prototype.copy=function(){var a=new d.Matrix;return a.mat4[0]=this.mat4[0],a.mat4[1]=this.mat4[1],a.mat4[2]=this.mat4[2],a.mat4[3]=this.mat4[3],a.mat4[4]=this.mat4[4],a.mat4[5]=this.mat4[5],a.mat4[6]=this.mat4[6],a.mat4[7]=this.mat4[7],a.mat4[8]=this.mat4[8],a.mat4[9]=this.mat4[9],a.mat4[10]=this.mat4[10],a.mat4[11]=this.mat4[11],a.mat4[12]=this.mat4[12],a.mat4[13]=this.mat4[13],a.mat4[14]=this.mat4[14],a.mat4[15]=this.mat4[15],a},d.Matrix.identity=function(){return new d.Matrix},d.Matrix.prototype.transpose=function(a){var b,c,e,f,h,i;return a instanceof d.Matrix?(b=a.mat4[1],c=a.mat4[2],e=a.mat4[3],f=a.mat4[6],h=a.mat4[7],i=a.mat4[11],this.mat4[0]=a.mat4[0],this.mat4[1]=a.mat4[4],this.mat4[2]=a.mat4[8],this.mat4[3]=a.mat4[12],this.mat4[4]=b,this.mat4[5]=a.mat4[5],this.mat4[6]=a.mat4[9],this.mat4[7]=a.mat4[13],this.mat4[8]=c,this.mat4[9]=f,this.mat4[10]=a.mat4[10],this.mat4[11]=a.mat4[14],this.mat4[12]=e,this.mat4[13]=h,this.mat4[14]=i,this.mat4[15]=a.mat4[15]):a instanceof g&&(b=a[1],c=a[2],e=a[3],f=a[6],h=a[7],i=a[11],this.mat4[0]=a[0],this.mat4[1]=a[4],this.mat4[2]=a[8],this.mat4[3]=a[12],this.mat4[4]=b,this.mat4[5]=a[5],this.mat4[6]=a[9],this.mat4[7]=a[13],this.mat4[8]=c,this.mat4[9]=f,this.mat4[10]=a[10],this.mat4[11]=a[14],this.mat4[12]=e,this.mat4[13]=h,this.mat4[14]=i,this.mat4[15]=a[15]),this},d.Matrix.prototype.invert=function(a){var b,c,e,f,h,i,j,k,l,m,n,o,p,q,r,s;a instanceof d.Matrix?(b=a.mat4[0],c=a.mat4[1],e=a.mat4[2],f=a.mat4[3],h=a.mat4[4],i=a.mat4[5],j=a.mat4[6],k=a.mat4[7],l=a.mat4[8],m=a.mat4[9],n=a.mat4[10],o=a.mat4[11],p=a.mat4[12],q=a.mat4[13],r=a.mat4[14],s=a.mat4[15]):a instanceof g&&(b=a[0],c=a[1],e=a[2],f=a[3],h=a[4],i=a[5],j=a[6],k=a[7],l=a[8],m=a[9],n=a[10],o=a[11],p=a[12],q=a[13],r=a[14],s=a[15]);var t=b*i-c*h,u=b*j-e*h,v=b*k-f*h,w=c*j-e*i,x=c*k-f*i,y=e*k-f*j,z=l*q-m*p,A=l*r-n*p,B=l*s-o*p,C=m*r-n*q,D=m*s-o*q,E=n*s-o*r,F=t*E-u*D+v*C+w*B-x*A+y*z;return F?(F=1/F,this.mat4[0]=(i*E-j*D+k*C)*F,this.mat4[1]=(e*D-c*E-f*C)*F,this.mat4[2]=(q*y-r*x+s*w)*F,this.mat4[3]=(n*x-m*y-o*w)*F,this.mat4[4]=(j*B-h*E-k*A)*F,this.mat4[5]=(b*E-e*B+f*A)*F,this.mat4[6]=(r*v-p*y-s*u)*F,this.mat4[7]=(l*y-n*v+o*u)*F,this.mat4[8]=(h*D-i*B+k*z)*F,this.mat4[9]=(c*B-b*D-f*z)*F,this.mat4[10]=(p*x-q*v+s*t)*F,this.mat4[11]=(m*v-l*x-o*t)*F,this.mat4[12]=(i*A-h*C-j*z)*F,this.mat4[13]=(b*C-c*A+e*z)*F,this.mat4[14]=(q*u-p*w-r*t)*F,this.mat4[15]=(l*w-m*u+n*t)*F,this):null},d.Matrix.prototype.invert3x3=function(){var a=this.mat3[0],b=this.mat3[1],c=this.mat3[2],d=this.mat3[3],e=this.mat3[4],f=this.mat3[5],g=this.mat3[6],h=this.mat3[7],i=this.mat3[8],j=i*e-f*h,k=-i*d+f*g,l=h*d-e*g,m=a*j+b*k+c*l;return m?(m=1/m,this.mat3[0]=j*m,this.mat3[1]=(-i*b+c*h)*m,this.mat3[2]=(f*b-c*e)*m,this.mat3[3]=k*m,this.mat3[4]=(i*a-c*g)*m,this.mat3[5]=(-f*a+c*d)*m,this.mat3[6]=l*m,this.mat3[7]=(-h*a+b*g)*m,this.mat3[8]=(e*a-b*d)*m,this):null},d.Matrix.prototype.transpose3x3=function(a){var b=a[1],c=a[2],d=a[5];return this.mat3[1]=a[3],this.mat3[2]=a[6],this.mat3[3]=b,this.mat3[5]=a[7],this.mat3[6]=c,this.mat3[7]=d,this},d.Matrix.prototype.inverseTranspose=function(a){return void 0===this.mat3?console.error("sorry, this function only works with mat3"):(this.mat3[0]=a.mat4[0],this.mat3[1]=a.mat4[1],this.mat3[2]=a.mat4[2],this.mat3[3]=a.mat4[4],this.mat3[4]=a.mat4[5],this.mat3[5]=a.mat4[6],this.mat3[6]=a.mat4[8],this.mat3[7]=a.mat4[9],this.mat3[8]=a.mat4[10]),this.invert3x3().transpose3x3(this.mat3),this},d.Matrix.prototype.determinant=function(){var a=this.mat4[0]*this.mat4[5]-this.mat4[1]*this.mat4[4],b=this.mat4[0]*this.mat4[6]-this.mat4[2]*this.mat4[4],c=this.mat4[0]*this.mat4[7]-this.mat4[3]*this.mat4[4],d=this.mat4[1]*this.mat4[6]-this.mat4[2]*this.mat4[5],e=this.mat4[1]*this.mat4[7]-this.mat4[3]*this.mat4[5],f=this.mat4[2]*this.mat4[7]-this.mat4[3]*this.mat4[6],g=this.mat4[8]*this.mat4[13]-this.mat4[9]*this.mat4[12],h=this.mat4[8]*this.mat4[14]-this.mat4[10]*this.mat4[12],i=this.mat4[8]*this.mat4[15]-this.mat4[11]*this.mat4[12],j=this.mat4[9]*this.mat4[14]-this.mat4[10]*this.mat4[13],k=this.mat4[9]*this.mat4[15]-this.mat4[11]*this.mat4[13],l=this.mat4[10]*this.mat4[15]-this.mat4[11]*this.mat4[14];return a*l-b*k+c*j+d*i-e*h+f*g},d.Matrix.prototype.mult=function(a){var b=new g(16),c=new g(16);a instanceof d.Matrix?c=a.mat4:a instanceof g&&(c=a);var e=this.mat4[0],f=this.mat4[1],h=this.mat4[2],i=this.mat4[3];return b[0]=e*c[0]+f*c[4]+h*c[8]+i*c[12],b[1]=e*c[1]+f*c[5]+h*c[9]+i*c[13],b[2]=e*c[2]+f*c[6]+h*c[10]+i*c[14],b[3]=e*c[3]+f*c[7]+h*c[11]+i*c[15],e=this.mat4[4],f=this.mat4[5],h=this.mat4[6],i=this.mat4[7],b[4]=e*c[0]+f*c[4]+h*c[8]+i*c[12],b[5]=e*c[1]+f*c[5]+h*c[9]+i*c[13],b[6]=e*c[2]+f*c[6]+h*c[10]+i*c[14],b[7]=e*c[3]+f*c[7]+h*c[11]+i*c[15],e=this.mat4[8],f=this.mat4[9],h=this.mat4[10],i=this.mat4[11],b[8]=e*c[0]+f*c[4]+h*c[8]+i*c[12],b[9]=e*c[1]+f*c[5]+h*c[9]+i*c[13],b[10]=e*c[2]+f*c[6]+h*c[10]+i*c[14],b[11]=e*c[3]+f*c[7]+h*c[11]+i*c[15],e=this.mat4[12],f=this.mat4[13],h=this.mat4[14],i=this.mat4[15],b[12]=e*c[0]+f*c[4]+h*c[8]+i*c[12],b[13]=e*c[1]+f*c[5]+h*c[9]+i*c[13],b[14]=e*c[2]+f*c[6]+h*c[10]+i*c[14],b[15]=e*c[3]+f*c[7]+h*c[11]+i*c[15],this.mat4=b,this},d.Matrix.prototype.scale=function(){for(var a,b,c,e=new Array(arguments.length),f=0;f<e.length;++f)e[f]=arguments[f];e[0]instanceof d.Vector?(a=e[0].x,b=e[0].y,c=e[0].z):e[0]instanceof Array&&(a=e[0][0],b=e[0][1],c=e[0][2]);var h=new g(16);return h[0]=this.mat4[0]*a,h[1]=this.mat4[1]*a,h[2]=this.mat4[2]*a,h[3]=this.mat4[3]*a,h[4]=this.mat4[4]*b,h[5]=this.mat4[5]*b,h[6]=this.mat4[6]*b,h[7]=this.mat4[7]*b,h[8]=this.mat4[8]*c,h[9]=this.mat4[9]*c,h[10]=this.mat4[10]*c,h[11]=this.mat4[11]*c,h[12]=this.mat4[12],h[13]=this.mat4[13],h[14]=this.mat4[14],h[15]=this.mat4[15],this.mat4=h,this},d.Matrix.prototype.rotate=function(a,b){var c,g,h,i,j;this.p5?this.p5._angleMode===f.DEGREES&&(i=e.degreesToRadians(a)):i=a,b instanceof d.Vector?(c=b.x,g=b.y,h=b.z):b instanceof Array&&(c=b[0],g=b[1],h=b[2]),j=Math.sqrt(c*c+g*g+h*h),c*=1/j,g*=1/j,h*=1/j;var k=this.mat4[0],l=this.mat4[1],m=this.mat4[2],n=this.mat4[3],o=this.mat4[4],p=this.mat4[5],q=this.mat4[6],r=this.mat4[7],s=this.mat4[8],t=this.mat4[9],u=this.mat4[10],v=this.mat4[11],w=Math.sin(i),x=Math.cos(i),y=1-x,z=c*c*y+x,A=g*c*y+h*w,B=h*c*y-g*w,C=c*g*y-h*w,D=g*g*y+x,E=h*g*y+c*w,F=c*h*y+g*w,G=g*h*y-c*w,H=h*h*y+x;return this.mat4[0]=k*z+o*A+s*B,this.mat4[1]=l*z+p*A+t*B,this.mat4[2]=m*z+q*A+u*B,this.mat4[3]=n*z+r*A+v*B,this.mat4[4]=k*C+o*D+s*E,this.mat4[5]=l*C+p*D+t*E,this.mat4[6]=m*C+q*D+u*E,this.mat4[7]=n*C+r*D+v*E,this.mat4[8]=k*F+o*G+s*H,this.mat4[9]=l*F+p*G+t*H,this.mat4[10]=m*F+q*G+u*H,this.mat4[11]=n*F+r*G+v*H,this},d.Matrix.prototype.translate=function(a){var b=a[0],c=a[1],d=a[2]||0;this.mat4[12]=this.mat4[0]*b+this.mat4[4]*c+this.mat4[8]*d+this.mat4[12],this.mat4[13]=this.mat4[1]*b+this.mat4[5]*c+this.mat4[9]*d+this.mat4[13],this.mat4[14]=this.mat4[2]*b+this.mat4[6]*c+this.mat4[10]*d+this.mat4[14],this.mat4[15]=this.mat4[3]*b+this.mat4[7]*c+this.mat4[11]*d+this.mat4[15]},d.Matrix.prototype.rotateX=function(a){this.rotate(a,[1,0,0])},d.Matrix.prototype.rotateY=function(a){this.rotate(a,[0,1,0])},d.Matrix.prototype.rotateZ=function(a){this.rotate(a,[0,0,1])},d.Matrix.prototype.perspective=function(a,b,c,d){var e=1/Math.tan(a/2),f=1/(c-d);return this.mat4[0]=e/b,this.mat4[1]=0,this.mat4[2]=0,this.mat4[3]=0,this.mat4[4]=0,this.mat4[5]=e,this.mat4[6]=0,this.mat4[7]=0,this.mat4[8]=0,this.mat4[9]=0,this.mat4[10]=(d+c)*f,this.mat4[11]=-1,this.mat4[12]=0,this.mat4[13]=0,this.mat4[14]=2*d*c*f,this.mat4[15]=0,this},d.Matrix.prototype.ortho=function(a,b,c,d,e,f){var g=1/(a-b),h=1/(c-d),i=1/(e-f);return this.mat4[0]=-2*g,this.mat4[1]=0,this.mat4[2]=0,this.mat4[3]=0,this.mat4[4]=0,this.mat4[5]=-2*h,this.mat4[6]=0,this.mat4[7]=0,this.mat4[8]=0,this.mat4[9]=0,this.mat4[10]=2*i,this.mat4[11]=0,this.mat4[12]=(a+b)*g,this.mat4[13]=(d+c)*h,this.mat4[14]=(f+e)*i,this.mat4[15]=1,this},b.exports=d.Matrix},{"../core/constants":36,"../core/core":37,"../math/polargeometry":67}],84:[function(a,b,c){"use strict";var d=a("../core/core"),e=a("../core/constants");d.RendererGL.prototype.beginShape=function(a){return this.immediateMode.shapeMode=void 0!==a?a:e.LINE_STRIP,void 0===this.immediateMode.vertexPositions?(this.immediateMode.vertexPositions=[],this.immediateMode.vertexColors=[],this.immediateMode.vertexBuffer=this.GL.createBuffer(),this.immediateMode.colorBuffer=this.GL.createBuffer()):(this.immediateMode.vertexPositions.length=0,this.immediateMode.vertexColors.length=0),this.isImmediateDrawing=!0,this},d.RendererGL.prototype.vertex=function(a,b,c){this.immediateMode.vertexPositions.push(a,b,c);var d=this.curFillColor||[.5,.5,.5,1];return this.immediateMode.vertexColors.push(d[0],d[1],d[2],d[3]),this},d.RendererGL.prototype.endShape=function(a,b,c,d,f,g){var h=this.GL;if(this._bindImmediateBuffers(this.immediateMode.vertexPositions,this.immediateMode.vertexColors),a)if("fill"===this.drawMode)switch(this.immediateMode.shapeMode){case e.LINE_STRIP:this.immediateMode.shapeMode=e.TRIANGLE_FAN;break;case e.LINES:this.immediateMode.shapeMode=e.TRIANGLE_FAN;break;case e.TRIANGLES:this.immediateMode.shapeMode=e.TRIANGLE_FAN}else switch(this.immediateMode.shapeMode){case e.LINE_STRIP:this.immediateMode.shapeMode=e.LINE_LOOP;break;case e.LINES:this.immediateMode.shapeMode=e.LINE_LOOP}if(this.immediateMode.shapeMode===e.QUADS||this.immediateMode.shapeMode===e.QUAD_STRIP)throw new Error("sorry, "+this.immediateMode.shapeMode+" not yet implemented in webgl mode.");return h.enable(h.BLEND),h.drawArrays(this.immediateMode.shapeMode,0,this.immediateMode.vertexPositions.length/3),this.immediateMode.vertexPositions.length=0,this.immediateMode.vertexColors.length=0,this.isImmediateDrawing=!1,this},d.RendererGL.prototype._bindImmediateBuffers=function(a,b){this._setDefaultCamera();var c=this.GL,d=this._getCurShaderId(),e=this.mHash[d];return e.vertexPositionAttribute=c.getAttribLocation(e,"aPosition"),c.enableVertexAttribArray(e.vertexPositionAttribute),c.bindBuffer(c.ARRAY_BUFFER,this.immediateMode.vertexBuffer),c.bufferData(c.ARRAY_BUFFER,new Float32Array(a),c.DYNAMIC_DRAW),c.vertexAttribPointer(e.vertexPositionAttribute,3,c.FLOAT,!1,0,0),e.vertexColorAttribute=c.getAttribLocation(e,"aVertexColor"),c.enableVertexAttribArray(e.vertexColorAttribute),c.bindBuffer(c.ARRAY_BUFFER,this.immediateMode.colorBuffer),c.bufferData(c.ARRAY_BUFFER,new Float32Array(b),c.DYNAMIC_DRAW),c.vertexAttribPointer(e.vertexColorAttribute,4,c.FLOAT,!1,0,0),this._setMatrixUniforms(d),this},d.RendererGL.prototype._getColorVertexShader=function(){var a,b=this.GL,c="immediateVert|vertexColorFrag";return this.materialInHash(c)?a=this.mHash[c]:(a=this._initShaders("immediateVert","vertexColorFrag",!0),this.mHash[c]=a,a.vertexColorAttribute=b.getAttribLocation(a,"aVertexColor"),b.enableVertexAttribArray(a.vertexColorAttribute)),a},b.exports=d.RendererGL},{"../core/constants":36,"../core/core":37}],85:[function(a,b,c){"use strict";function d(a){return a.length>0?a.reduce(function(a,b){return a.concat(b)}):[]}function e(a){return d(a.map(function(a){return[a.x,a.y,a.z]}))}var f=a("../core/core"),g=0;f.RendererGL.prototype._initBufferDefaults=function(a){if(g++,g>1e3){var b=Object.keys(this.gHash)[0];delete this.gHash[b],g--}var c=this.GL;this.gHash[a]={},this.gHash[a].vertexBuffer=c.createBuffer(),this.gHash[a].normalBuffer=c.createBuffer(),this.gHash[a].uvBuffer=c.createBuffer(),this.gHash[a].indexBuffer=c.createBuffer()},f.RendererGL.prototype.createBuffers=function(a,b){var c=this.GL;this._setDefaultCamera(),this._initBufferDefaults(a);var f=this.mHash[this._getCurShaderId()];this.gHash[a].numberOfItems=3*b.faces.length,c.bindBuffer(c.ARRAY_BUFFER,this.gHash[a].vertexBuffer),c.bufferData(c.ARRAY_BUFFER,new Float32Array(e(b.vertices)),c.STATIC_DRAW),f.vertexPositionAttribute=c.getAttribLocation(f,"aPosition"),c.enableVertexAttribArray(f.vertexPositionAttribute),c.vertexAttribPointer(f.vertexPositionAttribute,3,c.FLOAT,!1,0,0),c.bindBuffer(c.ARRAY_BUFFER,this.gHash[a].normalBuffer),c.bufferData(c.ARRAY_BUFFER,new Float32Array(e(b.vertexNormals)),c.STATIC_DRAW),f.vertexNormalAttribute=c.getAttribLocation(f,"aNormal"),c.enableVertexAttribArray(f.vertexNormalAttribute),c.vertexAttribPointer(f.vertexNormalAttribute,3,c.FLOAT,!1,0,0),c.bindBuffer(c.ARRAY_BUFFER,this.gHash[a].uvBuffer),c.bufferData(c.ARRAY_BUFFER,new Float32Array(d(b.uvs)),c.STATIC_DRAW),f.textureCoordAttribute=c.getAttribLocation(f,"aTexCoord"),c.enableVertexAttribArray(f.textureCoordAttribute),c.vertexAttribPointer(f.textureCoordAttribute,2,c.FLOAT,!1,0,0),c.bindBuffer(c.ELEMENT_ARRAY_BUFFER,this.gHash[a].indexBuffer),c.bufferData(c.ELEMENT_ARRAY_BUFFER,new Uint16Array(d(b.faces)),c.STATIC_DRAW)},f.RendererGL.prototype.drawBuffers=function(a){this._setDefaultCamera();var b=this.GL,c=this._getCurShaderId(),d=this.mHash[c];return b.bindBuffer(b.ARRAY_BUFFER,this.gHash[a].vertexBuffer),b.vertexAttribPointer(d.vertexPositionAttribute,3,b.FLOAT,!1,0,0),b.bindBuffer(b.ARRAY_BUFFER,this.gHash[a].normalBuffer),b.vertexAttribPointer(d.vertexNormalAttribute,3,b.FLOAT,!1,0,0),b.bindBuffer(b.ARRAY_BUFFER,this.gHash[a].uvBuffer),b.vertexAttribPointer(d.textureCoordAttribute,2,b.FLOAT,!1,0,0),b.bindBuffer(b.ELEMENT_ARRAY_BUFFER,this.gHash[a].indexBuffer),this._setMatrixUniforms(c),b.drawElements(b.TRIANGLES,this.gHash[a].numberOfItems,b.UNSIGNED_SHORT,0),this},b.exports=f.RendererGL},{"../core/core":37}],86:[function(a,b,c){"use strict";var d=a("../core/core"),e=a("./shader");a("../core/p5.Renderer"),a("./p5.Matrix");var f=[],g=1e3,h={alpha:!0,depth:!0,stencil:!0,antialias:!1,premultipliedAlpha:!1,preserveDrawingBuffer:!1};d.RendererGL=function(a,b,c){return d.Renderer.call(this,a,b,c),this._initContext(),this.isP3D=!0,this.GL=this.drawingContext,this.ambientLightCount=0,this.directionalLightCount=0,this.pointLightCount=0,this._curCamera=null,this.uMVMatrix=new d.Matrix,this.uPMatrix=new d.Matrix,this.uNMatrix=new d.Matrix("mat3"),this.gHash={},this.mHash={},this.isImmediateDrawing=!1,this.immediateMode={},this.curFillColor=[.5,.5,.5,1],this.curStrokeColor=[.5,.5,.5,1],this.pointSize=5,this},d.RendererGL.prototype=Object.create(d.Renderer.prototype),d.RendererGL.prototype._initContext=function(){try{if(this.drawingContext=this.canvas.getContext("webgl",h)||this.canvas.getContext("experimental-webgl",h),null===this.drawingContext)throw new Error("Error creating webgl context");console.log("p5.RendererGL: enabled webgl context");var a=this.drawingContext;a.enable(a.DEPTH_TEST),a.depthFunc(a.LEQUAL),a.viewport(0,0,a.drawingBufferWidth,a.drawingBufferHeight)}catch(b){throw new Error(b)}},d.RendererGL.prototype._setDefaultCamera=function(){if(null===this._curCamera){var a=this.width,b=this.height;this.uPMatrix=d.Matrix.identity(),this.uPMatrix.perspective(60/180*Math.PI,a/b,.1,100),this._curCamera="default"}},d.RendererGL.prototype._update=function(){this.uMVMatrix=d.Matrix.identity(),this.translate(0,0,-(this.height/2)/Math.tan(30*Math.PI/180)),this.ambientLightCount=0,this.directionalLightCount=0,this.pointLightCount=0},d.RendererGL.prototype.background=function(){var a=this.GL,b=this._pInst.color.apply(this._pInst,arguments),c=b.levels[0]/255,d=b.levels[1]/255,e=b.levels[2]/255,f=b.levels[3]/255;a.clearColor(c,d,e,f),a.clear(a.COLOR_BUFFER_BIT|a.DEPTH_BUFFER_BIT)},d.RendererGL.prototype._initShaders=function(a,b,c){var d=this.GL,f=d.createShader(d.VERTEX_SHADER);if(d.shaderSource(f,e[a]),d.compileShader(f),!d.getShaderParameter(f,d.COMPILE_STATUS))return alert("Yikes! An error occurred compiling the shaders:"+d.getShaderInfoLog(f)),null;var g=d.createShader(d.FRAGMENT_SHADER);if(d.shaderSource(g,e[b]),d.compileShader(g),!d.getShaderParameter(g,d.COMPILE_STATUS))return alert("Darn! An error occurred compiling the shaders:"+d.getShaderInfoLog(g)),null;var h=d.createProgram();return d.attachShader(h,f),d.attachShader(h,g),d.linkProgram(h),d.getProgramParameter(h,d.LINK_STATUS)||alert("Snap! Error linking shader program"),this._getLocation(h,c),h},d.RendererGL.prototype._getLocation=function(a,b){var c=this.GL;c.useProgram(a),a.uResolution=c.getUniformLocation(a,"uResolution"),c.uniform1f(a.uResolution,g),a.uPMatrixUniform=c.getUniformLocation(a,"uProjectionMatrix"),a.uMVMatrixUniform=c.getUniformLocation(a,"uModelViewMatrix"),void 0===b&&(a.uNMatrixUniform=c.getUniformLocation(a,"uNormalMatrix"),a.samplerUniform=c.getUniformLocation(a,"uSampler"))},d.RendererGL.prototype._setUniform1f=function(a,b,c){var d=this.GL,e=this.mHash[a];return d.useProgram(e),e[b]=d.getUniformLocation(e,b),d.uniform1f(e[b],c),this},d.RendererGL.prototype._setMatrixUniforms=function(a){var b=this.GL,c=this.mHash[a];b.useProgram(c),b.uniformMatrix4fv(c.uPMatrixUniform,!1,this.uPMatrix.mat4),b.uniformMatrix4fv(c.uMVMatrixUniform,!1,this.uMVMatrix.mat4),this.uNMatrix.inverseTranspose(this.uMVMatrix),b.uniformMatrix3fv(c.uNMatrixUniform,!1,this.uNMatrix.mat3)},d.RendererGL.prototype._getShader=function(a,b,c){var d=a+"|"+b;if(!this.materialInHash(d)){var e=this._initShaders(a,b,c);this.mHash[d]=e}return this.curShaderId=d,this.mHash[this.curShaderId]},d.RendererGL.prototype._getCurShaderId=function(){var a,b;return"fill"!==this.drawMode&&void 0===this.curShaderId?(a="normalVert|normalFrag",b=this._initShaders("normalVert","normalFrag"),this.mHash[a]=b,this.curShaderId=a):this.isImmediateDrawing&&"fill"===this.drawMode&&(a="immediateVert|vertexColorFrag",b=this._initShaders("immediateVert","vertexColorFrag"),this.mHash[a]=b,this.curShaderId=a),this.curShaderId},d.RendererGL.prototype.fill=function(a,b,c,d){var e,f=this.GL,g=this._applyColorBlend.apply(this,arguments);return this.curFillColor=g,this.drawMode="fill",this.isImmediateDrawing?(e=this._getShader("immediateVert","vertexColorFrag"),f.useProgram(e)):(e=this._getShader("normalVert","basicFrag"),f.useProgram(e),e.uMaterialColor=f.getUniformLocation(e,"uMaterialColor"),f.uniform4f(e.uMaterialColor,g[0],g[1],g[2],g[3])),this},d.RendererGL.prototype.stroke=function(a,b,c,d){var e=this._pInst.color.apply(this._pInst,arguments),f=e._array;return this.curStrokeColor=f,this.drawMode="stroke",this},d.RendererGL.prototype._strokeCheck=function(){if("stroke"===this.drawMode)throw new Error("stroke for shapes in 3D not yet implemented, use fill for now :(")},d.RendererGL.prototype.strokeWeight=function(a){return this.pointSize=a,this},d.RendererGL.prototype.geometryInHash=function(a){return void 0!==this.gHash[a]},d.RendererGL.prototype.materialInHash=function(a){return void 0!==this.mHash[a]},d.RendererGL.prototype.resize=function(a,b){var c=this.GL;d.Renderer.prototype.resize.call(this,a,b),c.viewport(0,0,c.drawingBufferWidth,c.drawingBufferHeight),"default"===this._curCamera&&(this._curCamera=null,this._setDefaultCamera())},d.RendererGL.prototype.clear=function(){var a=this.GL;a.clearColor(arguments[0],arguments[1],arguments[2],arguments[3]),a.clear(a.COLOR_BUFFER_BIT|a.DEPTH_BUFFER_BIT)},d.RendererGL.prototype.translate=function(a,b,c){return a/=g,b=-b/g,c/=g,this.uMVMatrix.translate([a,b,c]),this},d.RendererGL.prototype.scale=function(a,b,c){return this.uMVMatrix.scale([a,b,c]),this},d.RendererGL.prototype.rotate=function(a,b){return this.uMVMatrix.rotate(a,b),this},d.RendererGL.prototype.rotateX=function(a){return this.rotate(a,[1,0,0]),this},d.RendererGL.prototype.rotateY=function(a){return this.rotate(a,[0,1,0]),this},d.RendererGL.prototype.rotateZ=function(a){return this.rotate(a,[0,0,1]),this},d.RendererGL.prototype.push=function(){f.push(this.uMVMatrix.copy())},d.RendererGL.prototype.pop=function(){if(0===f.length)throw new Error("Invalid popMatrix!");this.uMVMatrix=f.pop()},d.RendererGL.prototype.resetMatrix=function(){return this.uMVMatrix=d.Matrix.identity(),this.translate(0,0,-800),this},d.RendererGL.prototype._applyTextProperties=function(){console.error("text commands not yet implemented in webgl")},b.exports=d.RendererGL},{"../core/core":37,"../core/p5.Renderer":43,"./p5.Matrix":83,"./shader":88}],87:[function(a,b,c){"use strict";var d=a("../core/core");a("./p5.Geometry"),d.prototype.plane=function(){for(var a=new Array(arguments.length),b=0;b<a.length;++b)a[b]=arguments[b];var c=a[0]||50,e=a[1]||c,f="number"==typeof a[2]?a[2]:1,g="number"==typeof a[3]?a[3]:1,h="plane|"+c+"|"+e+"|"+f+"|"+g;if(!this._renderer.geometryInHash(h)){var i=function(){for(var a,b,f,g=0;g<=this.detailY;g++){b=g/this.detailY;for(var h=0;h<=this.detailX;h++)a=h/this.detailX,f=new d.Vector(c*a-c/2,e*b-e/2,0),this.vertices.push(f),this.uvs.push([a,b])}},j=new d.Geometry(f,g,i);j.computeFaces().computeNormals(),this._renderer.createBuffers(h,j)}this._renderer.drawBuffers(h)},d.prototype.box=function(){for(var a=new Array(arguments.length),b=0;b<a.length;++b)a[b]=arguments[b];var c=a[0]||50,e=a[1]||c,f=a[2]||c,g="number"==typeof a[3]?a[3]:4,h="number"==typeof a[4]?a[4]:4,i="box|"+c+"|"+e+"|"+f+"|"+g+"|"+h;if(!this._renderer.geometryInHash(i)){var j=function(){for(var a=[[0,4,2,6],[1,3,5,7],[0,1,4,5],[2,6,3,7],[0,2,1,3],[4,5,6,7]],b=0,g=0;g<a.length;g++){ +for(var h=a[g],i=4*g,j=0;4>j;j++){var k=h[j],l=new d.Vector((2*(1&k)-1)*c/2,((2&k)-1)*e/2,((4&k)/2-1)*f/2);this.vertices.push(l),this.uvs.push([1&j,(2&j)/2]),b++}this.faces.push([i,i+1,i+2]),this.faces.push([i+2,i+1,i+3])}},k=new d.Geometry(g,h,j);k.computeNormals(),this._renderer.createBuffers(i,k)}return this._renderer.drawBuffers(i),this},d.prototype.sphere=function(){for(var a=new Array(arguments.length),b=0;b<a.length;++b)a[b]=arguments[b];var c=a[0]||50,e="number"==typeof a[1]?a[1]:24,f="number"==typeof a[2]?a[2]:16,g="sphere|"+c+"|"+e+"|"+f;if(!this._renderer.geometryInHash(g)){var h=function(){for(var a,b,e,f=0;f<=this.detailY;f++){b=f/this.detailY;for(var g=0;g<=this.detailX;g++){a=g/this.detailX;var h=2*Math.PI*a,i=Math.PI*b-Math.PI/2;e=new d.Vector(c*Math.cos(i)*Math.sin(h),c*Math.sin(i),c*Math.cos(i)*Math.cos(h)),this.vertices.push(e),this.uvs.push([a,b])}}},i=new d.Geometry(e,f,h);i.computeFaces().computeNormals().averageNormals().averagePoleNormals(),this._renderer.createBuffers(g,i)}return this._renderer.drawBuffers(g),this};var e=function(a,b,c,e,f,g,h){e=3>e?3:e,f=1>f?1:f,g=void 0===g?!0:g,h=void 0===h?!0:h;var i,j,k=(g?2:0)+(h?2:0),l=e+1,m=Math.atan2(a-b,c),n=g?-2:0,o=f+(h?2:0);for(i=n;o>=i;++i){var p,q=i/f,r=c*q;for(0>i?(r=0,q=1,p=a):i>f?(r=c,q=1,p=b):p=a+(b-a)*(i/f),(-2===i||i===f+2)&&(p=0,q=0),r-=c/2,j=0;l>j;++j)this.vertices.push(new d.Vector(Math.sin(j*Math.PI*2/e)*p,r,Math.cos(j*Math.PI*2/e)*p)),this.vertexNormals.push(new d.Vector(0>i||i>f?0:Math.sin(j*Math.PI*2/e)*Math.cos(m),0>i?-1:i>f?1:Math.sin(m),0>i||i>f?0:Math.cos(j*Math.PI*2/e)*Math.cos(m))),this.uvs.push([j/e,q])}for(i=0;f+k>i;++i)for(j=0;e>j;++j)this.faces.push([l*(i+0)+0+j,l*(i+0)+1+j,l*(i+1)+1+j]),this.faces.push([l*(i+0)+0+j,l*(i+1)+1+j,l*(i+1)+0+j])};d.prototype.cylinder=function(){for(var a=new Array(arguments.length),b=0;b<a.length;++b)a[b]=arguments[b];var c=a[0]||50,f=a[1]||c,g="number"==typeof a[2]?a[2]:24,h="number"==typeof a[3]?a[3]:16,i="cylinder|"+c+"|"+f+"|"+g+"|"+h;if(!this._renderer.geometryInHash(i)){var j=new d.Geometry(g,h);e.call(j,c,c,f,g,h,!0,!0),j.computeNormals(),this._renderer.createBuffers(i,j)}return this._renderer.drawBuffers(i),this},d.prototype.cone=function(){for(var a=new Array(arguments.length),b=0;b<a.length;++b)a[b]=arguments[b];var c=a[0]||50,f=a[1]||c,g="number"==typeof a[2]?a[2]:24,h="number"==typeof a[3]?a[3]:16,i="cone|"+c+"|"+f+"|"+g+"|"+h;if(!this._renderer.geometryInHash(i)){var j=new d.Geometry(g,h);e.call(j,c,0,f,g,h,!0,!0),j.computeNormals(),this._renderer.createBuffers(i,j)}return this._renderer.drawBuffers(i),this},d.prototype.ellipsoid=function(){for(var a=new Array(arguments.length),b=0;b<a.length;++b)a[b]=arguments[b];var c="number"==typeof a[3]?a[3]:24,e="number"==typeof a[4]?a[4]:24,f=a[0]||50,g=a[1]||f,h=a[2]||f,i="ellipsoid|"+f+"|"+g+"|"+h+"|"+c+"|"+e;if(!this._renderer.geometryInHash(i)){var j=function(){for(var a,b,c,e=0;e<=this.detailY;e++){b=e/this.detailY;for(var i=0;i<=this.detailX;i++){a=i/this.detailX;var j=2*Math.PI*a,k=Math.PI*b-Math.PI/2;c=new d.Vector(f*Math.cos(k)*Math.sin(j),g*Math.sin(k),h*Math.cos(k)*Math.cos(j)),this.vertices.push(c),this.uvs.push([a,b])}}},k=new d.Geometry(c,e,j);k.computeFaces().computeNormals(),this._renderer.createBuffers(i,k)}return this._renderer.drawBuffers(i),this},d.prototype.torus=function(){for(var a=new Array(arguments.length),b=0;b<a.length;++b)a[b]=arguments[b];var c="number"==typeof a[2]?a[2]:24,e="number"==typeof a[3]?a[3]:16,f=a[0]||50,g=a[1]||10,h="torus|"+f+"|"+g+"|"+c+"|"+e;if(!this._renderer.geometryInHash(h)){var i=function(){for(var a,b,c,e=0;e<=this.detailY;e++){b=e/this.detailY;for(var h=0;h<=this.detailX;h++){a=h/this.detailX;var i=2*Math.PI*a,j=2*Math.PI*b;c=new d.Vector((f+g*Math.cos(j))*Math.cos(i),(f+g*Math.cos(j))*Math.sin(i),g*Math.sin(j)),this.vertices.push(c),this.uvs.push([a,b])}}},j=new d.Geometry(c,e,i);j.computeFaces().computeNormals().averageNormals(),this._renderer.createBuffers(h,j)}return this._renderer.drawBuffers(h),this},d.RendererGL.prototype.point=function(a,b,c){return console.log("point not yet implemented in webgl"),this},d.RendererGL.prototype.triangle=function(a){var b=a[0],c=a[1],e=a[2],f=a[3],g=a[4],h=a[5],i="tri|"+b+"|"+c+"|"+e+"|"+f+"|"+g+"|"+h;if(!this.geometryInHash(i)){var j=function(){var a=[];a.push(new d.Vector(b,c,0)),a.push(new d.Vector(e,f,0)),a.push(new d.Vector(g,h,0)),this.vertices=a,this.faces=[[0,1,2]],this.uvs=[[0,0],[0,1],[1,1]]},k=new d.Geometry(1,1,j);k.computeNormals(),this.createBuffers(i,k)}return this.drawBuffers(i),this},d.RendererGL.prototype.ellipse=function(a){var b=a[0],c=a[1],e=a[2],f=a[3],g=a[4]||24,h=a[5]||16,i="ellipse|"+a[0]+"|"+a[1]+"|"+a[2]+"|"+a[3];if(!this.geometryInHash(i)){var j=function(){for(var a,g,h,i=b+.5*e,j=c+.5*f,k=0;k<=this.detailY;k++){g=k/this.detailY;for(var l=0;l<=this.detailX;l++){a=l/this.detailX;var m=2*Math.PI*a;if(0===g)h=new d.Vector(i,j,0);else{var n=i+.5*e*Math.cos(m),o=j+.5*f*Math.sin(m);h=new d.Vector(n,o,0)}this.vertices.push(h),this.uvs.push([a,g])}}},k=new d.Geometry(g,h,j);k.computeFaces().computeNormals(),this.createBuffers(i,k)}return this.drawBuffers(i),this},d.RendererGL.prototype.rect=function(a){var b="rect|"+a[0]+"|"+a[1]+"|"+a[2]+"|"+a[3],c=a[0],e=a[1],f=a[2],g=a[3],h=a[4]||24,i=a[5]||16;if(!this.geometryInHash(b)){var j=function(){for(var a,b,h,i=0;i<=this.detailY;i++){b=i/this.detailY;for(var j=0;j<=this.detailX;j++)a=j/this.detailX,h=new d.Vector(c+f*a,e+g*b,0),this.vertices.push(h),this.uvs.push([a,b])}},k=new d.Geometry(h,i,j);k.computeFaces().computeNormals(),this.createBuffers(b,k)}return this.drawBuffers(b),this},d.RendererGL.prototype.quad=function(){for(var a=new Array(arguments.length),b=0;b<a.length;++b)a[b]=arguments[b];var c=a[0],e=a[1],f=a[2],g=a[3],h=a[4],i=a[5],j=a[6],k=a[7],l="quad|"+c+"|"+e+"|"+f+"|"+g+"|"+h+"|"+i+"|"+j+"|"+k;if(!this.geometryInHash(l)){var m=function(){this.vertices.push(new d.Vector(c,e,0)),this.vertices.push(new d.Vector(f,g,0)),this.vertices.push(new d.Vector(h,i,0)),this.vertices.push(new d.Vector(j,k,0)),this.uvs.push([0,0],[1,0],[1,1],[0,1])},n=new d.Geometry(2,2,m);n.computeNormals(),n.faces=[[0,1,2],[2,3,0]],this.createBuffers(l,n)}return this.drawBuffers(l),this},d.RendererGL.prototype.bezier=function(a){var b=a[12]||20;this.beginShape();for(var c=[0,0,0,0],d=[0,0,0],e=0;b>=e;e++)c[0]=Math.pow(1-e/b,3),c[1]=3*(e/b)*Math.pow(1-e/b,2),c[2]=3*Math.pow(e/b,2)*(1-e/b),c[3]=Math.pow(e/b,3),d[0]=a[0]*c[0]+a[3]*c[1]+a[6]*c[2]+a[9]*c[3],d[1]=a[1]*c[0]+a[4]*c[1]+a[7]*c[2]+a[10]*c[3],d[2]=a[2]*c[0]+a[5]*c[1]+a[8]*c[2]+a[11]*c[3],this.vertex(d[0],d[1],d[2]);return this.endShape(),this},d.RendererGL.prototype.curve=function(a){var b=a[12];this.beginShape();for(var c=[0,0,0,0],d=[0,0,0],e=0;b>=e;e++)c[0]=.5*Math.pow(e/b,3),c[1]=.5*Math.pow(e/b,2),c[2]=e/b*.5,c[3]=.5,d[0]=c[0]*(-a[0]+3*a[3]-3*a[6]+a[9])+c[1]*(2*a[0]-5*a[3]+4*a[6]-a[9])+c[2]*(-a[0]+a[6])+2*c[3]*a[3],d[1]=c[0]*(-a[1]+3*a[4]-3*a[7]+a[10])+c[1]*(2*a[1]-5*a[4]+4*a[7]-a[10])+c[2]*(-a[1]+a[7])+2*c[3]*a[4],d[2]=c[0]*(-a[2]+3*a[5]-3*a[8]+a[11])+c[1]*(2*a[2]-5*a[5]+4*a[8]-a[11])+c[2]*(-a[2]+a[8])+2*c[3]*a[5],this.vertex(d[0],d[1],d[2]);return this.endShape(),this},b.exports=d},{"../core/core":37,"./p5.Geometry":82}],88:[function(a,b,c){b.exports={immediateVert:"attribute vec3 aPosition;\nattribute vec4 aVertexColor;\n\nuniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\nuniform float uResolution;\nuniform float uPointSize;\n\nvarying vec4 vColor;\nvoid main(void) {\n vec4 positionVec4 = vec4(aPosition / uResolution *vec3(1.0, -1.0, 1.0), 1.0);\n gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;\n vColor = aVertexColor;\n gl_PointSize = uPointSize;\n}\n",vertexColorVert:"attribute vec3 aPosition;\nattribute vec4 aVertexColor;\n\nuniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\nuniform float uResolution;\n\nvarying vec4 vColor;\n\nvoid main(void) {\n vec4 positionVec4 = vec4(aPosition / uResolution * vec3(1.0, -1.0, 1.0), 1.0);\n gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;\n vColor = aVertexColor;\n}\n",vertexColorFrag:"precision mediump float;\nvarying vec4 vColor;\nvoid main(void) {\n gl_FragColor = vColor;\n}",normalVert:"attribute vec3 aPosition;\nattribute vec3 aNormal;\nattribute vec2 aTexCoord;\n\nuniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\nuniform mat3 uNormalMatrix;\nuniform float uResolution;\n\nvarying vec3 vVertexNormal;\nvarying highp vec2 vVertTexCoord;\n\nvoid main(void) {\n vec4 positionVec4 = vec4(aPosition / uResolution * vec3(1.0, -1.0, 1.0), 1.0);\n gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;\n vVertexNormal = vec3( uNormalMatrix * aNormal );\n vVertTexCoord = aTexCoord;\n}\n",normalFrag:"precision mediump float;\nvarying vec3 vVertexNormal;\nvoid main(void) {\n gl_FragColor = vec4(vVertexNormal, 1.0);\n}",basicFrag:"precision mediump float;\nvarying vec3 vVertexNormal;\nuniform vec4 uMaterialColor;\nvoid main(void) {\n gl_FragColor = uMaterialColor;\n}",lightVert:"attribute vec3 aPosition;\nattribute vec3 aNormal;\nattribute vec2 aTexCoord;\n\nuniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\nuniform mat3 uNormalMatrix;\nuniform float uResolution;\nuniform int uAmbientLightCount;\nuniform int uDirectionalLightCount;\nuniform int uPointLightCount;\n\nuniform vec3 uAmbientColor[8];\nuniform vec3 uLightingDirection[8];\nuniform vec3 uDirectionalColor[8];\nuniform vec3 uPointLightLocation[8];\nuniform vec3 uPointLightColor[8];\nuniform bool uSpecular;\n\nvarying vec3 vVertexNormal;\nvarying vec2 vVertTexCoord;\nvarying vec3 vLightWeighting;\n\nvec3 ambientLightFactor = vec3(0.0, 0.0, 0.0);\nvec3 directionalLightFactor = vec3(0.0, 0.0, 0.0);\nvec3 pointLightFactor = vec3(0.0, 0.0, 0.0);\nvec3 pointLightFactor2 = vec3(0.0, 0.0, 0.0);\n\nvoid main(void){\n\n vec4 positionVec4 = vec4(aPosition / uResolution, 1.0);\n gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;\n\n vec3 vertexNormal = vec3( uNormalMatrix * aNormal );\n vVertexNormal = vertexNormal;\n vVertTexCoord = aTexCoord;\n\n vec4 mvPosition = uModelViewMatrix * vec4(aPosition / uResolution, 1.0);\n vec3 eyeDirection = normalize(-mvPosition.xyz);\n\n float shininess = 32.0;\n float specularFactor = 2.0;\n float diffuseFactor = 0.3;\n\n for(int i = 0; i < 8; i++){\n if(uAmbientLightCount == i) break;\n ambientLightFactor += uAmbientColor[i];\n }\n\n for(int j = 0; j < 8; j++){\n if(uDirectionalLightCount == j) break;\n vec3 dir = uLightingDirection[j];\n float directionalLightWeighting = max(dot(vertexNormal, dir), 0.0);\n directionalLightFactor += uDirectionalColor[j] * directionalLightWeighting;\n }\n\n for(int k = 0; k < 8; k++){\n if(uPointLightCount == k) break;\n vec3 loc = uPointLightLocation[k];\n //loc = loc / uResolution;\n vec3 lightDirection = normalize(loc - mvPosition.xyz);\n\n float directionalLightWeighting = max(dot(vertexNormal, lightDirection), 0.0);\n pointLightFactor += uPointLightColor[k] * directionalLightWeighting;\n\n //factor2 for specular\n vec3 reflectionDirection = reflect(-lightDirection, vertexNormal);\n float specularLightWeighting = pow(max(dot(reflectionDirection, eyeDirection), 0.0), shininess);\n\n pointLightFactor2 += uPointLightColor[k] * (specularFactor * specularLightWeighting\n + directionalLightWeighting * diffuseFactor);\n }\n\n if(!uSpecular){\n vLightWeighting = ambientLightFactor + directionalLightFactor + pointLightFactor;\n }else{\n vLightWeighting = ambientLightFactor + directionalLightFactor + pointLightFactor2;\n }\n\n}\n",lightTextureFrag:"precision mediump float;\n\nuniform vec4 uMaterialColor;\nuniform sampler2D uSampler;\nuniform bool isTexture;\n\nvarying vec3 vLightWeighting;\nvarying highp vec2 vVertTexCoord;\n\nvoid main(void) {\n if(!isTexture){\n gl_FragColor = vec4(vec3(uMaterialColor.rgb * vLightWeighting), uMaterialColor.a);\n }else{\n vec4 textureColor = texture2D(uSampler, vVertTexCoord);\n if(vLightWeighting == vec3(0., 0., 0.)){\n gl_FragColor = textureColor;\n }else{\n gl_FragColor = vec4(vec3(textureColor.rgb * vLightWeighting), textureColor.a);\n }\n }\n}"}},{}]},{},[28])(28)}); \ No newline at end of file diff --git a/public/js/p5.play.js b/public/js/p5.play.js new file mode 100644 index 0000000000000000000000000000000000000000..72af1187207b036a81f81dd4b691472eb0b8e15b --- /dev/null +++ b/public/js/p5.play.js @@ -0,0 +1,4366 @@ +/* +p5.play +by Paolo Pedercini/molleindustria, 2015 +http://molleindustria.org/ +*/ + +(function(root, factory) { +if (typeof define === 'function' && define.amd) +define('p5.play', ['p5'], function(p5) { (factory(p5)); }); +else if (typeof exports === 'object') +factory(require('../p5')); +else +factory(root.p5); +}(this, function(p5) { +/** + * p5.play is a library for p5.js to facilitate the creation of games and gamelike + * projects. + * + * It provides a flexible Sprite class to manage visual objects in 2D space + * and features such as animation support, basic collision detection + * and resolution, mouse and keyboard interactions, and a virtual camera. + * + * p5.play is not a box2D-derived physics engine, it doesn't use events, and it's + * designed to be understood and possibly modified by intermediate programmers. + * + * See the examples folder for more info on how to use this library. + * + * @module p5.play + * @submodule p5.play + * @for p5.play + * @main + */ + +// ============================================================================= +// initialization +// ============================================================================= + +// This provides a way for us to lazily define properties that +// are global to p5 instances. +// +// Note that this isn't just an optimization: p5 currently provides no +// way for add-ons to be notified when new p5 instances are created, so +// lazily creating these properties is the *only* mechanism available +// to us. For more information, see: +// +// https://github.com/processing/p5.js/issues/1263 +function defineLazyP5Property(name, getter) { + Object.defineProperty(p5.prototype, name, { + configurable: true, + enumerable: true, + get: function() { + var context = (this instanceof p5 && !this._isGlobal) ? this : window; + + if (typeof(context._p5PlayProperties) === 'undefined') { + context._p5PlayProperties = {}; + } + if (!(name in context._p5PlayProperties)) { + context._p5PlayProperties[name] = getter.call(context); + } + return context._p5PlayProperties[name]; + } + }); +} + +// This returns a factory function, suitable for passing to +// defineLazyP5Property, that returns a subclass of the given +// constructor that is always bound to a particular p5 instance. +function boundConstructorFactory(constructor) { + if (typeof(constructor) !== 'function') + throw new Error('constructor must be a function'); + + return function createBoundConstructor() { + var pInst = this; + + function F() { + var args = Array.prototype.slice.call(arguments); + + return constructor.apply(this, [pInst].concat(args)); + } + F.prototype = constructor.prototype; + + return F; + }; +} + +// This is a utility that makes it easy to define convenient aliases to +// pre-bound p5 instance methods. +// +// For example: +// +// var pInstBind = createPInstBinder(pInst); +// +// var createVector = pInstBind('createVector'); +// var loadImage = pInstBind('loadImage'); +// +// The above will create functions createVector and loadImage, which can be +// used similar to p5 global mode--however, they're bound to specific p5 +// instances, and can thus be used outside of global mode. +function createPInstBinder(pInst) { + return function pInstBind(methodName) { + var method = pInst[methodName]; + + if (typeof(method) !== 'function') + throw new Error('"' + methodName + '" is not a p5 method'); + return method.bind(pInst); + }; +} + +// These are utility p5 functions that don't depend on p5 instance state in +// order to work properly, so we'll go ahead and make them easy to +// access without needing to bind them to a p5 instance. +var abs = p5.prototype.abs; +var radians = p5.prototype.radians; +var dist = p5.prototype.dist; +var degrees = p5.prototype.degrees; +var pow = p5.prototype.pow; +var round = p5.prototype.round; + + +// ============================================================================= +// p5 additions +// ============================================================================= + +/** +* A Group containing all the sprites in the sketch. +* +* @property allSprites +* @type {Group} +*/ + +defineLazyP5Property('allSprites', function() { + return new p5.prototype.Group(); +}); + +p5.prototype.spriteUpdate = true; + +/** + * A Sprite is the main building block of p5.play: + * an element able to store images or animations with a set of + * properties such as position and visibility. + * A Sprite can have a collider that defines the active area to detect + * collisions or overlappings with other sprites and mouse interactions. + * + * Sprites created using createSprite (the preferred way) are added to the + * allSprites group and given a depth value that puts it in front of all + * other sprites. + * + * @method createSprite + * @param {Number} x Initial x coordinate + * @param {Number} y Initial y coordinate + * @param {Number} width Width of the placeholder rectangle and of the + * collider until an image or new collider are set + * @param {Number} height Height of the placeholder rectangle and of the + * collider until an image or new collider are set + * @return {Object} The new sprite instance + */ + +p5.prototype.createSprite = function(x, y, width, height) { + var s = new Sprite(this, x, y, width, height); + s.depth = this.allSprites.maxDepth()+1; + this.allSprites.add(s); + return s; +}; + + +/** + * Removes a Sprite from the sketch. + * The removed Sprite won't be drawn or updated anymore. + * Equivalent to Sprite.remove() + * + * @method removeSprite + * @param {Object} sprite Sprite to be removed +*/ +p5.prototype.removeSprite = function(sprite) { + sprite.remove(); +}; + +/** +* Updates all the sprites in the sketch (position, animation...) +* it's called automatically at every draw(). +* It can be paused by passing a parameter true or false; +* Note: it does not render the sprites. +* +* @method updateSprites +* @param {Boolean} updating false to pause the update, true to resume +*/ +p5.prototype.updateSprites = function(upd) { + + if(upd === false) + this.spriteUpdate = false; + if(upd === true) + this.spriteUpdate = true; + + if(this.spriteUpdate) + for(var i = 0; i<this.allSprites.size(); i++) + { + this.allSprites.get(i).update(); + } +}; + +/** +* Returns all the sprites in the sketch as an array +* +* @method getSprites +* @return {Array} Array of Sprites +*/ +p5.prototype.getSprites = function() { + + //draw everything + if(arguments.length===0) + { + return this.allSprites.toArray(); + } + else + { + var arr = []; + //for every tag + for(var j=0; j<arguments.length; j++) + { + for(var i = 0; i<this.allSprites.size(); i++) + { + if(this.allSprites.get(i).isTagged(arguments[j])) + arr.push(this.allSprites.get(i)); + } + } + + return arr; + } + +}; + +/** +* Displays a Group of sprites. +* If no parameter is specified, draws all sprites in the +* sketch. +* The drawing order is determined by the Sprite property "depth" +* +* @method drawSprites +* @param {Group} [group] Group of Sprites to be displayed +*/ +p5.prototype.drawSprites = function(group) { + // If no group is provided, draw the allSprites group. + group = group || this.allSprites; + + if (typeof group.draw !== 'function') + { + throw('Error: with drawSprites you can only draw all sprites or a group'); + } + + group.draw(); +}; + +/** +* Displays a Sprite. +* To be typically used in the main draw function. +* +* @method drawSprite +* @param {Sprite} sprite Sprite to be displayed +*/ +p5.prototype.drawSprite = function(sprite) { + if(sprite) + sprite.display(); +}; + +/** +* Loads an animation. +* To be typically used in the preload() function of the sketch. +* +* @method loadAnimation +* @param {Sprite} sprite Sprite to be displayed +*/ +p5.prototype.loadAnimation = function() { + return construct(this.Animation, arguments); +}; + +/** + * Loads a Sprite Sheet. + * To be typically used in the preload() function of the sketch. + * + * @method loadSpriteSheet + */ +p5.prototype.loadSpriteSheet = function() { + return construct(this.SpriteSheet, arguments); +}; + +/** +* Displays an animation. +* +* @method animation +* @param {Animation} anim Animation to be displayed +* @param {Number} x X coordinate +* @param {Number} y Y coordinate +* +*/ +p5.prototype.animation = function(anim, x, y) { + anim.draw(x, y); +}; + +//variable to detect instant presses +defineLazyP5Property('_p5play', function() { + return { + keyStates: {}, + mouseStates: {} + }; +}); + +var KEY_IS_UP = 0; +var KEY_WENT_DOWN = 1; +var KEY_IS_DOWN = 2; +var KEY_WENT_UP = 3; + +/** +* Detects if a key was pressed during the last cycle. +* It can be used to trigger events once, when a key is pressed or released. +* Example: Super Mario jumping. +* +* @method keyWentDown +* @param {Number|String} key Key code or character +* @return {Boolean} True if the key was pressed +*/ +p5.prototype.keyWentDown = function(key) { + return this._isKeyInState(key, KEY_WENT_DOWN); +}; + + +/** +* Detects if a key was released during the last cycle. +* It can be used to trigger events once, when a key is pressed or released. +* Example: Spaceship shooting. +* +* @method keyWentUp +* @param {Number|String} key Key code or character +* @return {Boolean} True if the key was released +*/ +p5.prototype.keyWentUp = function(key) { + return this._isKeyInState(key, KEY_WENT_UP); +}; + +/** +* Detects if a key is currently pressed +* Like p5 keyIsDown but accepts strings and codes +* +* @method keyDown +* @param {Number|String} key Key code or character +* @return {Boolean} True if the key is down +*/ +p5.prototype.keyDown = function(key) { + return this._isKeyInState(key, KEY_IS_DOWN); +}; + +/** + * Detects if a key is in the given state during the last cycle. + * Helper method encapsulating common key state logic; it may be preferable + * to call keyDown or other methods directly. + * + * @private + * @method _isKeyInState + * @param {Number|String} key Key code or character + * @param {Number} state Key state to check against + * @return {Boolean} True if the key is in the given state + */ +p5.prototype._isKeyInState = function(key, state) { + var keyCode; + var keyStates = this._p5play.keyStates; + + if(typeof key === 'string') + { + keyCode = this._keyCodeFromAlias(key); + } + else + { + keyCode = key; + } + + //if undefined start checking it + if(keyStates[keyCode]===undefined) + { + if(this.keyIsDown(keyCode)) + keyStates[keyCode] = KEY_IS_DOWN; + else + keyStates[keyCode] = KEY_IS_UP; + } + + return (keyStates[keyCode] === state); +}; + +/** +* Detects if a mouse button is currently down +* Combines mouseIsPressed and mouseButton of p5 +* +* @method mouseDown +* @param {Number} [buttonCode] Mouse button constant LEFT, RIGHT or CENTER +* @return {Boolean} True if the button is down +*/ +p5.prototype.mouseDown = function(buttonCode) { + return this._isMouseButtonInState(buttonCode, KEY_IS_DOWN); +}; + +/** +* Detects if a mouse button is currently up +* Combines mouseIsPressed and mouseButton of p5 +* +* @method mouseUp +* @param {Number} [buttonCode] Mouse button constant LEFT, RIGHT or CENTER +* @return {Boolean} True if the button is up +*/ +p5.prototype.mouseUp = function(buttonCode) { + return this._isMouseButtonInState(buttonCode, KEY_IS_UP); +}; + +/** + * Detects if a mouse button was released during the last cycle. + * It can be used to trigger events once, to be checked in the draw cycle + * + * @method mouseWentUp + * @param {Number} [buttonCode] Mouse button constant LEFT, RIGHT or CENTER + * @return {Boolean} True if the button was just released + */ +p5.prototype.mouseWentUp = function(buttonCode) { + return this._isMouseButtonInState(buttonCode, KEY_WENT_UP); +}; + + +/** + * Detects if a mouse button was pressed during the last cycle. + * It can be used to trigger events once, to be checked in the draw cycle + * + * @method mouseWentDown + * @param {Number} [buttonCode] Mouse button constant LEFT, RIGHT or CENTER + * @return {Boolean} True if the button was just pressed + */ +p5.prototype.mouseWentDown = function(buttonCode) { + return this._isMouseButtonInState(buttonCode, KEY_WENT_DOWN); +}; + +/** + * Detects if a mouse button is in the given state during the last cycle. + * Helper method encapsulating common mouse button state logic; it may be + * preferable to call mouseWentUp, etc, directly. + * + * @private + * @method _isMouseButtonInState + * @param {Number} [buttonCode] Mouse button constant LEFT, RIGHT or CENTER + * @param {Number} state + * @return {boolean} True if the button was in the given state + */ +p5.prototype._isMouseButtonInState = function(buttonCode, state) { + var mouseStates = this._p5play.mouseStates; + + if(buttonCode === undefined) + buttonCode = this.LEFT; + + //undefined = not tracked yet, start tracking + if(mouseStates[buttonCode]===undefined) + { + if(this.mouseIsPressed && this.mouseButton === buttonCode) + mouseStates[buttonCode] = KEY_IS_DOWN; + else + mouseStates[buttonCode] = KEY_IS_UP; + } + + return (mouseStates[buttonCode] === state); +}; + + +/** + * An object storing all useful keys for easy access + * Key.tab = 9 + * + * @private + * @property KEY + * @type {Object} + */ +p5.prototype.KEY = { + 'BACKSPACE': 8, + 'TAB': 9, + 'ENTER': 13, + 'SHIFT': 16, + 'CTRL': 17, + 'ALT': 18, + 'PAUSE': 19, + 'CAPS_LOCK': 20, + 'ESC': 27, + 'SPACE': 32, + ' ': 32, + 'PAGE_UP': 33, + 'PAGE_DOWN': 34, + 'END': 35, + 'HOME': 36, + 'LEFT_ARROW': 37, + 'LEFT': 37, + 'UP_ARROW': 38, + 'UP': 38, + 'RIGHT_ARROW': 39, + 'RIGHT': 39, + 'DOWN_ARROW': 40, + 'DOWN': 40, + 'INSERT': 45, + 'DELETE': 46, + '0': 48, + '1': 49, + '2': 50, + '3': 51, + '4': 52, + '5': 53, + '6': 54, + '7': 55, + '8': 56, + '9': 57, + 'A': 65, + 'B': 66, + 'C': 67, + 'D': 68, + 'E': 69, + 'F': 70, + 'G': 71, + 'H': 72, + 'I': 73, + 'J': 74, + 'K': 75, + 'L': 76, + 'M': 77, + 'N': 78, + 'O': 79, + 'P': 80, + 'Q': 81, + 'R': 82, + 'S': 83, + 'T': 84, + 'U': 85, + 'V': 86, + 'W': 87, + 'X': 88, + 'Y': 89, + 'Z': 90, + '0NUMPAD': 96, + '1NUMPAD': 97, + '2NUMPAD': 98, + '3NUMPAD': 99, + '4NUMPAD': 100, + '5NUMPAD': 101, + '6NUMPAD': 102, + '7NUMPAD': 103, + '8NUMPAD': 104, + '9NUMPAD': 105, + 'MULTIPLY': 106, + 'PLUS': 107, + 'MINUS': 109, + 'DOT': 110, + 'SLASH1': 111, + 'F1': 112, + 'F2': 113, + 'F3': 114, + 'F4': 115, + 'F5': 116, + 'F6': 117, + 'F7': 118, + 'F8': 119, + 'F9': 120, + 'F10': 121, + 'F11': 122, + 'F12': 123, + 'EQUAL': 187, + 'COMMA': 188, + 'SLASH': 191, + 'BACKSLASH': 220 +}; + +/** + * An object storing deprecated key aliases, which we still support but + * should be mapped to valid aliases and generate warnings. + * + * @private + * @property KEY_DEPRECATIONS + * @type {Object} + */ +p5.prototype.KEY_DEPRECATIONS = { + 'MINUT': 'MINUS', + 'COMA': 'COMMA' +}; + +/** + * Given a string key alias (as defined in the KEY property above), look up + * and return the numeric JavaScript key code for that key. If a deprecated + * alias is passed (as defined in the KEY_DEPRECATIONS property) it will be + * mapped to a valid key code, but will also generate a warning about use + * of the deprecated alias. + * + * @private + * @method _keyCodeFromAlias + * @param {!string} alias - a case-insensitive key alias + * @return {number|undefined} a numeric JavaScript key code, or undefined + * if no key code matching the given alias is found. + */ +p5.prototype._keyCodeFromAlias = function(alias) { + alias = alias.toUpperCase(); + if (this.KEY_DEPRECATIONS[alias]) { + this._warn('Key literal "' + alias + '" is deprecated and may be removed ' + + 'in a future version of p5.play. ' + + 'Please use "' + this.KEY_DEPRECATIONS[alias] + '" instead.'); + alias = this.KEY_DEPRECATIONS[alias]; + } + return this.KEY[alias]; +}; + +//pre draw: detect keyStates +p5.prototype.readPresses = function() { + var keyStates = this._p5play.keyStates; + var mouseStates = this._p5play.mouseStates; + + for (var key in keyStates) { + if(this.keyIsDown(key)) //if is down + { + if(keyStates[key] === KEY_IS_UP)//and was up + keyStates[key] = KEY_WENT_DOWN; + else + keyStates[key] = KEY_IS_DOWN; //now is simply down + } + else //if it's up + { + if(keyStates[key] === KEY_IS_DOWN)//and was up + keyStates[key] = KEY_WENT_UP; + else + keyStates[key] = KEY_IS_UP; //now is simply down + } + } + + //mouse + for (var btn in mouseStates) { + + if(this.mouseIsPressed && this.mouseButton === btn) //if is down + { + if(mouseStates[btn] === KEY_IS_UP)//and was up + mouseStates[btn] = KEY_WENT_DOWN; + else + mouseStates[btn] = KEY_IS_DOWN; //now is simply down + } + else //if it's up + { + if(mouseStates[btn] === KEY_IS_DOWN)//and was up + mouseStates[btn] = KEY_WENT_UP; + else + mouseStates[btn] = KEY_IS_UP; //now is simply down + } + } + +}; + +/** +* Turns the quadTree on or off. +* A quadtree is a data structure used to optimize collision detection. +* It can improve performance when there is a large number of Sprites to be +* checked continuously for overlapping. +* +* p5.play will create and update a quadtree automatically. +* +* @method useQuadTree +* @param {Boolean} use Pass true to enable, false to disable +*/ +p5.prototype.useQuadTree = function(use) { + + if(this.quadTree !== undefined) + { + if(use === undefined) + return this.quadTree.active; + else if(use) + this.quadTree.active = true; + else + this.quadTree.active = false; + } + else + return false; +}; + +//the actual quadTree +defineLazyP5Property('quadTree', function() { + return new Quadtree({ + x: 0, + y: 0, + width: 0, + height: 0 + }, 4); +}); + +/* +//framerate independent delta, doesn't really work +p5.prototype.deltaTime = 1; + +var now = Date.now(); +var then = Date.now(); +var INTERVAL_60 = 0.0166666; //60 fps + +function updateDelta() { +then = now; +now = Date.now(); +deltaTime = ((now - then) / 1000)/INTERVAL_60; // seconds since last frame +} +*/ + +/** + * A Sprite is the main building block of p5.play: + * an element able to store images or animations with a set of + * properties such as position and visibility. + * A Sprite can have a collider that defines the active area to detect + * collisions or overlappings with other sprites and mouse interactions. + * + * To create a Sprite, use + * {{#crossLink "p5.play/createSprite:method"}}{{/crossLink}}. + * + * @class Sprite + */ + +// For details on why these docs aren't in a YUIDoc comment block, see: +// +// https://github.com/molleindustria/p5.play/pull/67 +// +// @param {Number} x Initial x coordinate +// @param {Number} y Initial y coordinate +// @param {Number} width Width of the placeholder rectangle and of the +// collider until an image or new collider are set +// @param {Number} height Height of the placeholder rectangle and of the +// collider until an image or new collider are set +function Sprite(pInst, _x, _y, _w, _h) { + var pInstBind = createPInstBinder(pInst); + + var createVector = pInstBind('createVector'); + var color = pInstBind('color'); + var random = pInstBind('random'); + var print = pInstBind('print'); + var push = pInstBind('push'); + var pop = pInstBind('pop'); + var colorMode = pInstBind('colorMode'); + var noStroke = pInstBind('noStroke'); + var rectMode = pInstBind('rectMode'); + var ellipseMode = pInstBind('ellipseMode'); + var imageMode = pInstBind('imageMode'); + var translate = pInstBind('translate'); + var scale = pInstBind('scale'); + var rotate = pInstBind('rotate'); + var stroke = pInstBind('stroke'); + var strokeWeight = pInstBind('strokeWeight'); + var line = pInstBind('line'); + var noFill = pInstBind('noFill'); + var fill = pInstBind('fill'); + var textAlign = pInstBind('textAlign'); + var textSize = pInstBind('textSize'); + var text = pInstBind('text'); + var rect = pInstBind('rect'); + var cos = pInstBind('cos'); + var sin = pInstBind('sin'); + var atan2 = pInstBind('atan2'); + + var quadTree = pInst.quadTree; + var camera = pInst.camera; + + + // These are p5 constants that we'd like easy access to. + var RGB = p5.prototype.RGB; + var CENTER = p5.prototype.CENTER; + var LEFT = p5.prototype.LEFT; + var BOTTOM = p5.prototype.BOTTOM; + + /** + * The sprite's position of the sprite as a vector (x,y). + * @property position + * @type {p5.Vector} + */ + this.position = createVector(_x, _y); + + /** + * The sprite's position at the beginning of the last update as a vector (x,y). + * @property previousPosition + * @type {p5.Vector} + */ + this.previousPosition = createVector(_x, _y); + + /* + The sprite's position at the end of the last update as a vector (x,y). + Note: this will differ from position whenever the position is changed + directly by assignment. + */ + this.newPosition = createVector(_x, _y); + + //Position displacement on the x coordinate since the last update + this.deltaX = 0; + this.deltaY = 0; + + /** + * The sprite's velocity as a vector (x,y) + * Velocity is speed broken down to its vertical and horizontal components. + * + * @property velocity + * @type {p5.Vector} + */ + this.velocity = createVector(0, 0); + + /** + * Set a limit to the sprite's scalar speed regardless of the direction. + * The value can only be positive. If set to -1, there's no limit. + * + * @property maxSpeed + * @type {Number} + * @default -1 + */ + this.maxSpeed = -1; + + /** + * Friction factor, reduces the sprite's velocity. + * The friction should be close to 1 (eg. 0.99) + * 1: no friction + * + * @property friction + * @type {Number} + * @default 1 + */ + this.friction = 1; + + /** + * The sprite's current collider. + * It can either be an Axis Aligned Bounding Box (a non-rotated rectangle) + * or a circular collider. + * If the sprite is checked for collision, bounce, overlapping or mouse events the + * collider is automatically created from the width and height + * of the sprite or from the image dimension in case of animate sprites + * + * You can set a custom collider with Sprite.setCollider + * + * @property collider + * @type {Object} + */ + this.collider = undefined; + + //internal use + //"default" - no image or custom collider is specified, use the shape width / height + //"custom" - specified with setCollider + //"image" - no collider is set with setCollider and an image is added + this.colliderType = 'none'; + + /** + * Object containing information about the most recent collision/overlapping + * To be typically used in combination with Sprite.overlap or Sprite.collide + * functions. + * The properties are touching.left, touching.right, touching.top, + * touching.bottom and are either true or false depending on the side of the + * collider. + * + * @property touching + * @type {Object} + */ + this.touching = {}; + this.touching.left = false; + this.touching.right = false; + this.touching.top = false; + this.touching.bottom = false; + + /** + * The mass determines the velocity transfer when sprites bounce + * against each other. See Sprite.bounce + * The higher the mass the least the sprite will be affected by collisions. + * + * @property mass + * @type {Number} + * @default 1 + */ + this.mass = 1; + + /** + * If set to true the sprite won't bounce or be displaced by collisions + * Simulates an infinite mass or an anchored object. + * + * @property immovable + * @type {Boolean} + * @default false + */ + this.immovable = false; + + //Coefficient of restitution - velocity lost in the bouncing + //0 perfectly inelastic , 1 elastic, > 1 hyper elastic + + /** + * Coefficient of restitution. The velocity lost after bouncing. + * 1: perfectly elastic, no energy is lost + * 0: perfectly inelastic, no bouncing + * less than 1: inelastic, this is the most common in nature + * greater than 1: hyper elastic, energy is increased like in a pinball bumper + * + * @property restitution + * @type {Number} + * @default 1 + */ + this.restitution = 1; + + /** + * Rotation in degrees of the visual element (image or animation) + * Note: this is not the movement's direction, see getDirection. + * + * @property rotation + * @type {Number} + * @default 0 + */ + Object.defineProperty(this, 'rotation', { + enumerable: true, + get: function() { + return this._rotation; + }, + set: function(value) { + this._rotation = value; + if (this.rotateToDirection) { + this.setSpeed(this.getSpeed(), value); + } + } + }); + + /** + * Internal rotation variable (expressed in degrees). + * Note: external callers access this through the rotation property above. + * + * @private + * @property _rotation + * @type {Number} + * @default 0 + */ + this._rotation = 0; + + /** + * Rotation change in degrees per frame of thevisual element (image or animation) + * Note: this is not the movement's direction, see getDirection. + * + * @property rotationSpeed + * @type {Number} + * @default 0 + */ + this.rotationSpeed = 0; + + + /** + * Automatically lock the rotation property of the visual element + * (image or animation) to the sprite's movement direction and vice versa. + * + * @property rotateToDirection + * @type {Boolean} + * @default false + */ + this.rotateToDirection = false; + + + /** + * Determines the rendering order within a group: a sprite with + * lower depth will appear below the ones with higher depth. + * + * Note: drawing a group before another with drawSprites will make + * its members appear below the second one, like in normal p5 canvas + * drawing. + * + * @property depth + * @type {Number} + * @default One more than the greatest existing sprite depth, when calling + * createSprite(). When calling new Sprite() directly, depth will + * initialize to 0 (not recommended). + */ + this.depth = 0; + + /** + * Determines the sprite's scale. + * Example: 2 will be twice the native size of the visuals, + * 0.5 will be half. Scaling up may make images blurry. + * + * @property scale + * @type {Number} + * @default 1 + */ + this.scale = 1; + + var dirX = 1; + var dirY = 1; + + /** + * The sprite's visibility. + * + * @property visible + * @type {Boolean} + * @default true + */ + this.visible = true; + + /** + * If set to true sprite will track its mouse state. + * the properties mouseIsPressed and mouseIsOver will be updated. + * Note: automatically set to true if the functions + * onMouseReleased or onMousePressed are set. + * + * @property mouseActive + * @type {Boolean} + * @default false + */ + this.mouseActive = false; + + /** + * True if mouse is on the sprite's collider. + * Read only. + * + * @property mouseIsOver + * @type {Boolean} + */ + this.mouseIsOver = false; + + /** + * True if mouse is pressed on the sprite's collider. + * Read only. + * + * @property mouseIsPressed + * @type {Boolean} + */ + this.mouseIsPressed = false; + + /* + * Width of the sprite's current image. + * If no images or animations are set it's the width of the + * placeholder rectangle. + * Used internally to make calculations and draw the sprite. + * + * @private + * @property _internalWidth + * @type {Number} + * @default 100 + */ + this._internalWidth = _w; + + /* + * Height of the sprite's current image. + * If no images or animations are set it's the height of the + * placeholder rectangle. + * Used internally to make calculations and draw the sprite. + * + * @private + * @property _internalHeight + * @type {Number} + * @default 100 + */ + this._internalHeight = _h; + + /* + * _internalWidth and _internalHeight are used for all p5.play + * calculations, but width and height can be extended. For example, + * you may want users to always get and set a scaled width: + Object.defineProperty(this, 'width', { + enumerable: true, + configurable: true, + get: function() { + return this._internalWidth * this.scale; + }, + set: function(value) { + this._internalWidth = value / this.scale; + } + }); + */ + + /** + * Width of the sprite's current image. + * If no images or animations are set it's the width of the + * placeholder rectangle. + * + * @property width + * @type {Number} + * @default 100 + */ + Object.defineProperty(this, 'width', { + enumerable: true, + configurable: true, + get: function() { + return this._internalWidth; + }, + set: function(value) { + this._internalWidth = value; + } + }); + + if(_w === undefined) + this.width = 100; + else + this.width = _w; + + /** + * Height of the sprite's current image. + * If no images or animations are set it's the height of the + * placeholder rectangle. + * + * @property height + * @type {Number} + * @default 100 + */ + Object.defineProperty(this, 'height', { + enumerable: true, + configurable: true, + get: function() { + return this._internalHeight; + }, + set: function(value) { + this._internalHeight = value; + } + }); + + if(_h === undefined) + this.height = 100; + else + this.height = _h; + + /** + * Unscaled width of the sprite + * If no images or animations are set it's the width of the + * placeholder rectangle. + * + * @property originalWidth + * @type {Number} + * @default 100 + */ + this.originalWidth = this._internalWidth; + + /** + * Unscaled height of the sprite + * If no images or animations are set it's the height of the + * placeholder rectangle. + * + * @property originalHeight + * @type {Number} + * @default 100 + */ + this.originalHeight = this._internalHeight; + + /** + * True if the sprite has been removed. + * + * @property removed + * @type {Boolean} + */ + this.removed = false; + + /** + * Cycles before self removal. + * Set it to initiate a countdown, every draw cycle the property is + * reduced by 1 unit. At 0 it will call a sprite.remove() + * Disabled if set to -1. + * + * @property life + * @type {Number} + * @default -1 + */ + this.life = -1; + + /** + * If set to true, draws an outline of the collider, the depth, and center. + * + * @property debug + * @type {Boolean} + * @default false + */ + this.debug = false; + + /** + * If no image or animations are set this is color of the + * placeholder rectangle + * + * @property shapeColor + * @type {color} + */ + this.shapeColor = color(random(255), random(255), random(255)); + + /** + * Groups the sprite belongs to, including allSprites + * + * @property groups + * @type {Array} + */ + this.groups = []; + + var animations = {}; + + //The current animation's label. + var currentAnimation = ''; + + /** + * Reference to the current animation. + * + * @property animation + * @type {Animation} + */ + this.animation = undefined; + + /* + * @private + * Keep animation properties in sync with how the animation changes. + */ + this._syncAnimationSizes = function() { + //has an animation but the collider is still default + //the animation wasn't loaded. if the animation is not a 1x1 image + //it means it just finished loading + if(this.colliderType === 'default' && + animations[currentAnimation].getWidth() !== 1 && animations[currentAnimation].getHeight() !== 1) + { + this.collider = this.getBoundingBox(); + this.colliderType = 'image'; + this._internalWidth = animations[currentAnimation].getWidth()*abs(this._getScaleX()); + this._internalHeight = animations[currentAnimation].getHeight()*abs(this._getScaleY()); + //quadTree.insert(this); + } + + //update size and collider + if(animations[currentAnimation].frameChanged || this.width === undefined || this.height === undefined) + { + //this.collider = this.getBoundingBox(); + this._internalWidth = animations[currentAnimation].getWidth()*abs(this._getScaleX()); + this._internalHeight = animations[currentAnimation].getHeight()*abs(this._getScaleY()); + } + }; + + /** + * Updates the sprite. + * Called automatically at the beginning of the draw cycle. + * + * @method update + */ + this.update = function() { + + if(!this.removed) + { + //if there has been a change somewhere after the last update + //the old position is the last position registered in the update + if(this.newPosition !== this.position) + this.previousPosition = createVector(this.newPosition.x, this.newPosition.y); + else + this.previousPosition = createVector(this.position.x, this.position.y); + + this.velocity.x *= this.friction; + this.velocity.y *= this.friction; + + if(this.maxSpeed !== -1) + this.limitSpeed(this.maxSpeed); + + if(this.rotateToDirection && this.velocity.mag() > 0) + this._rotation = this.getDirection(); + + this.rotation += this.rotationSpeed; + + this.position.x += this.velocity.x; + this.position.y += this.velocity.y; + + this.newPosition = createVector(this.position.x, this.position.y); + + this.deltaX = this.position.x - this.previousPosition.x; + this.deltaY = this.position.y - this.previousPosition.y; + + //if there is an animation + if(animations[currentAnimation]) + { + //update it + animations[currentAnimation].update(); + + this._syncAnimationSizes(); + } + + //a collider is created either manually with setCollider or + //when I check this sprite for collisions or overlaps + if(this.collider) + { + if(this.collider instanceof AABB) + { + //scale / rotate collider + var t; + if (pInst._angleMode === pInst.RADIANS) { + t = radians(this.rotation); + } else { + t = this.rotation; + } + + if(this.colliderType === 'custom') + { + this.collider.extents.x = this.collider.originalExtents.x * abs(this._getScaleX()) * abs(cos(t)) + + this.collider.originalExtents.y * abs(this._getScaleY()) * abs(sin(t)); + + this.collider.extents.y = this.collider.originalExtents.x * abs(this._getScaleX()) * abs(sin(t)) + + this.collider.originalExtents.y * abs(this._getScaleY()) * abs(cos(t)); + } + else if(this.colliderType === 'default') + { + this.collider.extents.x = this._internalWidth * abs(this._getScaleX()) * abs(cos(t)) + + this._internalHeight * abs(this._getScaleY()) * abs(sin(t)); + this.collider.extents.y = this._internalWidth * abs(this._getScaleX()) * abs(sin(t)) + + this._internalHeight * abs(this._getScaleY()) * abs(cos(t)); + } + else if(this.colliderType === 'image') + { + this.collider.extents.x = this._internalWidth * abs(cos(t)) + + this._internalHeight * abs(sin(t)); + + this.collider.extents.y = this._internalWidth * abs(sin(t)) + + this._internalHeight * abs(cos(t)); + } + } + + if(this.collider instanceof CircleCollider) + { + //print(this.scale); + this.collider.radius = this.collider.originalRadius * abs(this.scale); + } + + }//end collider != null + + //mouse actions + if (this.mouseActive) + { + //if no collider set it + if(!this.collider) + this.setDefaultCollider(); + + this.mouseUpdate(); + } + else + { + if (typeof(this.onMouseOver) === 'function' || + typeof(this.onMouseOut) === 'function' || + typeof(this.onMousePressed) === 'function' || + typeof(this.onMouseReleased) === 'function') + { + //if a mouse function is set + //it's implied we want to have it mouse active so + //we do this automatically + this.mouseActive = true; + + //if no collider set it + if(!this.collider) + this.setDefaultCollider(); + + this.mouseUpdate(); + } + } + + //self destruction countdown + if (this.life>0) + this.life--; + if (this.life === 0) + this.remove(); + } + };//end update + + /** + * Creates a default collider matching the size of the + * placeholder rectangle or the bounding box of the image. + * + * @method setDefaultCollider + */ + this.setDefaultCollider = function() { + + //if has animation get the animation bounding box + //working only for preloaded images + if(animations[currentAnimation] && (animations[currentAnimation].getWidth() !== 1 && animations[currentAnimation].getHeight() !== 1)) + { + this.collider = this.getBoundingBox(); + this._internalWidth = animations[currentAnimation].getWidth()*abs(this._getScaleX()); + this._internalHeight = animations[currentAnimation].getHeight()*abs(this._getScaleY()); + //quadTree.insert(this); + this.colliderType = 'image'; + //print("IMAGE COLLIDER ADDED"); + } + else if(animations[currentAnimation] && animations[currentAnimation].getWidth() === 1 && animations[currentAnimation].getHeight() === 1) + { + //animation is still loading + //print("wait"); + } + else //get the with and height defined at the creation + { + this.collider = new AABB(pInst, this.position, createVector(this._internalWidth, this._internalHeight)); + //quadTree.insert(this); + this.colliderType = 'default'; + } + + pInst.quadTree.insert(this); + }; + + /** + * Updates the sprite mouse states and triggers the mouse events: + * onMouseOver, onMouseOut, onMousePressed, onMouseReleased + * + * @method mouseUpdate + */ + this.mouseUpdate = function() { + + var mouseWasOver = this.mouseIsOver; + var mouseWasPressed = this.mouseIsPressed; + + this.mouseIsOver = false; + this.mouseIsPressed = false; + + var mousePosition; + + if(camera.active) + mousePosition = createVector(camera.mouseX, camera.mouseY); + else + mousePosition = createVector(pInst.mouseX, pInst.mouseY); + + //rollover + if(this.collider) + { + + if (this.collider instanceof CircleCollider) + { + if (dist(mousePosition.x, mousePosition.y, this.collider.center.x, this.collider.center.y) < this.collider.radius) + this.mouseIsOver = true; + } else if (this.collider instanceof AABB) + { + if (mousePosition.x > this.collider.left() && + mousePosition.y > this.collider.top() && + mousePosition.x < this.collider.right() && + mousePosition.y < this.collider.bottom()) + { + this.mouseIsOver = true; + } + } + + //global p5 var + if(this.mouseIsOver && pInst.mouseIsPressed) + this.mouseIsPressed = true; + + //event change - call functions + if(!mouseWasOver && this.mouseIsOver && this.onMouseOver !== undefined) + if(typeof(this.onMouseOver) === 'function') + this.onMouseOver.call(this, this); + else + print('Warning: onMouseOver should be a function'); + + if(mouseWasOver && !this.mouseIsOver && this.onMouseOut !== undefined) + if(typeof(this.onMouseOut) === 'function') + this.onMouseOut.call(this, this); + else + print('Warning: onMouseOut should be a function'); + + if(!mouseWasPressed && this.mouseIsPressed && this.onMousePressed !== undefined) + if(typeof(this.onMousePressed) === 'function') + this.onMousePressed.call(this, this); + else + print('Warning: onMousePressed should be a function'); + + if(mouseWasPressed && !pInst.mouseIsPressed && !this.mouseIsPressed && this.onMouseReleased !== undefined) + if(typeof(this.onMouseReleased) === 'function') + this.onMouseReleased.call(this, this); + else + print('Warning: onMouseReleased should be a function'); + + } + + }; + + /** + * Sets a collider for the sprite. + * + * In p5.play a Collider is an invisible circle or rectangle + * that can have any size or position relative to the sprite and which + * will be used to detect collisions and overlapping with other sprites, + * or the mouse cursor. + * + * If the sprite is checked for collision, bounce, overlapping or mouse events a + * collider is automatically created from the width and height parameter passed at the + * creation of the sprite or the from the image dimension in case of animate sprites. + * + * Often the image bounding box is not appropriate as active area for + * a collision detection so you can set a circular or rectangular sprite with different + * dimensions and offset from the sprite's center. + * + * setCollider + * @method setCollider + * @param {String} type Either "rectangle" or "circle" + * @param {Number} offsetX Collider x position from the center of the sprite + * @param {Number} offsetY Collider y position from the center of the sprite + * @param {Number} width Collider width or radius + * @param {Number} height Collider height + * + */ + this.setCollider = function(type, offsetX, offsetY, width, height) { + + this.colliderType = 'custom'; + + var v = createVector(offsetX, offsetY); + if(type === 'rectangle' && arguments.length === 5) { + this.collider = new AABB(pInst, this.position, createVector(width, height), v); + } else if(type === 'circle') { + if(arguments.length !== 4) { + print('Warning: usage setCollider("circle", offsetX, offsetY, radius)'); + } + + this.collider = new CircleCollider(pInst, this.position, width, v); + } + + quadTree.insert(this); + }; + + /** + * Returns a the bounding box of the current image + * @method getBoundingBox + */ + this.getBoundingBox = function() { + + var w = animations[currentAnimation].getWidth()*abs(this._getScaleX()); + var h = animations[currentAnimation].getHeight()*abs(this._getScaleY()); + + //if the bounding box is 1x1 the image is not loaded + //potential issue with actual 1x1 images + if(w === 1 && h === 1) { + //not loaded yet + return new AABB(pInst, this.position, createVector(w, h)); + } + else { + return new AABB(pInst, this.position, createVector(w, h)); + } + }; + + /** + * Sets the sprite's horizontal mirroring. + * If 1 the images displayed normally + * If -1 the images are flipped horizontally + * If no argument returns the current x mirroring + * + * @method mirrorX + * @param {Number} dir Either 1 or -1 + * @return {Number} Current mirroring if no parameter is specified + */ + this.mirrorX = function(dir) { + if(dir === 1 || dir === -1) + dirX = dir; + else + return dirX; + }; + + /** + * Sets the sprite's vertical mirroring. + * If 1 the images displayed normally + * If -1 the images are flipped vertically + * If no argument returns the current y mirroring + * + * @method mirrorY + * @param {Number} dir Either 1 or -1 + * @return {Number} Current mirroring if no parameter is specified + */ + this.mirrorY = function(dir) { + if(dir === 1 || dir === -1) + dirY = dir; + else + return dirY; + }; + + /* + * Returns the value the sprite should be scaled in the X direction. + * Used to calculate rendering and collisions. + * @private + */ + this._getScaleX = function() + { + return this.scale; + }; + + /* + * Returns the value the sprite should be scaled in the Y direction. + * Used to calculate rendering and collisions. + * @private + */ + this._getScaleY = function() + { + return this.scale; + }; + + /** + * Manages the positioning, scale and rotation of the sprite + * Called automatically, it should not be overridden + * @private + * @final + * @method display + */ + this.display = function() + { + if (this.visible && !this.removed) + { + push(); + colorMode(RGB); + + noStroke(); + rectMode(CENTER); + ellipseMode(CENTER); + imageMode(CENTER); + + translate(this.position.x, this.position.y); + scale(this._getScaleX()*dirX, this._getScaleY()*dirY); + if (pInst._angleMode === pInst.RADIANS) { + rotate(radians(this.rotation)); + } else { + rotate(this.rotation); + } + this.draw(); + //draw debug info + pop(); + + + if(this.debug) + { + push(); + //draw the anchor point + stroke(0, 255, 0); + strokeWeight(1); + line(this.position.x-10, this.position.y, this.position.x+10, this.position.y); + line(this.position.x, this.position.y-10, this.position.x, this.position.y+10); + noFill(); + + //depth number + noStroke(); + fill(0, 255, 0); + textAlign(LEFT, BOTTOM); + textSize(16); + text(this.depth+'', this.position.x+4, this.position.y-2); + + noFill(); + stroke(0, 255, 0); + + //bounding box + if(this.collider !== undefined) + { + this.collider.draw(); + } + pop(); + } + + } + }; + + + /** + * Manages the visuals of the sprite. + * It can be overridden with a custom drawing function. + * The 0,0 point will be the center of the sprite. + * Example: + * sprite.draw = function() { ellipse(0,0,10,10) } + * Will display the sprite as circle. + * + * @method draw + */ + this.draw = function() + { + if(currentAnimation !== '' && animations) + { + if(animations[currentAnimation]) + animations[currentAnimation].draw(0, 0, 0); + } + else + { + noStroke(); + fill(this.shapeColor); + rect(0, 0, this._internalWidth, this._internalHeight); + } + }; + + /** + * Removes the Sprite from the sketch. + * The removed Sprite won't be drawn or updated anymore. + * + * @method remove + */ + this.remove = function() { + this.removed = true; + + quadTree.removeObject(this); + + //when removed from the "scene" also remove all the references in all the groups + while (this.groups.length > 0) { + this.groups[0].remove(this); + } + }; + + /** + * Sets the velocity vector. + * + * @method setVelocity + * @param {Number} x X component + * @param {Number} y Y component + */ + this.setVelocity = function(x, y) { + this.velocity.x = x; + this.velocity.y = y; + }; + + /** + * Calculates the scalar speed. + * + * @method getSpeed + * @return {Number} Scalar speed + */ + this.getSpeed = function() { + return this.velocity.mag(); + }; + + /** + * Calculates the movement's direction in degrees. + * + * @method getDirection + * @return {Number} Angle in degrees + */ + this.getDirection = function() { + + var direction = atan2(this.velocity.y, this.velocity.x); + + if(isNaN(direction)) + direction = 0; + + // Unlike Math.atan2, the atan2 method above will return degrees if + // the current p5 angleMode is DEGREES, and radians if the p5 angleMode is + // RADIANS. This method should always return degrees (for now). + // See https://github.com/molleindustria/p5.play/issues/94 + if (pInst._angleMode === pInst.RADIANS) { + direction = degrees(direction); + } + + return direction; + }; + + /** + * Adds the sprite to an existing group + * + * @method addToGroup + * @param {Object} group + */ + this.addToGroup = function(group) { + if(group instanceof Array) + group.add(this); + else + print('addToGroup error: '+group+' is not a group'); + }; + + /** + * Limits the scalar speed. + * + * @method limitSpeed + * @param {Number} max Max speed: positive number + */ + this.limitSpeed = function(max) { + + //update linear speed + var speed = this.getSpeed(); + + if(abs(speed)>max) + { + //find reduction factor + var k = max/abs(speed); + this.velocity.x *= k; + this.velocity.y *= k; + } + }; + + /** + * Set the speed and direction of the sprite. + * The action overwrites the current velocity. + * If direction is not supplied, the current direction is maintained. + * If direction is not supplied and there is no current velocity, the current + * rotation angle used for the direction. + * + * @method setSpeed + * @param {Number} speed Scalar speed + * @param {Number} [angle] Direction in degrees + */ + this.setSpeed = function(speed, angle) { + var a; + if (typeof angle === 'undefined') { + if (this.velocity.x !== 0 || this.velocity.y !== 0) { + a = pInst.atan2(this.velocity.y, this.velocity.x); + } else { + if (pInst._angleMode === pInst.RADIANS) { + a = radians(this._rotation); + } else { + a = this._rotation; + } + } + } else { + if (pInst._angleMode === pInst.RADIANS) { + a = radians(angle); + } else { + a = angle; + } + } + this.velocity.x = cos(a)*speed; + this.velocity.y = sin(a)*speed; + }; + + /** + * Pushes the sprite in a direction defined by an angle. + * The force is added to the current velocity. + * + * @method addSpeed + * @param {Number} speed Scalar speed to add + * @param {Number} angle Direction in degrees + */ + this.addSpeed = function(speed, angle) { + var a; + if (pInst._angleMode === pInst.RADIANS) { + a = radians(angle); + } else { + a = angle; + } + this.velocity.x += cos(a) * speed; + this.velocity.y += sin(a) * speed; + }; + + /** + * Pushes the sprite toward a point. + * The force is added to the current velocity. + * + * @method attractionPoint + * @param {Number} magnitude Scalar speed to add + * @param {Number} pointX Direction x coordinate + * @param {Number} pointY Direction y coordinate + */ + this.attractionPoint = function(magnitude, pointX, pointY) { + var angle = atan2(pointY-this.position.y, pointX-this.position.x); + this.velocity.x += cos(angle) * magnitude; + this.velocity.y += sin(angle) * magnitude; + }; + + + /** + * Adds an image to the sprite. + * An image will be considered a one-frame animation. + * The image should be preloaded in the preload() function using p5 loadImage. + * Animations require a identifying label (string) to change them. + * The image is stored in the sprite but not necessarily displayed + * until Sprite.changeAnimation(label) is called + * + * Usages: + * - sprite.addImage(label, image); + * - sprite.addImage(image); + * + * If only an image is passed no label is specified + * + * @method addImage + * @param {String|p5.Image} label Label or image + * @param {p5.Image} [img] Image + */ + this.addImage = function() + { + if(typeof arguments[0] === 'string' && arguments[1] instanceof p5.Image) + this.addAnimation(arguments[0], arguments[1]); + else if(arguments[0] instanceof p5.Image) + this.addAnimation('normal', arguments[0]); + else + throw('addImage error: allowed usages are <image> or <label>, <image>'); + }; + + /** + * Adds an animation to the sprite. + * The animation should be preloaded in the preload() function + * using loadAnimation. + * Animations require a identifying label (string) to change them. + * Animations are stored in the sprite but not necessarily displayed + * until Sprite.changeAnimation(label) is called. + * + * Usage: + * - sprite.addAnimation(label, animation); + * + * Alternative usages. See Animation for more information on file sequences: + * - sprite.addAnimation(label, firstFrame, lastFrame); + * - sprite.addAnimation(label, frame1, frame2, frame3...); + * + * @method addAnimation + * @param {String} label Animation identifier + * @param {Animation} animation The preloaded animation + */ + this.addAnimation = function(label) + { + var anim; + + if(typeof label !== 'string') + { + print('Sprite.addAnimation error: the first argument must be a label (String)'); + return -1; + } + else if(arguments.length < 2) + { + print('addAnimation error: you must specify a label and n frame images'); + return -1; + } + else if(arguments[1] instanceof Animation) + { + + var sourceAnimation = arguments[1]; + + var newAnimation = sourceAnimation.clone(); + + animations[label] = newAnimation; + + if(currentAnimation === '') + { + currentAnimation = label; + this.animation = newAnimation; + } + + newAnimation.isSpriteAnimation = true; + + this._internalWidth = newAnimation.getWidth()*abs(this._getScaleX()); + this._internalHeight = newAnimation.getHeight()*abs(this._getScaleY()); + + return newAnimation; + } + else + { + var animFrames = []; + for(var i=1; i<arguments.length; i++) + animFrames.push(arguments[i]); + + anim = construct(pInst.Animation, animFrames); + animations[label] = anim; + + if(currentAnimation === '') + { + currentAnimation = label; + this.animation = anim; + } + anim.isSpriteAnimation = true; + + this._internalWidth = anim.getWidth()*abs(this._getScaleX()); + this._internalHeight = anim.getHeight()*abs(this._getScaleY()); + + return anim; + } + + }; + + /** + * Changes the displayed image/animation. + * Equivalent to changeAnimation + * + * @method changeImage + * @param {String} label Image/Animation identifier + */ + this.changeImage = function(label) { + this.changeAnimation(label); + }; + + /** + * Returns the label of the current animation + * + * @method getAnimationLabel + * @return {String} label Image/Animation identifier + */ + this.getAnimationLabel = function() { + return currentAnimation; + }; + + /** + * Changes the displayed animation. + * See Animation for more control over the sequence. + * + * @method changeAnimation + * @param {String} label Animation identifier + */ + this.changeAnimation = function(label) { + if(!animations[label]) + print('changeAnimation error: no animation labeled '+label); + else + { + currentAnimation = label; + this.animation = animations[label]; + } + }; + + /** + * Checks if the given point corresponds to a transparent pixel + * in the sprite's current image. It can be used to check a point collision + * against only the visible part of the sprite. + * + * @method overlapPixel + * @param {Number} pointX x coordinate of the point to check + * @param {Number} pointY y coordinate of the point to check + * @return {Boolean} result True if non-transparent + */ + this.overlapPixel = function(pointX, pointY) { + var point = createVector(pointX, pointY); + + var img = this.animation.getFrameImage(); + + //convert point to img relative position + point.x -= this.position.x-img.width/2; + point.y -= this.position.y-img.height/2; + + //out of the image entirely + if(point.x<0 || point.x>img.width || point.y<0 || point.y>img.height) + return false; + else if(this.rotation === 0 && this.scale === 1) + { + //true if full opacity + var values = img.get(point.x, point.y); + return values[3] === 255; + } + else + { + print('Error: overlapPixel doesn\'t work with scaled or rotated sprites yet'); + //offscreen printing to be implemented bleurch + return false; + } + }; + + /** + * Checks if the given point is inside the sprite's collider. + * + * @method overlapPoint + * @param {Number} pointX x coordinate of the point to check + * @param {Number} pointY y coordinate of the point to check + * @return {Boolean} result True if inside + */ + this.overlapPoint = function(pointX, pointY) { + var point = createVector(pointX, pointY); + + if(!this.collider) + this.setDefaultCollider(); + + if(this.collider !== undefined) + { + if(this.collider instanceof AABB) + return (point.x > this.collider.left() && point.x < this.collider.right() && point.y > this.collider.top() && point.y < this.collider.bottom()); + if(this.collider instanceof CircleCollider) + { + var sqRadius = this.collider.radius * this.collider.radius; + var sqDist = pow(this.collider.center.x - point.x, 2) + pow(this.collider.center.y - point.y, 2); + return sqDist<sqRadius; + } + else + return false; + } + else + return false; + + }; + + + /** + * Checks if the the sprite is overlapping another sprite or a group. + * The check is performed using the colliders. If colliders are not set + * they will be created automatically from the image/animation bounding box. + * + * A callback function can be specified to perform additional operations + * when the overlap occours. + * If the target is a group the function will be called for each single + * sprite overlapping. The parameter of the function are respectively the + * current sprite and the colliding sprite. + * + * @example + * <code> + * sprite.overlap(otherSprite, explosion); + * + * function explosion(spriteA, spriteB) { + * spriteA.remove(); + * spriteB.score++; + * } + * </code> + * + * @method overlap + * @param {Object} target Sprite or group to check against the current one + * @param {Function} [callback] The function to be called if overlap is positive + * @return {Boolean} True if overlapping + */ + this.overlap = function(target, callback) { + //if(this.collider instanceof AABB && target.collider instanceof AABB) + return this.AABBops('overlap', target, callback); + }; + + /** + * Checks if the the sprite is overlapping another sprite or a group. + * If the overlap is positive the current sprite will be displace by + * the colliding one in the closest non-overlapping position. + * + * The check is performed using the colliders. If colliders are not set + * they will be created automatically from the image/animation bounding box. + * + * A callback function can be specified to perform additional operations + * when the collision occours. + * If the target is a group the function will be called for each single + * sprite colliding. The parameter of the function are respectively the + * current sprite and the colliding sprite. + * + * @example + * <code> + * sprite.collide(otherSprite, explosion); + * + * function explosion(spriteA, spriteB) { + * spriteA.remove(); + * spriteB.score++; + * } + * </code> + * + * @method collide + * @param {Object} target Sprite or group to check against the current one + * @param {Function} [callback] The function to be called if overlap is positive + * @return {Boolean} True if overlapping + */ + this.collide = function(target, callback) { + //if(this.collider instanceof AABB && target.collider instanceof AABB) + return this.AABBops('collide', target, callback); + }; + + /** + * Checks if the the sprite is overlapping another sprite or a group. + * If the overlap is positive the current sprite will displace + * the colliding one to the closest non-overlapping position. + * + * The check is performed using the colliders. If colliders are not set + * they will be created automatically from the image/animation bounding box. + * + * A callback function can be specified to perform additional operations + * when the collision occours. + * If the target is a group the function will be called for each single + * sprite colliding. The parameter of the function are respectively the + * current sprite and the colliding sprite. + * + * @example + * <code> + * sprite.displace(otherSprite, explosion); + * + * function explosion(spriteA, spriteB) { + * spriteA.remove(); + * spriteB.score++; + * } + * </code> + * + * @method displace + * @param {Object} target Sprite or group to check against the current one + * @param {Function} [callback] The function to be called if overlap is positive + * @return {Boolean} True if overlapping + */ + this.displace = function(target, callback) { + return this.AABBops('displace', target, callback); + }; + + /** + * Checks if the the sprite is overlapping another sprite or a group. + * If the overlap is positive the sprites will bounce affecting each + * other's trajectories depending on their .velocity, .mass and .restitution + * + * The check is performed using the colliders. If colliders are not set + * they will be created automatically from the image/animation bounding box. + * + * A callback function can be specified to perform additional operations + * when the collision occours. + * If the target is a group the function will be called for each single + * sprite colliding. The parameter of the function are respectively the + * current sprite and the colliding sprite. + * + * @example + * <code> + * sprite.bounce(otherSprite, explosion); + * + * function explosion(spriteA, spriteB) { + * spriteA.remove(); + * spriteB.score++; + * } + * </code> + * + * @method bounce + * @param {Object} target Sprite or group to check against the current one + * @param {Function} [callback] The function to be called if overlap is positive + * @return {Boolean} True if overlapping + */ + this.bounce = function(target, callback) { + return this.AABBops('bounce', target, callback); + }; + + // Internal collision detection function. Do not use directly. + this.AABBops = function(type, target, callback) { + + this.touching.left = false; + this.touching.right = false; + this.touching.top = false; + this.touching.bottom = false; + + var result = false; + + //if single sprite turn into array anyway + var others = []; + + if(target instanceof Sprite) + others.push(target); + else if(target instanceof Array) + { + if(quadTree !== undefined && quadTree.active) + others = quadTree.retrieveFromGroup( this, target); + + if(others.length === 0) + others = target; + + } + else + throw('Error: overlap can only be checked between sprites or groups'); + + for(var i=0; i<others.length; i++) + if(this !== others[i] && !this.removed) //you can check collisions within the same group but not on itself + { + var displacement; + var other = others[i]; + + if(this.collider === undefined) + this.setDefaultCollider(); + + if(other.collider === undefined) + other.setDefaultCollider(); + + /* + if(this.colliderType=="default" && animations[currentAnimation]!=null) + { + print("busted"); + return false; + }*/ + if(this.collider !== undefined && other.collider !== undefined) + { + if(type === 'overlap') { + var over; + + //if the other is a circle I calculate the displacement from here + if(this.collider instanceof CircleCollider) + over = other.collider.overlap(this.collider); + else + over = this.collider.overlap(other.collider); + + if(over) + { + + result = true; + + if(callback !== undefined && typeof callback === 'function') + callback.call(this, this, other); + } + } + else if(type === 'collide' || type === 'bounce') + { + displacement = createVector(0, 0); + + //if the sum of the speed is more than the collider i may + //have a tunnelling problem + var tunnelX = abs(this.velocity.x-other.velocity.x) >= other.collider.extents.x/2 && round(this.deltaX - this.velocity.x) === 0; + + var tunnelY = abs(this.velocity.y-other.velocity.y) >= other.collider.size().y/2 && round(this.deltaY - this.velocity.y) === 0; + + + if(tunnelX || tunnelY) + { + //instead of using the colliders I use the bounding box + //around the previous position and current position + //this is regardless of the collider type + + //the center is the average of the coll centers + var c = createVector( + (this.position.x+this.previousPosition.x)/2, + (this.position.y+this.previousPosition.y)/2); + + //the extents are the distance between the coll centers + //plus the extents of both + var e = createVector( + abs(this.position.x -this.previousPosition.x) + this.collider.extents.x, + abs(this.position.y -this.previousPosition.y) + this.collider.extents.y); + + var bbox = new AABB(pInst, c, e, this.collider.offset); + + //bbox.draw(); + + if(bbox.overlap(other.collider)) + { + if(tunnelX) { + + //entering from the right + if(this.velocity.x < 0) + displacement.x = other.collider.right() - this.collider.left() + 1; + else if(this.velocity.x > 0 ) + displacement.x = other.collider.left() - this.collider.right() -1; + } + + if(tunnelY) { + //from top + if(this.velocity.y > 0) + displacement.y = other.collider.top() - this.collider.bottom() - 1; + else if(this.velocity.y < 0 ) + displacement.y = other.collider.bottom() - this.collider.top() + 1; + + } + + }//end overlap + + } + else //non tunnel overlap + { + + //if the other is a circle I calculate the displacement from here + //and reverse it + if(this.collider instanceof CircleCollider) + { + displacement = other.collider.collide(this.collider).mult(-1); + } + else + displacement = this.collider.collide(other.collider); + + } + + if(displacement.x !== 0 || displacement.y !== 0) + { + var newVelX1, newVelY1, newVelX2, newVelY2; + + if(!this.immovable) + { + this.position.add(displacement); + this.previousPosition = createVector(this.position.x, this.position.y); + this.newPosition = createVector(this.position.x, this.position.y); + } + + if(displacement.x > 0) + this.touching.left = true; + if(displacement.x < 0) + this.touching.right = true; + if(displacement.y < 0) + this.touching.bottom = true; + if(displacement.y > 0) + this.touching.top = true; + + if(type === 'bounce') + { + if (this.collider instanceof CircleCollider && other.collider instanceof CircleCollider) { + var dx1 = p5.Vector.sub(this.position, other.position); + var dx2 = p5.Vector.sub(other.position, this.position); + var magnitude = dx1.magSq(); + var totalMass = this.mass + other.mass; + var m1 = 0, m2 = 0; + if (this.immovable) { + m2 = 2; + } else if (other.immovable) { + m1 = 2; + } else { + m1 = 2 * other.mass / totalMass; + m2 = 2 * this.mass / totalMass; + } + var newVel1 = dx1.mult(m1 * p5.Vector.sub(this.velocity, other.velocity).dot(dx1) / magnitude); + var newVel2 = dx2.mult(m2 * p5.Vector.sub(other.velocity, this.velocity).dot(dx2) / magnitude); + + this.velocity.sub(newVel1.mult(this.restitution)); + other.velocity.sub(newVel2.mult(other.restitution)); + } + else { + if(other.immovable) + { + newVelX1 = -this.velocity.x+other.velocity.x; + newVelY1 = -this.velocity.y+other.velocity.y; + } + else + { + newVelX1 = (this.velocity.x * (this.mass - other.mass) + (2 * other.mass * other.velocity.x)) / (this.mass + other.mass); + newVelY1 = (this.velocity.y * (this.mass - other.mass) + (2 * other.mass * other.velocity.y)) / (this.mass + other.mass); + newVelX2 = (other.velocity.x * (other.mass - this.mass) + (2 * this.mass * this.velocity.x)) / (this.mass + other.mass); + newVelY2 = (other.velocity.y * (other.mass - this.mass) + (2 * this.mass * this.velocity.y)) / (this.mass + other.mass); + } + + //var bothCircles = (this.collider instanceof CircleCollider && + // other.collider instanceof CircleCollider); + + //if(this.touching.left || this.touching.right || this.collider instanceof CircleCollider) + + //print(displacement); + + if(abs(displacement.x)>abs(displacement.y)) + { + + + if(!this.immovable) + { + this.velocity.x = newVelX1*this.restitution; + + } + + if(!other.immovable) + other.velocity.x = newVelX2*other.restitution; + + } + //if(this.touching.top || this.touching.bottom || this.collider instanceof CircleCollider) + if(abs(displacement.x)<abs(displacement.y)) + { + + if(!this.immovable) + this.velocity.y = newVelY1*this.restitution; + + if(!other.immovable) + other.velocity.y = newVelY2*other.restitution; + } + } + } + //else if(type == "collide") + //this.velocity = createVector(0,0); + + if(callback !== undefined && typeof callback === 'function') + callback.call(this, this, other); + + result = true; + } + + + + } + else if(type === 'displace') { + + //if the other is a circle I calculate the displacement from here + //and reverse it + if(this.collider instanceof CircleCollider) + displacement = other.collider.collide(this.collider).mult(-1); + else + displacement = this.collider.collide(other.collider); + + + if(displacement.x !== 0 || displacement.y !== 0 ) + { + other.position.sub(displacement); + + if(displacement.x > 0) + this.touching.left = true; + if(displacement.x < 0) + this.touching.right = true; + if(displacement.y < 0) + this.touching.bottom = true; + if(displacement.y > 0) + this.touching.top = true; + + if(callback !== undefined && typeof callback === 'function') + callback.call(this, this, other); + + result = true; + } + } + }//end collider exists + } + + return result; + }; +} //end Sprite class + +defineLazyP5Property('Sprite', boundConstructorFactory(Sprite)); + + + +/** + * The sketch camera automatically created at the beginning of a sketch. + * A camera facilitates scrolling and zooming for scenes extending beyond + * the canvas. A camera has a position, a zoom factor, and the mouse + * coordinates relative to the view. + * + * In p5.js terms the camera wraps the whole drawing cycle in a + * transformation matrix but it can be disable anytime during the draw + * cycle for example to draw interface elements in an absolute position. + * + * @property camera + * @type {camera} + */ + +defineLazyP5Property('camera', function() { + var camera = new Camera(this, 0, 0, 1); + camera.init = false; + return camera; +}); + +/** + * A camera facilitates scrolling and zooming for scenes extending beyond + * the canvas. A camera has a position, a zoom factor, and the mouse + * coordinates relative to the view. + * The camera is automatically created on the first draw cycle. + * + * In p5.js terms the camera wraps the whole drawing cycle in a + * transformation matrix but it can be disable anytime during the draw + * cycle for example to draw interface elements in an absolute position. + * + * @class Camera + * @constructor + * @param {Number} x Initial x coordinate + * @param {Number} y Initial y coordinate + * @param {Number} zoom magnification + **/ +function Camera(pInst, x, y, zoom) { + /** + * Camera position. Defines the global offset of the sketch. + * + * @property position + * @type {p5.Vector} + */ + this.position = pInst.createVector(x, y); + + /** + * Camera zoom. Defines the global scale of the sketch. + * A scale of 1 will be the normal size. Setting it to 2 will make everything + * twice the size. .5 will make everything half size. + * + * @property zoom + * @type {Number} + */ + this.zoom = zoom; + + /** + * MouseX translated to the camera view. + * Offsetting and scaling the canvas will not change the sprites' position + * nor the mouseX and mouseY variables. Use this property to read the mouse + * position if the camera moved or zoomed. + * + * @property mouseX + * @type {Number} + */ + this.mouseX = pInst.mouseX; + + /** + * MouseY translated to the camera view. + * Offsetting and scaling the canvas will not change the sprites' position + * nor the mouseX and mouseY variables. Use this property to read the mouse + * position if the camera moved or zoomed. + * + * @property mouseY + * @type {Number} + */ + this.mouseY = pInst.mouseY; + + /** + * True if the camera is active. + * Read only property. Use the methods Camera.on() and Camera.off() + * to enable or disable the camera. + * + * @property active + * @type {Boolean} + */ + this.active = false; + + /** + * Activates the camera. + * The canvas will be drawn according to the camera position and scale until + * Camera.off() is called + * + * @method on + */ + this.on = function() { + if(!this.active) + { + cameraPush.call(pInst); + this.active = true; + } + }; + + /** + * Deactivates the camera. + * The canvas will be drawn normally, ignoring the camera's position + * and scale until Camera.on() is called + * + * @method off + */ + this.off = function() { + if(this.active) + { + cameraPop.call(pInst); + this.active = false; + } + }; +} //end camera class + +defineLazyP5Property('Camera', boundConstructorFactory(Camera)); + +//called pre draw by default +function cameraPush() { + var pInst = this; + var camera = pInst.camera; + + //awkward but necessary in order to have the camera at the center + //of the canvas by default + if(!camera.init && camera.position.x === 0 && camera.position.y === 0) + { + camera.position.x=pInst.width/2; + camera.position.y=pInst.height/2; + camera.init = true; + } + + camera.mouseX = pInst.mouseX+camera.position.x-pInst.width/2; + camera.mouseY = pInst.mouseY+camera.position.y-pInst.height/2; + + if(!camera.active) + { + camera.active = true; + pInst.push(); + pInst.scale(camera.zoom); + pInst.translate(-camera.position.x+pInst.width/2/camera.zoom, -camera.position.y+pInst.height/2/camera.zoom); + } +} + +//called postdraw by default +function cameraPop() { + var pInst = this; + + if(pInst.camera.active) + { + pInst.pop(); + pInst.camera.active = false; + } +} + + + + +/** + * In p5.play groups are collections of sprites with similar behavior. + * For example a group may contain all the sprites in the background + * or all the sprites that "kill" the player. + * + * Groups are "extended" arrays and inherit all their properties + * e.g. group.length + * + * Since groups contain only references, a sprite can be in multiple + * groups and deleting a group doesn't affect the sprites themselves. + * + * Sprite.remove() will also remove the sprite from all the groups + * it belongs to. + * + * @class Group + * @constructor + */ +function Group() { + + //basically extending the array + var array = []; + + /** + * Gets the member at index i. + * + * @method get + * @param {Number} i The index of the object to retrieve + */ + array.get = function(i) { + return array[i]; + }; + + /** + * Checks if the group contains a sprite. + * + * @method contains + * @param {Sprite} sprite The sprite to search + * @return {Number} Index or -1 if not found + */ + array.contains = function(sprite) { + return this.indexOf(sprite)>-1; + }; + + /** + * Same as Group.contains + * @method indexOf + */ + array.indexOf = function(item) { + for (var i = 0, len = array.length; i < len; ++i) { + if (virtEquals(item, array[i])) { + return i; + } + } + return -1; + }; + + /** + * Adds a sprite to the group. + * + * @method add + * @param {Sprite} s The sprite to be added + */ + array.add = function(s) { + if(!(s instanceof Sprite)) { + throw('Error: you can only add sprites to a group'); + } + + if (-1 === this.indexOf(s)) { + array.push(s); + s.groups.push(this); + } + }; + + /** + * Same as group.length + * @method size + */ + array.size = function() { + return array.length; + }; + + /** + * Removes all the sprites in the group + * from the scene. + * + * @method removeSprites + */ + array.removeSprites = function() { + while (array.length > 0) { + array[0].remove(); + } + }; + + /** + * Removes all references to the group. + * Does not remove the actual sprites. + * + * @method clear + */ + array.clear = function() { + array.length = 0; + }; + + /** + * Removes a sprite from the group. + * Does not remove the actual sprite, only the affiliation (reference). + * + * @method remove + * @param {Sprite} item The sprite to be removed + * @return {Boolean} True if sprite was found and removed + */ + array.remove = function(item) { + if(!(item instanceof Sprite)) { + throw('Error: you can only remove sprites from a group'); + } + + var i, removed = false; + for (i = array.length - 1; i >= 0; i--) { + if (array[i] === item) { + array.splice(i, 1); + removed = true; + } + } + + if (removed) { + for (i = item.groups.length - 1; i >= 0; i--) { + if (item.groups[i] === this) { + item.groups.splice(i, 1); + } + } + } + + return removed; + }; + + /** + * Returns a copy of the group as standard array. + * @method toArray + */ + array.toArray = function() { + return array.slice(0); + }; + + /** + * Returns the highest depth in a group + * + * @method maxDepth + * @return {Number} The depth of the sprite drawn on the top + */ + array.maxDepth = function() { + if (array.length === 0) { + return 0; + } + + return array.reduce(function(maxDepth, sprite) { + return Math.max(maxDepth, sprite.depth); + }, -Infinity); + }; + + /** + * Returns the lowest depth in a group + * + * @method minDepth + * @return {Number} The depth of the sprite drawn on the bottom + */ + array.minDepth = function() { + if (array.length === 0) { + return 99999; + } + + return array.reduce(function(minDepth, sprite) { + return Math.min(minDepth, sprite.depth); + }, Infinity); + }; + + /** + * Draws all the sprites in the group. + * + * @method draw + */ + array.draw = function() { + + //sort by depth + this.sort(function(a, b) { + return a.depth - b.depth; + }); + + for(var i = 0; i<this.size(); i++) + { + this.get(i).display(); + } + }; + + //internal use + function virtEquals(obj, other) { + if (obj === null || other === null) { + return (obj === null) && (other === null); + } + if (typeof (obj) === 'string') { + return obj === other; + } + if (typeof(obj) !== 'object') { + return obj === other; + } + if (obj.equals instanceof Function) { + return obj.equals(other); + } + return obj === other; + } + + /** + * Collide each member of group against the target using the given collision + * type. Return true if any collision occurred. + * Internal use + * + * @private + * @method _groupCollide + * @param {!string} type one of 'overlap', 'collide', 'displace', 'bounce' + * @param {Object} target Group or Sprite + * @param {Function} [callback] on collision. + * @return {boolean} True if any collision/overlap occurred + */ + function _groupCollide(type, target, callback) { + var didCollide = false; + for(var i = 0; i<this.size(); i++) + didCollide = this.get(i).AABBops(type, target, callback) || didCollide; + return didCollide; + } + + /** + * Checks if the the group is overlapping another group or sprite. + * The check is performed using the colliders. If colliders are not set + * they will be created automatically from the image/animation bounding box. + * + * A callback function can be specified to perform additional operations + * when the overlap occurs. + * The function will be called for each single sprite overlapping. + * The parameter of the function are respectively the + * member of the current group and the other sprite passed as parameter. + * + * @example + * <code> + * group.overlap(otherSprite, explosion); + * + * function explosion(spriteA, spriteB) { + * spriteA.remove(); + * spriteB.score++; + * } + * </code> + * + * @method overlap + * @param {Object} target Group or Sprite to check against the current one + * @param {Function} [callback] The function to be called if overlap is positive + * @return {Boolean} True if overlapping + */ + array.overlap = _groupCollide.bind(array, 'overlap'); + + + /** + * Checks if the the group is overlapping another group or sprite. + * If the overlap is positive the sprites in the group will be displaced + * by the colliding one to the closest non-overlapping positions. + * + * The check is performed using the colliders. If colliders are not set + * they will be created automatically from the image/animation bounding box. + * + * A callback function can be specified to perform additional operations + * when the overlap occours. + * The function will be called for each single sprite overlapping. + * The parameter of the function are respectively the + * member of the current group and the other sprite passed as parameter. + * + * @example + * <code> + * group.collide(otherSprite, explosion); + * + * function explosion(spriteA, spriteB) { + * spriteA.remove(); + * spriteB.score++; + * } + * </code> + * + * @method collide + * @param {Object} target Group or Sprite to check against the current one + * @param {Function} [callback] The function to be called if overlap is positive + * @return {Boolean} True if overlapping + */ + array.collide = _groupCollide.bind(array, 'collide'); + + /** + * Checks if the the group is overlapping another group or sprite. + * If the overlap is positive the sprites in the group will displace + * the colliding ones to the closest non-overlapping positions. + * + * The check is performed using the colliders. If colliders are not set + * they will be created automatically from the image/animation bounding box. + * + * A callback function can be specified to perform additional operations + * when the overlap occurs. + * The function will be called for each single sprite overlapping. + * The parameter of the function are respectively the + * member of the current group and the other sprite passed as parameter. + * + * @example + * <code> + * group.displace(otherSprite, explosion); + * + * function explosion(spriteA, spriteB) { + * spriteA.remove(); + * spriteB.score++; + * } + * </code> + * + * @method displace + * @param {Object} target Group or Sprite to check against the current one + * @param {Function} [callback] The function to be called if overlap is positive + * @return {Boolean} True if overlapping + */ + array.displace = _groupCollide.bind(array, 'displace'); + + /** + * Checks if the the group is overlapping another group or sprite. + * If the overlap is positive the sprites will bounce affecting each + * other's trajectories depending on their .velocity, .mass and .restitution. + * + * The check is performed using the colliders. If colliders are not set + * they will be created automatically from the image/animation bounding box. + * + * A callback function can be specified to perform additional operations + * when the overlap occours. + * The function will be called for each single sprite overlapping. + * The parameter of the function are respectively the + * member of the current group and the other sprite passed as parameter. + * + * @example + * <code> + * group.bounce(otherSprite, explosion); + * + * function explosion(spriteA, spriteB) { + * spriteA.remove(); + * spriteB.score++; + * } + * </code> + * + * @method bounce + * @param {Object} target Group or Sprite to check against the current one + * @param {Function} [callback] The function to be called if overlap is positive + * @return {Boolean} True if overlapping + */ + array.bounce = _groupCollide.bind(array, 'bounce'); + + return array; +} + +p5.prototype.Group = Group; + +//circle collider - used internally +function CircleCollider(pInst, _center, _radius, _offset) { + var pInstBind = createPInstBinder(pInst); + + var createVector = pInstBind('createVector'); + + var CENTER = p5.prototype.CENTER; + + this.center = _center; + this.radius = _radius; + this.originalRadius = _radius; + + if(_offset === undefined) + this.offset = createVector(0, 0); + else + this.offset = _offset; + this.extents = createVector(_radius*2, _radius*2); + + this.draw = function() + { + pInst.noFill(); + pInst.stroke(0, 255, 0); + pInst.rectMode(CENTER); + pInst.ellipse(this.center.x+this.offset.x, this.center.y+this.offset.y, this.radius*2, this.radius*2); + }; + + //should be called only for circle vs circle + this.overlap = function(other) + { + //square dist + var r = this.radius + other.radius; + r *= r; + var sqDist = pow(this.center.x - other.center.x, 2) + pow(this.center.y - other.center.y, 2); + return r > sqDist; + }; + + //should be called only for circle vs circle + this.collide = function(other) + { + + if(this.overlap(other)) + { + var a = pInst.atan2(this.center.y-other.center.y, this.center.x-other.center.x); + var radii = this.radius+other.radius; + var intersection = abs(radii - dist(this.center.x, this.center.y, other.center.x, other.center.y)); + + var displacement = createVector(pInst.cos(a)*intersection, pInst.sin(a)*intersection); + + return displacement; + } + else + return createVector(0, 0); + }; + + this.size = function() + { + return createVector(this.radius*2, this.radius*2); + }; + + this.left = function() + { + return this.center.x+this.offset.x - this.radius; + }; + + this.right = function() + { + return this.center.x+this.offset.x + this.radius; + }; + + this.top = function() + { + return this.center.y+this.offset.y - this.radius; + }; + + this.bottom = function() + { + return this.center.y+this.offset.y + this.radius; + }; + + + +} +defineLazyP5Property('CircleCollider', boundConstructorFactory(CircleCollider)); + +//axis aligned bounding box - extents are the half sizes - used internally +function AABB(pInst, _center, _extents, _offset) { + var pInstBind = createPInstBinder(pInst); + + var createVector = pInstBind('createVector'); + + var CENTER = p5.prototype.CENTER; + var PI = p5.prototype.PI; + + this.center = _center; + this.extents = _extents; + this.originalExtents = _extents.copy(); + + if(_offset === undefined) + this.offset = createVector(0, 0); + else + this.offset = _offset; + + this.min = function() + { + return createVector(this.center.x+this.offset.x - this.extents.x, this.center.y+this.offset.y - this.extents.y); + }; + + this.max = function() + { + return createVector(this.center.x+this.offset.x + this.extents.x, this.center.y+this.offset.y + this.extents.y); + }; + + this.right = function() + { + return this.center.x+this.offset.x + this.extents.x/2; + }; + + this.left = function() + { + return this.center.x+this.offset.x - this.extents.x/2; + }; + + this.top = function() + { + return this.center.y+this.offset.y - this.extents.y/2; + }; + + this.bottom = function() + { + return this.center.y+this.offset.y + this.extents.y/2; + }; + + this.size = function() + { + return createVector(this.extents.x * 2, this.extents.y * 2); + }; + + this.rotate = function(r) + { + //rotate the bbox + var t; + if (pInst._angleMode === pInst.RADIANS) { + t = radians(r); + } else { + t = r; + } + + var w2 = this.extents.x * abs(pInst.cos(t)) + this.extents.y * abs(pInst.sin(t)); + var h2 = this.extents.x * abs(pInst.sin(t)) + this.extents.y * abs(pInst.cos(t)); + + this.extents.x = w2; + this.extents.y = h2; + + }; + + this.draw = function() + { + //fill(col); + pInst.noFill(); + pInst.stroke(0, 255, 0); + pInst.rectMode(CENTER); + pInst.rect(this.center.x+this.offset.x, this.center.y+this.offset.y, this.size().x/2, this.size().y/2); + }; + + this.overlap = function(other) + { + //box vs box + if(other instanceof AABB) + { + var md = other.minkowskiDifference(this); + + if (md.min().x <= 0 && + md.max().x >= 0 && + md.min().y <= 0 && + md.max().y >= 0) + { + return true; + } + else + return false; + } + //box vs circle + else if(other instanceof CircleCollider) + { + + //find closest point to the circle on the box + var pt = createVector(other.center.x, other.center.y); + + //I don't know what's going o try to trace a line from centers to see + if( other.center.x < this.left() ) + pt.x = this.left(); + else if( other.center.x > this.right()) + pt.x = this.right(); + + if( other.center.y < this.top() ) + pt.y = this.top(); + else if( other.center.y > this.bottom()) + pt.y = this.bottom(); + + var distance = pt.dist(other.center); + + return distance<other.radius; + } + }; + + this.collide = function(other) + { + + if(other instanceof AABB) + { + var md = other.minkowskiDifference(this); + + if (md.min().x <= 0 && + md.max().x >= 0 && + md.min().y <= 0 && + md.max().y >= 0) + { + var boundsPoint = md.closestPointOnBoundsToPoint(createVector(0, 0)); + + return boundsPoint; + } + else + return createVector(0, 0); + } + //box vs circle + else if(other instanceof CircleCollider) + { + + //find closest point to the circle on the box + var pt = createVector(other.center.x, other.center.y); + + //I don't know what's going o try to trace a line from centers to see + if( other.center.x < this.left() ) + pt.x = this.left(); + else if( other.center.x > this.right()) + pt.x = this.right(); + + if( other.center.y < this.top() ) + pt.y = this.top(); + else if( other.center.y > this.bottom()) + pt.y = this.bottom(); + + + var distance = pt.dist(other.center); + var a; + + if(distance<other.radius) + { + //reclamp point + if(pt.x === other.center.x && pt.y === other.center.y) + { + var xOverlap = pt.x - this.center.x; + var yOverlap = pt.y - this.center.y; + + + if(abs(xOverlap) < abs(yOverlap)) + { + if(xOverlap > 0 ) + pt.x = this.right(); + else + pt.x = this.left(); + } + else + { + if(yOverlap < 0 ) + pt.y = this.top(); + else + pt.y = this.bottom(); + } + + a = pInst.atan2(other.center.y-pt.y, other.center.x-pt.x); + + //fix exceptions + if(a === 0) + { + if(pt.x === this.right()) a = PI; + if(pt.y === this.top()) a = PI/2; + if(pt.y === this.bottom()) a = -PI/2; + } + } + else + { + //angle bw point and center + a = pInst.atan2(pt.y-other.center.y, pt.x-other.center.x); + //project the normal (line between pt and center) onto the circle + } + + var d = createVector(pt.x-other.center.x, pt.y-other.center.y); + var displacement = createVector(pInst.cos(a)*other.radius-d.x, pInst.sin(a)*other.radius-d.y); + + //if(pt.x === other.center.x && pt.y === other.center.y) + //displacement = displacement.mult(-1); + + return displacement; + //return createVector(0,0); + } + else + return createVector(0, 0); + } + }; + + this.minkowskiDifference = function(other) + { + var topLeft = this.min().sub(other.max()); + var fullSize = this.size().add(other.size()); + return new AABB(pInst, topLeft.add(fullSize.div(2)), fullSize.div(2)); + }; + + + this.closestPointOnBoundsToPoint = function(point) + { + // test x first + var minDist = abs(point.x - this.min().x); + var boundsPoint = createVector(this.min().x, point.y); + + if (abs(this.max().x - point.x) < minDist) + { + minDist = abs(this.max().x - point.x); + boundsPoint = createVector(this.max().x, point.y); + } + + if (abs(this.max().y - point.y) < minDist) + { + minDist = abs(this.max().y - point.y); + boundsPoint = createVector(point.x, this.max().y); + } + + if (abs(this.min().y - point.y) < minDist) + { + minDist = abs(this.min.y - point.y); + boundsPoint = createVector(point.x, this.min().y); + } + + return boundsPoint; + }; + + +}//end AABB +defineLazyP5Property('AABB', boundConstructorFactory(AABB)); + + + +/** + * An Animation object contains a series of images (p5.Image) that + * can be displayed sequentially. + * + * All files must be png images. You must include the directory from the sketch root, + * and the extension .png + * + * A sprite can have multiple labeled animations, see Sprite.addAnimation + * and Sprite.changeAnimation, however an animation can be used independently. + * + * An animation can be created either by passing a series of file names, + * no matter how many or by passing the first and the last file name + * of a numbered sequence. + * p5.play will try to detect the sequence pattern. + * + * For example if the given filenames are + * "data/file0001.png" and "data/file0005.png" the images + * "data/file0003.png" and "data/file0004.png" will be loaded as well. + * + * @example + * <code> + * var sequenceAnimation;<br> + * var glitch;<br><br> + * function preload() {<br> + * sequenceAnimation = loadAnimation("data/walking0001.png", "data/walking0005.png");<br> + * glitch = loadAnimation("data/dog.png", "data/horse.png", "data/cat.png", "data/snake.png");<br> + * }<br><br> + * function setup() {<br> + * createCanvas(800, 600);<br> + * }<br><br> + * function draw() {<br> + * background(0);<br> + * animation(sequenceAnimation, 100, 100);<br> + * animation(glitch, 200, 100);<br> + * } + * </code> + * + * @class Animation + * @constructor + * @param {String} fileName1 First file in a sequence OR first image file + * @param {String} fileName2 Last file in a sequence OR second image file + * @param {String} [...fileNameN] Any number of image files after the first two + */ + +function Animation(pInst) { + var frameArguments = Array.prototype.slice.call(arguments, 1); + var i; + + var CENTER = p5.prototype.CENTER; + + /** + * Array of frames (p5.Image) + * + * @property images + * @type {Array} + */ + this.images = []; + + var frame = 0; + var cycles = 0; + var targetFrame = -1; + + this.offX = 0; + this.offY = 0; + + /** + * Delay between frames in number of draw cycles. + * If set to 4 the framerate of the anymation would be the + * sketch framerate divided by 4 (60fps = 15fps) + * + * @property frameDelay + * @type {Number} + * @default 2 + */ + this.frameDelay = 4; + + /** + * True if the animation is currently playing. + * + * @property playing + * @type {Boolean} + * @default true + */ + this.playing = true; + + /** + * Animation visibility. + * + * @property visible + * @type {Boolean} + * @default true + */ + this.visible = true; + + /** + * If set to false the animation will stop after reaching the last frame + * + * @property looping + * @type {Boolean} + * @default true + */ + this.looping = true; + + /** + * True if frame changed during the last draw cycle + * + * @property frameChanged + * @type {Boolean} + */ + this.frameChanged = false; + + //is the collider defined manually or defined + //by the current frame size + this.imageCollider = false; + + + //sequence mode + if(frameArguments.length === 2 && typeof frameArguments[0] === 'string' && typeof frameArguments[1] === 'string') + { + var from = frameArguments[0]; + var to = frameArguments[1]; + + //print("sequence mode "+from+" -> "+to); + + //make sure the extensions are fine + var ext1 = from.substring(from.length-4, from.length); + if(ext1 !== '.png') + { + pInst.println('Animation error: you need to use .png files (filename '+from+')'); + from = -1; + } + + var ext2 = to.substring(to.length-4, to.length); + if(ext2 !== '.png') + { + pInst.println('Animation error: you need to use .png files (filename '+to+')'); + to = -1; + } + + //extensions are fine + if(from !== -1 && to !== -1) + { + var digits1 = 0; + var digits2 = 0; + + //skip extension work backwards to find the numbers + for (i = from.length-5; i >= 0; i--) { + if(from.charAt(i) >= '0' && from.charAt(i) <= '9') + digits1++; + } + + for (i = to.length-5; i >= 0; i--) { + if(to.charAt(i) >= '0' && to.charAt(i) <= '9') + digits2++; + } + + var prefix1 = from.substring(0, from.length-(4+digits1)); + var prefix2 = to.substring(0, to.length-(4+digits2) ); + + // Our numbers likely have leading zeroes, which means that some + // browsers (e.g., PhantomJS) will interpret them as base 8 (octal) + // instead of decimal. To fix this, we'll explicity tell parseInt to + // use a base of 10 (decimal). For more details on this issue, see + // http://stackoverflow.com/a/8763427/2422398. + var number1 = parseInt(from.substring(from.length-(4+digits1), from.length-4), 10); + var number2 = parseInt(to.substring(to.length-(4+digits2), to.length-4), 10); + + //swap if inverted + if(number2<number1) + { + var t = number2; + number2 = number1; + number1 = t; + } + + //two different frames + if(prefix1 !== prefix2 ) + { + //print("2 separate images"); + this.images.push(pInst.loadImage(from)); + this.images.push(pInst.loadImage(to)); + } + //same digits: case img0001, img0002 + else + { + var fileName; + if(digits1 === digits2) + { + + //load all images + for (i = number1; i <= number2; i++) { + // Use nf() to number format 'i' into four digits + fileName = prefix1 + pInst.nf(i, digits1) + '.png'; + this.images.push(pInst.loadImage(fileName)); + + } + + } + else //case: case img1, img2 + { + //print("from "+prefix1+" "+number1 +" to "+number2); + for (i = number1; i <= number2; i++) { + // Use nf() to number format 'i' into four digits + fileName = prefix1 + i + '.png'; + this.images.push(pInst.loadImage(fileName)); + + } + + } + } + + }//end no ext error + + }//end sequence mode + // Sprite sheet mode + else if (frameArguments.length === 1 && (frameArguments[0] instanceof SpriteSheet)) + { + this.spriteSheet = frameArguments[0]; + this.images = this.spriteSheet.frames.map( function(f) { + return f.frame; + }); + } + else if(frameArguments.length !== 0)//arbitrary list of images + { + //print("Animation arbitrary mode"); + for (i = 0; i < frameArguments.length; i++) { + //print("loading "+fileNames[i]); + if(frameArguments[i] instanceof p5.Image) + this.images.push(frameArguments[i]); + else + this.images.push(pInst.loadImage(frameArguments[i])); + } + } + + /** + * Objects are passed by reference so to have different sprites + * using the same animation you need to clone it. + * + * @method clone + * @return {Animation} A clone of the current animation + */ + this.clone = function() { + var myClone = new Animation(pInst); //empty + myClone.images = []; + + if (this.spriteSheet) { + myClone.spriteSheet = this.spriteSheet.clone(); + } + myClone.images = this.images.slice(); + + myClone.offX = this.offX; + myClone.offY = this.offY; + myClone.frameDelay = this.frameDelay; + myClone.playing = this.playing; + myClone.looping = this.looping; + + return myClone; + }; + + /** + * Draws the animation at coordinate x and y. + * Updates the frames automatically. + * + * @method draw + * @param {Number} x x coordinate + * @param {Number} y y coordinate + * @param {Number} [r=0] rotation + */ + this.draw = function(x, y, r) { + this.xpos = x; + this.ypos = y; + this.rotation = r || 0; + + if (this.visible) + { + + //only connection with the sprite class + //if animation is used independently draw and update are the sam + if(!this.isSpriteAnimation) + this.update(); + + //this.currentImageMode = g.imageMode; + pInst.push(); + pInst.imageMode(CENTER); + + pInst.translate(this.xpos, this.ypos); + if (pInst._angleMode === pInst.RADIANS) { + pInst.rotate(radians(this.rotation)); + } else { + pInst.rotate(this.rotation); + } + + if(this.images[frame] !== undefined) + { + if (this.spriteSheet) { + var frame_info = this.images[frame]; + pInst.image(this.spriteSheet.image, frame_info.x, frame_info.y, frame_info.width, + frame_info.height, this.offX, this.offY, frame_info.width, frame_info.height); + } else { + pInst.image(this.images[frame], this.offX, this.offY); + } + } + else + { + pInst.print('Warning undefined frame '+frame); + //this.isActive = false; + } + + pInst.pop(); + } + }; + + //called by draw + this.update = function() { + cycles++; + var previousFrame = frame; + this.frameChanged = false; + + + //go to frame + if(this.images.length === 1) + { + this.playing = false; + frame = 0; + } + + if ( this.playing && cycles%this.frameDelay === 0) + { + //going to target frame up + if(targetFrame>frame && targetFrame !== -1) + { + frame++; + } + //going to taget frame down + else if(targetFrame<frame && targetFrame !== -1) + { + frame--; + } + else if(targetFrame === frame && targetFrame !== -1) + { + this.playing=false; + } + else if (this.looping) //advance frame + { + //if next frame is too high + if (frame>=this.images.length-1) + frame = 0; + else + frame++; + } else + { + //if next frame is too high + if (frame<this.images.length-1) + frame++; + } + } + + if(previousFrame !== frame) + this.frameChanged = true; + + };//end update + + /** + * Plays the animation. + * + * @method play + */ + this.play = function() { + this.playing = true; + targetFrame = -1; + }; + + /** + * Stops the animation. + * + * @method stop + */ + this.stop = function(){ + this.playing = false; + }; + + /** + * Rewinds the animation to the first frame. + * + * @method rewind + */ + this.rewind = function() { + frame = 0; + }; + + /** + * Changes the current frame. + * + * @method changeFrame + * @param {Number} frame Frame number (starts from 0). + */ + this.changeFrame = function(f) { + if (f<this.images.length) + frame = f; + else + frame = this.images.length - 1; + + targetFrame = -1; + //this.playing = false; + }; + + /** + * Goes to the next frame and stops. + * + * @method changeFrame + */ + this.nextFrame = function() { + + if (frame<this.images.length-1) + frame = frame+1; + else if(this.looping) + frame = 0; + + targetFrame = -1; + this.playing = false; + }; + + /** + * Goes to the next frame and stops. + * + * @method changeFrame + */ + this.previousFrame = function() { + + if (frame>0) + frame = frame-1; + else if(this.looping) + frame = this.images.length-1; + + targetFrame = -1; + this.playing = false; + }; + + /** + * Plays the animation forward or backward toward a target frame. + * + * @method goToFrame + * @param {Number} targetFrame Frame number destination (starts from 0) + */ + this.goToFrame = function(f) { + this.f = f; + + if(this.f>=0 && this.f<this.images.length) + targetFrame = this.f; + + if(targetFrame !== frame) + this.playing = true; + }; + + /** + * Returns the current frame number. + * + * @method getFrame + * @return {Number} Current frame (starts from 0) + */ + this.getFrame = function() { + return frame; + }; + + /** + * Returns the last frame number. + * + * @method getLastFrame + * @return {Number} Last frame number (starts from 0) + */ + this.getLastFrame = function() { + return this.images.length-1; + }; + + /** + * Returns the current frame image as p5.Image. + * + * @method getFrameImage + * @return {p5.Image} Current frame image + */ + this.getFrameImage = function() { + return this.images[frame]; + }; + + /** + * Returns the frame image at the specified frame number. + * + * @method getImageAt + * @param {Number} frame Frame number + * @return {p5.Image} Frame image + */ + this.getImageAt = function(f) { + return this.images[f]; + }; + + /** + * Returns the current frame width in pixels. + * If there is no image loaded, returns 1. + * + * @method getWidth + * @return {Number} Frame width + */ + this.getWidth = function() { + if (this.images[frame]) { + return this.images[frame].width; + } else { + return 1; + } + }; + + /** + * Returns the current frame height in pixels. + * If there is no image loaded, returns 1. + * + * @method getHeight + * @return {Number} Frame height + */ + this.getHeight = function() { + if (this.images[frame]) { + return this.images[frame].height; + } else { + return 1; + } + }; + +} + +defineLazyP5Property('Animation', boundConstructorFactory(Animation)); + +/** + * Represents a sprite sheet and all it's frames. To be used with Animation, + * or static drawing single frames. + * + * There are two different ways to load a SpriteSheet + * + * 1. Given width, height that will be used for every frame and the + * number of frames to cycle through. The sprite sheet must have a + * uniform grid with consistent rows and columns. + * + * 2. Given an array of frame objects that define the position and + * dimensions of each frame. This is Flexible because you can use + * sprite sheets that don't have uniform rows and columns. + * + * @example + * <code> + * // Method 1 - Using width, height for each frame and number of frames<br/> + * explode_sprite_sheet = loadSpriteSheet('assets/explode_sprite_sheet.png', 171, 158, 11); + * <br/><br/> + * // Method 2 - Using an array of objects that define each frame + * var player_frames = loadJSON('assets/tiles.json');<br/> + * player_sprite_sheet = loadSpriteSheet('assets/player_spritesheet.png', player_frames);<br/> + * </code> + * + * @class SpriteSheet + * @constructor + * @param image String image path or p5.Image object + */ +function SpriteSheet(pInst) { + var spriteSheetArgs = Array.prototype.slice.call(arguments, 1); + + this.image = null; + this.frames = []; + this.frame_width = 0; + this.frame_height = 0; + this.num_frames = 0; + + /** + * Generate the frames data for this sprite sheet baesd on user params + * @private + * @method _generateSheetFrames + */ + this._generateSheetFrames = function() { + var sX = 0, sY = 0; + for (var i = 0; i < this.num_frames; i++) { + this.frames.push( + { + 'name': i, + 'frame': { + 'x': sX, + 'y': sY, + 'width': this.frame_width, + 'height': this.frame_height + } + }); + sX += this.frame_width; + if (sX >= this.image.width) { + sX = 0; + sY += this.frame_height; + if (sY >= this.image.height) { + sY = 0; + } + } + } + }; + + if (spriteSheetArgs.length === 2 && Array.isArray(spriteSheetArgs[1])) { + this.frames = spriteSheetArgs[1]; + this.num_frames = this.frames.length; + } else if (spriteSheetArgs.length === 4 && + (typeof spriteSheetArgs[1] === 'number') && + (typeof spriteSheetArgs[2] === 'number') && + (typeof spriteSheetArgs[3] === 'number')) { + this.frame_width = spriteSheetArgs[1]; + this.frame_height = spriteSheetArgs[2]; + this.num_frames = spriteSheetArgs[3]; + } + + if(spriteSheetArgs[0] instanceof p5.Image) { + this.image = spriteSheetArgs[0]; + if (spriteSheetArgs.length === 4) { + this._generateSheetFrames(); + } + } else { + if (spriteSheetArgs.length === 2) { + this.image = pInst.loadImage(spriteSheetArgs[0]); + } else if (spriteSheetArgs.length === 4) { + this.image = pInst.loadImage(spriteSheetArgs[0], this._generateSheetFrames.bind(this)); + } + } + + /** + * Draws a specific frame to the canvas. + * @param frame_name Can either be a string name, or a numeric index. + * @param x x position to draw the frame at + * @param y y position to draw the frame at + * @param [width] optional width to draw the frame + * @param [height] optional height to draw the frame + * @method drawFrame + */ + this.drawFrame = function(frame_name, x, y, width, height) { + var frameToDraw; + if (typeof frame_name === 'number') { + frameToDraw = this.frames[frame_name].frame; + } else { + for (var i = 0; i < this.frames.length; i++) { + if (this.frames[i].name === frame_name) { + frameToDraw = this.frames[i].frame; + break; + } + } + } + var dWidth = width || frameToDraw.width; + var dHeight = height || frameToDraw.height; + pInst.image(this.image, frameToDraw.x, frameToDraw.y, + frameToDraw.width, frameToDraw.height, x, y, dWidth, dHeight); + }; + + /** + * Objects are passed by reference so to have different sprites + * using the same animation you need to clone it. + * + * @method clone + * @return {SpriteSheet} A clone of the current SpriteSheet + */ + this.clone = function() { + var myClone = new SpriteSheet(pInst); //empty + + // Deep clone the frames by value not reference + for(var i = 0; i < this.frames.length; i++) { + var frame = this.frames[i].frame; + var cloneFrame = { + 'name':frame.name, + 'frame': { + 'x':frame.x, + 'y':frame.y, + 'width':frame.width, + 'height':frame.height + } + }; + myClone.frames.push(cloneFrame); + } + + // clone other fields + myClone.image = this.image; + myClone.frame_width = this.frame_width; + myClone.frame_height = this.frame_height; + myClone.num_frames = this.num_frames; + + return myClone; + }; +} + +defineLazyP5Property('SpriteSheet', boundConstructorFactory(SpriteSheet)); + +//general constructor to be able to feed arguments as array +function construct(constructor, args) { + function F() { + return constructor.apply(this, args); + } + F.prototype = constructor.prototype; + return new F(); +} + + + + + +/* + * Javascript Quadtree + * based on + * https://github.com/timohausmann/quadtree-js/ + * Copyright © 2012 Timo Hausmann +*/ + +function Quadtree( bounds, max_objects, max_levels, level ) { + + this.active = true; + this.max_objects = max_objects || 10; + this.max_levels = max_levels || 4; + + this.level = level || 0; + this.bounds = bounds; + + this.objects = []; + this.object_refs = []; + this.nodes = []; +} + +Quadtree.prototype.updateBounds = function() { + + //find maximum area + var objects = this.getAll(); + var x = 10000; + var y = 10000; + var w = -10000; + var h = -10000; + + for( var i=0; i < objects.length; i++ ) + { + if(objects[i].position.x < x) + x = objects[i].position.x; + if(objects[i].position.y < y) + y = objects[i].position.y; + if(objects[i].position.x > w) + w = objects[i].position.x; + if(objects[i].position.y > h) + h = objects[i].position.y; + } + + + this.bounds = { + x:x, + y:y, + width:w, + height:h + }; + //print(this.bounds); +}; + +/* + * Split the node into 4 subnodes + */ +Quadtree.prototype.split = function() { + + var nextLevel = this.level + 1, + subWidth = Math.round( this.bounds.width / 2 ), + subHeight = Math.round( this.bounds.height / 2 ), + x = Math.round( this.bounds.x ), + y = Math.round( this.bounds.y ); + + //top right node + this.nodes[0] = new Quadtree({ + x : x + subWidth, + y : y, + width : subWidth, + height : subHeight + }, this.max_objects, this.max_levels, nextLevel); + + //top left node + this.nodes[1] = new Quadtree({ + x : x, + y : y, + width : subWidth, + height : subHeight + }, this.max_objects, this.max_levels, nextLevel); + + //bottom left node + this.nodes[2] = new Quadtree({ + x : x, + y : y + subHeight, + width : subWidth, + height : subHeight + }, this.max_objects, this.max_levels, nextLevel); + + //bottom right node + this.nodes[3] = new Quadtree({ + x : x + subWidth, + y : y + subHeight, + width : subWidth, + height : subHeight + }, this.max_objects, this.max_levels, nextLevel); +}; + + +/* + * Determine the quadtrant for an area in this node + */ +Quadtree.prototype.getIndex = function( pRect ) { + if(!pRect.collider) + return -1; + else + { + var index = -1, + verticalMidpoint = this.bounds.x + (this.bounds.width / 2), + horizontalMidpoint = this.bounds.y + (this.bounds.height / 2), + + //pRect can completely fit within the top quadrants + topQuadrant = (pRect.collider.top() < horizontalMidpoint && pRect.collider.top() + pRect.collider.size().y < horizontalMidpoint), + + //pRect can completely fit within the bottom quadrants + bottomQuadrant = (pRect.collider.top() > horizontalMidpoint); + + //pRect can completely fit within the left quadrants + if( pRect.collider.left() < verticalMidpoint && pRect.collider.left() + pRect.collider.size().x < verticalMidpoint ) { + if( topQuadrant ) { + index = 1; + } else if( bottomQuadrant ) { + index = 2; + } + + //pRect can completely fit within the right quadrants + } else if( pRect.collider.left() > verticalMidpoint ) { + if( topQuadrant ) { + index = 0; + } else if( bottomQuadrant ) { + index = 3; + } + } + + return index; + } +}; + + +/* + * Insert an object into the node. If the node + * exceeds the capacity, it will split and add all + * objects to their corresponding subnodes. + */ +Quadtree.prototype.insert = function( obj ) { + //avoid double insertion + if(this.objects.indexOf(obj) === -1) + { + + var i = 0, + index; + + //if we have subnodes ... + if( typeof this.nodes[0] !== 'undefined' ) { + index = this.getIndex( obj ); + + if( index !== -1 ) { + this.nodes[index].insert( obj ); + return; + } + } + + this.objects.push( obj ); + + if( this.objects.length > this.max_objects && this.level < this.max_levels ) { + + //split if we don't already have subnodes + if( typeof this.nodes[0] === 'undefined' ) { + this.split(); + } + + //add all objects to there corresponding subnodes + while( i < this.objects.length ) { + + index = this.getIndex( this.objects[i] ); + + if( index !== -1 ) { + this.nodes[index].insert( this.objects.splice(i, 1)[0] ); + } else { + i = i + 1; + } + } + } + } +}; + + +/* + * Return all objects that could collide with a given area + */ +Quadtree.prototype.retrieve = function( pRect ) { + + + var index = this.getIndex( pRect ), + returnObjects = this.objects; + + //if we have subnodes ... + if( typeof this.nodes[0] !== 'undefined' ) { + + //if pRect fits into a subnode .. + if( index !== -1 ) { + returnObjects = returnObjects.concat( this.nodes[index].retrieve( pRect ) ); + + //if pRect does not fit into a subnode, check it against all subnodes + } else { + for( var i=0; i < this.nodes.length; i=i+1 ) { + returnObjects = returnObjects.concat( this.nodes[i].retrieve( pRect ) ); + } + } + } + + return returnObjects; +}; + +Quadtree.prototype.retrieveFromGroup = function( pRect, group ) { + + var results = []; + var candidates = this.retrieve(pRect); + + for(var i=0; i<candidates.length; i++) + if(group.contains(candidates[i])) + results.push(candidates[i]); + + return results; +}; + +/* + * Get all objects stored in the quadtree + */ +Quadtree.prototype.getAll = function() { + + var objects = this.objects; + + for( var i=0; i < this.nodes.length; i=i+1 ) { + objects = objects.concat( this.nodes[i].getAll() ); + } + + return objects; +}; + + +/* + * Get the node in which a certain object is stored + */ +Quadtree.prototype.getObjectNode = function( obj ) { + + var index; + + //if there are no subnodes, object must be here + if( !this.nodes.length ) { + + return this; + + } else { + + index = this.getIndex( obj ); + + //if the object does not fit into a subnode, it must be here + if( index === -1 ) { + + return this; + + //if it fits into a subnode, continue deeper search there + } else { + var node = this.nodes[index].getObjectNode( obj ); + if( node ) return node; + } + } + + return false; +}; + + +/* + * Removes a specific object from the quadtree + * Does not delete empty subnodes. See cleanup-function + */ +Quadtree.prototype.removeObject = function( obj ) { + + var node = this.getObjectNode( obj ), + index = node.objects.indexOf( obj ); + + if( index === -1 ) return false; + + node.objects.splice( index, 1); +}; + + +/* + * Clear the quadtree and delete all objects + */ +Quadtree.prototype.clear = function() { + + this.objects = []; + + if( !this.nodes.length ) return; + + for( var i=0; i < this.nodes.length; i=i+1 ) { + + this.nodes[i].clear(); + } + + this.nodes = []; +}; + + +/* + * Clean up the quadtree + * Like clear, but objects won't be deleted but re-inserted + */ +Quadtree.prototype.cleanup = function() { + + var objects = this.getAll(); + + this.clear(); + + for( var i=0; i < objects.length; i++ ) { + this.insert( objects[i] ); + } +}; + + + +function updateTree() { + if(this.quadTree.active) + { + this.quadTree.updateBounds(); + this.quadTree.cleanup(); + } +} + +//keyboard input +p5.prototype.registerMethod('pre', p5.prototype.readPresses); + +//automatic sprite update +p5.prototype.registerMethod('pre', p5.prototype.updateSprites); + +//quadtree update +p5.prototype.registerMethod('post', updateTree); + +//camera push and pop +p5.prototype.registerMethod('pre', cameraPush); +p5.prototype.registerMethod('post', cameraPop); + +//deltaTime +//p5.prototype.registerMethod('pre', updateDelta); + +/** + * Log a warning message to the host console, using native `console.warn` + * if it is available but falling back on `console.log` if not. If no + * console is available, this method will fail silently. + * @method _warn + * @param {!string} message + * @private + */ +p5.prototype._warn = function(message) { + var console = window.console; + + if(console) + { + if('function' === typeof console.warn) + { + console.warn(message); + } + else if('function' === typeof console.log) + { + console.log('Warning: ' + message); + } + } +}; + +})); diff --git a/public/js/socket.io.js b/public/js/socket.io.js new file mode 100644 index 0000000000000000000000000000000000000000..4ab69266c1404b2ff4a04b4edde03d379de4b233 --- /dev/null +++ b/public/js/socket.io.js @@ -0,0 +1,8284 @@ +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define([], factory); + else if(typeof exports === 'object') + exports["io"] = factory(); + else + root["io"] = factory(); +})(this, function() { +return /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; + +/******/ // The require function +/******/ function __webpack_require__(moduleId) { + +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) +/******/ return installedModules[moduleId].exports; + +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ exports: {}, +/******/ id: moduleId, +/******/ loaded: false +/******/ }; + +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); + +/******/ // Flag the module as loaded +/******/ module.loaded = true; + +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } + + +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; + +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; + +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; + +/******/ // Load entry module and return exports +/******/ return __webpack_require__(0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + + /** + * Module dependencies. + */ + + var url = __webpack_require__(1); + var parser = __webpack_require__(7); + var Manager = __webpack_require__(17); + var debug = __webpack_require__(3)('socket.io-client'); + + /** + * Module exports. + */ + + module.exports = exports = lookup; + + /** + * Managers cache. + */ + + var cache = exports.managers = {}; + + /** + * Looks up an existing `Manager` for multiplexing. + * If the user summons: + * + * `io('http://localhost/a');` + * `io('http://localhost/b');` + * + * We reuse the existing instance based on same scheme/port/host, + * and we initialize sockets for each namespace. + * + * @api public + */ + + function lookup(uri, opts) { + if ((typeof uri === 'undefined' ? 'undefined' : _typeof(uri)) === 'object') { + opts = uri; + uri = undefined; + } + + opts = opts || {}; + + var parsed = url(uri); + var source = parsed.source; + var id = parsed.id; + var path = parsed.path; + var sameNamespace = cache[id] && path in cache[id].nsps; + var newConnection = opts.forceNew || opts['force new connection'] || false === opts.multiplex || sameNamespace; + + var io; + + if (newConnection) { + debug('ignoring socket cache for %s', source); + io = Manager(source, opts); + } else { + if (!cache[id]) { + debug('new io instance for %s', source); + cache[id] = Manager(source, opts); + } + io = cache[id]; + } + if (parsed.query && !opts.query) { + opts.query = parsed.query; + } else if (opts && 'object' === _typeof(opts.query)) { + opts.query = encodeQueryString(opts.query); + } + return io.socket(parsed.path, opts); + } + /** + * Helper method to parse query objects to string. + * @param {object} query + * @returns {string} + */ + function encodeQueryString(obj) { + var str = []; + for (var p in obj) { + if (obj.hasOwnProperty(p)) { + str.push(encodeURIComponent(p) + '=' + encodeURIComponent(obj[p])); + } + } + return str.join('&'); + } + /** + * Protocol version. + * + * @api public + */ + + exports.protocol = parser.protocol; + + /** + * `connect`. + * + * @param {String} uri + * @api public + */ + + exports.connect = lookup; + + /** + * Expose constructors for standalone build. + * + * @api public + */ + + exports.Manager = __webpack_require__(17); + exports.Socket = __webpack_require__(45); + +/***/ }, +/* 1 */ +/***/ function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(global) {'use strict'; + + /** + * Module dependencies. + */ + + var parseuri = __webpack_require__(2); + var debug = __webpack_require__(3)('socket.io-client:url'); + + /** + * Module exports. + */ + + module.exports = url; + + /** + * URL parser. + * + * @param {String} url + * @param {Object} An object meant to mimic window.location. + * Defaults to window.location. + * @api public + */ + + function url(uri, loc) { + var obj = uri; + + // default to window.location + loc = loc || global.location; + if (null == uri) uri = loc.protocol + '//' + loc.host; + + // relative path support + if ('string' === typeof uri) { + if ('/' === uri.charAt(0)) { + if ('/' === uri.charAt(1)) { + uri = loc.protocol + uri; + } else { + uri = loc.host + uri; + } + } + + if (!/^(https?|wss?):\/\//.test(uri)) { + debug('protocol-less url %s', uri); + if ('undefined' !== typeof loc) { + uri = loc.protocol + '//' + uri; + } else { + uri = 'https://' + uri; + } + } + + // parse + debug('parse %s', uri); + obj = parseuri(uri); + } + + // make sure we treat `localhost:80` and `localhost` equally + if (!obj.port) { + if (/^(http|ws)$/.test(obj.protocol)) { + obj.port = '80'; + } else if (/^(http|ws)s$/.test(obj.protocol)) { + obj.port = '443'; + } + } + + obj.path = obj.path || '/'; + + var ipv6 = obj.host.indexOf(':') !== -1; + var host = ipv6 ? '[' + obj.host + ']' : obj.host; + + // define unique id + obj.id = obj.protocol + '://' + host + ':' + obj.port; + // define href + obj.href = obj.protocol + '://' + host + (loc && loc.port === obj.port ? '' : ':' + obj.port); + + return obj; + } + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) + +/***/ }, +/* 2 */ +/***/ function(module, exports) { + + /** + * Parses an URI + * + * @author Steven Levithan <stevenlevithan.com> (MIT license) + * @api private + */ + + var re = /^(?:(?![^:@]+:[^:@\/]*@)(http|https|ws|wss):\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/; + + var parts = [ + 'source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', 'anchor' + ]; + + module.exports = function parseuri(str) { + var src = str, + b = str.indexOf('['), + e = str.indexOf(']'); + + if (b != -1 && e != -1) { + str = str.substring(0, b) + str.substring(b, e).replace(/:/g, ';') + str.substring(e, str.length); + } + + var m = re.exec(str || ''), + uri = {}, + i = 14; + + while (i--) { + uri[parts[i]] = m[i] || ''; + } + + if (b != -1 && e != -1) { + uri.source = src; + uri.host = uri.host.substring(1, uri.host.length - 1).replace(/;/g, ':'); + uri.authority = uri.authority.replace('[', '').replace(']', '').replace(/;/g, ':'); + uri.ipv6uri = true; + } + + return uri; + }; + + +/***/ }, +/* 3 */ +/***/ function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(process) { + /** + * This is the web browser implementation of `debug()`. + * + * Expose `debug()` as the module. + */ + + exports = module.exports = __webpack_require__(5); + exports.log = log; + exports.formatArgs = formatArgs; + exports.save = save; + exports.load = load; + exports.useColors = useColors; + exports.storage = 'undefined' != typeof chrome + && 'undefined' != typeof chrome.storage + ? chrome.storage.local + : localstorage(); + + /** + * Colors. + */ + + exports.colors = [ + 'lightseagreen', + 'forestgreen', + 'goldenrod', + 'dodgerblue', + 'darkorchid', + 'crimson' + ]; + + /** + * Currently only WebKit-based Web Inspectors, Firefox >= v31, + * and the Firebug extension (any Firefox version) are known + * to support "%c" CSS customizations. + * + * TODO: add a `localStorage` variable to explicitly enable/disable colors + */ + + function useColors() { + // is webkit? http://stackoverflow.com/a/16459606/376773 + // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632 + return (typeof document !== 'undefined' && 'WebkitAppearance' in document.documentElement.style) || + // is firebug? http://stackoverflow.com/a/398120/376773 + (window.console && (console.firebug || (console.exception && console.table))) || + // is firefox >= v31? + // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages + (navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31); + } + + /** + * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. + */ + + exports.formatters.j = function(v) { + try { + return JSON.stringify(v); + } catch (err) { + return '[UnexpectedJSONParseError]: ' + err.message; + } + }; + + + /** + * Colorize log arguments if enabled. + * + * @api public + */ + + function formatArgs() { + var args = arguments; + var useColors = this.useColors; + + args[0] = (useColors ? '%c' : '') + + this.namespace + + (useColors ? ' %c' : ' ') + + args[0] + + (useColors ? '%c ' : ' ') + + '+' + exports.humanize(this.diff); + + if (!useColors) return args; + + var c = 'color: ' + this.color; + args = [args[0], c, 'color: inherit'].concat(Array.prototype.slice.call(args, 1)); + + // the final "%c" is somewhat tricky, because there could be other + // arguments passed either before or after the %c, so we need to + // figure out the correct index to insert the CSS into + var index = 0; + var lastC = 0; + args[0].replace(/%[a-z%]/g, function(match) { + if ('%%' === match) return; + index++; + if ('%c' === match) { + // we only are interested in the *last* %c + // (the user may have provided their own) + lastC = index; + } + }); + + args.splice(lastC, 0, c); + return args; + } + + /** + * Invokes `console.log()` when available. + * No-op when `console.log` is not a "function". + * + * @api public + */ + + function log() { + // this hackery is required for IE8/9, where + // the `console.log` function doesn't have 'apply' + return 'object' === typeof console + && console.log + && Function.prototype.apply.call(console.log, console, arguments); + } + + /** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ + + function save(namespaces) { + try { + if (null == namespaces) { + exports.storage.removeItem('debug'); + } else { + exports.storage.debug = namespaces; + } + } catch(e) {} + } + + /** + * Load `namespaces`. + * + * @return {String} returns the previously persisted debug modes + * @api private + */ + + function load() { + var r; + try { + return exports.storage.debug; + } catch(e) {} + + // If debug isn't set in LS, and we're in Electron, try to load $DEBUG + if (typeof process !== 'undefined' && 'env' in process) { + return process.env.DEBUG; + } + } + + /** + * Enable namespaces listed in `localStorage.debug` initially. + */ + + exports.enable(load()); + + /** + * Localstorage attempts to return the localstorage. + * + * This is necessary because safari throws + * when a user disables cookies/localstorage + * and you attempt to access it. + * + * @return {LocalStorage} + * @api private + */ + + function localstorage(){ + try { + return window.localStorage; + } catch (e) {} + } + + /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(4))) + +/***/ }, +/* 4 */ +/***/ function(module, exports) { + + // shim for using process in browser + var process = module.exports = {}; + + // cached from whatever global is present so that test runners that stub it + // don't break things. But we need to wrap it in a try catch in case it is + // wrapped in strict mode code which doesn't define any globals. It's inside a + // function because try/catches deoptimize in certain engines. + + var cachedSetTimeout; + var cachedClearTimeout; + + function defaultSetTimout() { + throw new Error('setTimeout has not been defined'); + } + function defaultClearTimeout () { + throw new Error('clearTimeout has not been defined'); + } + (function () { + try { + if (typeof setTimeout === 'function') { + cachedSetTimeout = setTimeout; + } else { + cachedSetTimeout = defaultSetTimout; + } + } catch (e) { + cachedSetTimeout = defaultSetTimout; + } + try { + if (typeof clearTimeout === 'function') { + cachedClearTimeout = clearTimeout; + } else { + cachedClearTimeout = defaultClearTimeout; + } + } catch (e) { + cachedClearTimeout = defaultClearTimeout; + } + } ()) + function runTimeout(fun) { + if (cachedSetTimeout === setTimeout) { + //normal enviroments in sane situations + return setTimeout(fun, 0); + } + // if setTimeout wasn't available but was latter defined + if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { + cachedSetTimeout = setTimeout; + return setTimeout(fun, 0); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedSetTimeout(fun, 0); + } catch(e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedSetTimeout.call(null, fun, 0); + } catch(e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error + return cachedSetTimeout.call(this, fun, 0); + } + } + + + } + function runClearTimeout(marker) { + if (cachedClearTimeout === clearTimeout) { + //normal enviroments in sane situations + return clearTimeout(marker); + } + // if clearTimeout wasn't available but was latter defined + if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { + cachedClearTimeout = clearTimeout; + return clearTimeout(marker); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedClearTimeout(marker); + } catch (e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedClearTimeout.call(null, marker); + } catch (e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. + // Some versions of I.E. have different rules for clearTimeout vs setTimeout + return cachedClearTimeout.call(this, marker); + } + } + + + + } + var queue = []; + var draining = false; + var currentQueue; + var queueIndex = -1; + + function cleanUpNextTick() { + if (!draining || !currentQueue) { + return; + } + draining = false; + if (currentQueue.length) { + queue = currentQueue.concat(queue); + } else { + queueIndex = -1; + } + if (queue.length) { + drainQueue(); + } + } + + function drainQueue() { + if (draining) { + return; + } + var timeout = runTimeout(cleanUpNextTick); + draining = true; + + var len = queue.length; + while(len) { + currentQueue = queue; + queue = []; + while (++queueIndex < len) { + if (currentQueue) { + currentQueue[queueIndex].run(); + } + } + queueIndex = -1; + len = queue.length; + } + currentQueue = null; + draining = false; + runClearTimeout(timeout); + } + + process.nextTick = function (fun) { + var args = new Array(arguments.length - 1); + if (arguments.length > 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + queue.push(new Item(fun, args)); + if (queue.length === 1 && !draining) { + runTimeout(drainQueue); + } + }; + + // v8 likes predictible objects + function Item(fun, array) { + this.fun = fun; + this.array = array; + } + Item.prototype.run = function () { + this.fun.apply(null, this.array); + }; + process.title = 'browser'; + process.browser = true; + process.env = {}; + process.argv = []; + process.version = ''; // empty string to avoid regexp issues + process.versions = {}; + + function noop() {} + + process.on = noop; + process.addListener = noop; + process.once = noop; + process.off = noop; + process.removeListener = noop; + process.removeAllListeners = noop; + process.emit = noop; + + process.binding = function (name) { + throw new Error('process.binding is not supported'); + }; + + process.cwd = function () { return '/' }; + process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); + }; + process.umask = function() { return 0; }; + + +/***/ }, +/* 5 */ +/***/ function(module, exports, __webpack_require__) { + + + /** + * This is the common logic for both the Node.js and web browser + * implementations of `debug()`. + * + * Expose `debug()` as the module. + */ + + exports = module.exports = debug.debug = debug; + exports.coerce = coerce; + exports.disable = disable; + exports.enable = enable; + exports.enabled = enabled; + exports.humanize = __webpack_require__(6); + + /** + * The currently active debug mode names, and names to skip. + */ + + exports.names = []; + exports.skips = []; + + /** + * Map of special "%n" handling functions, for the debug "format" argument. + * + * Valid key names are a single, lowercased letter, i.e. "n". + */ + + exports.formatters = {}; + + /** + * Previously assigned color. + */ + + var prevColor = 0; + + /** + * Previous log timestamp. + */ + + var prevTime; + + /** + * Select a color. + * + * @return {Number} + * @api private + */ + + function selectColor() { + return exports.colors[prevColor++ % exports.colors.length]; + } + + /** + * Create a debugger with the given `namespace`. + * + * @param {String} namespace + * @return {Function} + * @api public + */ + + function debug(namespace) { + + // define the `disabled` version + function disabled() { + } + disabled.enabled = false; + + // define the `enabled` version + function enabled() { + + var self = enabled; + + // set `diff` timestamp + var curr = +new Date(); + var ms = curr - (prevTime || curr); + self.diff = ms; + self.prev = prevTime; + self.curr = curr; + prevTime = curr; + + // add the `color` if not set + if (null == self.useColors) self.useColors = exports.useColors(); + if (null == self.color && self.useColors) self.color = selectColor(); + + var args = new Array(arguments.length); + for (var i = 0; i < args.length; i++) { + args[i] = arguments[i]; + } + + args[0] = exports.coerce(args[0]); + + if ('string' !== typeof args[0]) { + // anything else let's inspect with %o + args = ['%o'].concat(args); + } + + // apply any `formatters` transformations + var index = 0; + args[0] = args[0].replace(/%([a-z%])/g, function(match, format) { + // if we encounter an escaped % then don't increase the array index + if (match === '%%') return match; + index++; + var formatter = exports.formatters[format]; + if ('function' === typeof formatter) { + var val = args[index]; + match = formatter.call(self, val); + + // now we need to remove `args[index]` since it's inlined in the `format` + args.splice(index, 1); + index--; + } + return match; + }); + + // apply env-specific formatting + args = exports.formatArgs.apply(self, args); + + var logFn = enabled.log || exports.log || console.log.bind(console); + logFn.apply(self, args); + } + enabled.enabled = true; + + var fn = exports.enabled(namespace) ? enabled : disabled; + + fn.namespace = namespace; + + return fn; + } + + /** + * Enables a debug mode by namespaces. This can include modes + * separated by a colon and wildcards. + * + * @param {String} namespaces + * @api public + */ + + function enable(namespaces) { + exports.save(namespaces); + + var split = (namespaces || '').split(/[\s,]+/); + var len = split.length; + + for (var i = 0; i < len; i++) { + if (!split[i]) continue; // ignore empty strings + namespaces = split[i].replace(/[\\^$+?.()|[\]{}]/g, '\\$&').replace(/\*/g, '.*?'); + if (namespaces[0] === '-') { + exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); + } else { + exports.names.push(new RegExp('^' + namespaces + '$')); + } + } + } + + /** + * Disable debug output. + * + * @api public + */ + + function disable() { + exports.enable(''); + } + + /** + * Returns true if the given mode name is enabled, false otherwise. + * + * @param {String} name + * @return {Boolean} + * @api public + */ + + function enabled(name) { + var i, len; + for (i = 0, len = exports.skips.length; i < len; i++) { + if (exports.skips[i].test(name)) { + return false; + } + } + for (i = 0, len = exports.names.length; i < len; i++) { + if (exports.names[i].test(name)) { + return true; + } + } + return false; + } + + /** + * Coerce `val`. + * + * @param {Mixed} val + * @return {Mixed} + * @api private + */ + + function coerce(val) { + if (val instanceof Error) return val.stack || val.message; + return val; + } + + +/***/ }, +/* 6 */ +/***/ function(module, exports) { + + /** + * Helpers. + */ + + var s = 1000 + var m = s * 60 + var h = m * 60 + var d = h * 24 + var y = d * 365.25 + + /** + * Parse or format the given `val`. + * + * Options: + * + * - `long` verbose formatting [false] + * + * @param {String|Number} val + * @param {Object} options + * @throws {Error} throw an error if val is not a non-empty string or a number + * @return {String|Number} + * @api public + */ + + module.exports = function (val, options) { + options = options || {} + var type = typeof val + if (type === 'string' && val.length > 0) { + return parse(val) + } else if (type === 'number' && isNaN(val) === false) { + return options.long ? + fmtLong(val) : + fmtShort(val) + } + throw new Error('val is not a non-empty string or a valid number. val=' + JSON.stringify(val)) + } + + /** + * Parse the given `str` and return milliseconds. + * + * @param {String} str + * @return {Number} + * @api private + */ + + function parse(str) { + str = String(str) + if (str.length > 10000) { + return + } + var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(str) + if (!match) { + return + } + var n = parseFloat(match[1]) + var type = (match[2] || 'ms').toLowerCase() + switch (type) { + case 'years': + case 'year': + case 'yrs': + case 'yr': + case 'y': + return n * y + case 'days': + case 'day': + case 'd': + return n * d + case 'hours': + case 'hour': + case 'hrs': + case 'hr': + case 'h': + return n * h + case 'minutes': + case 'minute': + case 'mins': + case 'min': + case 'm': + return n * m + case 'seconds': + case 'second': + case 'secs': + case 'sec': + case 's': + return n * s + case 'milliseconds': + case 'millisecond': + case 'msecs': + case 'msec': + case 'ms': + return n + default: + return undefined + } + } + + /** + * Short format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + + function fmtShort(ms) { + if (ms >= d) { + return Math.round(ms / d) + 'd' + } + if (ms >= h) { + return Math.round(ms / h) + 'h' + } + if (ms >= m) { + return Math.round(ms / m) + 'm' + } + if (ms >= s) { + return Math.round(ms / s) + 's' + } + return ms + 'ms' + } + + /** + * Long format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + + function fmtLong(ms) { + return plural(ms, d, 'day') || + plural(ms, h, 'hour') || + plural(ms, m, 'minute') || + plural(ms, s, 'second') || + ms + ' ms' + } + + /** + * Pluralization helper. + */ + + function plural(ms, n, name) { + if (ms < n) { + return + } + if (ms < n * 1.5) { + return Math.floor(ms / n) + ' ' + name + } + return Math.ceil(ms / n) + ' ' + name + 's' + } + + +/***/ }, +/* 7 */ +/***/ function(module, exports, __webpack_require__) { + + + /** + * Module dependencies. + */ + + var debug = __webpack_require__(8)('socket.io-parser'); + var json = __webpack_require__(11); + var Emitter = __webpack_require__(13); + var binary = __webpack_require__(14); + var isBuf = __webpack_require__(16); + + /** + * Protocol version. + * + * @api public + */ + + exports.protocol = 4; + + /** + * Packet types. + * + * @api public + */ + + exports.types = [ + 'CONNECT', + 'DISCONNECT', + 'EVENT', + 'ACK', + 'ERROR', + 'BINARY_EVENT', + 'BINARY_ACK' + ]; + + /** + * Packet type `connect`. + * + * @api public + */ + + exports.CONNECT = 0; + + /** + * Packet type `disconnect`. + * + * @api public + */ + + exports.DISCONNECT = 1; + + /** + * Packet type `event`. + * + * @api public + */ + + exports.EVENT = 2; + + /** + * Packet type `ack`. + * + * @api public + */ + + exports.ACK = 3; + + /** + * Packet type `error`. + * + * @api public + */ + + exports.ERROR = 4; + + /** + * Packet type 'binary event' + * + * @api public + */ + + exports.BINARY_EVENT = 5; + + /** + * Packet type `binary ack`. For acks with binary arguments. + * + * @api public + */ + + exports.BINARY_ACK = 6; + + /** + * Encoder constructor. + * + * @api public + */ + + exports.Encoder = Encoder; + + /** + * Decoder constructor. + * + * @api public + */ + + exports.Decoder = Decoder; + + /** + * A socket.io Encoder instance + * + * @api public + */ + + function Encoder() {} + + /** + * Encode a packet as a single string if non-binary, or as a + * buffer sequence, depending on packet type. + * + * @param {Object} obj - packet object + * @param {Function} callback - function to handle encodings (likely engine.write) + * @return Calls callback with Array of encodings + * @api public + */ + + Encoder.prototype.encode = function(obj, callback){ + debug('encoding packet %j', obj); + + if (exports.BINARY_EVENT == obj.type || exports.BINARY_ACK == obj.type) { + encodeAsBinary(obj, callback); + } + else { + var encoding = encodeAsString(obj); + callback([encoding]); + } + }; + + /** + * Encode packet as string. + * + * @param {Object} packet + * @return {String} encoded + * @api private + */ + + function encodeAsString(obj) { + var str = ''; + var nsp = false; + + // first is type + str += obj.type; + + // attachments if we have them + if (exports.BINARY_EVENT == obj.type || exports.BINARY_ACK == obj.type) { + str += obj.attachments; + str += '-'; + } + + // if we have a namespace other than `/` + // we append it followed by a comma `,` + if (obj.nsp && '/' != obj.nsp) { + nsp = true; + str += obj.nsp; + } + + // immediately followed by the id + if (null != obj.id) { + if (nsp) { + str += ','; + nsp = false; + } + str += obj.id; + } + + // json data + if (null != obj.data) { + if (nsp) str += ','; + str += json.stringify(obj.data); + } + + debug('encoded %j as %s', obj, str); + return str; + } + + /** + * Encode packet as 'buffer sequence' by removing blobs, and + * deconstructing packet into object with placeholders and + * a list of buffers. + * + * @param {Object} packet + * @return {Buffer} encoded + * @api private + */ + + function encodeAsBinary(obj, callback) { + + function writeEncoding(bloblessData) { + var deconstruction = binary.deconstructPacket(bloblessData); + var pack = encodeAsString(deconstruction.packet); + var buffers = deconstruction.buffers; + + buffers.unshift(pack); // add packet info to beginning of data list + callback(buffers); // write all the buffers + } + + binary.removeBlobs(obj, writeEncoding); + } + + /** + * A socket.io Decoder instance + * + * @return {Object} decoder + * @api public + */ + + function Decoder() { + this.reconstructor = null; + } + + /** + * Mix in `Emitter` with Decoder. + */ + + Emitter(Decoder.prototype); + + /** + * Decodes an ecoded packet string into packet JSON. + * + * @param {String} obj - encoded packet + * @return {Object} packet + * @api public + */ + + Decoder.prototype.add = function(obj) { + var packet; + if ('string' == typeof obj) { + packet = decodeString(obj); + if (exports.BINARY_EVENT == packet.type || exports.BINARY_ACK == packet.type) { // binary packet's json + this.reconstructor = new BinaryReconstructor(packet); + + // no attachments, labeled binary but no binary data to follow + if (this.reconstructor.reconPack.attachments === 0) { + this.emit('decoded', packet); + } + } else { // non-binary full packet + this.emit('decoded', packet); + } + } + else if (isBuf(obj) || obj.base64) { // raw binary data + if (!this.reconstructor) { + throw new Error('got binary data when not reconstructing a packet'); + } else { + packet = this.reconstructor.takeBinaryData(obj); + if (packet) { // received final buffer + this.reconstructor = null; + this.emit('decoded', packet); + } + } + } + else { + throw new Error('Unknown type: ' + obj); + } + }; + + /** + * Decode a packet String (JSON data) + * + * @param {String} str + * @return {Object} packet + * @api private + */ + + function decodeString(str) { + var p = {}; + var i = 0; + + // look up type + p.type = Number(str.charAt(0)); + if (null == exports.types[p.type]) return error(); + + // look up attachments if type binary + if (exports.BINARY_EVENT == p.type || exports.BINARY_ACK == p.type) { + var buf = ''; + while (str.charAt(++i) != '-') { + buf += str.charAt(i); + if (i == str.length) break; + } + if (buf != Number(buf) || str.charAt(i) != '-') { + throw new Error('Illegal attachments'); + } + p.attachments = Number(buf); + } + + // look up namespace (if any) + if ('/' == str.charAt(i + 1)) { + p.nsp = ''; + while (++i) { + var c = str.charAt(i); + if (',' == c) break; + p.nsp += c; + if (i == str.length) break; + } + } else { + p.nsp = '/'; + } + + // look up id + var next = str.charAt(i + 1); + if ('' !== next && Number(next) == next) { + p.id = ''; + while (++i) { + var c = str.charAt(i); + if (null == c || Number(c) != c) { + --i; + break; + } + p.id += str.charAt(i); + if (i == str.length) break; + } + p.id = Number(p.id); + } + + // look up json data + if (str.charAt(++i)) { + p = tryParse(p, str.substr(i)); + } + + debug('decoded %s as %j', str, p); + return p; + } + + function tryParse(p, str) { + try { + p.data = json.parse(str); + } catch(e){ + return error(); + } + return p; + }; + + /** + * Deallocates a parser's resources + * + * @api public + */ + + Decoder.prototype.destroy = function() { + if (this.reconstructor) { + this.reconstructor.finishedReconstruction(); + } + }; + + /** + * A manager of a binary event's 'buffer sequence'. Should + * be constructed whenever a packet of type BINARY_EVENT is + * decoded. + * + * @param {Object} packet + * @return {BinaryReconstructor} initialized reconstructor + * @api private + */ + + function BinaryReconstructor(packet) { + this.reconPack = packet; + this.buffers = []; + } + + /** + * Method to be called when binary data received from connection + * after a BINARY_EVENT packet. + * + * @param {Buffer | ArrayBuffer} binData - the raw binary data received + * @return {null | Object} returns null if more binary data is expected or + * a reconstructed packet object if all buffers have been received. + * @api private + */ + + BinaryReconstructor.prototype.takeBinaryData = function(binData) { + this.buffers.push(binData); + if (this.buffers.length == this.reconPack.attachments) { // done with buffer list + var packet = binary.reconstructPacket(this.reconPack, this.buffers); + this.finishedReconstruction(); + return packet; + } + return null; + }; + + /** + * Cleans up binary packet reconstruction variables. + * + * @api private + */ + + BinaryReconstructor.prototype.finishedReconstruction = function() { + this.reconPack = null; + this.buffers = []; + }; + + function error(data){ + return { + type: exports.ERROR, + data: 'parser error' + }; + } + + +/***/ }, +/* 8 */ +/***/ function(module, exports, __webpack_require__) { + + + /** + * This is the web browser implementation of `debug()`. + * + * Expose `debug()` as the module. + */ + + exports = module.exports = __webpack_require__(9); + exports.log = log; + exports.formatArgs = formatArgs; + exports.save = save; + exports.load = load; + exports.useColors = useColors; + exports.storage = 'undefined' != typeof chrome + && 'undefined' != typeof chrome.storage + ? chrome.storage.local + : localstorage(); + + /** + * Colors. + */ + + exports.colors = [ + 'lightseagreen', + 'forestgreen', + 'goldenrod', + 'dodgerblue', + 'darkorchid', + 'crimson' + ]; + + /** + * Currently only WebKit-based Web Inspectors, Firefox >= v31, + * and the Firebug extension (any Firefox version) are known + * to support "%c" CSS customizations. + * + * TODO: add a `localStorage` variable to explicitly enable/disable colors + */ + + function useColors() { + // is webkit? http://stackoverflow.com/a/16459606/376773 + return ('WebkitAppearance' in document.documentElement.style) || + // is firebug? http://stackoverflow.com/a/398120/376773 + (window.console && (console.firebug || (console.exception && console.table))) || + // is firefox >= v31? + // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages + (navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31); + } + + /** + * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. + */ + + exports.formatters.j = function(v) { + return JSON.stringify(v); + }; + + + /** + * Colorize log arguments if enabled. + * + * @api public + */ + + function formatArgs() { + var args = arguments; + var useColors = this.useColors; + + args[0] = (useColors ? '%c' : '') + + this.namespace + + (useColors ? ' %c' : ' ') + + args[0] + + (useColors ? '%c ' : ' ') + + '+' + exports.humanize(this.diff); + + if (!useColors) return args; + + var c = 'color: ' + this.color; + args = [args[0], c, 'color: inherit'].concat(Array.prototype.slice.call(args, 1)); + + // the final "%c" is somewhat tricky, because there could be other + // arguments passed either before or after the %c, so we need to + // figure out the correct index to insert the CSS into + var index = 0; + var lastC = 0; + args[0].replace(/%[a-z%]/g, function(match) { + if ('%%' === match) return; + index++; + if ('%c' === match) { + // we only are interested in the *last* %c + // (the user may have provided their own) + lastC = index; + } + }); + + args.splice(lastC, 0, c); + return args; + } + + /** + * Invokes `console.log()` when available. + * No-op when `console.log` is not a "function". + * + * @api public + */ + + function log() { + // this hackery is required for IE8/9, where + // the `console.log` function doesn't have 'apply' + return 'object' === typeof console + && console.log + && Function.prototype.apply.call(console.log, console, arguments); + } + + /** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ + + function save(namespaces) { + try { + if (null == namespaces) { + exports.storage.removeItem('debug'); + } else { + exports.storage.debug = namespaces; + } + } catch(e) {} + } + + /** + * Load `namespaces`. + * + * @return {String} returns the previously persisted debug modes + * @api private + */ + + function load() { + var r; + try { + r = exports.storage.debug; + } catch(e) {} + return r; + } + + /** + * Enable namespaces listed in `localStorage.debug` initially. + */ + + exports.enable(load()); + + /** + * Localstorage attempts to return the localstorage. + * + * This is necessary because safari throws + * when a user disables cookies/localstorage + * and you attempt to access it. + * + * @return {LocalStorage} + * @api private + */ + + function localstorage(){ + try { + return window.localStorage; + } catch (e) {} + } + + +/***/ }, +/* 9 */ +/***/ function(module, exports, __webpack_require__) { + + + /** + * This is the common logic for both the Node.js and web browser + * implementations of `debug()`. + * + * Expose `debug()` as the module. + */ + + exports = module.exports = debug; + exports.coerce = coerce; + exports.disable = disable; + exports.enable = enable; + exports.enabled = enabled; + exports.humanize = __webpack_require__(10); + + /** + * The currently active debug mode names, and names to skip. + */ + + exports.names = []; + exports.skips = []; + + /** + * Map of special "%n" handling functions, for the debug "format" argument. + * + * Valid key names are a single, lowercased letter, i.e. "n". + */ + + exports.formatters = {}; + + /** + * Previously assigned color. + */ + + var prevColor = 0; + + /** + * Previous log timestamp. + */ + + var prevTime; + + /** + * Select a color. + * + * @return {Number} + * @api private + */ + + function selectColor() { + return exports.colors[prevColor++ % exports.colors.length]; + } + + /** + * Create a debugger with the given `namespace`. + * + * @param {String} namespace + * @return {Function} + * @api public + */ + + function debug(namespace) { + + // define the `disabled` version + function disabled() { + } + disabled.enabled = false; + + // define the `enabled` version + function enabled() { + + var self = enabled; + + // set `diff` timestamp + var curr = +new Date(); + var ms = curr - (prevTime || curr); + self.diff = ms; + self.prev = prevTime; + self.curr = curr; + prevTime = curr; + + // add the `color` if not set + if (null == self.useColors) self.useColors = exports.useColors(); + if (null == self.color && self.useColors) self.color = selectColor(); + + var args = Array.prototype.slice.call(arguments); + + args[0] = exports.coerce(args[0]); + + if ('string' !== typeof args[0]) { + // anything else let's inspect with %o + args = ['%o'].concat(args); + } + + // apply any `formatters` transformations + var index = 0; + args[0] = args[0].replace(/%([a-z%])/g, function(match, format) { + // if we encounter an escaped % then don't increase the array index + if (match === '%%') return match; + index++; + var formatter = exports.formatters[format]; + if ('function' === typeof formatter) { + var val = args[index]; + match = formatter.call(self, val); + + // now we need to remove `args[index]` since it's inlined in the `format` + args.splice(index, 1); + index--; + } + return match; + }); + + if ('function' === typeof exports.formatArgs) { + args = exports.formatArgs.apply(self, args); + } + var logFn = enabled.log || exports.log || console.log.bind(console); + logFn.apply(self, args); + } + enabled.enabled = true; + + var fn = exports.enabled(namespace) ? enabled : disabled; + + fn.namespace = namespace; + + return fn; + } + + /** + * Enables a debug mode by namespaces. This can include modes + * separated by a colon and wildcards. + * + * @param {String} namespaces + * @api public + */ + + function enable(namespaces) { + exports.save(namespaces); + + var split = (namespaces || '').split(/[\s,]+/); + var len = split.length; + + for (var i = 0; i < len; i++) { + if (!split[i]) continue; // ignore empty strings + namespaces = split[i].replace(/\*/g, '.*?'); + if (namespaces[0] === '-') { + exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); + } else { + exports.names.push(new RegExp('^' + namespaces + '$')); + } + } + } + + /** + * Disable debug output. + * + * @api public + */ + + function disable() { + exports.enable(''); + } + + /** + * Returns true if the given mode name is enabled, false otherwise. + * + * @param {String} name + * @return {Boolean} + * @api public + */ + + function enabled(name) { + var i, len; + for (i = 0, len = exports.skips.length; i < len; i++) { + if (exports.skips[i].test(name)) { + return false; + } + } + for (i = 0, len = exports.names.length; i < len; i++) { + if (exports.names[i].test(name)) { + return true; + } + } + return false; + } + + /** + * Coerce `val`. + * + * @param {Mixed} val + * @return {Mixed} + * @api private + */ + + function coerce(val) { + if (val instanceof Error) return val.stack || val.message; + return val; + } + + +/***/ }, +/* 10 */ +/***/ function(module, exports) { + + /** + * Helpers. + */ + + var s = 1000; + var m = s * 60; + var h = m * 60; + var d = h * 24; + var y = d * 365.25; + + /** + * Parse or format the given `val`. + * + * Options: + * + * - `long` verbose formatting [false] + * + * @param {String|Number} val + * @param {Object} options + * @return {String|Number} + * @api public + */ + + module.exports = function(val, options){ + options = options || {}; + if ('string' == typeof val) return parse(val); + return options.long + ? long(val) + : short(val); + }; + + /** + * Parse the given `str` and return milliseconds. + * + * @param {String} str + * @return {Number} + * @api private + */ + + function parse(str) { + str = '' + str; + if (str.length > 10000) return; + var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(str); + if (!match) return; + var n = parseFloat(match[1]); + var type = (match[2] || 'ms').toLowerCase(); + switch (type) { + case 'years': + case 'year': + case 'yrs': + case 'yr': + case 'y': + return n * y; + case 'days': + case 'day': + case 'd': + return n * d; + case 'hours': + case 'hour': + case 'hrs': + case 'hr': + case 'h': + return n * h; + case 'minutes': + case 'minute': + case 'mins': + case 'min': + case 'm': + return n * m; + case 'seconds': + case 'second': + case 'secs': + case 'sec': + case 's': + return n * s; + case 'milliseconds': + case 'millisecond': + case 'msecs': + case 'msec': + case 'ms': + return n; + } + } + + /** + * Short format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + + function short(ms) { + if (ms >= d) return Math.round(ms / d) + 'd'; + if (ms >= h) return Math.round(ms / h) + 'h'; + if (ms >= m) return Math.round(ms / m) + 'm'; + if (ms >= s) return Math.round(ms / s) + 's'; + return ms + 'ms'; + } + + /** + * Long format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + + function long(ms) { + return plural(ms, d, 'day') + || plural(ms, h, 'hour') + || plural(ms, m, 'minute') + || plural(ms, s, 'second') + || ms + ' ms'; + } + + /** + * Pluralization helper. + */ + + function plural(ms, n, name) { + if (ms < n) return; + if (ms < n * 1.5) return Math.floor(ms / n) + ' ' + name; + return Math.ceil(ms / n) + ' ' + name + 's'; + } + + +/***/ }, +/* 11 */ +/***/ function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(module, global) {/*** IMPORTS FROM imports-loader ***/ + var define = false; + + /*! JSON v3.3.2 | http://bestiejs.github.io/json3 | Copyright 2012-2014, Kit Cambridge | http://kit.mit-license.org */ + ;(function () { + // Detect the `define` function exposed by asynchronous module loaders. The + // strict `define` check is necessary for compatibility with `r.js`. + var isLoader = typeof define === "function" && define.amd; + + // A set of types used to distinguish objects from primitives. + var objectTypes = { + "function": true, + "object": true + }; + + // Detect the `exports` object exposed by CommonJS implementations. + var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports; + + // Use the `global` object exposed by Node (including Browserify via + // `insert-module-globals`), Narwhal, and Ringo as the default context, + // and the `window` object in browsers. Rhino exports a `global` function + // instead. + var root = objectTypes[typeof window] && window || this, + freeGlobal = freeExports && objectTypes[typeof module] && module && !module.nodeType && typeof global == "object" && global; + + if (freeGlobal && (freeGlobal["global"] === freeGlobal || freeGlobal["window"] === freeGlobal || freeGlobal["self"] === freeGlobal)) { + root = freeGlobal; + } + + // Public: Initializes JSON 3 using the given `context` object, attaching the + // `stringify` and `parse` functions to the specified `exports` object. + function runInContext(context, exports) { + context || (context = root["Object"]()); + exports || (exports = root["Object"]()); + + // Native constructor aliases. + var Number = context["Number"] || root["Number"], + String = context["String"] || root["String"], + Object = context["Object"] || root["Object"], + Date = context["Date"] || root["Date"], + SyntaxError = context["SyntaxError"] || root["SyntaxError"], + TypeError = context["TypeError"] || root["TypeError"], + Math = context["Math"] || root["Math"], + nativeJSON = context["JSON"] || root["JSON"]; + + // Delegate to the native `stringify` and `parse` implementations. + if (typeof nativeJSON == "object" && nativeJSON) { + exports.stringify = nativeJSON.stringify; + exports.parse = nativeJSON.parse; + } + + // Convenience aliases. + var objectProto = Object.prototype, + getClass = objectProto.toString, + isProperty, forEach, undef; + + // Test the `Date#getUTC*` methods. Based on work by @Yaffle. + var isExtended = new Date(-3509827334573292); + try { + // The `getUTCFullYear`, `Month`, and `Date` methods return nonsensical + // results for certain dates in Opera >= 10.53. + isExtended = isExtended.getUTCFullYear() == -109252 && isExtended.getUTCMonth() === 0 && isExtended.getUTCDate() === 1 && + // Safari < 2.0.2 stores the internal millisecond time value correctly, + // but clips the values returned by the date methods to the range of + // signed 32-bit integers ([-2 ** 31, 2 ** 31 - 1]). + isExtended.getUTCHours() == 10 && isExtended.getUTCMinutes() == 37 && isExtended.getUTCSeconds() == 6 && isExtended.getUTCMilliseconds() == 708; + } catch (exception) {} + + // Internal: Determines whether the native `JSON.stringify` and `parse` + // implementations are spec-compliant. Based on work by Ken Snyder. + function has(name) { + if (has[name] !== undef) { + // Return cached feature test result. + return has[name]; + } + var isSupported; + if (name == "bug-string-char-index") { + // IE <= 7 doesn't support accessing string characters using square + // bracket notation. IE 8 only supports this for primitives. + isSupported = "a"[0] != "a"; + } else if (name == "json") { + // Indicates whether both `JSON.stringify` and `JSON.parse` are + // supported. + isSupported = has("json-stringify") && has("json-parse"); + } else { + var value, serialized = '{"a":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}'; + // Test `JSON.stringify`. + if (name == "json-stringify") { + var stringify = exports.stringify, stringifySupported = typeof stringify == "function" && isExtended; + if (stringifySupported) { + // A test function object with a custom `toJSON` method. + (value = function () { + return 1; + }).toJSON = value; + try { + stringifySupported = + // Firefox 3.1b1 and b2 serialize string, number, and boolean + // primitives as object literals. + stringify(0) === "0" && + // FF 3.1b1, b2, and JSON 2 serialize wrapped primitives as object + // literals. + stringify(new Number()) === "0" && + stringify(new String()) == '""' && + // FF 3.1b1, 2 throw an error if the value is `null`, `undefined`, or + // does not define a canonical JSON representation (this applies to + // objects with `toJSON` properties as well, *unless* they are nested + // within an object or array). + stringify(getClass) === undef && + // IE 8 serializes `undefined` as `"undefined"`. Safari <= 5.1.7 and + // FF 3.1b3 pass this test. + stringify(undef) === undef && + // Safari <= 5.1.7 and FF 3.1b3 throw `Error`s and `TypeError`s, + // respectively, if the value is omitted entirely. + stringify() === undef && + // FF 3.1b1, 2 throw an error if the given value is not a number, + // string, array, object, Boolean, or `null` literal. This applies to + // objects with custom `toJSON` methods as well, unless they are nested + // inside object or array literals. YUI 3.0.0b1 ignores custom `toJSON` + // methods entirely. + stringify(value) === "1" && + stringify([value]) == "[1]" && + // Prototype <= 1.6.1 serializes `[undefined]` as `"[]"` instead of + // `"[null]"`. + stringify([undef]) == "[null]" && + // YUI 3.0.0b1 fails to serialize `null` literals. + stringify(null) == "null" && + // FF 3.1b1, 2 halts serialization if an array contains a function: + // `[1, true, getClass, 1]` serializes as "[1,true,],". FF 3.1b3 + // elides non-JSON values from objects and arrays, unless they + // define custom `toJSON` methods. + stringify([undef, getClass, null]) == "[null,null,null]" && + // Simple serialization test. FF 3.1b1 uses Unicode escape sequences + // where character escape codes are expected (e.g., `\b` => `\u0008`). + stringify({ "a": [value, true, false, null, "\x00\b\n\f\r\t"] }) == serialized && + // FF 3.1b1 and b2 ignore the `filter` and `width` arguments. + stringify(null, value) === "1" && + stringify([1, 2], null, 1) == "[\n 1,\n 2\n]" && + // JSON 2, Prototype <= 1.7, and older WebKit builds incorrectly + // serialize extended years. + stringify(new Date(-8.64e15)) == '"-271821-04-20T00:00:00.000Z"' && + // The milliseconds are optional in ES 5, but required in 5.1. + stringify(new Date(8.64e15)) == '"+275760-09-13T00:00:00.000Z"' && + // Firefox <= 11.0 incorrectly serializes years prior to 0 as negative + // four-digit years instead of six-digit years. Credits: @Yaffle. + stringify(new Date(-621987552e5)) == '"-000001-01-01T00:00:00.000Z"' && + // Safari <= 5.1.5 and Opera >= 10.53 incorrectly serialize millisecond + // values less than 1000. Credits: @Yaffle. + stringify(new Date(-1)) == '"1969-12-31T23:59:59.999Z"'; + } catch (exception) { + stringifySupported = false; + } + } + isSupported = stringifySupported; + } + // Test `JSON.parse`. + if (name == "json-parse") { + var parse = exports.parse; + if (typeof parse == "function") { + try { + // FF 3.1b1, b2 will throw an exception if a bare literal is provided. + // Conforming implementations should also coerce the initial argument to + // a string prior to parsing. + if (parse("0") === 0 && !parse(false)) { + // Simple parsing test. + value = parse(serialized); + var parseSupported = value["a"].length == 5 && value["a"][0] === 1; + if (parseSupported) { + try { + // Safari <= 5.1.2 and FF 3.1b1 allow unescaped tabs in strings. + parseSupported = !parse('"\t"'); + } catch (exception) {} + if (parseSupported) { + try { + // FF 4.0 and 4.0.1 allow leading `+` signs and leading + // decimal points. FF 4.0, 4.0.1, and IE 9-10 also allow + // certain octal literals. + parseSupported = parse("01") !== 1; + } catch (exception) {} + } + if (parseSupported) { + try { + // FF 4.0, 4.0.1, and Rhino 1.7R3-R4 allow trailing decimal + // points. These environments, along with FF 3.1b1 and 2, + // also allow trailing commas in JSON objects and arrays. + parseSupported = parse("1.") !== 1; + } catch (exception) {} + } + } + } + } catch (exception) { + parseSupported = false; + } + } + isSupported = parseSupported; + } + } + return has[name] = !!isSupported; + } + + if (!has("json")) { + // Common `[[Class]]` name aliases. + var functionClass = "[object Function]", + dateClass = "[object Date]", + numberClass = "[object Number]", + stringClass = "[object String]", + arrayClass = "[object Array]", + booleanClass = "[object Boolean]"; + + // Detect incomplete support for accessing string characters by index. + var charIndexBuggy = has("bug-string-char-index"); + + // Define additional utility methods if the `Date` methods are buggy. + if (!isExtended) { + var floor = Math.floor; + // A mapping between the months of the year and the number of days between + // January 1st and the first of the respective month. + var Months = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]; + // Internal: Calculates the number of days between the Unix epoch and the + // first day of the given month. + var getDay = function (year, month) { + return Months[month] + 365 * (year - 1970) + floor((year - 1969 + (month = +(month > 1))) / 4) - floor((year - 1901 + month) / 100) + floor((year - 1601 + month) / 400); + }; + } + + // Internal: Determines if a property is a direct property of the given + // object. Delegates to the native `Object#hasOwnProperty` method. + if (!(isProperty = objectProto.hasOwnProperty)) { + isProperty = function (property) { + var members = {}, constructor; + if ((members.__proto__ = null, members.__proto__ = { + // The *proto* property cannot be set multiple times in recent + // versions of Firefox and SeaMonkey. + "toString": 1 + }, members).toString != getClass) { + // Safari <= 2.0.3 doesn't implement `Object#hasOwnProperty`, but + // supports the mutable *proto* property. + isProperty = function (property) { + // Capture and break the object's prototype chain (see section 8.6.2 + // of the ES 5.1 spec). The parenthesized expression prevents an + // unsafe transformation by the Closure Compiler. + var original = this.__proto__, result = property in (this.__proto__ = null, this); + // Restore the original prototype chain. + this.__proto__ = original; + return result; + }; + } else { + // Capture a reference to the top-level `Object` constructor. + constructor = members.constructor; + // Use the `constructor` property to simulate `Object#hasOwnProperty` in + // other environments. + isProperty = function (property) { + var parent = (this.constructor || constructor).prototype; + return property in this && !(property in parent && this[property] === parent[property]); + }; + } + members = null; + return isProperty.call(this, property); + }; + } + + // Internal: Normalizes the `for...in` iteration algorithm across + // environments. Each enumerated key is yielded to a `callback` function. + forEach = function (object, callback) { + var size = 0, Properties, members, property; + + // Tests for bugs in the current environment's `for...in` algorithm. The + // `valueOf` property inherits the non-enumerable flag from + // `Object.prototype` in older versions of IE, Netscape, and Mozilla. + (Properties = function () { + this.valueOf = 0; + }).prototype.valueOf = 0; + + // Iterate over a new instance of the `Properties` class. + members = new Properties(); + for (property in members) { + // Ignore all properties inherited from `Object.prototype`. + if (isProperty.call(members, property)) { + size++; + } + } + Properties = members = null; + + // Normalize the iteration algorithm. + if (!size) { + // A list of non-enumerable properties inherited from `Object.prototype`. + members = ["valueOf", "toString", "toLocaleString", "propertyIsEnumerable", "isPrototypeOf", "hasOwnProperty", "constructor"]; + // IE <= 8, Mozilla 1.0, and Netscape 6.2 ignore shadowed non-enumerable + // properties. + forEach = function (object, callback) { + var isFunction = getClass.call(object) == functionClass, property, length; + var hasProperty = !isFunction && typeof object.constructor != "function" && objectTypes[typeof object.hasOwnProperty] && object.hasOwnProperty || isProperty; + for (property in object) { + // Gecko <= 1.0 enumerates the `prototype` property of functions under + // certain conditions; IE does not. + if (!(isFunction && property == "prototype") && hasProperty.call(object, property)) { + callback(property); + } + } + // Manually invoke the callback for each non-enumerable property. + for (length = members.length; property = members[--length]; hasProperty.call(object, property) && callback(property)); + }; + } else if (size == 2) { + // Safari <= 2.0.4 enumerates shadowed properties twice. + forEach = function (object, callback) { + // Create a set of iterated properties. + var members = {}, isFunction = getClass.call(object) == functionClass, property; + for (property in object) { + // Store each property name to prevent double enumeration. The + // `prototype` property of functions is not enumerated due to cross- + // environment inconsistencies. + if (!(isFunction && property == "prototype") && !isProperty.call(members, property) && (members[property] = 1) && isProperty.call(object, property)) { + callback(property); + } + } + }; + } else { + // No bugs detected; use the standard `for...in` algorithm. + forEach = function (object, callback) { + var isFunction = getClass.call(object) == functionClass, property, isConstructor; + for (property in object) { + if (!(isFunction && property == "prototype") && isProperty.call(object, property) && !(isConstructor = property === "constructor")) { + callback(property); + } + } + // Manually invoke the callback for the `constructor` property due to + // cross-environment inconsistencies. + if (isConstructor || isProperty.call(object, (property = "constructor"))) { + callback(property); + } + }; + } + return forEach(object, callback); + }; + + // Public: Serializes a JavaScript `value` as a JSON string. The optional + // `filter` argument may specify either a function that alters how object and + // array members are serialized, or an array of strings and numbers that + // indicates which properties should be serialized. The optional `width` + // argument may be either a string or number that specifies the indentation + // level of the output. + if (!has("json-stringify")) { + // Internal: A map of control characters and their escaped equivalents. + var Escapes = { + 92: "\\\\", + 34: '\\"', + 8: "\\b", + 12: "\\f", + 10: "\\n", + 13: "\\r", + 9: "\\t" + }; + + // Internal: Converts `value` into a zero-padded string such that its + // length is at least equal to `width`. The `width` must be <= 6. + var leadingZeroes = "000000"; + var toPaddedString = function (width, value) { + // The `|| 0` expression is necessary to work around a bug in + // Opera <= 7.54u2 where `0 == -0`, but `String(-0) !== "0"`. + return (leadingZeroes + (value || 0)).slice(-width); + }; + + // Internal: Double-quotes a string `value`, replacing all ASCII control + // characters (characters with code unit values between 0 and 31) with + // their escaped equivalents. This is an implementation of the + // `Quote(value)` operation defined in ES 5.1 section 15.12.3. + var unicodePrefix = "\\u00"; + var quote = function (value) { + var result = '"', index = 0, length = value.length, useCharIndex = !charIndexBuggy || length > 10; + var symbols = useCharIndex && (charIndexBuggy ? value.split("") : value); + for (; index < length; index++) { + var charCode = value.charCodeAt(index); + // If the character is a control character, append its Unicode or + // shorthand escape sequence; otherwise, append the character as-is. + switch (charCode) { + case 8: case 9: case 10: case 12: case 13: case 34: case 92: + result += Escapes[charCode]; + break; + default: + if (charCode < 32) { + result += unicodePrefix + toPaddedString(2, charCode.toString(16)); + break; + } + result += useCharIndex ? symbols[index] : value.charAt(index); + } + } + return result + '"'; + }; + + // Internal: Recursively serializes an object. Implements the + // `Str(key, holder)`, `JO(value)`, and `JA(value)` operations. + var serialize = function (property, object, callback, properties, whitespace, indentation, stack) { + var value, className, year, month, date, time, hours, minutes, seconds, milliseconds, results, element, index, length, prefix, result; + try { + // Necessary for host object support. + value = object[property]; + } catch (exception) {} + if (typeof value == "object" && value) { + className = getClass.call(value); + if (className == dateClass && !isProperty.call(value, "toJSON")) { + if (value > -1 / 0 && value < 1 / 0) { + // Dates are serialized according to the `Date#toJSON` method + // specified in ES 5.1 section 15.9.5.44. See section 15.9.1.15 + // for the ISO 8601 date time string format. + if (getDay) { + // Manually compute the year, month, date, hours, minutes, + // seconds, and milliseconds if the `getUTC*` methods are + // buggy. Adapted from @Yaffle's `date-shim` project. + date = floor(value / 864e5); + for (year = floor(date / 365.2425) + 1970 - 1; getDay(year + 1, 0) <= date; year++); + for (month = floor((date - getDay(year, 0)) / 30.42); getDay(year, month + 1) <= date; month++); + date = 1 + date - getDay(year, month); + // The `time` value specifies the time within the day (see ES + // 5.1 section 15.9.1.2). The formula `(A % B + B) % B` is used + // to compute `A modulo B`, as the `%` operator does not + // correspond to the `modulo` operation for negative numbers. + time = (value % 864e5 + 864e5) % 864e5; + // The hours, minutes, seconds, and milliseconds are obtained by + // decomposing the time within the day. See section 15.9.1.10. + hours = floor(time / 36e5) % 24; + minutes = floor(time / 6e4) % 60; + seconds = floor(time / 1e3) % 60; + milliseconds = time % 1e3; + } else { + year = value.getUTCFullYear(); + month = value.getUTCMonth(); + date = value.getUTCDate(); + hours = value.getUTCHours(); + minutes = value.getUTCMinutes(); + seconds = value.getUTCSeconds(); + milliseconds = value.getUTCMilliseconds(); + } + // Serialize extended years correctly. + value = (year <= 0 || year >= 1e4 ? (year < 0 ? "-" : "+") + toPaddedString(6, year < 0 ? -year : year) : toPaddedString(4, year)) + + "-" + toPaddedString(2, month + 1) + "-" + toPaddedString(2, date) + + // Months, dates, hours, minutes, and seconds should have two + // digits; milliseconds should have three. + "T" + toPaddedString(2, hours) + ":" + toPaddedString(2, minutes) + ":" + toPaddedString(2, seconds) + + // Milliseconds are optional in ES 5.0, but required in 5.1. + "." + toPaddedString(3, milliseconds) + "Z"; + } else { + value = null; + } + } else if (typeof value.toJSON == "function" && ((className != numberClass && className != stringClass && className != arrayClass) || isProperty.call(value, "toJSON"))) { + // Prototype <= 1.6.1 adds non-standard `toJSON` methods to the + // `Number`, `String`, `Date`, and `Array` prototypes. JSON 3 + // ignores all `toJSON` methods on these objects unless they are + // defined directly on an instance. + value = value.toJSON(property); + } + } + if (callback) { + // If a replacement function was provided, call it to obtain the value + // for serialization. + value = callback.call(object, property, value); + } + if (value === null) { + return "null"; + } + className = getClass.call(value); + if (className == booleanClass) { + // Booleans are represented literally. + return "" + value; + } else if (className == numberClass) { + // JSON numbers must be finite. `Infinity` and `NaN` are serialized as + // `"null"`. + return value > -1 / 0 && value < 1 / 0 ? "" + value : "null"; + } else if (className == stringClass) { + // Strings are double-quoted and escaped. + return quote("" + value); + } + // Recursively serialize objects and arrays. + if (typeof value == "object") { + // Check for cyclic structures. This is a linear search; performance + // is inversely proportional to the number of unique nested objects. + for (length = stack.length; length--;) { + if (stack[length] === value) { + // Cyclic structures cannot be serialized by `JSON.stringify`. + throw TypeError(); + } + } + // Add the object to the stack of traversed objects. + stack.push(value); + results = []; + // Save the current indentation level and indent one additional level. + prefix = indentation; + indentation += whitespace; + if (className == arrayClass) { + // Recursively serialize array elements. + for (index = 0, length = value.length; index < length; index++) { + element = serialize(index, value, callback, properties, whitespace, indentation, stack); + results.push(element === undef ? "null" : element); + } + result = results.length ? (whitespace ? "[\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "]" : ("[" + results.join(",") + "]")) : "[]"; + } else { + // Recursively serialize object members. Members are selected from + // either a user-specified list of property names, or the object + // itself. + forEach(properties || value, function (property) { + var element = serialize(property, value, callback, properties, whitespace, indentation, stack); + if (element !== undef) { + // According to ES 5.1 section 15.12.3: "If `gap` {whitespace} + // is not the empty string, let `member` {quote(property) + ":"} + // be the concatenation of `member` and the `space` character." + // The "`space` character" refers to the literal space + // character, not the `space` {width} argument provided to + // `JSON.stringify`. + results.push(quote(property) + ":" + (whitespace ? " " : "") + element); + } + }); + result = results.length ? (whitespace ? "{\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "}" : ("{" + results.join(",") + "}")) : "{}"; + } + // Remove the object from the traversed object stack. + stack.pop(); + return result; + } + }; + + // Public: `JSON.stringify`. See ES 5.1 section 15.12.3. + exports.stringify = function (source, filter, width) { + var whitespace, callback, properties, className; + if (objectTypes[typeof filter] && filter) { + if ((className = getClass.call(filter)) == functionClass) { + callback = filter; + } else if (className == arrayClass) { + // Convert the property names array into a makeshift set. + properties = {}; + for (var index = 0, length = filter.length, value; index < length; value = filter[index++], ((className = getClass.call(value)), className == stringClass || className == numberClass) && (properties[value] = 1)); + } + } + if (width) { + if ((className = getClass.call(width)) == numberClass) { + // Convert the `width` to an integer and create a string containing + // `width` number of space characters. + if ((width -= width % 1) > 0) { + for (whitespace = "", width > 10 && (width = 10); whitespace.length < width; whitespace += " "); + } + } else if (className == stringClass) { + whitespace = width.length <= 10 ? width : width.slice(0, 10); + } + } + // Opera <= 7.54u2 discards the values associated with empty string keys + // (`""`) only if they are used directly within an object member list + // (e.g., `!("" in { "": 1})`). + return serialize("", (value = {}, value[""] = source, value), callback, properties, whitespace, "", []); + }; + } + + // Public: Parses a JSON source string. + if (!has("json-parse")) { + var fromCharCode = String.fromCharCode; + + // Internal: A map of escaped control characters and their unescaped + // equivalents. + var Unescapes = { + 92: "\\", + 34: '"', + 47: "/", + 98: "\b", + 116: "\t", + 110: "\n", + 102: "\f", + 114: "\r" + }; + + // Internal: Stores the parser state. + var Index, Source; + + // Internal: Resets the parser state and throws a `SyntaxError`. + var abort = function () { + Index = Source = null; + throw SyntaxError(); + }; + + // Internal: Returns the next token, or `"$"` if the parser has reached + // the end of the source string. A token may be a string, number, `null` + // literal, or Boolean literal. + var lex = function () { + var source = Source, length = source.length, value, begin, position, isSigned, charCode; + while (Index < length) { + charCode = source.charCodeAt(Index); + switch (charCode) { + case 9: case 10: case 13: case 32: + // Skip whitespace tokens, including tabs, carriage returns, line + // feeds, and space characters. + Index++; + break; + case 123: case 125: case 91: case 93: case 58: case 44: + // Parse a punctuator token (`{`, `}`, `[`, `]`, `:`, or `,`) at + // the current position. + value = charIndexBuggy ? source.charAt(Index) : source[Index]; + Index++; + return value; + case 34: + // `"` delimits a JSON string; advance to the next character and + // begin parsing the string. String tokens are prefixed with the + // sentinel `@` character to distinguish them from punctuators and + // end-of-string tokens. + for (value = "@", Index++; Index < length;) { + charCode = source.charCodeAt(Index); + if (charCode < 32) { + // Unescaped ASCII control characters (those with a code unit + // less than the space character) are not permitted. + abort(); + } else if (charCode == 92) { + // A reverse solidus (`\`) marks the beginning of an escaped + // control character (including `"`, `\`, and `/`) or Unicode + // escape sequence. + charCode = source.charCodeAt(++Index); + switch (charCode) { + case 92: case 34: case 47: case 98: case 116: case 110: case 102: case 114: + // Revive escaped control characters. + value += Unescapes[charCode]; + Index++; + break; + case 117: + // `\u` marks the beginning of a Unicode escape sequence. + // Advance to the first character and validate the + // four-digit code point. + begin = ++Index; + for (position = Index + 4; Index < position; Index++) { + charCode = source.charCodeAt(Index); + // A valid sequence comprises four hexdigits (case- + // insensitive) that form a single hexadecimal value. + if (!(charCode >= 48 && charCode <= 57 || charCode >= 97 && charCode <= 102 || charCode >= 65 && charCode <= 70)) { + // Invalid Unicode escape sequence. + abort(); + } + } + // Revive the escaped character. + value += fromCharCode("0x" + source.slice(begin, Index)); + break; + default: + // Invalid escape sequence. + abort(); + } + } else { + if (charCode == 34) { + // An unescaped double-quote character marks the end of the + // string. + break; + } + charCode = source.charCodeAt(Index); + begin = Index; + // Optimize for the common case where a string is valid. + while (charCode >= 32 && charCode != 92 && charCode != 34) { + charCode = source.charCodeAt(++Index); + } + // Append the string as-is. + value += source.slice(begin, Index); + } + } + if (source.charCodeAt(Index) == 34) { + // Advance to the next character and return the revived string. + Index++; + return value; + } + // Unterminated string. + abort(); + default: + // Parse numbers and literals. + begin = Index; + // Advance past the negative sign, if one is specified. + if (charCode == 45) { + isSigned = true; + charCode = source.charCodeAt(++Index); + } + // Parse an integer or floating-point value. + if (charCode >= 48 && charCode <= 57) { + // Leading zeroes are interpreted as octal literals. + if (charCode == 48 && ((charCode = source.charCodeAt(Index + 1)), charCode >= 48 && charCode <= 57)) { + // Illegal octal literal. + abort(); + } + isSigned = false; + // Parse the integer component. + for (; Index < length && ((charCode = source.charCodeAt(Index)), charCode >= 48 && charCode <= 57); Index++); + // Floats cannot contain a leading decimal point; however, this + // case is already accounted for by the parser. + if (source.charCodeAt(Index) == 46) { + position = ++Index; + // Parse the decimal component. + for (; position < length && ((charCode = source.charCodeAt(position)), charCode >= 48 && charCode <= 57); position++); + if (position == Index) { + // Illegal trailing decimal. + abort(); + } + Index = position; + } + // Parse exponents. The `e` denoting the exponent is + // case-insensitive. + charCode = source.charCodeAt(Index); + if (charCode == 101 || charCode == 69) { + charCode = source.charCodeAt(++Index); + // Skip past the sign following the exponent, if one is + // specified. + if (charCode == 43 || charCode == 45) { + Index++; + } + // Parse the exponential component. + for (position = Index; position < length && ((charCode = source.charCodeAt(position)), charCode >= 48 && charCode <= 57); position++); + if (position == Index) { + // Illegal empty exponent. + abort(); + } + Index = position; + } + // Coerce the parsed value to a JavaScript number. + return +source.slice(begin, Index); + } + // A negative sign may only precede numbers. + if (isSigned) { + abort(); + } + // `true`, `false`, and `null` literals. + if (source.slice(Index, Index + 4) == "true") { + Index += 4; + return true; + } else if (source.slice(Index, Index + 5) == "false") { + Index += 5; + return false; + } else if (source.slice(Index, Index + 4) == "null") { + Index += 4; + return null; + } + // Unrecognized token. + abort(); + } + } + // Return the sentinel `$` character if the parser has reached the end + // of the source string. + return "$"; + }; + + // Internal: Parses a JSON `value` token. + var get = function (value) { + var results, hasMembers; + if (value == "$") { + // Unexpected end of input. + abort(); + } + if (typeof value == "string") { + if ((charIndexBuggy ? value.charAt(0) : value[0]) == "@") { + // Remove the sentinel `@` character. + return value.slice(1); + } + // Parse object and array literals. + if (value == "[") { + // Parses a JSON array, returning a new JavaScript array. + results = []; + for (;; hasMembers || (hasMembers = true)) { + value = lex(); + // A closing square bracket marks the end of the array literal. + if (value == "]") { + break; + } + // If the array literal contains elements, the current token + // should be a comma separating the previous element from the + // next. + if (hasMembers) { + if (value == ",") { + value = lex(); + if (value == "]") { + // Unexpected trailing `,` in array literal. + abort(); + } + } else { + // A `,` must separate each array element. + abort(); + } + } + // Elisions and leading commas are not permitted. + if (value == ",") { + abort(); + } + results.push(get(value)); + } + return results; + } else if (value == "{") { + // Parses a JSON object, returning a new JavaScript object. + results = {}; + for (;; hasMembers || (hasMembers = true)) { + value = lex(); + // A closing curly brace marks the end of the object literal. + if (value == "}") { + break; + } + // If the object literal contains members, the current token + // should be a comma separator. + if (hasMembers) { + if (value == ",") { + value = lex(); + if (value == "}") { + // Unexpected trailing `,` in object literal. + abort(); + } + } else { + // A `,` must separate each object member. + abort(); + } + } + // Leading commas are not permitted, object property names must be + // double-quoted strings, and a `:` must separate each property + // name and value. + if (value == "," || typeof value != "string" || (charIndexBuggy ? value.charAt(0) : value[0]) != "@" || lex() != ":") { + abort(); + } + results[value.slice(1)] = get(lex()); + } + return results; + } + // Unexpected token encountered. + abort(); + } + return value; + }; + + // Internal: Updates a traversed object member. + var update = function (source, property, callback) { + var element = walk(source, property, callback); + if (element === undef) { + delete source[property]; + } else { + source[property] = element; + } + }; + + // Internal: Recursively traverses a parsed JSON object, invoking the + // `callback` function for each value. This is an implementation of the + // `Walk(holder, name)` operation defined in ES 5.1 section 15.12.2. + var walk = function (source, property, callback) { + var value = source[property], length; + if (typeof value == "object" && value) { + // `forEach` can't be used to traverse an array in Opera <= 8.54 + // because its `Object#hasOwnProperty` implementation returns `false` + // for array indices (e.g., `![1, 2, 3].hasOwnProperty("0")`). + if (getClass.call(value) == arrayClass) { + for (length = value.length; length--;) { + update(value, length, callback); + } + } else { + forEach(value, function (property) { + update(value, property, callback); + }); + } + } + return callback.call(source, property, value); + }; + + // Public: `JSON.parse`. See ES 5.1 section 15.12.2. + exports.parse = function (source, callback) { + var result, value; + Index = 0; + Source = "" + source; + result = get(lex()); + // If a JSON string contains multiple tokens, it is invalid. + if (lex() != "$") { + abort(); + } + // Reset the parser state. + Index = Source = null; + return callback && getClass.call(callback) == functionClass ? walk((value = {}, value[""] = result, value), "", callback) : result; + }; + } + } + + exports["runInContext"] = runInContext; + return exports; + } + + if (freeExports && !isLoader) { + // Export for CommonJS environments. + runInContext(root, freeExports); + } else { + // Export for web browsers and JavaScript engines. + var nativeJSON = root.JSON, + previousJSON = root["JSON3"], + isRestored = false; + + var JSON3 = runInContext(root, (root["JSON3"] = { + // Public: Restores the original value of the global `JSON` object and + // returns a reference to the `JSON3` object. + "noConflict": function () { + if (!isRestored) { + isRestored = true; + root.JSON = nativeJSON; + root["JSON3"] = previousJSON; + nativeJSON = previousJSON = null; + } + return JSON3; + } + })); + + root.JSON = { + "parse": JSON3.parse, + "stringify": JSON3.stringify + }; + } + + // Export for asynchronous module loaders. + if (isLoader) { + define(function () { + return JSON3; + }); + } + }).call(this); + + + /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(12)(module), (function() { return this; }()))) + +/***/ }, +/* 12 */ +/***/ function(module, exports) { + + module.exports = function(module) { + if(!module.webpackPolyfill) { + module.deprecate = function() {}; + module.paths = []; + // module.parent = undefined by default + module.children = []; + module.webpackPolyfill = 1; + } + return module; + } + + +/***/ }, +/* 13 */ +/***/ function(module, exports) { + + + /** + * Expose `Emitter`. + */ + + module.exports = Emitter; + + /** + * Initialize a new `Emitter`. + * + * @api public + */ + + function Emitter(obj) { + if (obj) return mixin(obj); + }; + + /** + * Mixin the emitter properties. + * + * @param {Object} obj + * @return {Object} + * @api private + */ + + function mixin(obj) { + for (var key in Emitter.prototype) { + obj[key] = Emitter.prototype[key]; + } + return obj; + } + + /** + * Listen on the given `event` with `fn`. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public + */ + + Emitter.prototype.on = + Emitter.prototype.addEventListener = function(event, fn){ + this._callbacks = this._callbacks || {}; + (this._callbacks[event] = this._callbacks[event] || []) + .push(fn); + return this; + }; + + /** + * Adds an `event` listener that will be invoked a single + * time then automatically removed. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public + */ + + Emitter.prototype.once = function(event, fn){ + var self = this; + this._callbacks = this._callbacks || {}; + + function on() { + self.off(event, on); + fn.apply(this, arguments); + } + + on.fn = fn; + this.on(event, on); + return this; + }; + + /** + * Remove the given callback for `event` or all + * registered callbacks. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public + */ + + Emitter.prototype.off = + Emitter.prototype.removeListener = + Emitter.prototype.removeAllListeners = + Emitter.prototype.removeEventListener = function(event, fn){ + this._callbacks = this._callbacks || {}; + + // all + if (0 == arguments.length) { + this._callbacks = {}; + return this; + } + + // specific event + var callbacks = this._callbacks[event]; + if (!callbacks) return this; + + // remove all handlers + if (1 == arguments.length) { + delete this._callbacks[event]; + return this; + } + + // remove specific handler + var cb; + for (var i = 0; i < callbacks.length; i++) { + cb = callbacks[i]; + if (cb === fn || cb.fn === fn) { + callbacks.splice(i, 1); + break; + } + } + return this; + }; + + /** + * Emit `event` with the given args. + * + * @param {String} event + * @param {Mixed} ... + * @return {Emitter} + */ + + Emitter.prototype.emit = function(event){ + this._callbacks = this._callbacks || {}; + var args = [].slice.call(arguments, 1) + , callbacks = this._callbacks[event]; + + if (callbacks) { + callbacks = callbacks.slice(0); + for (var i = 0, len = callbacks.length; i < len; ++i) { + callbacks[i].apply(this, args); + } + } + + return this; + }; + + /** + * Return array of callbacks for `event`. + * + * @param {String} event + * @return {Array} + * @api public + */ + + Emitter.prototype.listeners = function(event){ + this._callbacks = this._callbacks || {}; + return this._callbacks[event] || []; + }; + + /** + * Check if this emitter has `event` handlers. + * + * @param {String} event + * @return {Boolean} + * @api public + */ + + Emitter.prototype.hasListeners = function(event){ + return !! this.listeners(event).length; + }; + + +/***/ }, +/* 14 */ +/***/ function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(global) {/*global Blob,File*/ + + /** + * Module requirements + */ + + var isArray = __webpack_require__(15); + var isBuf = __webpack_require__(16); + + /** + * Replaces every Buffer | ArrayBuffer in packet with a numbered placeholder. + * Anything with blobs or files should be fed through removeBlobs before coming + * here. + * + * @param {Object} packet - socket.io event packet + * @return {Object} with deconstructed packet and list of buffers + * @api public + */ + + exports.deconstructPacket = function(packet){ + var buffers = []; + var packetData = packet.data; + + function _deconstructPacket(data) { + if (!data) return data; + + if (isBuf(data)) { + var placeholder = { _placeholder: true, num: buffers.length }; + buffers.push(data); + return placeholder; + } else if (isArray(data)) { + var newData = new Array(data.length); + for (var i = 0; i < data.length; i++) { + newData[i] = _deconstructPacket(data[i]); + } + return newData; + } else if ('object' == typeof data && !(data instanceof Date)) { + var newData = {}; + for (var key in data) { + newData[key] = _deconstructPacket(data[key]); + } + return newData; + } + return data; + } + + var pack = packet; + pack.data = _deconstructPacket(packetData); + pack.attachments = buffers.length; // number of binary 'attachments' + return {packet: pack, buffers: buffers}; + }; + + /** + * Reconstructs a binary packet from its placeholder packet and buffers + * + * @param {Object} packet - event packet with placeholders + * @param {Array} buffers - binary buffers to put in placeholder positions + * @return {Object} reconstructed packet + * @api public + */ + + exports.reconstructPacket = function(packet, buffers) { + var curPlaceHolder = 0; + + function _reconstructPacket(data) { + if (data && data._placeholder) { + var buf = buffers[data.num]; // appropriate buffer (should be natural order anyway) + return buf; + } else if (isArray(data)) { + for (var i = 0; i < data.length; i++) { + data[i] = _reconstructPacket(data[i]); + } + return data; + } else if (data && 'object' == typeof data) { + for (var key in data) { + data[key] = _reconstructPacket(data[key]); + } + return data; + } + return data; + } + + packet.data = _reconstructPacket(packet.data); + packet.attachments = undefined; // no longer useful + return packet; + }; + + /** + * Asynchronously removes Blobs or Files from data via + * FileReader's readAsArrayBuffer method. Used before encoding + * data as msgpack. Calls callback with the blobless data. + * + * @param {Object} data + * @param {Function} callback + * @api private + */ + + exports.removeBlobs = function(data, callback) { + function _removeBlobs(obj, curKey, containingObject) { + if (!obj) return obj; + + // convert any blob + if ((global.Blob && obj instanceof Blob) || + (global.File && obj instanceof File)) { + pendingBlobs++; + + // async filereader + var fileReader = new FileReader(); + fileReader.onload = function() { // this.result == arraybuffer + if (containingObject) { + containingObject[curKey] = this.result; + } + else { + bloblessData = this.result; + } + + // if nothing pending its callback time + if(! --pendingBlobs) { + callback(bloblessData); + } + }; + + fileReader.readAsArrayBuffer(obj); // blob -> arraybuffer + } else if (isArray(obj)) { // handle array + for (var i = 0; i < obj.length; i++) { + _removeBlobs(obj[i], i, obj); + } + } else if (obj && 'object' == typeof obj && !isBuf(obj)) { // and object + for (var key in obj) { + _removeBlobs(obj[key], key, obj); + } + } + } + + var pendingBlobs = 0; + var bloblessData = data; + _removeBlobs(bloblessData); + if (!pendingBlobs) { + callback(bloblessData); + } + }; + + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) + +/***/ }, +/* 15 */ +/***/ function(module, exports) { + + module.exports = Array.isArray || function (arr) { + return Object.prototype.toString.call(arr) == '[object Array]'; + }; + + +/***/ }, +/* 16 */ +/***/ function(module, exports) { + + /* WEBPACK VAR INJECTION */(function(global) { + module.exports = isBuf; + + /** + * Returns true if obj is a buffer or an arraybuffer. + * + * @api private + */ + + function isBuf(obj) { + return (global.Buffer && global.Buffer.isBuffer(obj)) || + (global.ArrayBuffer && obj instanceof ArrayBuffer); + } + + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) + +/***/ }, +/* 17 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + + /** + * Module dependencies. + */ + + var eio = __webpack_require__(18); + var Socket = __webpack_require__(45); + var Emitter = __webpack_require__(36); + var parser = __webpack_require__(7); + var on = __webpack_require__(47); + var bind = __webpack_require__(48); + var debug = __webpack_require__(3)('socket.io-client:manager'); + var indexOf = __webpack_require__(43); + var Backoff = __webpack_require__(51); + + /** + * IE6+ hasOwnProperty + */ + + var has = Object.prototype.hasOwnProperty; + + /** + * Module exports + */ + + module.exports = Manager; + + /** + * `Manager` constructor. + * + * @param {String} engine instance or engine uri/opts + * @param {Object} options + * @api public + */ + + function Manager(uri, opts) { + if (!(this instanceof Manager)) return new Manager(uri, opts); + if (uri && 'object' === (typeof uri === 'undefined' ? 'undefined' : _typeof(uri))) { + opts = uri; + uri = undefined; + } + opts = opts || {}; + + opts.path = opts.path || '/socket.io'; + this.nsps = {}; + this.subs = []; + this.opts = opts; + this.reconnection(opts.reconnection !== false); + this.reconnectionAttempts(opts.reconnectionAttempts || Infinity); + this.reconnectionDelay(opts.reconnectionDelay || 1000); + this.reconnectionDelayMax(opts.reconnectionDelayMax || 5000); + this.randomizationFactor(opts.randomizationFactor || 0.5); + this.backoff = new Backoff({ + min: this.reconnectionDelay(), + max: this.reconnectionDelayMax(), + jitter: this.randomizationFactor() + }); + this.timeout(null == opts.timeout ? 20000 : opts.timeout); + this.readyState = 'closed'; + this.uri = uri; + this.connecting = []; + this.lastPing = null; + this.encoding = false; + this.packetBuffer = []; + this.encoder = new parser.Encoder(); + this.decoder = new parser.Decoder(); + this.autoConnect = opts.autoConnect !== false; + if (this.autoConnect) this.open(); + } + + /** + * Propagate given event to sockets and emit on `this` + * + * @api private + */ + + Manager.prototype.emitAll = function () { + this.emit.apply(this, arguments); + for (var nsp in this.nsps) { + if (has.call(this.nsps, nsp)) { + this.nsps[nsp].emit.apply(this.nsps[nsp], arguments); + } + } + }; + + /** + * Update `socket.id` of all sockets + * + * @api private + */ + + Manager.prototype.updateSocketIds = function () { + for (var nsp in this.nsps) { + if (has.call(this.nsps, nsp)) { + this.nsps[nsp].id = this.engine.id; + } + } + }; + + /** + * Mix in `Emitter`. + */ + + Emitter(Manager.prototype); + + /** + * Sets the `reconnection` config. + * + * @param {Boolean} true/false if it should automatically reconnect + * @return {Manager} self or value + * @api public + */ + + Manager.prototype.reconnection = function (v) { + if (!arguments.length) return this._reconnection; + this._reconnection = !!v; + return this; + }; + + /** + * Sets the reconnection attempts config. + * + * @param {Number} max reconnection attempts before giving up + * @return {Manager} self or value + * @api public + */ + + Manager.prototype.reconnectionAttempts = function (v) { + if (!arguments.length) return this._reconnectionAttempts; + this._reconnectionAttempts = v; + return this; + }; + + /** + * Sets the delay between reconnections. + * + * @param {Number} delay + * @return {Manager} self or value + * @api public + */ + + Manager.prototype.reconnectionDelay = function (v) { + if (!arguments.length) return this._reconnectionDelay; + this._reconnectionDelay = v; + this.backoff && this.backoff.setMin(v); + return this; + }; + + Manager.prototype.randomizationFactor = function (v) { + if (!arguments.length) return this._randomizationFactor; + this._randomizationFactor = v; + this.backoff && this.backoff.setJitter(v); + return this; + }; + + /** + * Sets the maximum delay between reconnections. + * + * @param {Number} delay + * @return {Manager} self or value + * @api public + */ + + Manager.prototype.reconnectionDelayMax = function (v) { + if (!arguments.length) return this._reconnectionDelayMax; + this._reconnectionDelayMax = v; + this.backoff && this.backoff.setMax(v); + return this; + }; + + /** + * Sets the connection timeout. `false` to disable + * + * @return {Manager} self or value + * @api public + */ + + Manager.prototype.timeout = function (v) { + if (!arguments.length) return this._timeout; + this._timeout = v; + return this; + }; + + /** + * Starts trying to reconnect if reconnection is enabled and we have not + * started reconnecting yet + * + * @api private + */ + + Manager.prototype.maybeReconnectOnOpen = function () { + // Only try to reconnect if it's the first time we're connecting + if (!this.reconnecting && this._reconnection && this.backoff.attempts === 0) { + // keeps reconnection from firing twice for the same reconnection loop + this.reconnect(); + } + }; + + /** + * Sets the current transport `socket`. + * + * @param {Function} optional, callback + * @return {Manager} self + * @api public + */ + + Manager.prototype.open = Manager.prototype.connect = function (fn, opts) { + debug('readyState %s', this.readyState); + if (~this.readyState.indexOf('open')) return this; + + debug('opening %s', this.uri); + this.engine = eio(this.uri, this.opts); + var socket = this.engine; + var self = this; + this.readyState = 'opening'; + this.skipReconnect = false; + + // emit `open` + var openSub = on(socket, 'open', function () { + self.onopen(); + fn && fn(); + }); + + // emit `connect_error` + var errorSub = on(socket, 'error', function (data) { + debug('connect_error'); + self.cleanup(); + self.readyState = 'closed'; + self.emitAll('connect_error', data); + if (fn) { + var err = new Error('Connection error'); + err.data = data; + fn(err); + } else { + // Only do this if there is no fn to handle the error + self.maybeReconnectOnOpen(); + } + }); + + // emit `connect_timeout` + if (false !== this._timeout) { + var timeout = this._timeout; + debug('connect attempt will timeout after %d', timeout); + + // set timer + var timer = setTimeout(function () { + debug('connect attempt timed out after %d', timeout); + openSub.destroy(); + socket.close(); + socket.emit('error', 'timeout'); + self.emitAll('connect_timeout', timeout); + }, timeout); + + this.subs.push({ + destroy: function destroy() { + clearTimeout(timer); + } + }); + } + + this.subs.push(openSub); + this.subs.push(errorSub); + + return this; + }; + + /** + * Called upon transport open. + * + * @api private + */ + + Manager.prototype.onopen = function () { + debug('open'); + + // clear old subs + this.cleanup(); + + // mark as open + this.readyState = 'open'; + this.emit('open'); + + // add new subs + var socket = this.engine; + this.subs.push(on(socket, 'data', bind(this, 'ondata'))); + this.subs.push(on(socket, 'ping', bind(this, 'onping'))); + this.subs.push(on(socket, 'pong', bind(this, 'onpong'))); + this.subs.push(on(socket, 'error', bind(this, 'onerror'))); + this.subs.push(on(socket, 'close', bind(this, 'onclose'))); + this.subs.push(on(this.decoder, 'decoded', bind(this, 'ondecoded'))); + }; + + /** + * Called upon a ping. + * + * @api private + */ + + Manager.prototype.onping = function () { + this.lastPing = new Date(); + this.emitAll('ping'); + }; + + /** + * Called upon a packet. + * + * @api private + */ + + Manager.prototype.onpong = function () { + this.emitAll('pong', new Date() - this.lastPing); + }; + + /** + * Called with data. + * + * @api private + */ + + Manager.prototype.ondata = function (data) { + this.decoder.add(data); + }; + + /** + * Called when parser fully decodes a packet. + * + * @api private + */ + + Manager.prototype.ondecoded = function (packet) { + this.emit('packet', packet); + }; + + /** + * Called upon socket error. + * + * @api private + */ + + Manager.prototype.onerror = function (err) { + debug('error', err); + this.emitAll('error', err); + }; + + /** + * Creates a new socket for the given `nsp`. + * + * @return {Socket} + * @api public + */ + + Manager.prototype.socket = function (nsp, opts) { + var socket = this.nsps[nsp]; + if (!socket) { + socket = new Socket(this, nsp, opts); + this.nsps[nsp] = socket; + var self = this; + socket.on('connecting', onConnecting); + socket.on('connect', function () { + socket.id = self.engine.id; + }); + + if (this.autoConnect) { + // manually call here since connecting evnet is fired before listening + onConnecting(); + } + } + + function onConnecting() { + if (!~indexOf(self.connecting, socket)) { + self.connecting.push(socket); + } + } + + return socket; + }; + + /** + * Called upon a socket close. + * + * @param {Socket} socket + */ + + Manager.prototype.destroy = function (socket) { + var index = indexOf(this.connecting, socket); + if (~index) this.connecting.splice(index, 1); + if (this.connecting.length) return; + + this.close(); + }; + + /** + * Writes a packet. + * + * @param {Object} packet + * @api private + */ + + Manager.prototype.packet = function (packet) { + debug('writing packet %j', packet); + var self = this; + if (packet.query && packet.type === 0) packet.nsp += '?' + packet.query; + + if (!self.encoding) { + // encode, then write to engine with result + self.encoding = true; + this.encoder.encode(packet, function (encodedPackets) { + for (var i = 0; i < encodedPackets.length; i++) { + self.engine.write(encodedPackets[i], packet.options); + } + self.encoding = false; + self.processPacketQueue(); + }); + } else { + // add packet to the queue + self.packetBuffer.push(packet); + } + }; + + /** + * If packet buffer is non-empty, begins encoding the + * next packet in line. + * + * @api private + */ + + Manager.prototype.processPacketQueue = function () { + if (this.packetBuffer.length > 0 && !this.encoding) { + var pack = this.packetBuffer.shift(); + this.packet(pack); + } + }; + + /** + * Clean up transport subscriptions and packet buffer. + * + * @api private + */ + + Manager.prototype.cleanup = function () { + debug('cleanup'); + + var subsLength = this.subs.length; + for (var i = 0; i < subsLength; i++) { + var sub = this.subs.shift(); + sub.destroy(); + } + + this.packetBuffer = []; + this.encoding = false; + this.lastPing = null; + + this.decoder.destroy(); + }; + + /** + * Close the current socket. + * + * @api private + */ + + Manager.prototype.close = Manager.prototype.disconnect = function () { + debug('disconnect'); + this.skipReconnect = true; + this.reconnecting = false; + if ('opening' === this.readyState) { + // `onclose` will not fire because + // an open event never happened + this.cleanup(); + } + this.backoff.reset(); + this.readyState = 'closed'; + if (this.engine) this.engine.close(); + }; + + /** + * Called upon engine close. + * + * @api private + */ + + Manager.prototype.onclose = function (reason) { + debug('onclose'); + + this.cleanup(); + this.backoff.reset(); + this.readyState = 'closed'; + this.emit('close', reason); + + if (this._reconnection && !this.skipReconnect) { + this.reconnect(); + } + }; + + /** + * Attempt a reconnection. + * + * @api private + */ + + Manager.prototype.reconnect = function () { + if (this.reconnecting || this.skipReconnect) return this; + + var self = this; + + if (this.backoff.attempts >= this._reconnectionAttempts) { + debug('reconnect failed'); + this.backoff.reset(); + this.emitAll('reconnect_failed'); + this.reconnecting = false; + } else { + var delay = this.backoff.duration(); + debug('will wait %dms before reconnect attempt', delay); + + this.reconnecting = true; + var timer = setTimeout(function () { + if (self.skipReconnect) return; + + debug('attempting reconnect'); + self.emitAll('reconnect_attempt', self.backoff.attempts); + self.emitAll('reconnecting', self.backoff.attempts); + + // check again for the case socket closed in above events + if (self.skipReconnect) return; + + self.open(function (err) { + if (err) { + debug('reconnect attempt error'); + self.reconnecting = false; + self.reconnect(); + self.emitAll('reconnect_error', err.data); + } else { + debug('reconnect success'); + self.onreconnect(); + } + }); + }, delay); + + this.subs.push({ + destroy: function destroy() { + clearTimeout(timer); + } + }); + } + }; + + /** + * Called upon successful reconnect. + * + * @api private + */ + + Manager.prototype.onreconnect = function () { + var attempt = this.backoff.attempts; + this.reconnecting = false; + this.backoff.reset(); + this.updateSocketIds(); + this.emitAll('reconnect', attempt); + }; + +/***/ }, +/* 18 */ +/***/ function(module, exports, __webpack_require__) { + + + module.exports = __webpack_require__(19); + + +/***/ }, +/* 19 */ +/***/ function(module, exports, __webpack_require__) { + + + module.exports = __webpack_require__(20); + + /** + * Exports parser + * + * @api public + * + */ + module.exports.parser = __webpack_require__(27); + + +/***/ }, +/* 20 */ +/***/ function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(global) {/** + * Module dependencies. + */ + + var transports = __webpack_require__(21); + var Emitter = __webpack_require__(36); + var debug = __webpack_require__(3)('engine.io-client:socket'); + var index = __webpack_require__(43); + var parser = __webpack_require__(27); + var parseuri = __webpack_require__(2); + var parsejson = __webpack_require__(44); + var parseqs = __webpack_require__(37); + + /** + * Module exports. + */ + + module.exports = Socket; + + /** + * Socket constructor. + * + * @param {String|Object} uri or options + * @param {Object} options + * @api public + */ + + function Socket (uri, opts) { + if (!(this instanceof Socket)) return new Socket(uri, opts); + + opts = opts || {}; + + if (uri && 'object' === typeof uri) { + opts = uri; + uri = null; + } + + if (uri) { + uri = parseuri(uri); + opts.hostname = uri.host; + opts.secure = uri.protocol === 'https' || uri.protocol === 'wss'; + opts.port = uri.port; + if (uri.query) opts.query = uri.query; + } else if (opts.host) { + opts.hostname = parseuri(opts.host).host; + } + + this.secure = null != opts.secure ? opts.secure + : (global.location && 'https:' === location.protocol); + + if (opts.hostname && !opts.port) { + // if no port is specified manually, use the protocol default + opts.port = this.secure ? '443' : '80'; + } + + this.agent = opts.agent || false; + this.hostname = opts.hostname || + (global.location ? location.hostname : 'localhost'); + this.port = opts.port || (global.location && location.port + ? location.port + : (this.secure ? 443 : 80)); + this.query = opts.query || {}; + if ('string' === typeof this.query) this.query = parseqs.decode(this.query); + this.upgrade = false !== opts.upgrade; + this.path = (opts.path || '/engine.io').replace(/\/$/, '') + '/'; + this.forceJSONP = !!opts.forceJSONP; + this.jsonp = false !== opts.jsonp; + this.forceBase64 = !!opts.forceBase64; + this.enablesXDR = !!opts.enablesXDR; + this.timestampParam = opts.timestampParam || 't'; + this.timestampRequests = opts.timestampRequests; + this.transports = opts.transports || ['polling', 'websocket']; + this.readyState = ''; + this.writeBuffer = []; + this.prevBufferLen = 0; + this.policyPort = opts.policyPort || 843; + this.rememberUpgrade = opts.rememberUpgrade || false; + this.binaryType = null; + this.onlyBinaryUpgrades = opts.onlyBinaryUpgrades; + this.perMessageDeflate = false !== opts.perMessageDeflate ? (opts.perMessageDeflate || {}) : false; + + if (true === this.perMessageDeflate) this.perMessageDeflate = {}; + if (this.perMessageDeflate && null == this.perMessageDeflate.threshold) { + this.perMessageDeflate.threshold = 1024; + } + + // SSL options for Node.js client + this.pfx = opts.pfx || null; + this.key = opts.key || null; + this.passphrase = opts.passphrase || null; + this.cert = opts.cert || null; + this.ca = opts.ca || null; + this.ciphers = opts.ciphers || null; + this.rejectUnauthorized = opts.rejectUnauthorized === undefined ? null : opts.rejectUnauthorized; + this.forceNode = !!opts.forceNode; + + // other options for Node.js client + var freeGlobal = typeof global === 'object' && global; + if (freeGlobal.global === freeGlobal) { + if (opts.extraHeaders && Object.keys(opts.extraHeaders).length > 0) { + this.extraHeaders = opts.extraHeaders; + } + + if (opts.localAddress) { + this.localAddress = opts.localAddress; + } + } + + // set on handshake + this.id = null; + this.upgrades = null; + this.pingInterval = null; + this.pingTimeout = null; + + // set on heartbeat + this.pingIntervalTimer = null; + this.pingTimeoutTimer = null; + + this.open(); + } + + Socket.priorWebsocketSuccess = false; + + /** + * Mix in `Emitter`. + */ + + Emitter(Socket.prototype); + + /** + * Protocol version. + * + * @api public + */ + + Socket.protocol = parser.protocol; // this is an int + + /** + * Expose deps for legacy compatibility + * and standalone browser access. + */ + + Socket.Socket = Socket; + Socket.Transport = __webpack_require__(26); + Socket.transports = __webpack_require__(21); + Socket.parser = __webpack_require__(27); + + /** + * Creates transport of the given type. + * + * @param {String} transport name + * @return {Transport} + * @api private + */ + + Socket.prototype.createTransport = function (name) { + debug('creating transport "%s"', name); + var query = clone(this.query); + + // append engine.io protocol identifier + query.EIO = parser.protocol; + + // transport name + query.transport = name; + + // session id if we already have one + if (this.id) query.sid = this.id; + + var transport = new transports[name]({ + agent: this.agent, + hostname: this.hostname, + port: this.port, + secure: this.secure, + path: this.path, + query: query, + forceJSONP: this.forceJSONP, + jsonp: this.jsonp, + forceBase64: this.forceBase64, + enablesXDR: this.enablesXDR, + timestampRequests: this.timestampRequests, + timestampParam: this.timestampParam, + policyPort: this.policyPort, + socket: this, + pfx: this.pfx, + key: this.key, + passphrase: this.passphrase, + cert: this.cert, + ca: this.ca, + ciphers: this.ciphers, + rejectUnauthorized: this.rejectUnauthorized, + perMessageDeflate: this.perMessageDeflate, + extraHeaders: this.extraHeaders, + forceNode: this.forceNode, + localAddress: this.localAddress + }); + + return transport; + }; + + function clone (obj) { + var o = {}; + for (var i in obj) { + if (obj.hasOwnProperty(i)) { + o[i] = obj[i]; + } + } + return o; + } + + /** + * Initializes transport to use and starts probe. + * + * @api private + */ + Socket.prototype.open = function () { + var transport; + if (this.rememberUpgrade && Socket.priorWebsocketSuccess && this.transports.indexOf('websocket') !== -1) { + transport = 'websocket'; + } else if (0 === this.transports.length) { + // Emit error on next tick so it can be listened to + var self = this; + setTimeout(function () { + self.emit('error', 'No transports available'); + }, 0); + return; + } else { + transport = this.transports[0]; + } + this.readyState = 'opening'; + + // Retry with the next transport if the transport is disabled (jsonp: false) + try { + transport = this.createTransport(transport); + } catch (e) { + this.transports.shift(); + this.open(); + return; + } + + transport.open(); + this.setTransport(transport); + }; + + /** + * Sets the current transport. Disables the existing one (if any). + * + * @api private + */ + + Socket.prototype.setTransport = function (transport) { + debug('setting transport %s', transport.name); + var self = this; + + if (this.transport) { + debug('clearing existing transport %s', this.transport.name); + this.transport.removeAllListeners(); + } + + // set up transport + this.transport = transport; + + // set up transport listeners + transport + .on('drain', function () { + self.onDrain(); + }) + .on('packet', function (packet) { + self.onPacket(packet); + }) + .on('error', function (e) { + self.onError(e); + }) + .on('close', function () { + self.onClose('transport close'); + }); + }; + + /** + * Probes a transport. + * + * @param {String} transport name + * @api private + */ + + Socket.prototype.probe = function (name) { + debug('probing transport "%s"', name); + var transport = this.createTransport(name, { probe: 1 }); + var failed = false; + var self = this; + + Socket.priorWebsocketSuccess = false; + + function onTransportOpen () { + if (self.onlyBinaryUpgrades) { + var upgradeLosesBinary = !this.supportsBinary && self.transport.supportsBinary; + failed = failed || upgradeLosesBinary; + } + if (failed) return; + + debug('probe transport "%s" opened', name); + transport.send([{ type: 'ping', data: 'probe' }]); + transport.once('packet', function (msg) { + if (failed) return; + if ('pong' === msg.type && 'probe' === msg.data) { + debug('probe transport "%s" pong', name); + self.upgrading = true; + self.emit('upgrading', transport); + if (!transport) return; + Socket.priorWebsocketSuccess = 'websocket' === transport.name; + + debug('pausing current transport "%s"', self.transport.name); + self.transport.pause(function () { + if (failed) return; + if ('closed' === self.readyState) return; + debug('changing transport and sending upgrade packet'); + + cleanup(); + + self.setTransport(transport); + transport.send([{ type: 'upgrade' }]); + self.emit('upgrade', transport); + transport = null; + self.upgrading = false; + self.flush(); + }); + } else { + debug('probe transport "%s" failed', name); + var err = new Error('probe error'); + err.transport = transport.name; + self.emit('upgradeError', err); + } + }); + } + + function freezeTransport () { + if (failed) return; + + // Any callback called by transport should be ignored since now + failed = true; + + cleanup(); + + transport.close(); + transport = null; + } + + // Handle any error that happens while probing + function onerror (err) { + var error = new Error('probe error: ' + err); + error.transport = transport.name; + + freezeTransport(); + + debug('probe transport "%s" failed because of error: %s', name, err); + + self.emit('upgradeError', error); + } + + function onTransportClose () { + onerror('transport closed'); + } + + // When the socket is closed while we're probing + function onclose () { + onerror('socket closed'); + } + + // When the socket is upgraded while we're probing + function onupgrade (to) { + if (transport && to.name !== transport.name) { + debug('"%s" works - aborting "%s"', to.name, transport.name); + freezeTransport(); + } + } + + // Remove all listeners on the transport and on self + function cleanup () { + transport.removeListener('open', onTransportOpen); + transport.removeListener('error', onerror); + transport.removeListener('close', onTransportClose); + self.removeListener('close', onclose); + self.removeListener('upgrading', onupgrade); + } + + transport.once('open', onTransportOpen); + transport.once('error', onerror); + transport.once('close', onTransportClose); + + this.once('close', onclose); + this.once('upgrading', onupgrade); + + transport.open(); + }; + + /** + * Called when connection is deemed open. + * + * @api public + */ + + Socket.prototype.onOpen = function () { + debug('socket open'); + this.readyState = 'open'; + Socket.priorWebsocketSuccess = 'websocket' === this.transport.name; + this.emit('open'); + this.flush(); + + // we check for `readyState` in case an `open` + // listener already closed the socket + if ('open' === this.readyState && this.upgrade && this.transport.pause) { + debug('starting upgrade probes'); + for (var i = 0, l = this.upgrades.length; i < l; i++) { + this.probe(this.upgrades[i]); + } + } + }; + + /** + * Handles a packet. + * + * @api private + */ + + Socket.prototype.onPacket = function (packet) { + if ('opening' === this.readyState || 'open' === this.readyState || + 'closing' === this.readyState) { + debug('socket receive: type "%s", data "%s"', packet.type, packet.data); + + this.emit('packet', packet); + + // Socket is live - any packet counts + this.emit('heartbeat'); + + switch (packet.type) { + case 'open': + this.onHandshake(parsejson(packet.data)); + break; + + case 'pong': + this.setPing(); + this.emit('pong'); + break; + + case 'error': + var err = new Error('server error'); + err.code = packet.data; + this.onError(err); + break; + + case 'message': + this.emit('data', packet.data); + this.emit('message', packet.data); + break; + } + } else { + debug('packet received with socket readyState "%s"', this.readyState); + } + }; + + /** + * Called upon handshake completion. + * + * @param {Object} handshake obj + * @api private + */ + + Socket.prototype.onHandshake = function (data) { + this.emit('handshake', data); + this.id = data.sid; + this.transport.query.sid = data.sid; + this.upgrades = this.filterUpgrades(data.upgrades); + this.pingInterval = data.pingInterval; + this.pingTimeout = data.pingTimeout; + this.onOpen(); + // In case open handler closes socket + if ('closed' === this.readyState) return; + this.setPing(); + + // Prolong liveness of socket on heartbeat + this.removeListener('heartbeat', this.onHeartbeat); + this.on('heartbeat', this.onHeartbeat); + }; + + /** + * Resets ping timeout. + * + * @api private + */ + + Socket.prototype.onHeartbeat = function (timeout) { + clearTimeout(this.pingTimeoutTimer); + var self = this; + self.pingTimeoutTimer = setTimeout(function () { + if ('closed' === self.readyState) return; + self.onClose('ping timeout'); + }, timeout || (self.pingInterval + self.pingTimeout)); + }; + + /** + * Pings server every `this.pingInterval` and expects response + * within `this.pingTimeout` or closes connection. + * + * @api private + */ + + Socket.prototype.setPing = function () { + var self = this; + clearTimeout(self.pingIntervalTimer); + self.pingIntervalTimer = setTimeout(function () { + debug('writing ping packet - expecting pong within %sms', self.pingTimeout); + self.ping(); + self.onHeartbeat(self.pingTimeout); + }, self.pingInterval); + }; + + /** + * Sends a ping packet. + * + * @api private + */ + + Socket.prototype.ping = function () { + var self = this; + this.sendPacket('ping', function () { + self.emit('ping'); + }); + }; + + /** + * Called on `drain` event + * + * @api private + */ + + Socket.prototype.onDrain = function () { + this.writeBuffer.splice(0, this.prevBufferLen); + + // setting prevBufferLen = 0 is very important + // for example, when upgrading, upgrade packet is sent over, + // and a nonzero prevBufferLen could cause problems on `drain` + this.prevBufferLen = 0; + + if (0 === this.writeBuffer.length) { + this.emit('drain'); + } else { + this.flush(); + } + }; + + /** + * Flush write buffers. + * + * @api private + */ + + Socket.prototype.flush = function () { + if ('closed' !== this.readyState && this.transport.writable && + !this.upgrading && this.writeBuffer.length) { + debug('flushing %d packets in socket', this.writeBuffer.length); + this.transport.send(this.writeBuffer); + // keep track of current length of writeBuffer + // splice writeBuffer and callbackBuffer on `drain` + this.prevBufferLen = this.writeBuffer.length; + this.emit('flush'); + } + }; + + /** + * Sends a message. + * + * @param {String} message. + * @param {Function} callback function. + * @param {Object} options. + * @return {Socket} for chaining. + * @api public + */ + + Socket.prototype.write = + Socket.prototype.send = function (msg, options, fn) { + this.sendPacket('message', msg, options, fn); + return this; + }; + + /** + * Sends a packet. + * + * @param {String} packet type. + * @param {String} data. + * @param {Object} options. + * @param {Function} callback function. + * @api private + */ + + Socket.prototype.sendPacket = function (type, data, options, fn) { + if ('function' === typeof data) { + fn = data; + data = undefined; + } + + if ('function' === typeof options) { + fn = options; + options = null; + } + + if ('closing' === this.readyState || 'closed' === this.readyState) { + return; + } + + options = options || {}; + options.compress = false !== options.compress; + + var packet = { + type: type, + data: data, + options: options + }; + this.emit('packetCreate', packet); + this.writeBuffer.push(packet); + if (fn) this.once('flush', fn); + this.flush(); + }; + + /** + * Closes the connection. + * + * @api private + */ + + Socket.prototype.close = function () { + if ('opening' === this.readyState || 'open' === this.readyState) { + this.readyState = 'closing'; + + var self = this; + + if (this.writeBuffer.length) { + this.once('drain', function () { + if (this.upgrading) { + waitForUpgrade(); + } else { + close(); + } + }); + } else if (this.upgrading) { + waitForUpgrade(); + } else { + close(); + } + } + + function close () { + self.onClose('forced close'); + debug('socket closing - telling transport to close'); + self.transport.close(); + } + + function cleanupAndClose () { + self.removeListener('upgrade', cleanupAndClose); + self.removeListener('upgradeError', cleanupAndClose); + close(); + } + + function waitForUpgrade () { + // wait for upgrade to finish since we can't send packets while pausing a transport + self.once('upgrade', cleanupAndClose); + self.once('upgradeError', cleanupAndClose); + } + + return this; + }; + + /** + * Called upon transport error + * + * @api private + */ + + Socket.prototype.onError = function (err) { + debug('socket error %j', err); + Socket.priorWebsocketSuccess = false; + this.emit('error', err); + this.onClose('transport error', err); + }; + + /** + * Called upon transport close. + * + * @api private + */ + + Socket.prototype.onClose = function (reason, desc) { + if ('opening' === this.readyState || 'open' === this.readyState || 'closing' === this.readyState) { + debug('socket close with reason: "%s"', reason); + var self = this; + + // clear timers + clearTimeout(this.pingIntervalTimer); + clearTimeout(this.pingTimeoutTimer); + + // stop event from firing again for transport + this.transport.removeAllListeners('close'); + + // ensure transport won't stay open + this.transport.close(); + + // ignore further transport communication + this.transport.removeAllListeners(); + + // set ready state + this.readyState = 'closed'; + + // clear session id + this.id = null; + + // emit close event + this.emit('close', reason, desc); + + // clean buffers after, so users can still + // grab the buffers on `close` event + self.writeBuffer = []; + self.prevBufferLen = 0; + } + }; + + /** + * Filters upgrades, returning only those matching client transports. + * + * @param {Array} server upgrades + * @api private + * + */ + + Socket.prototype.filterUpgrades = function (upgrades) { + var filteredUpgrades = []; + for (var i = 0, j = upgrades.length; i < j; i++) { + if (~index(this.transports, upgrades[i])) filteredUpgrades.push(upgrades[i]); + } + return filteredUpgrades; + }; + + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) + +/***/ }, +/* 21 */ +/***/ function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(global) {/** + * Module dependencies + */ + + var XMLHttpRequest = __webpack_require__(22); + var XHR = __webpack_require__(24); + var JSONP = __webpack_require__(40); + var websocket = __webpack_require__(41); + + /** + * Export transports. + */ + + exports.polling = polling; + exports.websocket = websocket; + + /** + * Polling transport polymorphic constructor. + * Decides on xhr vs jsonp based on feature detection. + * + * @api private + */ + + function polling (opts) { + var xhr; + var xd = false; + var xs = false; + var jsonp = false !== opts.jsonp; + + if (global.location) { + var isSSL = 'https:' === location.protocol; + var port = location.port; + + // some user agents have empty `location.port` + if (!port) { + port = isSSL ? 443 : 80; + } + + xd = opts.hostname !== location.hostname || port !== opts.port; + xs = opts.secure !== isSSL; + } + + opts.xdomain = xd; + opts.xscheme = xs; + xhr = new XMLHttpRequest(opts); + + if ('open' in xhr && !opts.forceJSONP) { + return new XHR(opts); + } else { + if (!jsonp) throw new Error('JSONP disabled'); + return new JSONP(opts); + } + } + + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) + +/***/ }, +/* 22 */ +/***/ function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(global) {// browser shim for xmlhttprequest module + + var hasCORS = __webpack_require__(23); + + module.exports = function (opts) { + var xdomain = opts.xdomain; + + // scheme must be same when usign XDomainRequest + // http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx + var xscheme = opts.xscheme; + + // XDomainRequest has a flow of not sending cookie, therefore it should be disabled as a default. + // https://github.com/Automattic/engine.io-client/pull/217 + var enablesXDR = opts.enablesXDR; + + // XMLHttpRequest can be disabled on IE + try { + if ('undefined' !== typeof XMLHttpRequest && (!xdomain || hasCORS)) { + return new XMLHttpRequest(); + } + } catch (e) { } + + // Use XDomainRequest for IE8 if enablesXDR is true + // because loading bar keeps flashing when using jsonp-polling + // https://github.com/yujiosaka/socke.io-ie8-loading-example + try { + if ('undefined' !== typeof XDomainRequest && !xscheme && enablesXDR) { + return new XDomainRequest(); + } + } catch (e) { } + + if (!xdomain) { + try { + return new global[['Active'].concat('Object').join('X')]('Microsoft.XMLHTTP'); + } catch (e) { } + } + }; + + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) + +/***/ }, +/* 23 */ +/***/ function(module, exports) { + + + /** + * Module exports. + * + * Logic borrowed from Modernizr: + * + * - https://github.com/Modernizr/Modernizr/blob/master/feature-detects/cors.js + */ + + try { + module.exports = typeof XMLHttpRequest !== 'undefined' && + 'withCredentials' in new XMLHttpRequest(); + } catch (err) { + // if XMLHttp support is disabled in IE then it will throw + // when trying to create + module.exports = false; + } + + +/***/ }, +/* 24 */ +/***/ function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(global) {/** + * Module requirements. + */ + + var XMLHttpRequest = __webpack_require__(22); + var Polling = __webpack_require__(25); + var Emitter = __webpack_require__(36); + var inherit = __webpack_require__(38); + var debug = __webpack_require__(3)('engine.io-client:polling-xhr'); + + /** + * Module exports. + */ + + module.exports = XHR; + module.exports.Request = Request; + + /** + * Empty function + */ + + function empty () {} + + /** + * XHR Polling constructor. + * + * @param {Object} opts + * @api public + */ + + function XHR (opts) { + Polling.call(this, opts); + this.requestTimeout = opts.requestTimeout; + + if (global.location) { + var isSSL = 'https:' === location.protocol; + var port = location.port; + + // some user agents have empty `location.port` + if (!port) { + port = isSSL ? 443 : 80; + } + + this.xd = opts.hostname !== global.location.hostname || + port !== opts.port; + this.xs = opts.secure !== isSSL; + } else { + this.extraHeaders = opts.extraHeaders; + } + } + + /** + * Inherits from Polling. + */ + + inherit(XHR, Polling); + + /** + * XHR supports binary + */ + + XHR.prototype.supportsBinary = true; + + /** + * Creates a request. + * + * @param {String} method + * @api private + */ + + XHR.prototype.request = function (opts) { + opts = opts || {}; + opts.uri = this.uri(); + opts.xd = this.xd; + opts.xs = this.xs; + opts.agent = this.agent || false; + opts.supportsBinary = this.supportsBinary; + opts.enablesXDR = this.enablesXDR; + + // SSL options for Node.js client + opts.pfx = this.pfx; + opts.key = this.key; + opts.passphrase = this.passphrase; + opts.cert = this.cert; + opts.ca = this.ca; + opts.ciphers = this.ciphers; + opts.rejectUnauthorized = this.rejectUnauthorized; + opts.requestTimeout = this.requestTimeout; + + // other options for Node.js client + opts.extraHeaders = this.extraHeaders; + + return new Request(opts); + }; + + /** + * Sends data. + * + * @param {String} data to send. + * @param {Function} called upon flush. + * @api private + */ + + XHR.prototype.doWrite = function (data, fn) { + var isBinary = typeof data !== 'string' && data !== undefined; + var req = this.request({ method: 'POST', data: data, isBinary: isBinary }); + var self = this; + req.on('success', fn); + req.on('error', function (err) { + self.onError('xhr post error', err); + }); + this.sendXhr = req; + }; + + /** + * Starts a poll cycle. + * + * @api private + */ + + XHR.prototype.doPoll = function () { + debug('xhr poll'); + var req = this.request(); + var self = this; + req.on('data', function (data) { + self.onData(data); + }); + req.on('error', function (err) { + self.onError('xhr poll error', err); + }); + this.pollXhr = req; + }; + + /** + * Request constructor + * + * @param {Object} options + * @api public + */ + + function Request (opts) { + this.method = opts.method || 'GET'; + this.uri = opts.uri; + this.xd = !!opts.xd; + this.xs = !!opts.xs; + this.async = false !== opts.async; + this.data = undefined !== opts.data ? opts.data : null; + this.agent = opts.agent; + this.isBinary = opts.isBinary; + this.supportsBinary = opts.supportsBinary; + this.enablesXDR = opts.enablesXDR; + this.requestTimeout = opts.requestTimeout; + + // SSL options for Node.js client + this.pfx = opts.pfx; + this.key = opts.key; + this.passphrase = opts.passphrase; + this.cert = opts.cert; + this.ca = opts.ca; + this.ciphers = opts.ciphers; + this.rejectUnauthorized = opts.rejectUnauthorized; + + // other options for Node.js client + this.extraHeaders = opts.extraHeaders; + + this.create(); + } + + /** + * Mix in `Emitter`. + */ + + Emitter(Request.prototype); + + /** + * Creates the XHR object and sends the request. + * + * @api private + */ + + Request.prototype.create = function () { + var opts = { agent: this.agent, xdomain: this.xd, xscheme: this.xs, enablesXDR: this.enablesXDR }; + + // SSL options for Node.js client + opts.pfx = this.pfx; + opts.key = this.key; + opts.passphrase = this.passphrase; + opts.cert = this.cert; + opts.ca = this.ca; + opts.ciphers = this.ciphers; + opts.rejectUnauthorized = this.rejectUnauthorized; + + var xhr = this.xhr = new XMLHttpRequest(opts); + var self = this; + + try { + debug('xhr open %s: %s', this.method, this.uri); + xhr.open(this.method, this.uri, this.async); + try { + if (this.extraHeaders) { + xhr.setDisableHeaderCheck(true); + for (var i in this.extraHeaders) { + if (this.extraHeaders.hasOwnProperty(i)) { + xhr.setRequestHeader(i, this.extraHeaders[i]); + } + } + } + } catch (e) {} + if (this.supportsBinary) { + // This has to be done after open because Firefox is stupid + // http://stackoverflow.com/questions/13216903/get-binary-data-with-xmlhttprequest-in-a-firefox-extension + xhr.responseType = 'arraybuffer'; + } + + if ('POST' === this.method) { + try { + if (this.isBinary) { + xhr.setRequestHeader('Content-type', 'application/octet-stream'); + } else { + xhr.setRequestHeader('Content-type', 'text/plain;charset=UTF-8'); + } + } catch (e) {} + } + + try { + xhr.setRequestHeader('Accept', '*/*'); + } catch (e) {} + + // ie6 check + if ('withCredentials' in xhr) { + xhr.withCredentials = true; + } + + if (this.requestTimeout) { + xhr.timeout = this.requestTimeout; + } + + if (this.hasXDR()) { + xhr.onload = function () { + self.onLoad(); + }; + xhr.onerror = function () { + self.onError(xhr.responseText); + }; + } else { + xhr.onreadystatechange = function () { + if (4 !== xhr.readyState) return; + if (200 === xhr.status || 1223 === xhr.status) { + self.onLoad(); + } else { + // make sure the `error` event handler that's user-set + // does not throw in the same tick and gets caught here + setTimeout(function () { + self.onError(xhr.status); + }, 0); + } + }; + } + + debug('xhr data %s', this.data); + xhr.send(this.data); + } catch (e) { + // Need to defer since .create() is called directly fhrom the constructor + // and thus the 'error' event can only be only bound *after* this exception + // occurs. Therefore, also, we cannot throw here at all. + setTimeout(function () { + self.onError(e); + }, 0); + return; + } + + if (global.document) { + this.index = Request.requestsCount++; + Request.requests[this.index] = this; + } + }; + + /** + * Called upon successful response. + * + * @api private + */ + + Request.prototype.onSuccess = function () { + this.emit('success'); + this.cleanup(); + }; + + /** + * Called if we have data. + * + * @api private + */ + + Request.prototype.onData = function (data) { + this.emit('data', data); + this.onSuccess(); + }; + + /** + * Called upon error. + * + * @api private + */ + + Request.prototype.onError = function (err) { + this.emit('error', err); + this.cleanup(true); + }; + + /** + * Cleans up house. + * + * @api private + */ + + Request.prototype.cleanup = function (fromError) { + if ('undefined' === typeof this.xhr || null === this.xhr) { + return; + } + // xmlhttprequest + if (this.hasXDR()) { + this.xhr.onload = this.xhr.onerror = empty; + } else { + this.xhr.onreadystatechange = empty; + } + + if (fromError) { + try { + this.xhr.abort(); + } catch (e) {} + } + + if (global.document) { + delete Request.requests[this.index]; + } + + this.xhr = null; + }; + + /** + * Called upon load. + * + * @api private + */ + + Request.prototype.onLoad = function () { + var data; + try { + var contentType; + try { + contentType = this.xhr.getResponseHeader('Content-Type').split(';')[0]; + } catch (e) {} + if (contentType === 'application/octet-stream') { + data = this.xhr.response || this.xhr.responseText; + } else { + if (!this.supportsBinary) { + data = this.xhr.responseText; + } else { + try { + data = String.fromCharCode.apply(null, new Uint8Array(this.xhr.response)); + } catch (e) { + var ui8Arr = new Uint8Array(this.xhr.response); + var dataArray = []; + for (var idx = 0, length = ui8Arr.length; idx < length; idx++) { + dataArray.push(ui8Arr[idx]); + } + + data = String.fromCharCode.apply(null, dataArray); + } + } + } + } catch (e) { + this.onError(e); + } + if (null != data) { + this.onData(data); + } + }; + + /** + * Check if it has XDomainRequest. + * + * @api private + */ + + Request.prototype.hasXDR = function () { + return 'undefined' !== typeof global.XDomainRequest && !this.xs && this.enablesXDR; + }; + + /** + * Aborts the request. + * + * @api public + */ + + Request.prototype.abort = function () { + this.cleanup(); + }; + + /** + * Aborts pending requests when unloading the window. This is needed to prevent + * memory leaks (e.g. when using IE) and to ensure that no spurious error is + * emitted. + */ + + Request.requestsCount = 0; + Request.requests = {}; + + if (global.document) { + if (global.attachEvent) { + global.attachEvent('onunload', unloadHandler); + } else if (global.addEventListener) { + global.addEventListener('beforeunload', unloadHandler, false); + } + } + + function unloadHandler () { + for (var i in Request.requests) { + if (Request.requests.hasOwnProperty(i)) { + Request.requests[i].abort(); + } + } + } + + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) + +/***/ }, +/* 25 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Module dependencies. + */ + + var Transport = __webpack_require__(26); + var parseqs = __webpack_require__(37); + var parser = __webpack_require__(27); + var inherit = __webpack_require__(38); + var yeast = __webpack_require__(39); + var debug = __webpack_require__(3)('engine.io-client:polling'); + + /** + * Module exports. + */ + + module.exports = Polling; + + /** + * Is XHR2 supported? + */ + + var hasXHR2 = (function () { + var XMLHttpRequest = __webpack_require__(22); + var xhr = new XMLHttpRequest({ xdomain: false }); + return null != xhr.responseType; + })(); + + /** + * Polling interface. + * + * @param {Object} opts + * @api private + */ + + function Polling (opts) { + var forceBase64 = (opts && opts.forceBase64); + if (!hasXHR2 || forceBase64) { + this.supportsBinary = false; + } + Transport.call(this, opts); + } + + /** + * Inherits from Transport. + */ + + inherit(Polling, Transport); + + /** + * Transport name. + */ + + Polling.prototype.name = 'polling'; + + /** + * Opens the socket (triggers polling). We write a PING message to determine + * when the transport is open. + * + * @api private + */ + + Polling.prototype.doOpen = function () { + this.poll(); + }; + + /** + * Pauses polling. + * + * @param {Function} callback upon buffers are flushed and transport is paused + * @api private + */ + + Polling.prototype.pause = function (onPause) { + var self = this; + + this.readyState = 'pausing'; + + function pause () { + debug('paused'); + self.readyState = 'paused'; + onPause(); + } + + if (this.polling || !this.writable) { + var total = 0; + + if (this.polling) { + debug('we are currently polling - waiting to pause'); + total++; + this.once('pollComplete', function () { + debug('pre-pause polling complete'); + --total || pause(); + }); + } + + if (!this.writable) { + debug('we are currently writing - waiting to pause'); + total++; + this.once('drain', function () { + debug('pre-pause writing complete'); + --total || pause(); + }); + } + } else { + pause(); + } + }; + + /** + * Starts polling cycle. + * + * @api public + */ + + Polling.prototype.poll = function () { + debug('polling'); + this.polling = true; + this.doPoll(); + this.emit('poll'); + }; + + /** + * Overloads onData to detect payloads. + * + * @api private + */ + + Polling.prototype.onData = function (data) { + var self = this; + debug('polling got data %s', data); + var callback = function (packet, index, total) { + // if its the first message we consider the transport open + if ('opening' === self.readyState) { + self.onOpen(); + } + + // if its a close packet, we close the ongoing requests + if ('close' === packet.type) { + self.onClose(); + return false; + } + + // otherwise bypass onData and handle the message + self.onPacket(packet); + }; + + // decode payload + parser.decodePayload(data, this.socket.binaryType, callback); + + // if an event did not trigger closing + if ('closed' !== this.readyState) { + // if we got data we're not polling + this.polling = false; + this.emit('pollComplete'); + + if ('open' === this.readyState) { + this.poll(); + } else { + debug('ignoring poll - transport state "%s"', this.readyState); + } + } + }; + + /** + * For polling, send a close packet. + * + * @api private + */ + + Polling.prototype.doClose = function () { + var self = this; + + function close () { + debug('writing close packet'); + self.write([{ type: 'close' }]); + } + + if ('open' === this.readyState) { + debug('transport open - closing'); + close(); + } else { + // in case we're trying to close while + // handshaking is in progress (GH-164) + debug('transport not open - deferring close'); + this.once('open', close); + } + }; + + /** + * Writes a packets payload. + * + * @param {Array} data packets + * @param {Function} drain callback + * @api private + */ + + Polling.prototype.write = function (packets) { + var self = this; + this.writable = false; + var callbackfn = function () { + self.writable = true; + self.emit('drain'); + }; + + parser.encodePayload(packets, this.supportsBinary, function (data) { + self.doWrite(data, callbackfn); + }); + }; + + /** + * Generates uri for connection. + * + * @api private + */ + + Polling.prototype.uri = function () { + var query = this.query || {}; + var schema = this.secure ? 'https' : 'http'; + var port = ''; + + // cache busting is forced + if (false !== this.timestampRequests) { + query[this.timestampParam] = yeast(); + } + + if (!this.supportsBinary && !query.sid) { + query.b64 = 1; + } + + query = parseqs.encode(query); + + // avoid port if default for schema + if (this.port && (('https' === schema && Number(this.port) !== 443) || + ('http' === schema && Number(this.port) !== 80))) { + port = ':' + this.port; + } + + // prepend ? to query + if (query.length) { + query = '?' + query; + } + + var ipv6 = this.hostname.indexOf(':') !== -1; + return schema + '://' + (ipv6 ? '[' + this.hostname + ']' : this.hostname) + port + this.path + query; + }; + + +/***/ }, +/* 26 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Module dependencies. + */ + + var parser = __webpack_require__(27); + var Emitter = __webpack_require__(36); + + /** + * Module exports. + */ + + module.exports = Transport; + + /** + * Transport abstract constructor. + * + * @param {Object} options. + * @api private + */ + + function Transport (opts) { + this.path = opts.path; + this.hostname = opts.hostname; + this.port = opts.port; + this.secure = opts.secure; + this.query = opts.query; + this.timestampParam = opts.timestampParam; + this.timestampRequests = opts.timestampRequests; + this.readyState = ''; + this.agent = opts.agent || false; + this.socket = opts.socket; + this.enablesXDR = opts.enablesXDR; + + // SSL options for Node.js client + this.pfx = opts.pfx; + this.key = opts.key; + this.passphrase = opts.passphrase; + this.cert = opts.cert; + this.ca = opts.ca; + this.ciphers = opts.ciphers; + this.rejectUnauthorized = opts.rejectUnauthorized; + this.forceNode = opts.forceNode; + + // other options for Node.js client + this.extraHeaders = opts.extraHeaders; + this.localAddress = opts.localAddress; + } + + /** + * Mix in `Emitter`. + */ + + Emitter(Transport.prototype); + + /** + * Emits an error. + * + * @param {String} str + * @return {Transport} for chaining + * @api public + */ + + Transport.prototype.onError = function (msg, desc) { + var err = new Error(msg); + err.type = 'TransportError'; + err.description = desc; + this.emit('error', err); + return this; + }; + + /** + * Opens the transport. + * + * @api public + */ + + Transport.prototype.open = function () { + if ('closed' === this.readyState || '' === this.readyState) { + this.readyState = 'opening'; + this.doOpen(); + } + + return this; + }; + + /** + * Closes the transport. + * + * @api private + */ + + Transport.prototype.close = function () { + if ('opening' === this.readyState || 'open' === this.readyState) { + this.doClose(); + this.onClose(); + } + + return this; + }; + + /** + * Sends multiple packets. + * + * @param {Array} packets + * @api private + */ + + Transport.prototype.send = function (packets) { + if ('open' === this.readyState) { + this.write(packets); + } else { + throw new Error('Transport not open'); + } + }; + + /** + * Called upon open + * + * @api private + */ + + Transport.prototype.onOpen = function () { + this.readyState = 'open'; + this.writable = true; + this.emit('open'); + }; + + /** + * Called with data. + * + * @param {String} data + * @api private + */ + + Transport.prototype.onData = function (data) { + var packet = parser.decodePacket(data, this.socket.binaryType); + this.onPacket(packet); + }; + + /** + * Called with a decoded packet. + */ + + Transport.prototype.onPacket = function (packet) { + this.emit('packet', packet); + }; + + /** + * Called upon close. + * + * @api private + */ + + Transport.prototype.onClose = function () { + this.readyState = 'closed'; + this.emit('close'); + }; + + +/***/ }, +/* 27 */ +/***/ function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(global) {/** + * Module dependencies. + */ + + var keys = __webpack_require__(28); + var hasBinary = __webpack_require__(29); + var sliceBuffer = __webpack_require__(31); + var after = __webpack_require__(32); + var utf8 = __webpack_require__(33); + + var base64encoder; + if (global && global.ArrayBuffer) { + base64encoder = __webpack_require__(34); + } + + /** + * Check if we are running an android browser. That requires us to use + * ArrayBuffer with polling transports... + * + * http://ghinda.net/jpeg-blob-ajax-android/ + */ + + var isAndroid = typeof navigator !== 'undefined' && /Android/i.test(navigator.userAgent); + + /** + * Check if we are running in PhantomJS. + * Uploading a Blob with PhantomJS does not work correctly, as reported here: + * https://github.com/ariya/phantomjs/issues/11395 + * @type boolean + */ + var isPhantomJS = typeof navigator !== 'undefined' && /PhantomJS/i.test(navigator.userAgent); + + /** + * When true, avoids using Blobs to encode payloads. + * @type boolean + */ + var dontSendBlobs = isAndroid || isPhantomJS; + + /** + * Current protocol version. + */ + + exports.protocol = 3; + + /** + * Packet types. + */ + + var packets = exports.packets = { + open: 0 // non-ws + , close: 1 // non-ws + , ping: 2 + , pong: 3 + , message: 4 + , upgrade: 5 + , noop: 6 + }; + + var packetslist = keys(packets); + + /** + * Premade error packet. + */ + + var err = { type: 'error', data: 'parser error' }; + + /** + * Create a blob api even for blob builder when vendor prefixes exist + */ + + var Blob = __webpack_require__(35); + + /** + * Encodes a packet. + * + * <packet type id> [ <data> ] + * + * Example: + * + * 5hello world + * 3 + * 4 + * + * Binary is encoded in an identical principle + * + * @api private + */ + + exports.encodePacket = function (packet, supportsBinary, utf8encode, callback) { + if ('function' == typeof supportsBinary) { + callback = supportsBinary; + supportsBinary = false; + } + + if ('function' == typeof utf8encode) { + callback = utf8encode; + utf8encode = null; + } + + var data = (packet.data === undefined) + ? undefined + : packet.data.buffer || packet.data; + + if (global.ArrayBuffer && data instanceof ArrayBuffer) { + return encodeArrayBuffer(packet, supportsBinary, callback); + } else if (Blob && data instanceof global.Blob) { + return encodeBlob(packet, supportsBinary, callback); + } + + // might be an object with { base64: true, data: dataAsBase64String } + if (data && data.base64) { + return encodeBase64Object(packet, callback); + } + + // Sending data as a utf-8 string + var encoded = packets[packet.type]; + + // data fragment is optional + if (undefined !== packet.data) { + encoded += utf8encode ? utf8.encode(String(packet.data)) : String(packet.data); + } + + return callback('' + encoded); + + }; + + function encodeBase64Object(packet, callback) { + // packet data is an object { base64: true, data: dataAsBase64String } + var message = 'b' + exports.packets[packet.type] + packet.data.data; + return callback(message); + } + + /** + * Encode packet helpers for binary types + */ + + function encodeArrayBuffer(packet, supportsBinary, callback) { + if (!supportsBinary) { + return exports.encodeBase64Packet(packet, callback); + } + + var data = packet.data; + var contentArray = new Uint8Array(data); + var resultBuffer = new Uint8Array(1 + data.byteLength); + + resultBuffer[0] = packets[packet.type]; + for (var i = 0; i < contentArray.length; i++) { + resultBuffer[i+1] = contentArray[i]; + } + + return callback(resultBuffer.buffer); + } + + function encodeBlobAsArrayBuffer(packet, supportsBinary, callback) { + if (!supportsBinary) { + return exports.encodeBase64Packet(packet, callback); + } + + var fr = new FileReader(); + fr.onload = function() { + packet.data = fr.result; + exports.encodePacket(packet, supportsBinary, true, callback); + }; + return fr.readAsArrayBuffer(packet.data); + } + + function encodeBlob(packet, supportsBinary, callback) { + if (!supportsBinary) { + return exports.encodeBase64Packet(packet, callback); + } + + if (dontSendBlobs) { + return encodeBlobAsArrayBuffer(packet, supportsBinary, callback); + } + + var length = new Uint8Array(1); + length[0] = packets[packet.type]; + var blob = new Blob([length.buffer, packet.data]); + + return callback(blob); + } + + /** + * Encodes a packet with binary data in a base64 string + * + * @param {Object} packet, has `type` and `data` + * @return {String} base64 encoded message + */ + + exports.encodeBase64Packet = function(packet, callback) { + var message = 'b' + exports.packets[packet.type]; + if (Blob && packet.data instanceof global.Blob) { + var fr = new FileReader(); + fr.onload = function() { + var b64 = fr.result.split(',')[1]; + callback(message + b64); + }; + return fr.readAsDataURL(packet.data); + } + + var b64data; + try { + b64data = String.fromCharCode.apply(null, new Uint8Array(packet.data)); + } catch (e) { + // iPhone Safari doesn't let you apply with typed arrays + var typed = new Uint8Array(packet.data); + var basic = new Array(typed.length); + for (var i = 0; i < typed.length; i++) { + basic[i] = typed[i]; + } + b64data = String.fromCharCode.apply(null, basic); + } + message += global.btoa(b64data); + return callback(message); + }; + + /** + * Decodes a packet. Changes format to Blob if requested. + * + * @return {Object} with `type` and `data` (if any) + * @api private + */ + + exports.decodePacket = function (data, binaryType, utf8decode) { + if (data === undefined) { + return err; + } + // String data + if (typeof data == 'string') { + if (data.charAt(0) == 'b') { + return exports.decodeBase64Packet(data.substr(1), binaryType); + } + + if (utf8decode) { + data = tryDecode(data); + if (data === false) { + return err; + } + } + var type = data.charAt(0); + + if (Number(type) != type || !packetslist[type]) { + return err; + } + + if (data.length > 1) { + return { type: packetslist[type], data: data.substring(1) }; + } else { + return { type: packetslist[type] }; + } + } + + var asArray = new Uint8Array(data); + var type = asArray[0]; + var rest = sliceBuffer(data, 1); + if (Blob && binaryType === 'blob') { + rest = new Blob([rest]); + } + return { type: packetslist[type], data: rest }; + }; + + function tryDecode(data) { + try { + data = utf8.decode(data); + } catch (e) { + return false; + } + return data; + } + + /** + * Decodes a packet encoded in a base64 string + * + * @param {String} base64 encoded message + * @return {Object} with `type` and `data` (if any) + */ + + exports.decodeBase64Packet = function(msg, binaryType) { + var type = packetslist[msg.charAt(0)]; + if (!base64encoder) { + return { type: type, data: { base64: true, data: msg.substr(1) } }; + } + + var data = base64encoder.decode(msg.substr(1)); + + if (binaryType === 'blob' && Blob) { + data = new Blob([data]); + } + + return { type: type, data: data }; + }; + + /** + * Encodes multiple messages (payload). + * + * <length>:data + * + * Example: + * + * 11:hello world2:hi + * + * If any contents are binary, they will be encoded as base64 strings. Base64 + * encoded strings are marked with a b before the length specifier + * + * @param {Array} packets + * @api private + */ + + exports.encodePayload = function (packets, supportsBinary, callback) { + if (typeof supportsBinary == 'function') { + callback = supportsBinary; + supportsBinary = null; + } + + var isBinary = hasBinary(packets); + + if (supportsBinary && isBinary) { + if (Blob && !dontSendBlobs) { + return exports.encodePayloadAsBlob(packets, callback); + } + + return exports.encodePayloadAsArrayBuffer(packets, callback); + } + + if (!packets.length) { + return callback('0:'); + } + + function setLengthHeader(message) { + return message.length + ':' + message; + } + + function encodeOne(packet, doneCallback) { + exports.encodePacket(packet, !isBinary ? false : supportsBinary, true, function(message) { + doneCallback(null, setLengthHeader(message)); + }); + } + + map(packets, encodeOne, function(err, results) { + return callback(results.join('')); + }); + }; + + /** + * Async array map using after + */ + + function map(ary, each, done) { + var result = new Array(ary.length); + var next = after(ary.length, done); + + var eachWithIndex = function(i, el, cb) { + each(el, function(error, msg) { + result[i] = msg; + cb(error, result); + }); + }; + + for (var i = 0; i < ary.length; i++) { + eachWithIndex(i, ary[i], next); + } + } + + /* + * Decodes data when a payload is maybe expected. Possible binary contents are + * decoded from their base64 representation + * + * @param {String} data, callback method + * @api public + */ + + exports.decodePayload = function (data, binaryType, callback) { + if (typeof data != 'string') { + return exports.decodePayloadAsBinary(data, binaryType, callback); + } + + if (typeof binaryType === 'function') { + callback = binaryType; + binaryType = null; + } + + var packet; + if (data == '') { + // parser error - ignoring payload + return callback(err, 0, 1); + } + + var length = '' + , n, msg; + + for (var i = 0, l = data.length; i < l; i++) { + var chr = data.charAt(i); + + if (':' != chr) { + length += chr; + } else { + if ('' == length || (length != (n = Number(length)))) { + // parser error - ignoring payload + return callback(err, 0, 1); + } + + msg = data.substr(i + 1, n); + + if (length != msg.length) { + // parser error - ignoring payload + return callback(err, 0, 1); + } + + if (msg.length) { + packet = exports.decodePacket(msg, binaryType, true); + + if (err.type == packet.type && err.data == packet.data) { + // parser error in individual packet - ignoring payload + return callback(err, 0, 1); + } + + var ret = callback(packet, i + n, l); + if (false === ret) return; + } + + // advance cursor + i += n; + length = ''; + } + } + + if (length != '') { + // parser error - ignoring payload + return callback(err, 0, 1); + } + + }; + + /** + * Encodes multiple messages (payload) as binary. + * + * <1 = binary, 0 = string><number from 0-9><number from 0-9>[...]<number + * 255><data> + * + * Example: + * 1 3 255 1 2 3, if the binary contents are interpreted as 8 bit integers + * + * @param {Array} packets + * @return {ArrayBuffer} encoded payload + * @api private + */ + + exports.encodePayloadAsArrayBuffer = function(packets, callback) { + if (!packets.length) { + return callback(new ArrayBuffer(0)); + } + + function encodeOne(packet, doneCallback) { + exports.encodePacket(packet, true, true, function(data) { + return doneCallback(null, data); + }); + } + + map(packets, encodeOne, function(err, encodedPackets) { + var totalLength = encodedPackets.reduce(function(acc, p) { + var len; + if (typeof p === 'string'){ + len = p.length; + } else { + len = p.byteLength; + } + return acc + len.toString().length + len + 2; // string/binary identifier + separator = 2 + }, 0); + + var resultArray = new Uint8Array(totalLength); + + var bufferIndex = 0; + encodedPackets.forEach(function(p) { + var isString = typeof p === 'string'; + var ab = p; + if (isString) { + var view = new Uint8Array(p.length); + for (var i = 0; i < p.length; i++) { + view[i] = p.charCodeAt(i); + } + ab = view.buffer; + } + + if (isString) { // not true binary + resultArray[bufferIndex++] = 0; + } else { // true binary + resultArray[bufferIndex++] = 1; + } + + var lenStr = ab.byteLength.toString(); + for (var i = 0; i < lenStr.length; i++) { + resultArray[bufferIndex++] = parseInt(lenStr[i]); + } + resultArray[bufferIndex++] = 255; + + var view = new Uint8Array(ab); + for (var i = 0; i < view.length; i++) { + resultArray[bufferIndex++] = view[i]; + } + }); + + return callback(resultArray.buffer); + }); + }; + + /** + * Encode as Blob + */ + + exports.encodePayloadAsBlob = function(packets, callback) { + function encodeOne(packet, doneCallback) { + exports.encodePacket(packet, true, true, function(encoded) { + var binaryIdentifier = new Uint8Array(1); + binaryIdentifier[0] = 1; + if (typeof encoded === 'string') { + var view = new Uint8Array(encoded.length); + for (var i = 0; i < encoded.length; i++) { + view[i] = encoded.charCodeAt(i); + } + encoded = view.buffer; + binaryIdentifier[0] = 0; + } + + var len = (encoded instanceof ArrayBuffer) + ? encoded.byteLength + : encoded.size; + + var lenStr = len.toString(); + var lengthAry = new Uint8Array(lenStr.length + 1); + for (var i = 0; i < lenStr.length; i++) { + lengthAry[i] = parseInt(lenStr[i]); + } + lengthAry[lenStr.length] = 255; + + if (Blob) { + var blob = new Blob([binaryIdentifier.buffer, lengthAry.buffer, encoded]); + doneCallback(null, blob); + } + }); + } + + map(packets, encodeOne, function(err, results) { + return callback(new Blob(results)); + }); + }; + + /* + * Decodes data when a payload is maybe expected. Strings are decoded by + * interpreting each byte as a key code for entries marked to start with 0. See + * description of encodePayloadAsBinary + * + * @param {ArrayBuffer} data, callback method + * @api public + */ + + exports.decodePayloadAsBinary = function (data, binaryType, callback) { + if (typeof binaryType === 'function') { + callback = binaryType; + binaryType = null; + } + + var bufferTail = data; + var buffers = []; + + var numberTooLong = false; + while (bufferTail.byteLength > 0) { + var tailArray = new Uint8Array(bufferTail); + var isString = tailArray[0] === 0; + var msgLength = ''; + + for (var i = 1; ; i++) { + if (tailArray[i] == 255) break; + + if (msgLength.length > 310) { + numberTooLong = true; + break; + } + + msgLength += tailArray[i]; + } + + if(numberTooLong) return callback(err, 0, 1); + + bufferTail = sliceBuffer(bufferTail, 2 + msgLength.length); + msgLength = parseInt(msgLength); + + var msg = sliceBuffer(bufferTail, 0, msgLength); + if (isString) { + try { + msg = String.fromCharCode.apply(null, new Uint8Array(msg)); + } catch (e) { + // iPhone Safari doesn't let you apply to typed arrays + var typed = new Uint8Array(msg); + msg = ''; + for (var i = 0; i < typed.length; i++) { + msg += String.fromCharCode(typed[i]); + } + } + } + + buffers.push(msg); + bufferTail = sliceBuffer(bufferTail, msgLength); + } + + var total = buffers.length; + buffers.forEach(function(buffer, i) { + callback(exports.decodePacket(buffer, binaryType, true), i, total); + }); + }; + + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) + +/***/ }, +/* 28 */ +/***/ function(module, exports) { + + + /** + * Gets the keys for an object. + * + * @return {Array} keys + * @api private + */ + + module.exports = Object.keys || function keys (obj){ + var arr = []; + var has = Object.prototype.hasOwnProperty; + + for (var i in obj) { + if (has.call(obj, i)) { + arr.push(i); + } + } + return arr; + }; + + +/***/ }, +/* 29 */ +/***/ function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(global) { + /* + * Module requirements. + */ + + var isArray = __webpack_require__(30); + + /** + * Module exports. + */ + + module.exports = hasBinary; + + /** + * Checks for binary data. + * + * Right now only Buffer and ArrayBuffer are supported.. + * + * @param {Object} anything + * @api public + */ + + function hasBinary(data) { + + function _hasBinary(obj) { + if (!obj) return false; + + if ( (global.Buffer && global.Buffer.isBuffer(obj)) || + (global.ArrayBuffer && obj instanceof ArrayBuffer) || + (global.Blob && obj instanceof Blob) || + (global.File && obj instanceof File) + ) { + return true; + } + + if (isArray(obj)) { + for (var i = 0; i < obj.length; i++) { + if (_hasBinary(obj[i])) { + return true; + } + } + } else if (obj && 'object' == typeof obj) { + if (obj.toJSON) { + obj = obj.toJSON(); + } + + for (var key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key) && _hasBinary(obj[key])) { + return true; + } + } + } + + return false; + } + + return _hasBinary(data); + } + + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) + +/***/ }, +/* 30 */ +/***/ function(module, exports) { + + module.exports = Array.isArray || function (arr) { + return Object.prototype.toString.call(arr) == '[object Array]'; + }; + + +/***/ }, +/* 31 */ +/***/ function(module, exports) { + + /** + * An abstraction for slicing an arraybuffer even when + * ArrayBuffer.prototype.slice is not supported + * + * @api public + */ + + module.exports = function(arraybuffer, start, end) { + var bytes = arraybuffer.byteLength; + start = start || 0; + end = end || bytes; + + if (arraybuffer.slice) { return arraybuffer.slice(start, end); } + + if (start < 0) { start += bytes; } + if (end < 0) { end += bytes; } + if (end > bytes) { end = bytes; } + + if (start >= bytes || start >= end || bytes === 0) { + return new ArrayBuffer(0); + } + + var abv = new Uint8Array(arraybuffer); + var result = new Uint8Array(end - start); + for (var i = start, ii = 0; i < end; i++, ii++) { + result[ii] = abv[i]; + } + return result.buffer; + }; + + +/***/ }, +/* 32 */ +/***/ function(module, exports) { + + module.exports = after + + function after(count, callback, err_cb) { + var bail = false + err_cb = err_cb || noop + proxy.count = count + + return (count === 0) ? callback() : proxy + + function proxy(err, result) { + if (proxy.count <= 0) { + throw new Error('after called too many times') + } + --proxy.count + + // after first error, rest are passed to err_cb + if (err) { + bail = true + callback(err) + // future error callbacks will go to error handler + callback = err_cb + } else if (proxy.count === 0 && !bail) { + callback(null, result) + } + } + } + + function noop() {} + + +/***/ }, +/* 33 */ +/***/ function(module, exports, __webpack_require__) { + + var __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(module, global) {/*! https://mths.be/wtf8 v1.0.0 by @mathias */ + ;(function(root) { + + // Detect free variables `exports` + var freeExports = typeof exports == 'object' && exports; + + // Detect free variable `module` + var freeModule = typeof module == 'object' && module && + module.exports == freeExports && module; + + // Detect free variable `global`, from Node.js or Browserified code, + // and use it as `root` + var freeGlobal = typeof global == 'object' && global; + if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) { + root = freeGlobal; + } + + /*--------------------------------------------------------------------------*/ + + var stringFromCharCode = String.fromCharCode; + + // Taken from https://mths.be/punycode + function ucs2decode(string) { + var output = []; + var counter = 0; + var length = string.length; + var value; + var extra; + while (counter < length) { + value = string.charCodeAt(counter++); + if (value >= 0xD800 && value <= 0xDBFF && counter < length) { + // high surrogate, and there is a next character + extra = string.charCodeAt(counter++); + if ((extra & 0xFC00) == 0xDC00) { // low surrogate + output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000); + } else { + // unmatched surrogate; only append this code unit, in case the next + // code unit is the high surrogate of a surrogate pair + output.push(value); + counter--; + } + } else { + output.push(value); + } + } + return output; + } + + // Taken from https://mths.be/punycode + function ucs2encode(array) { + var length = array.length; + var index = -1; + var value; + var output = ''; + while (++index < length) { + value = array[index]; + if (value > 0xFFFF) { + value -= 0x10000; + output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800); + value = 0xDC00 | value & 0x3FF; + } + output += stringFromCharCode(value); + } + return output; + } + + /*--------------------------------------------------------------------------*/ + + function createByte(codePoint, shift) { + return stringFromCharCode(((codePoint >> shift) & 0x3F) | 0x80); + } + + function encodeCodePoint(codePoint) { + if ((codePoint & 0xFFFFFF80) == 0) { // 1-byte sequence + return stringFromCharCode(codePoint); + } + var symbol = ''; + if ((codePoint & 0xFFFFF800) == 0) { // 2-byte sequence + symbol = stringFromCharCode(((codePoint >> 6) & 0x1F) | 0xC0); + } + else if ((codePoint & 0xFFFF0000) == 0) { // 3-byte sequence + symbol = stringFromCharCode(((codePoint >> 12) & 0x0F) | 0xE0); + symbol += createByte(codePoint, 6); + } + else if ((codePoint & 0xFFE00000) == 0) { // 4-byte sequence + symbol = stringFromCharCode(((codePoint >> 18) & 0x07) | 0xF0); + symbol += createByte(codePoint, 12); + symbol += createByte(codePoint, 6); + } + symbol += stringFromCharCode((codePoint & 0x3F) | 0x80); + return symbol; + } + + function wtf8encode(string) { + var codePoints = ucs2decode(string); + var length = codePoints.length; + var index = -1; + var codePoint; + var byteString = ''; + while (++index < length) { + codePoint = codePoints[index]; + byteString += encodeCodePoint(codePoint); + } + return byteString; + } + + /*--------------------------------------------------------------------------*/ + + function readContinuationByte() { + if (byteIndex >= byteCount) { + throw Error('Invalid byte index'); + } + + var continuationByte = byteArray[byteIndex] & 0xFF; + byteIndex++; + + if ((continuationByte & 0xC0) == 0x80) { + return continuationByte & 0x3F; + } + + // If we end up here, it’s not a continuation byte. + throw Error('Invalid continuation byte'); + } + + function decodeSymbol() { + var byte1; + var byte2; + var byte3; + var byte4; + var codePoint; + + if (byteIndex > byteCount) { + throw Error('Invalid byte index'); + } + + if (byteIndex == byteCount) { + return false; + } + + // Read the first byte. + byte1 = byteArray[byteIndex] & 0xFF; + byteIndex++; + + // 1-byte sequence (no continuation bytes) + if ((byte1 & 0x80) == 0) { + return byte1; + } + + // 2-byte sequence + if ((byte1 & 0xE0) == 0xC0) { + var byte2 = readContinuationByte(); + codePoint = ((byte1 & 0x1F) << 6) | byte2; + if (codePoint >= 0x80) { + return codePoint; + } else { + throw Error('Invalid continuation byte'); + } + } + + // 3-byte sequence (may include unpaired surrogates) + if ((byte1 & 0xF0) == 0xE0) { + byte2 = readContinuationByte(); + byte3 = readContinuationByte(); + codePoint = ((byte1 & 0x0F) << 12) | (byte2 << 6) | byte3; + if (codePoint >= 0x0800) { + return codePoint; + } else { + throw Error('Invalid continuation byte'); + } + } + + // 4-byte sequence + if ((byte1 & 0xF8) == 0xF0) { + byte2 = readContinuationByte(); + byte3 = readContinuationByte(); + byte4 = readContinuationByte(); + codePoint = ((byte1 & 0x0F) << 0x12) | (byte2 << 0x0C) | + (byte3 << 0x06) | byte4; + if (codePoint >= 0x010000 && codePoint <= 0x10FFFF) { + return codePoint; + } + } + + throw Error('Invalid WTF-8 detected'); + } + + var byteArray; + var byteCount; + var byteIndex; + function wtf8decode(byteString) { + byteArray = ucs2decode(byteString); + byteCount = byteArray.length; + byteIndex = 0; + var codePoints = []; + var tmp; + while ((tmp = decodeSymbol()) !== false) { + codePoints.push(tmp); + } + return ucs2encode(codePoints); + } + + /*--------------------------------------------------------------------------*/ + + var wtf8 = { + 'version': '1.0.0', + 'encode': wtf8encode, + 'decode': wtf8decode + }; + + // Some AMD build optimizers, like r.js, check for specific condition patterns + // like the following: + if ( + true + ) { + !(__WEBPACK_AMD_DEFINE_RESULT__ = function() { + return wtf8; + }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + } else if (freeExports && !freeExports.nodeType) { + if (freeModule) { // in Node.js or RingoJS v0.8.0+ + freeModule.exports = wtf8; + } else { // in Narwhal or RingoJS v0.7.0- + var object = {}; + var hasOwnProperty = object.hasOwnProperty; + for (var key in wtf8) { + hasOwnProperty.call(wtf8, key) && (freeExports[key] = wtf8[key]); + } + } + } else { // in Rhino or a web browser + root.wtf8 = wtf8; + } + + }(this)); + + /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(12)(module), (function() { return this; }()))) + +/***/ }, +/* 34 */ +/***/ function(module, exports) { + + /* + * base64-arraybuffer + * https://github.com/niklasvh/base64-arraybuffer + * + * Copyright (c) 2012 Niklas von Hertzen + * Licensed under the MIT license. + */ + (function(){ + "use strict"; + + var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + // Use a lookup table to find the index. + var lookup = new Uint8Array(256); + for (var i = 0; i < chars.length; i++) { + lookup[chars.charCodeAt(i)] = i; + } + + exports.encode = function(arraybuffer) { + var bytes = new Uint8Array(arraybuffer), + i, len = bytes.length, base64 = ""; + + for (i = 0; i < len; i+=3) { + base64 += chars[bytes[i] >> 2]; + base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)]; + base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)]; + base64 += chars[bytes[i + 2] & 63]; + } + + if ((len % 3) === 2) { + base64 = base64.substring(0, base64.length - 1) + "="; + } else if (len % 3 === 1) { + base64 = base64.substring(0, base64.length - 2) + "=="; + } + + return base64; + }; + + exports.decode = function(base64) { + var bufferLength = base64.length * 0.75, + len = base64.length, i, p = 0, + encoded1, encoded2, encoded3, encoded4; + + if (base64[base64.length - 1] === "=") { + bufferLength--; + if (base64[base64.length - 2] === "=") { + bufferLength--; + } + } + + var arraybuffer = new ArrayBuffer(bufferLength), + bytes = new Uint8Array(arraybuffer); + + for (i = 0; i < len; i+=4) { + encoded1 = lookup[base64.charCodeAt(i)]; + encoded2 = lookup[base64.charCodeAt(i+1)]; + encoded3 = lookup[base64.charCodeAt(i+2)]; + encoded4 = lookup[base64.charCodeAt(i+3)]; + + bytes[p++] = (encoded1 << 2) | (encoded2 >> 4); + bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2); + bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63); + } + + return arraybuffer; + }; + })(); + + +/***/ }, +/* 35 */ +/***/ function(module, exports) { + + /* WEBPACK VAR INJECTION */(function(global) {/** + * Create a blob builder even when vendor prefixes exist + */ + + var BlobBuilder = global.BlobBuilder + || global.WebKitBlobBuilder + || global.MSBlobBuilder + || global.MozBlobBuilder; + + /** + * Check if Blob constructor is supported + */ + + var blobSupported = (function() { + try { + var a = new Blob(['hi']); + return a.size === 2; + } catch(e) { + return false; + } + })(); + + /** + * Check if Blob constructor supports ArrayBufferViews + * Fails in Safari 6, so we need to map to ArrayBuffers there. + */ + + var blobSupportsArrayBufferView = blobSupported && (function() { + try { + var b = new Blob([new Uint8Array([1,2])]); + return b.size === 2; + } catch(e) { + return false; + } + })(); + + /** + * Check if BlobBuilder is supported + */ + + var blobBuilderSupported = BlobBuilder + && BlobBuilder.prototype.append + && BlobBuilder.prototype.getBlob; + + /** + * Helper function that maps ArrayBufferViews to ArrayBuffers + * Used by BlobBuilder constructor and old browsers that didn't + * support it in the Blob constructor. + */ + + function mapArrayBufferViews(ary) { + for (var i = 0; i < ary.length; i++) { + var chunk = ary[i]; + if (chunk.buffer instanceof ArrayBuffer) { + var buf = chunk.buffer; + + // if this is a subarray, make a copy so we only + // include the subarray region from the underlying buffer + if (chunk.byteLength !== buf.byteLength) { + var copy = new Uint8Array(chunk.byteLength); + copy.set(new Uint8Array(buf, chunk.byteOffset, chunk.byteLength)); + buf = copy.buffer; + } + + ary[i] = buf; + } + } + } + + function BlobBuilderConstructor(ary, options) { + options = options || {}; + + var bb = new BlobBuilder(); + mapArrayBufferViews(ary); + + for (var i = 0; i < ary.length; i++) { + bb.append(ary[i]); + } + + return (options.type) ? bb.getBlob(options.type) : bb.getBlob(); + }; + + function BlobConstructor(ary, options) { + mapArrayBufferViews(ary); + return new Blob(ary, options || {}); + }; + + module.exports = (function() { + if (blobSupported) { + return blobSupportsArrayBufferView ? global.Blob : BlobConstructor; + } else if (blobBuilderSupported) { + return BlobBuilderConstructor; + } else { + return undefined; + } + })(); + + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) + +/***/ }, +/* 36 */ +/***/ function(module, exports, __webpack_require__) { + + + /** + * Expose `Emitter`. + */ + + if (true) { + module.exports = Emitter; + } + + /** + * Initialize a new `Emitter`. + * + * @api public + */ + + function Emitter(obj) { + if (obj) return mixin(obj); + }; + + /** + * Mixin the emitter properties. + * + * @param {Object} obj + * @return {Object} + * @api private + */ + + function mixin(obj) { + for (var key in Emitter.prototype) { + obj[key] = Emitter.prototype[key]; + } + return obj; + } + + /** + * Listen on the given `event` with `fn`. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public + */ + + Emitter.prototype.on = + Emitter.prototype.addEventListener = function(event, fn){ + this._callbacks = this._callbacks || {}; + (this._callbacks['$' + event] = this._callbacks['$' + event] || []) + .push(fn); + return this; + }; + + /** + * Adds an `event` listener that will be invoked a single + * time then automatically removed. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public + */ + + Emitter.prototype.once = function(event, fn){ + function on() { + this.off(event, on); + fn.apply(this, arguments); + } + + on.fn = fn; + this.on(event, on); + return this; + }; + + /** + * Remove the given callback for `event` or all + * registered callbacks. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public + */ + + Emitter.prototype.off = + Emitter.prototype.removeListener = + Emitter.prototype.removeAllListeners = + Emitter.prototype.removeEventListener = function(event, fn){ + this._callbacks = this._callbacks || {}; + + // all + if (0 == arguments.length) { + this._callbacks = {}; + return this; + } + + // specific event + var callbacks = this._callbacks['$' + event]; + if (!callbacks) return this; + + // remove all handlers + if (1 == arguments.length) { + delete this._callbacks['$' + event]; + return this; + } + + // remove specific handler + var cb; + for (var i = 0; i < callbacks.length; i++) { + cb = callbacks[i]; + if (cb === fn || cb.fn === fn) { + callbacks.splice(i, 1); + break; + } + } + return this; + }; + + /** + * Emit `event` with the given args. + * + * @param {String} event + * @param {Mixed} ... + * @return {Emitter} + */ + + Emitter.prototype.emit = function(event){ + this._callbacks = this._callbacks || {}; + var args = [].slice.call(arguments, 1) + , callbacks = this._callbacks['$' + event]; + + if (callbacks) { + callbacks = callbacks.slice(0); + for (var i = 0, len = callbacks.length; i < len; ++i) { + callbacks[i].apply(this, args); + } + } + + return this; + }; + + /** + * Return array of callbacks for `event`. + * + * @param {String} event + * @return {Array} + * @api public + */ + + Emitter.prototype.listeners = function(event){ + this._callbacks = this._callbacks || {}; + return this._callbacks['$' + event] || []; + }; + + /** + * Check if this emitter has `event` handlers. + * + * @param {String} event + * @return {Boolean} + * @api public + */ + + Emitter.prototype.hasListeners = function(event){ + return !! this.listeners(event).length; + }; + + +/***/ }, +/* 37 */ +/***/ function(module, exports) { + + /** + * Compiles a querystring + * Returns string representation of the object + * + * @param {Object} + * @api private + */ + + exports.encode = function (obj) { + var str = ''; + + for (var i in obj) { + if (obj.hasOwnProperty(i)) { + if (str.length) str += '&'; + str += encodeURIComponent(i) + '=' + encodeURIComponent(obj[i]); + } + } + + return str; + }; + + /** + * Parses a simple querystring into an object + * + * @param {String} qs + * @api private + */ + + exports.decode = function(qs){ + var qry = {}; + var pairs = qs.split('&'); + for (var i = 0, l = pairs.length; i < l; i++) { + var pair = pairs[i].split('='); + qry[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]); + } + return qry; + }; + + +/***/ }, +/* 38 */ +/***/ function(module, exports) { + + + module.exports = function(a, b){ + var fn = function(){}; + fn.prototype = b.prototype; + a.prototype = new fn; + a.prototype.constructor = a; + }; + +/***/ }, +/* 39 */ +/***/ function(module, exports) { + + 'use strict'; + + var alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_'.split('') + , length = 64 + , map = {} + , seed = 0 + , i = 0 + , prev; + + /** + * Return a string representing the specified number. + * + * @param {Number} num The number to convert. + * @returns {String} The string representation of the number. + * @api public + */ + function encode(num) { + var encoded = ''; + + do { + encoded = alphabet[num % length] + encoded; + num = Math.floor(num / length); + } while (num > 0); + + return encoded; + } + + /** + * Return the integer value specified by the given string. + * + * @param {String} str The string to convert. + * @returns {Number} The integer value represented by the string. + * @api public + */ + function decode(str) { + var decoded = 0; + + for (i = 0; i < str.length; i++) { + decoded = decoded * length + map[str.charAt(i)]; + } + + return decoded; + } + + /** + * Yeast: A tiny growing id generator. + * + * @returns {String} A unique id. + * @api public + */ + function yeast() { + var now = encode(+new Date()); + + if (now !== prev) return seed = 0, prev = now; + return now +'.'+ encode(seed++); + } + + // + // Map each character to its index. + // + for (; i < length; i++) map[alphabet[i]] = i; + + // + // Expose the `yeast`, `encode` and `decode` functions. + // + yeast.encode = encode; + yeast.decode = decode; + module.exports = yeast; + + +/***/ }, +/* 40 */ +/***/ function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(global) { + /** + * Module requirements. + */ + + var Polling = __webpack_require__(25); + var inherit = __webpack_require__(38); + + /** + * Module exports. + */ + + module.exports = JSONPPolling; + + /** + * Cached regular expressions. + */ + + var rNewline = /\n/g; + var rEscapedNewline = /\\n/g; + + /** + * Global JSONP callbacks. + */ + + var callbacks; + + /** + * Noop. + */ + + function empty () { } + + /** + * JSONP Polling constructor. + * + * @param {Object} opts. + * @api public + */ + + function JSONPPolling (opts) { + Polling.call(this, opts); + + this.query = this.query || {}; + + // define global callbacks array if not present + // we do this here (lazily) to avoid unneeded global pollution + if (!callbacks) { + // we need to consider multiple engines in the same page + if (!global.___eio) global.___eio = []; + callbacks = global.___eio; + } + + // callback identifier + this.index = callbacks.length; + + // add callback to jsonp global + var self = this; + callbacks.push(function (msg) { + self.onData(msg); + }); + + // append to query string + this.query.j = this.index; + + // prevent spurious errors from being emitted when the window is unloaded + if (global.document && global.addEventListener) { + global.addEventListener('beforeunload', function () { + if (self.script) self.script.onerror = empty; + }, false); + } + } + + /** + * Inherits from Polling. + */ + + inherit(JSONPPolling, Polling); + + /* + * JSONP only supports binary as base64 encoded strings + */ + + JSONPPolling.prototype.supportsBinary = false; + + /** + * Closes the socket. + * + * @api private + */ + + JSONPPolling.prototype.doClose = function () { + if (this.script) { + this.script.parentNode.removeChild(this.script); + this.script = null; + } + + if (this.form) { + this.form.parentNode.removeChild(this.form); + this.form = null; + this.iframe = null; + } + + Polling.prototype.doClose.call(this); + }; + + /** + * Starts a poll cycle. + * + * @api private + */ + + JSONPPolling.prototype.doPoll = function () { + var self = this; + var script = document.createElement('script'); + + if (this.script) { + this.script.parentNode.removeChild(this.script); + this.script = null; + } + + script.async = true; + script.src = this.uri(); + script.onerror = function (e) { + self.onError('jsonp poll error', e); + }; + + var insertAt = document.getElementsByTagName('script')[0]; + if (insertAt) { + insertAt.parentNode.insertBefore(script, insertAt); + } else { + (document.head || document.body).appendChild(script); + } + this.script = script; + + var isUAgecko = 'undefined' !== typeof navigator && /gecko/i.test(navigator.userAgent); + + if (isUAgecko) { + setTimeout(function () { + var iframe = document.createElement('iframe'); + document.body.appendChild(iframe); + document.body.removeChild(iframe); + }, 100); + } + }; + + /** + * Writes with a hidden iframe. + * + * @param {String} data to send + * @param {Function} called upon flush. + * @api private + */ + + JSONPPolling.prototype.doWrite = function (data, fn) { + var self = this; + + if (!this.form) { + var form = document.createElement('form'); + var area = document.createElement('textarea'); + var id = this.iframeId = 'eio_iframe_' + this.index; + var iframe; + + form.className = 'socketio'; + form.style.position = 'absolute'; + form.style.top = '-1000px'; + form.style.left = '-1000px'; + form.target = id; + form.method = 'POST'; + form.setAttribute('accept-charset', 'utf-8'); + area.name = 'd'; + form.appendChild(area); + document.body.appendChild(form); + + this.form = form; + this.area = area; + } + + this.form.action = this.uri(); + + function complete () { + initIframe(); + fn(); + } + + function initIframe () { + if (self.iframe) { + try { + self.form.removeChild(self.iframe); + } catch (e) { + self.onError('jsonp polling iframe removal error', e); + } + } + + try { + // ie6 dynamic iframes with target="" support (thanks Chris Lambacher) + var html = '<iframe src="javascript:0" name="' + self.iframeId + '">'; + iframe = document.createElement(html); + } catch (e) { + iframe = document.createElement('iframe'); + iframe.name = self.iframeId; + iframe.src = 'javascript:0'; + } + + iframe.id = self.iframeId; + + self.form.appendChild(iframe); + self.iframe = iframe; + } + + initIframe(); + + // escape \n to prevent it from being converted into \r\n by some UAs + // double escaping is required for escaped new lines because unescaping of new lines can be done safely on server-side + data = data.replace(rEscapedNewline, '\\\n'); + this.area.value = data.replace(rNewline, '\\n'); + + try { + this.form.submit(); + } catch (e) {} + + if (this.iframe.attachEvent) { + this.iframe.onreadystatechange = function () { + if (self.iframe.readyState === 'complete') { + complete(); + } + }; + } else { + this.iframe.onload = complete; + } + }; + + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) + +/***/ }, +/* 41 */ +/***/ function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(global) {/** + * Module dependencies. + */ + + var Transport = __webpack_require__(26); + var parser = __webpack_require__(27); + var parseqs = __webpack_require__(37); + var inherit = __webpack_require__(38); + var yeast = __webpack_require__(39); + var debug = __webpack_require__(3)('engine.io-client:websocket'); + var BrowserWebSocket = global.WebSocket || global.MozWebSocket; + var NodeWebSocket; + if (typeof window === 'undefined') { + try { + NodeWebSocket = __webpack_require__(42); + } catch (e) { } + } + + /** + * Get either the `WebSocket` or `MozWebSocket` globals + * in the browser or try to resolve WebSocket-compatible + * interface exposed by `ws` for Node-like environment. + */ + + var WebSocket = BrowserWebSocket; + if (!WebSocket && typeof window === 'undefined') { + WebSocket = NodeWebSocket; + } + + /** + * Module exports. + */ + + module.exports = WS; + + /** + * WebSocket transport constructor. + * + * @api {Object} connection options + * @api public + */ + + function WS (opts) { + var forceBase64 = (opts && opts.forceBase64); + if (forceBase64) { + this.supportsBinary = false; + } + this.perMessageDeflate = opts.perMessageDeflate; + this.usingBrowserWebSocket = BrowserWebSocket && !opts.forceNode; + if (!this.usingBrowserWebSocket) { + WebSocket = NodeWebSocket; + } + Transport.call(this, opts); + } + + /** + * Inherits from Transport. + */ + + inherit(WS, Transport); + + /** + * Transport name. + * + * @api public + */ + + WS.prototype.name = 'websocket'; + + /* + * WebSockets support binary + */ + + WS.prototype.supportsBinary = true; + + /** + * Opens socket. + * + * @api private + */ + + WS.prototype.doOpen = function () { + if (!this.check()) { + // let probe timeout + return; + } + + var uri = this.uri(); + var protocols = void (0); + var opts = { + agent: this.agent, + perMessageDeflate: this.perMessageDeflate + }; + + // SSL options for Node.js client + opts.pfx = this.pfx; + opts.key = this.key; + opts.passphrase = this.passphrase; + opts.cert = this.cert; + opts.ca = this.ca; + opts.ciphers = this.ciphers; + opts.rejectUnauthorized = this.rejectUnauthorized; + if (this.extraHeaders) { + opts.headers = this.extraHeaders; + } + if (this.localAddress) { + opts.localAddress = this.localAddress; + } + + try { + this.ws = this.usingBrowserWebSocket ? new WebSocket(uri) : new WebSocket(uri, protocols, opts); + } catch (err) { + return this.emit('error', err); + } + + if (this.ws.binaryType === undefined) { + this.supportsBinary = false; + } + + if (this.ws.supports && this.ws.supports.binary) { + this.supportsBinary = true; + this.ws.binaryType = 'nodebuffer'; + } else { + this.ws.binaryType = 'arraybuffer'; + } + + this.addEventListeners(); + }; + + /** + * Adds event listeners to the socket + * + * @api private + */ + + WS.prototype.addEventListeners = function () { + var self = this; + + this.ws.onopen = function () { + self.onOpen(); + }; + this.ws.onclose = function () { + self.onClose(); + }; + this.ws.onmessage = function (ev) { + self.onData(ev.data); + }; + this.ws.onerror = function (e) { + self.onError('websocket error', e); + }; + }; + + /** + * Writes data to socket. + * + * @param {Array} array of packets. + * @api private + */ + + WS.prototype.write = function (packets) { + var self = this; + this.writable = false; + + // encodePacket efficient as it uses WS framing + // no need for encodePayload + var total = packets.length; + for (var i = 0, l = total; i < l; i++) { + (function (packet) { + parser.encodePacket(packet, self.supportsBinary, function (data) { + if (!self.usingBrowserWebSocket) { + // always create a new object (GH-437) + var opts = {}; + if (packet.options) { + opts.compress = packet.options.compress; + } + + if (self.perMessageDeflate) { + var len = 'string' === typeof data ? global.Buffer.byteLength(data) : data.length; + if (len < self.perMessageDeflate.threshold) { + opts.compress = false; + } + } + } + + // Sometimes the websocket has already been closed but the browser didn't + // have a chance of informing us about it yet, in that case send will + // throw an error + try { + if (self.usingBrowserWebSocket) { + // TypeError is thrown when passing the second argument on Safari + self.ws.send(data); + } else { + self.ws.send(data, opts); + } + } catch (e) { + debug('websocket closed before onclose event'); + } + + --total || done(); + }); + })(packets[i]); + } + + function done () { + self.emit('flush'); + + // fake drain + // defer to next tick to allow Socket to clear writeBuffer + setTimeout(function () { + self.writable = true; + self.emit('drain'); + }, 0); + } + }; + + /** + * Called upon close + * + * @api private + */ + + WS.prototype.onClose = function () { + Transport.prototype.onClose.call(this); + }; + + /** + * Closes socket. + * + * @api private + */ + + WS.prototype.doClose = function () { + if (typeof this.ws !== 'undefined') { + this.ws.close(); + } + }; + + /** + * Generates uri for connection. + * + * @api private + */ + + WS.prototype.uri = function () { + var query = this.query || {}; + var schema = this.secure ? 'wss' : 'ws'; + var port = ''; + + // avoid port if default for schema + if (this.port && (('wss' === schema && Number(this.port) !== 443) || + ('ws' === schema && Number(this.port) !== 80))) { + port = ':' + this.port; + } + + // append timestamp to URI + if (this.timestampRequests) { + query[this.timestampParam] = yeast(); + } + + // communicate binary support capabilities + if (!this.supportsBinary) { + query.b64 = 1; + } + + query = parseqs.encode(query); + + // prepend ? to query + if (query.length) { + query = '?' + query; + } + + var ipv6 = this.hostname.indexOf(':') !== -1; + return schema + '://' + (ipv6 ? '[' + this.hostname + ']' : this.hostname) + port + this.path + query; + }; + + /** + * Feature detection for WebSocket. + * + * @return {Boolean} whether this transport is available. + * @api public + */ + + WS.prototype.check = function () { + return !!WebSocket && !('__initialize' in WebSocket && this.name === WS.prototype.name); + }; + + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) + +/***/ }, +/* 42 */ +/***/ function(module, exports) { + + /* (ignored) */ + +/***/ }, +/* 43 */ +/***/ function(module, exports) { + + + var indexOf = [].indexOf; + + module.exports = function(arr, obj){ + if (indexOf) return arr.indexOf(obj); + for (var i = 0; i < arr.length; ++i) { + if (arr[i] === obj) return i; + } + return -1; + }; + +/***/ }, +/* 44 */ +/***/ function(module, exports) { + + /* WEBPACK VAR INJECTION */(function(global) {/** + * JSON parse. + * + * @see Based on jQuery#parseJSON (MIT) and JSON2 + * @api private + */ + + var rvalidchars = /^[\],:{}\s]*$/; + var rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g; + var rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g; + var rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g; + var rtrimLeft = /^\s+/; + var rtrimRight = /\s+$/; + + module.exports = function parsejson(data) { + if ('string' != typeof data || !data) { + return null; + } + + data = data.replace(rtrimLeft, '').replace(rtrimRight, ''); + + // Attempt to parse using the native JSON parser first + if (global.JSON && JSON.parse) { + return JSON.parse(data); + } + + if (rvalidchars.test(data.replace(rvalidescape, '@') + .replace(rvalidtokens, ']') + .replace(rvalidbraces, ''))) { + return (new Function('return ' + data))(); + } + }; + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) + +/***/ }, +/* 45 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + /** + * Module dependencies. + */ + + var parser = __webpack_require__(7); + var Emitter = __webpack_require__(36); + var toArray = __webpack_require__(46); + var on = __webpack_require__(47); + var bind = __webpack_require__(48); + var debug = __webpack_require__(3)('socket.io-client:socket'); + var hasBin = __webpack_require__(49); + + /** + * Module exports. + */ + + module.exports = exports = Socket; + + /** + * Internal events (blacklisted). + * These events can't be emitted by the user. + * + * @api private + */ + + var events = { + connect: 1, + connect_error: 1, + connect_timeout: 1, + connecting: 1, + disconnect: 1, + error: 1, + reconnect: 1, + reconnect_attempt: 1, + reconnect_failed: 1, + reconnect_error: 1, + reconnecting: 1, + ping: 1, + pong: 1 + }; + + /** + * Shortcut to `Emitter#emit`. + */ + + var emit = Emitter.prototype.emit; + + /** + * `Socket` constructor. + * + * @api public + */ + + function Socket(io, nsp, opts) { + this.io = io; + this.nsp = nsp; + this.json = this; // compat + this.ids = 0; + this.acks = {}; + this.receiveBuffer = []; + this.sendBuffer = []; + this.connected = false; + this.disconnected = true; + if (opts && opts.query) { + this.query = opts.query; + } + if (this.io.autoConnect) this.open(); + } + + /** + * Mix in `Emitter`. + */ + + Emitter(Socket.prototype); + + /** + * Subscribe to open, close and packet events + * + * @api private + */ + + Socket.prototype.subEvents = function () { + if (this.subs) return; + + var io = this.io; + this.subs = [on(io, 'open', bind(this, 'onopen')), on(io, 'packet', bind(this, 'onpacket')), on(io, 'close', bind(this, 'onclose'))]; + }; + + /** + * "Opens" the socket. + * + * @api public + */ + + Socket.prototype.open = Socket.prototype.connect = function () { + if (this.connected) return this; + + this.subEvents(); + this.io.open(); // ensure open + if ('open' === this.io.readyState) this.onopen(); + this.emit('connecting'); + return this; + }; + + /** + * Sends a `message` event. + * + * @return {Socket} self + * @api public + */ + + Socket.prototype.send = function () { + var args = toArray(arguments); + args.unshift('message'); + this.emit.apply(this, args); + return this; + }; + + /** + * Override `emit`. + * If the event is in `events`, it's emitted normally. + * + * @param {String} event name + * @return {Socket} self + * @api public + */ + + Socket.prototype.emit = function (ev) { + if (events.hasOwnProperty(ev)) { + emit.apply(this, arguments); + return this; + } + + var args = toArray(arguments); + var parserType = parser.EVENT; // default + if (hasBin(args)) { + parserType = parser.BINARY_EVENT; + } // binary + var packet = { type: parserType, data: args }; + + packet.options = {}; + packet.options.compress = !this.flags || false !== this.flags.compress; + + // event ack callback + if ('function' === typeof args[args.length - 1]) { + debug('emitting packet with ack id %d', this.ids); + this.acks[this.ids] = args.pop(); + packet.id = this.ids++; + } + + if (this.connected) { + this.packet(packet); + } else { + this.sendBuffer.push(packet); + } + + delete this.flags; + + return this; + }; + + /** + * Sends a packet. + * + * @param {Object} packet + * @api private + */ + + Socket.prototype.packet = function (packet) { + packet.nsp = this.nsp; + this.io.packet(packet); + }; + + /** + * Called upon engine `open`. + * + * @api private + */ + + Socket.prototype.onopen = function () { + debug('transport is open - connecting'); + + // write connect packet if necessary + if ('/' !== this.nsp) { + if (this.query) { + this.packet({ type: parser.CONNECT, query: this.query }); + } else { + this.packet({ type: parser.CONNECT }); + } + } + }; + + /** + * Called upon engine `close`. + * + * @param {String} reason + * @api private + */ + + Socket.prototype.onclose = function (reason) { + debug('close (%s)', reason); + this.connected = false; + this.disconnected = true; + delete this.id; + this.emit('disconnect', reason); + }; + + /** + * Called with socket packet. + * + * @param {Object} packet + * @api private + */ + + Socket.prototype.onpacket = function (packet) { + if (packet.nsp !== this.nsp) return; + + switch (packet.type) { + case parser.CONNECT: + this.onconnect(); + break; + + case parser.EVENT: + this.onevent(packet); + break; + + case parser.BINARY_EVENT: + this.onevent(packet); + break; + + case parser.ACK: + this.onack(packet); + break; + + case parser.BINARY_ACK: + this.onack(packet); + break; + + case parser.DISCONNECT: + this.ondisconnect(); + break; + + case parser.ERROR: + this.emit('error', packet.data); + break; + } + }; + + /** + * Called upon a server event. + * + * @param {Object} packet + * @api private + */ + + Socket.prototype.onevent = function (packet) { + var args = packet.data || []; + debug('emitting event %j', args); + + if (null != packet.id) { + debug('attaching ack callback to event'); + args.push(this.ack(packet.id)); + } + + if (this.connected) { + emit.apply(this, args); + } else { + this.receiveBuffer.push(args); + } + }; + + /** + * Produces an ack callback to emit with an event. + * + * @api private + */ + + Socket.prototype.ack = function (id) { + var self = this; + var sent = false; + return function () { + // prevent double callbacks + if (sent) return; + sent = true; + var args = toArray(arguments); + debug('sending ack %j', args); + + var type = hasBin(args) ? parser.BINARY_ACK : parser.ACK; + self.packet({ + type: type, + id: id, + data: args + }); + }; + }; + + /** + * Called upon a server acknowlegement. + * + * @param {Object} packet + * @api private + */ + + Socket.prototype.onack = function (packet) { + var ack = this.acks[packet.id]; + if ('function' === typeof ack) { + debug('calling ack %s with %j', packet.id, packet.data); + ack.apply(this, packet.data); + delete this.acks[packet.id]; + } else { + debug('bad ack %s', packet.id); + } + }; + + /** + * Called upon server connect. + * + * @api private + */ + + Socket.prototype.onconnect = function () { + this.connected = true; + this.disconnected = false; + this.emit('connect'); + this.emitBuffered(); + }; + + /** + * Emit buffered events (received and emitted). + * + * @api private + */ + + Socket.prototype.emitBuffered = function () { + var i; + for (i = 0; i < this.receiveBuffer.length; i++) { + emit.apply(this, this.receiveBuffer[i]); + } + this.receiveBuffer = []; + + for (i = 0; i < this.sendBuffer.length; i++) { + this.packet(this.sendBuffer[i]); + } + this.sendBuffer = []; + }; + + /** + * Called upon server disconnect. + * + * @api private + */ + + Socket.prototype.ondisconnect = function () { + debug('server disconnect (%s)', this.nsp); + this.destroy(); + this.onclose('io server disconnect'); + }; + + /** + * Called upon forced client/server side disconnections, + * this method ensures the manager stops tracking us and + * that reconnections don't get triggered for this. + * + * @api private. + */ + + Socket.prototype.destroy = function () { + if (this.subs) { + // clean subscriptions to avoid reconnections + for (var i = 0; i < this.subs.length; i++) { + this.subs[i].destroy(); + } + this.subs = null; + } + + this.io.destroy(this); + }; + + /** + * Disconnects the socket manually. + * + * @return {Socket} self + * @api public + */ + + Socket.prototype.close = Socket.prototype.disconnect = function () { + if (this.connected) { + debug('performing disconnect (%s)', this.nsp); + this.packet({ type: parser.DISCONNECT }); + } + + // remove socket from pool + this.destroy(); + + if (this.connected) { + // fire events + this.onclose('io client disconnect'); + } + return this; + }; + + /** + * Sets the compress flag. + * + * @param {Boolean} if `true`, compresses the sending data + * @return {Socket} self + * @api public + */ + + Socket.prototype.compress = function (compress) { + this.flags = this.flags || {}; + this.flags.compress = compress; + return this; + }; + +/***/ }, +/* 46 */ +/***/ function(module, exports) { + + module.exports = toArray + + function toArray(list, index) { + var array = [] + + index = index || 0 + + for (var i = index || 0; i < list.length; i++) { + array[i - index] = list[i] + } + + return array + } + + +/***/ }, +/* 47 */ +/***/ function(module, exports) { + + "use strict"; + + /** + * Module exports. + */ + + module.exports = on; + + /** + * Helper for subscriptions. + * + * @param {Object|EventEmitter} obj with `Emitter` mixin or `EventEmitter` + * @param {String} event name + * @param {Function} callback + * @api public + */ + + function on(obj, ev, fn) { + obj.on(ev, fn); + return { + destroy: function destroy() { + obj.removeListener(ev, fn); + } + }; + } + +/***/ }, +/* 48 */ +/***/ function(module, exports) { + + /** + * Slice reference. + */ + + var slice = [].slice; + + /** + * Bind `obj` to `fn`. + * + * @param {Object} obj + * @param {Function|String} fn or string + * @return {Function} + * @api public + */ + + module.exports = function(obj, fn){ + if ('string' == typeof fn) fn = obj[fn]; + if ('function' != typeof fn) throw new Error('bind() requires a function'); + var args = slice.call(arguments, 2); + return function(){ + return fn.apply(obj, args.concat(slice.call(arguments))); + } + }; + + +/***/ }, +/* 49 */ +/***/ function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(global) { + /* + * Module requirements. + */ + + var isArray = __webpack_require__(50); + + /** + * Module exports. + */ + + module.exports = hasBinary; + + /** + * Checks for binary data. + * + * Right now only Buffer and ArrayBuffer are supported.. + * + * @param {Object} anything + * @api public + */ + + function hasBinary(data) { + + function _hasBinary(obj) { + if (!obj) return false; + + if ( (global.Buffer && global.Buffer.isBuffer && global.Buffer.isBuffer(obj)) || + (global.ArrayBuffer && obj instanceof ArrayBuffer) || + (global.Blob && obj instanceof Blob) || + (global.File && obj instanceof File) + ) { + return true; + } + + if (isArray(obj)) { + for (var i = 0; i < obj.length; i++) { + if (_hasBinary(obj[i])) { + return true; + } + } + } else if (obj && 'object' == typeof obj) { + // see: https://github.com/Automattic/has-binary/pull/4 + if (obj.toJSON && 'function' == typeof obj.toJSON) { + obj = obj.toJSON(); + } + + for (var key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key) && _hasBinary(obj[key])) { + return true; + } + } + } + + return false; + } + + return _hasBinary(data); + } + + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) + +/***/ }, +/* 50 */ +/***/ function(module, exports) { + + module.exports = Array.isArray || function (arr) { + return Object.prototype.toString.call(arr) == '[object Array]'; + }; + + +/***/ }, +/* 51 */ +/***/ function(module, exports) { + + + /** + * Expose `Backoff`. + */ + + module.exports = Backoff; + + /** + * Initialize backoff timer with `opts`. + * + * - `min` initial timeout in milliseconds [100] + * - `max` max timeout [10000] + * - `jitter` [0] + * - `factor` [2] + * + * @param {Object} opts + * @api public + */ + + function Backoff(opts) { + opts = opts || {}; + this.ms = opts.min || 100; + this.max = opts.max || 10000; + this.factor = opts.factor || 2; + this.jitter = opts.jitter > 0 && opts.jitter <= 1 ? opts.jitter : 0; + this.attempts = 0; + } + + /** + * Return the backoff duration. + * + * @return {Number} + * @api public + */ + + Backoff.prototype.duration = function(){ + var ms = this.ms * Math.pow(this.factor, this.attempts++); + if (this.jitter) { + var rand = Math.random(); + var deviation = Math.floor(rand * this.jitter * ms); + ms = (Math.floor(rand * 10) & 1) == 0 ? ms - deviation : ms + deviation; + } + return Math.min(ms, this.max) | 0; + }; + + /** + * Reset the number of attempts. + * + * @api public + */ + + Backoff.prototype.reset = function(){ + this.attempts = 0; + }; + + /** + * Set the minimum duration + * + * @api public + */ + + Backoff.prototype.setMin = function(min){ + this.ms = min; + }; + + /** + * Set the maximum duration + * + * @api public + */ + + Backoff.prototype.setMax = function(max){ + this.max = max; + }; + + /** + * Set the jitter + * + * @api public + */ + + Backoff.prototype.setJitter = function(jitter){ + this.jitter = jitter; + }; + + + +/***/ } +/******/ ]) +}); +; +//# sourceMappingURL=socket.io.js.map \ No newline at end of file diff --git a/public/mainPage.html b/public/mainPage.html new file mode 100644 index 0000000000000000000000000000000000000000..3260068e35115de483a0bcf1346809a14060637c --- /dev/null +++ b/public/mainPage.html @@ -0,0 +1,36 @@ +<html> + <head> + <link rel="stylesheet" href="./css/style.css" > + <script src="./js/p5.min.js" type="text/javascript"></script> + <script src="./js/javascriptWindow.js"></script> + <title>Acceuil</title> + </head> + + <body> + <header> + <div class="container"> + <p><img src="./picture/ram.png" alt="Logo" id="smallLogo"/></p> + <p><img src="./picture/screwdriver.png" alt="Logo" id="smallLogo"/></p> + <p><a class="hiddenlink" href="./"><h1>Midi de la Bidouille - javascript</h1></a></p> + </div> + </header> + + <div class="container"> + <nav> + <ul class = "navlist"> + <li><h3><a class="navlink" href="./mainPage.html">Acceuil</a></h3></li> + </ul> + </nav> + + <section> + <h2>Page principale</h2> + <p>Votre espace de jeu : </p> + <div id="javascriptWindow"> </div> + </section> + </div> + + <footer> + <p>Concu par le SED d'INRIA BSO</p> + </footer> + </body> +</html> \ No newline at end of file diff --git a/public/picture/ram.png b/public/picture/ram.png new file mode 100644 index 0000000000000000000000000000000000000000..546ca02a2d2beeff2cb13709fabd7856d672ca9a Binary files /dev/null and b/public/picture/ram.png differ diff --git a/public/picture/screwdriver.png b/public/picture/screwdriver.png new file mode 100644 index 0000000000000000000000000000000000000000..cd1f837d0f99f10940614e617b1ddbcf9e07a8e0 Binary files /dev/null and b/public/picture/screwdriver.png differ diff --git a/server.js b/server.js new file mode 100644 index 0000000000000000000000000000000000000000..638161596b59b15dff74e00718081b87916fcacf --- /dev/null +++ b/server.js @@ -0,0 +1,82 @@ +var hexapodServerHttp = require('http').createServer(handler); +var url= require('url') +var fs = require('fs') + + +// Http handler function +function handler (req, res) +{ + // Using URL to parse the requested URL + var path = url.parse(req.url).pathname; + + // Managing the root route + if (path == '/') + { + index = fs.readFile (__dirname+'/public/mainPage.html', + function(error,data) + { + if (error) { + res.writeHead(500); + return res.end("Error: unable to load mainPage.html"); + } + res.writeHead(200,{'Content-Type': 'text/html'}); + res.end(data); + } + ); + } + // Managing the route for the javascript files + else if( /\.(js)$/.test(path) ) + { + index = fs.readFile (__dirname+'/public'+path, + function(error,data) + { + if (error) { + res.writeHead(500); + return res.end("Error: unable to load " + path); + } + res.writeHead(200,{'Content-Type': 'text/plain'}); + res.end(data); + } + ); + } + // Managing the route for the css files + else if( /\.(css)$/.test(path) ) + { + index = fs.readFile (__dirname+'/public'+path, + function(error,data) + { + if (error) + { + res.writeHead(500); + return res.end("Error: unable to load " + path); + } + res.writeHead(200,{'Content-Type': 'text/plain'}); + res.end(data); + } + ); + } + // Catch security issue + else if (path.indexOf("..") !== -1) + { + // This might be something like "../../../../etc/passwd". + res.writeHead(403); + res.end(""); + } + // Try open standart page + else + { + index = fs.readFile (__dirname+'/public'+path, + function(error,data) + { + if (error) { + res.writeHead(404); + return res.end("Error: 404 - File not found."); + } + res.writeHead(200,{'Content-Type': 'text/html'}); + res.end(data); + } + ); + } +} + +hexapodServerHttp.listen(80);