From 0fb9e2f03dc11627cafe63d199eed315a83d076b Mon Sep 17 00:00:00 2001
From: LAINE Thibault <titwin@live.fr>
Date: Wed, 20 Nov 2019 17:50:53 +0100
Subject: [PATCH] =?UTF-8?q?Exercice=20termin=C3=A9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

il suffit de supprimer tt l'interieur du script "javascriptWindow.js" pour avoir le demarrage de l'exo
---
 public/css/segoepr.ttf         |   Bin 0 -> 172732 bytes
 public/css/segoeprb.ttf        |   Bin 0 -> 172092 bytes
 public/css/style.css           |   201 +
 public/js/javascriptWindow.js  |   134 +
 public/js/jquery-3.1.1.min.js  |     4 +
 public/js/p5.js                | 33029 +++++++++++++++++++++++++++++++
 public/js/p5.min.js            |     9 +
 public/js/p5.play.js           |  4366 ++++
 public/js/socket.io.js         |  8284 ++++++++
 public/mainPage.html           |    36 +
 public/picture/ram.png         |   Bin 0 -> 19373 bytes
 public/picture/screwdriver.png |   Bin 0 -> 22759 bytes
 server.js                      |    82 +
 13 files changed, 46145 insertions(+)
 create mode 100644 public/css/segoepr.ttf
 create mode 100644 public/css/segoeprb.ttf
 create mode 100644 public/css/style.css
 create mode 100644 public/js/javascriptWindow.js
 create mode 100644 public/js/jquery-3.1.1.min.js
 create mode 100644 public/js/p5.js
 create mode 100644 public/js/p5.min.js
 create mode 100644 public/js/p5.play.js
 create mode 100644 public/js/socket.io.js
 create mode 100644 public/mainPage.html
 create mode 100644 public/picture/ram.png
 create mode 100644 public/picture/screwdriver.png
 create mode 100644 server.js

diff --git a/public/css/segoepr.ttf b/public/css/segoepr.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..38f696d4f4cbcd6d4b78614ed82db86c604a2c18
GIT binary patch
literal 172732
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
Z3#PDZKb73&df)SuqvnE-2PB;t7y!8!ueJaH

literal 0
HcmV?d00001

diff --git a/public/css/segoeprb.ttf b/public/css/segoeprb.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..c7e267ec70457e35ce75980c07c2db18769c220d
GIT binary patch
literal 172092
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

literal 0
HcmV?d00001

diff --git a/public/css/style.css b/public/css/style.css
new file mode 100644
index 0000000..14d06aa
--- /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 0000000..4fd38d7
--- /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 0000000..4c5be4c
--- /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 0000000..c90badc
--- /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&lt;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:
+ * &lt;div id="myContainer">&lt;/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, '&amp;')
+    .replace(/</g, '&lt;')
+    .replace(/>/g, '&gt;')
+    .replace(/"/g, '&quot;')
+    .replace(/'/g, '&#039;');
+}
+
+/**
+ *  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"?>
+ * // &lt;mammals&gt;
+ * //   &lt;animal id="0" species="Capra hircus">Goat&lt;/animal&gt;
+ * //   &lt;animal id="1" species="Panthera pardus">Leopard&lt;/animal&gt;
+ * //   &lt;animal id="2" species="Equus zebra">Zebra&lt;/animal&gt;
+ * // &lt;/mammals&gt;
+ *
+ * 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"?>
+ * // &lt;mammals&gt;
+ * //   &lt;animal id="0" species="Capra hircus">Goat&lt;/animal&gt;
+ * //   &lt;animal id="1" species="Panthera pardus">Leopard&lt;/animal&gt;
+ * //   &lt;animal id="2" species="Equus zebra">Zebra&lt;/animal&gt;
+ * // &lt;/mammals&gt;
+ *
+ * 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&lt;animal
+ * <div class='norender'><code>
+ * // The following short XML file called "mammals.xml" is parsed
+ * // in the code below.
+ * //
+ * // <?xml version="1.0"?>
+ * // &lt;mammals&gt;
+ * //   &lt;animal id="0" species="Capra hircus">Goat&lt;/animal&gt;
+ * //   &lt;animal id="1" species="Panthera pardus">Leopard&lt;/animal&gt;
+ * //   &lt;animal id="2" species="Equus zebra">Zebra&lt;/animal&gt;
+ * // &lt;/mammals&gt;
+ *
+ * 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&lt;animal
+ * <div class='norender'><code>
+ * // The following short XML file called "mammals.xml" is parsed
+ * // in the code below.
+ * //
+ * // <?xml version="1.0"?>
+ * // &lt;mammals&gt;
+ * //   &lt;animal id="0" species="Capra hircus">Goat&lt;/animal&gt;
+ * //   &lt;animal id="1" species="Panthera pardus">Leopard&lt;/animal&gt;
+ * //   &lt;animal id="2" species="Equus zebra">Zebra&lt;/animal&gt;
+ * // &lt;/mammals&gt;
+ *
+ * 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&lt;animal
+ * <div class='norender'><code>
+ * // The following short XML file called "mammals.xml" is parsed
+ * // in the code below.
+ * //
+ * // <?xml version="1.0"?>
+ * // &lt;mammals&gt;
+ * //   &lt;animal id="0" species="Capra hircus">Goat&lt;/animal&gt;
+ * //   &lt;animal id="1" species="Panthera pardus">Leopard&lt;/animal&gt;
+ * //   &lt;animal id="2" species="Equus zebra">Zebra&lt;/animal&gt;
+ * // &lt;/mammals&gt;
+ *
+ * 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&lt;animal
+ * <div class='norender'><code>
+ * // The following short XML file called "mammals.xml" is parsed
+ * // in the code below.
+ * //
+ * // <?xml version="1.0"?>
+ * // &lt;mammals&gt;
+ * //   &lt;animal id="0" species="Capra hircus">Goat&lt;/animal&gt;
+ * //   &lt;animal id="1" species="Panthera pardus">Leopard&lt;/animal&gt;
+ * //   &lt;animal id="2" species="Equus zebra">Zebra&lt;/animal&gt;
+ * // &lt;/mammals&gt;
+ *
+ * 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&lt;animal
+ * <div class='norender'><code>
+ * // The following short XML file called "mammals.xml" is parsed
+ * // in the code below.
+ * //
+ * // <?xml version="1.0"?>
+ * // &lt;mammals&gt;
+ * //   &lt;animal id="0" species="Capra hircus">Goat&lt;/animal&gt;
+ * //   &lt;animal id="1" species="Panthera pardus">Leopard&lt;/animal&gt;
+ * //   &lt;animal id="2" species="Equus zebra">Zebra&lt;/animal&gt;
+ * // &lt;/mammals&gt;
+ *
+ * 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&lt;animal
+ * <div class='norender'><code>
+ * // The following short XML file called "mammals.xml" is parsed
+ * // in the code below.
+ * //
+ * // <?xml version="1.0"?>
+ * // &lt;mammals&gt;
+ * //   &lt;animal id="0" species="Capra hircus">Goat&lt;/animal&gt;
+ * //   &lt;animal id="1" species="Panthera pardus">Leopard&lt;/animal&gt;
+ * //   &lt;animal id="2" species="Equus zebra">Zebra&lt;/animal&gt;
+ * // &lt;/mammals&gt;
+ *
+ * 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"?>
+ * // &lt;mammals&gt;
+ * //   &lt;animal id="0" species="Capra hircus">Goat&lt;/animal&gt;
+ * //   &lt;animal id="1" species="Panthera pardus">Leopard&lt;/animal&gt;
+ * //   &lt;animal id="2" species="Equus zebra">Zebra&lt;/animal&gt;
+ * // &lt;/mammals&gt;
+ *
+ * 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"?>
+ * // &lt;mammals&gt;
+ * //   &lt;animal id="0" species="Capra hircus">Goat&lt;/animal&gt;
+ * //   &lt;animal id="1" species="Panthera pardus">Leopard&lt;/animal&gt;
+ * //   &lt;animal id="2" species="Equus zebra">Zebra&lt;/animal&gt;
+ * // &lt;/mammals&gt;
+ *
+ * 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"?>
+ * // &lt;mammals&gt;
+ * //   &lt;animal id="0" species="Capra hircus">Goat&lt;/animal&gt;
+ * //   &lt;animal id="1" species="Panthera pardus">Leopard&lt;/animal&gt;
+ * //   &lt;animal id="2" species="Equus zebra">Zebra&lt;/animal&gt;
+ * // &lt;/mammals&gt;
+ *
+ * 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"?>
+ * // &lt;mammals&gt;
+ * //   &lt;animal id="0" species="Capra hircus">Goat&lt;/animal&gt;
+ * //   &lt;animal id="1" species="Panthera pardus">Leopard&lt;/animal&gt;
+ * //   &lt;animal id="2" species="Equus zebra">Zebra&lt;/animal&gt;
+ * // &lt;/mammals&gt;
+ *
+ * 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"?>
+ * // &lt;mammals&gt;
+ * //   &lt;animal id="0" species="Capra hircus">Goat&lt;/animal&gt;
+ * //   &lt;animal id="1" species="Panthera pardus">Leopard&lt;/animal&gt;
+ * //   &lt;animal id="2" species="Equus zebra">Zebra&lt;/animal&gt;
+ * // &lt;/mammals&gt;
+ *
+ * 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"?>
+ * // &lt;mammals&gt;
+ * //   &lt;animal id="0" species="Capra hircus">Goat&lt;/animal&gt;
+ * //   &lt;animal id="1" species="Panthera pardus">Leopard&lt;/animal&gt;
+ * //   &lt;animal id="2" species="Equus zebra">Zebra&lt;/animal&gt;
+ * // &lt;/mammals&gt;
+ *
+ * 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"?>
+ * // &lt;mammals&gt;
+ * //   &lt;animal id="0" species="Capra hircus">Goat&lt;/animal&gt;
+ * //   &lt;animal id="1" species="Panthera pardus">Leopard&lt;/animal&gt;
+ * //   &lt;animal id="2" species="Equus zebra">Zebra&lt;/animal&gt;
+ * // &lt;/mammals&gt;
+ *
+ * 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"?>
+ * // &lt;mammals&gt;
+ * //   &lt;animal id="0" species="Capra hircus">Goat&lt;/animal&gt;
+ * //   &lt;animal id="1" species="Panthera pardus">Leopard&lt;/animal&gt;
+ * //   &lt;animal id="2" species="Equus zebra">Zebra&lt;/animal&gt;
+ * // &lt;/mammals&gt;
+ *
+ * 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"?>
+ * // &lt;mammals&gt;
+ * //   &lt;animal id="0" species="Capra hircus">Goat&lt;/animal&gt;
+ * //   &lt;animal id="1" species="Panthera pardus">Leopard&lt;/animal&gt;
+ * //   &lt;animal id="2" species="Equus zebra">Zebra&lt;/animal&gt;
+ * // &lt;/mammals&gt;
+ *
+ * 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 0000000..4205309
--- /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,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}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 0000000..72af118
--- /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 0000000..4ab6926
--- /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 0000000..3260068
--- /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
GIT binary patch
literal 19373
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

literal 0
HcmV?d00001

diff --git a/public/picture/screwdriver.png b/public/picture/screwdriver.png
new file mode 100644
index 0000000000000000000000000000000000000000..cd1f837d0f99f10940614e617b1ddbcf9e07a8e0
GIT binary patch
literal 22759
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
KpUXO@geCwuUet&H

literal 0
HcmV?d00001

diff --git a/server.js b/server.js
new file mode 100644
index 0000000..6381615
--- /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);
-- 
GitLab