UNPKG

@lilonga/output-khan-processing

Version:

Renders input code using Khan Academy's custom Processing.js environment.

1 lines 778 kB
"use strict";function _interopDefault(e){return e&&"object"==typeof e&&"default"in e?e.default:e}var React=_interopDefault(require("react")),PropTypes=_interopDefault(require("prop-types"));function _classCallCheck(e,n){if(!(e instanceof n))throw new TypeError("Cannot call a class as a function")}function _defineProperties(e,n){for(var t=0;t<n.length;t++){var r=n[t];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function _createClass(e,n,t){return n&&_defineProperties(e.prototype,n),t&&_defineProperties(e,t),e}function _defineProperty(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function _extends(){return(_extends=Object.assign||function(e){for(var n=1;n<arguments.length;n++){var t=arguments[n];for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r])}return e}).apply(this,arguments)}function _inherits(e,n){if("function"!=typeof n&&null!==n)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(n&&n.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),n&&_setPrototypeOf(e,n)}function _getPrototypeOf(e){return(_getPrototypeOf=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function _setPrototypeOf(e,n){return(_setPrototypeOf=Object.setPrototypeOf||function(e,n){return e.__proto__=n,e})(e,n)}function _assertThisInitialized(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function _possibleConstructorReturn(e,n){return!n||"object"!=typeof n&&"function"!=typeof n?_assertThisInitialized(e):n}var kpjs='(function(window, document, Math, undef) {\n\n var nop = function(){};\n\n var debug = (function() {\n if ("console" in window) {\n return function(msg) {\n window.console.log(\'Processing.js: \' + msg);\n };\n }\n return nop();\n }());\n\n var ajax = function(url) {\n var xhr = new XMLHttpRequest();\n xhr.open("GET", url, false);\n if (xhr.overrideMimeType) {\n xhr.overrideMimeType("text/plain");\n }\n xhr.setRequestHeader("If-Modified-Since", "Fri, 01 Jan 1960 00:00:00 GMT");\n xhr.send(null);\n // failed request?\n if (xhr.status !== 200 && xhr.status !== 0) { throw ("XMLHttpRequest failed, status code " + xhr.status); }\n return xhr.responseText;\n };\n\n var isDOMPresent = ("document" in this) && !("fake" in this.document);\n\n // document.head polyfill for the benefit of Firefox 3.6\n document.head = document.head || document.getElementsByTagName(\'head\')[0];\n\n // Typed Arrays: fallback to WebGL arrays or Native JS arrays if unavailable\n function setupTypedArray(name, fallback) {\n // Check if TypedArray exists, and use if so.\n if (name in window) {\n return window[name];\n }\n\n // Check if WebGLArray exists\n if (typeof window[fallback] === "function") {\n return window[fallback];\n }\n\n // Use Native JS array\n return function(obj) {\n if (obj instanceof Array) {\n return obj;\n }\n if (typeof obj === "number") {\n var arr = [];\n arr.length = obj;\n return arr;\n }\n };\n }\n\n var Float32Array = setupTypedArray("Float32Array", "WebGLFloatArray"),\n Int32Array = setupTypedArray("Int32Array", "WebGLIntArray"),\n Uint16Array = setupTypedArray("Uint16Array", "WebGLUnsignedShortArray"),\n Uint8Array = setupTypedArray("Uint8Array", "WebGLUnsignedByteArray");\n\n /* Browsers fixes end */\n\n /**\n * NOTE: in releases we replace symbolic PConstants.* names with their values.\n * Using PConstants.* in code below is fine. See tools/rewrite-pconstants.js.\n */\n var PConstants = {\n // NOTE(jeresig): Disable some constants as they were confusing users.\n //X: 0,\n //Y: 1,\n //Z: 2,\n\n //R: 3,\n //G: 4,\n //B: 5,\n //A: 6,\n\n //U: 7,\n //V: 8,\n\n NX: 9,\n NY: 10,\n NZ: 11,\n\n EDGE: 12,\n\n // Stroke\n SR: 13,\n SG: 14,\n SB: 15,\n SA: 16,\n\n SW: 17,\n\n // Transformations (2D and 3D)\n TX: 18,\n TY: 19,\n TZ: 20,\n\n VX: 21,\n VY: 22,\n VZ: 23,\n VW: 24,\n\n // Material properties\n AR: 25,\n AG: 26,\n AB: 27,\n\n DR: 3,\n DG: 4,\n DB: 5,\n DA: 6,\n\n SPR: 28,\n SPG: 29,\n SPB: 30,\n\n SHINE: 31,\n\n ER: 32,\n EG: 33,\n EB: 34,\n\n BEEN_LIT: 35,\n\n VERTEX_FIELD_COUNT: 36,\n\n // Renderers\n P2D: 1,\n JAVA2D: 1,\n WEBGL: 2,\n P3D: 2,\n OPENGL: 2,\n PDF: 0,\n DXF: 0,\n\n // Platform IDs\n OTHER: 0,\n WINDOWS: 1,\n MAXOSX: 2,\n LINUX: 3,\n\n EPSILON: 0.0001,\n\n MAX_FLOAT: 3.4028235e+38,\n MIN_FLOAT: -3.4028235e+38,\n MAX_INT: 2147483647,\n MIN_INT: -2147483648,\n\n PI: Math.PI,\n TWO_PI: 2 * Math.PI,\n HALF_PI: Math.PI / 2,\n THIRD_PI: Math.PI / 3,\n QUARTER_PI: Math.PI / 4,\n TAU: 2 * Math.PI,\n\n DEG_TO_RAD: Math.PI / 180,\n RAD_TO_DEG: 180 / Math.PI,\n\n WHITESPACE: " \\t\\n\\r\\f\\u00A0",\n\n // Color modes\n RGB: 1,\n ARGB: 2,\n HSB: 3,\n ALPHA: 4,\n CMYK: 5,\n\n // Image file types\n TIFF: 0,\n TARGA: 1,\n JPEG: 2,\n GIF: 3,\n\n // Filter/convert types\n BLUR: 11,\n GRAY: 12,\n INVERT: 13,\n OPAQUE: 14,\n POSTERIZE: 15,\n THRESHOLD: 16,\n ERODE: 17,\n DILATE: 18,\n\n // Blend modes\n REPLACE: 0,\n BLEND: 1 << 0,\n ADD: 1 << 1,\n SUBTRACT: 1 << 2,\n LIGHTEST: 1 << 3,\n DARKEST: 1 << 4,\n DIFFERENCE: 1 << 5,\n EXCLUSION: 1 << 6,\n MULTIPLY: 1 << 7,\n SCREEN: 1 << 8,\n OVERLAY: 1 << 9,\n HARD_LIGHT: 1 << 10,\n SOFT_LIGHT: 1 << 11,\n DODGE: 1 << 12,\n BURN: 1 << 13,\n\n // Color component bit masks\n ALPHA_MASK: 0xff000000,\n RED_MASK: 0x00ff0000,\n GREEN_MASK: 0x0000ff00,\n BLUE_MASK: 0x000000ff,\n\n // Projection matrices\n CUSTOM: 0,\n ORTHOGRAPHIC: 2,\n PERSPECTIVE: 3,\n\n // Shapes\n POINT: 2,\n POINTS: 2,\n LINE: 4,\n LINES: 4,\n TRIANGLE: 8,\n TRIANGLES: 9,\n TRIANGLE_STRIP: 10,\n TRIANGLE_FAN: 11,\n QUAD: 16,\n QUADS: 16,\n QUAD_STRIP: 17,\n POLYGON: 20,\n PATH: 21,\n RECT: 30,\n ELLIPSE: 31,\n ARC: 32,\n SPHERE: 40,\n BOX: 41,\n\n GROUP: 0,\n PRIMITIVE: 1,\n //PATH: 21, // shared with Shape PATH\n GEOMETRY: 3,\n\n // Shape Vertex\n VERTEX: 0,\n BEZIER_VERTEX: 1,\n CURVE_VERTEX: 2,\n BREAK: 3,\n CLOSESHAPE: 4,\n\n // Shape closing modes\n OPEN: 1,\n CLOSE: 2,\n\n // Shape drawing modes\n CORNER: 0, // Draw mode convention to use (x, y) to (width, height)\n CORNERS: 1, // Draw mode convention to use (x1, y1) to (x2, y2) coordinates\n RADIUS: 2, // Draw mode from the center, and using the radius\n CENTER_RADIUS: 2, // Deprecated! Use RADIUS instead\n CENTER: 3, // Draw from the center, using second pair of values as the diameter\n DIAMETER: 3, // Synonym for the CENTER constant. Draw from the center\n CENTER_DIAMETER: 3, // Deprecated! Use DIAMETER instead\n\n // Text vertical alignment modes\n BASELINE: 0, // Default vertical alignment for text placement\n TOP: 101, // Align text to the top\n BOTTOM: 102, // Align text from the bottom, using the baseline\n\n // UV Texture coordinate modes\n NORMAL: 1,\n NORMALIZED: 1,\n IMAGE: 2,\n\n // Text placement modes\n MODEL: 4,\n SHAPE: 5,\n\n // Stroke modes\n SQUARE: \'butt\',\n ROUND: \'round\',\n PROJECT: \'square\',\n MITER: \'miter\',\n BEVEL: \'bevel\',\n\n // Lighting modes\n AMBIENT: 0,\n DIRECTIONAL: 1,\n //POINT: 2, Shared with Shape constant\n SPOT: 3,\n\n // Key constants\n\n // Both key and keyCode will be equal to these values\n BACKSPACE: 8,\n TAB: 9,\n ENTER: 10,\n RETURN: 13,\n ESC: 27,\n DELETE: 127,\n CODED: 0xffff,\n\n // p.key will be CODED and p.keyCode will be this value\n SHIFT: 16,\n CONTROL: 17,\n ALT: 18,\n CAPSLK: 20,\n PGUP: 33,\n PGDN: 34,\n END: 35,\n HOME: 36,\n LEFT: 37,\n UP: 38,\n RIGHT: 39,\n DOWN: 40,\n F1: 112,\n F2: 113,\n F3: 114,\n F4: 115,\n F5: 116,\n F6: 117,\n F7: 118,\n F8: 119,\n F9: 120,\n F10: 121,\n F11: 122,\n F12: 123,\n NUMLK: 144,\n META: 157,\n INSERT: 155,\n\n // Cursor types\n ARROW: \'default\',\n CROSS: \'crosshair\',\n HAND: \'pointer\',\n MOVE: \'move\',\n TEXT: \'text\',\n WAIT: \'wait\',\n NOCURSOR: "url(\'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==\'), auto",\n\n // Hints\n DISABLE_OPENGL_2X_SMOOTH: 1,\n ENABLE_OPENGL_2X_SMOOTH: -1,\n ENABLE_OPENGL_4X_SMOOTH: 2,\n ENABLE_NATIVE_FONTS: 3,\n DISABLE_DEPTH_TEST: 4,\n ENABLE_DEPTH_TEST: -4,\n ENABLE_DEPTH_SORT: 5,\n DISABLE_DEPTH_SORT: -5,\n DISABLE_OPENGL_ERROR_REPORT: 6,\n ENABLE_OPENGL_ERROR_REPORT: -6,\n ENABLE_ACCURATE_TEXTURES: 7,\n DISABLE_ACCURATE_TEXTURES: -7,\n HINT_COUNT: 10,\n\n // PJS defined constants\n SINCOS_LENGTH: 720, // every half degree\n PRECISIONB: 15, // fixed point precision is limited to 15 bits!!\n PRECISIONF: 1 << 15,\n PREC_MAXVAL: (1 << 15) - 1,\n PREC_ALPHA_SHIFT: 24 - 15,\n PREC_RED_SHIFT: 16 - 15,\n NORMAL_MODE_AUTO: 0,\n NORMAL_MODE_SHAPE: 1,\n NORMAL_MODE_VERTEX: 2,\n MAX_LIGHTS: 8\n };\n\n /**\n * Returns Java hashCode() result for the object. If the object has the "hashCode" function,\n * it preforms the call of this function. Otherwise it uses/creates the "$id" property,\n * which is used as the hashCode.\n *\n * @param {Object} obj The object.\n * @returns {int} The object\'s hash code.\n */\n function virtHashCode(obj) {\n if (typeof(obj) === "string") {\n var hash = 0;\n for (var i = 0; i < obj.length; ++i) {\n hash = (hash * 31 + obj.charCodeAt(i)) & 0xFFFFFFFF;\n }\n return hash;\n }\n if (typeof(obj) !== "object") {\n return obj & 0xFFFFFFFF;\n }\n if (obj.hashCode instanceof Function) {\n return obj.hashCode();\n }\n if (obj.$id === undef) {\n obj.$id = ((Math.floor(Math.random() * 0x10000) - 0x8000) << 16) | Math.floor(Math.random() * 0x10000);\n }\n return obj.$id;\n }\n\n /**\n * Returns Java equals() result for two objects. If the first object\n * has the "equals" function, it preforms the call of this function.\n * Otherwise the method uses the JavaScript === operator.\n *\n * @param {Object} obj The first object.\n * @param {Object} other The second object.\n *\n * @returns {boolean} true if the objects are equal.\n */\n function virtEquals(obj, other) {\n if (obj === null || other === null) {\n return (obj === null) && (other === null);\n }\n if (typeof (obj) === "string") {\n return obj === other;\n }\n if (typeof(obj) !== "object") {\n return obj === other;\n }\n if (obj.equals instanceof Function) {\n return obj.equals(other);\n }\n return obj === other;\n }\n\n /**\n * A ObjectIterator is an iterator wrapper for objects. If passed object contains\n * the iterator method, the object instance will be replaced by the result returned by\n * this method call. If passed object is an array, the ObjectIterator instance iterates\n * through its items.\n *\n * @param {Object} obj The object to be iterated.\n */\n var ObjectIterator = function(obj) {\n if (obj.iterator instanceof Function) {\n return obj.iterator();\n }\n if (obj instanceof Array) {\n // iterate through array items\n var index = -1;\n this.hasNext = function() {\n return ++index < obj.length;\n };\n this.next = function() {\n return obj[index];\n };\n } else {\n throw "Unable to iterate: " + obj;\n }\n };\n\n /**\n * An ArrayList stores a variable number of objects.\n *\n * @param {int} initialCapacity optional defines the initial capacity of the list, it\'s empty by default\n *\n * @returns {ArrayList} new ArrayList object\n */\n var ArrayList = (function() {\n function Iterator(array) {\n var index = 0;\n this.hasNext = function() {\n return index < array.length;\n };\n\n this.next = function() {\n return array[index++];\n };\n\n this.remove = function() {\n array.splice(index, 1);\n };\n }\n\n function ArrayList() {\n var array;\n if (arguments.length === 0) {\n array = [];\n } else if (arguments.length > 0 && typeof arguments[0] !== \'number\') {\n array = arguments[0].toArray();\n } else {\n array = [];\n array.length = 0 | arguments[0];\n }\n\n /**\n * @member ArrayList\n * ArrayList.get() Returns the element at the specified position in this list.\n *\n * @param {int} i index of element to return\n *\n * @returns {Object} the element at the specified position in this list.\n */\n this.get = function(i) {\n return array[i];\n };\n /**\n * @member ArrayList\n * ArrayList.contains() Returns true if this list contains the specified element.\n *\n * @param {Object} item element whose presence in this List is to be tested.\n *\n * @returns {boolean} true if the specified element is present; false otherwise.\n */\n this.contains = function(item) {\n return this.indexOf(item)>-1;\n };\n /**\n * @member ArrayList\n * ArrayList.indexOf() Returns the position this element takes in the list, or -1 if the element is not found.\n *\n * @param {Object} item element whose position in this List is to be tested.\n *\n * @returns {int} the list position that the first match for this element holds in the list, or -1 if it is not in the list.\n */\n this.indexOf = function(item) {\n for (var i = 0, len = array.length; i < len; ++i) {\n if (virtEquals(item, array[i])) {\n return i;\n }\n }\n return -1;\n };\n /**\n * @member ArrayList\n * ArrayList.add() Adds the specified element to this list.\n *\n * @param {int} index optional index at which the specified element is to be inserted\n * @param {Object} object element to be added to the list\n */\n this.add = function() {\n if (arguments.length === 1) {\n array.push(arguments[0]); // for add(Object)\n } else if (arguments.length === 2) {\n var arg0 = arguments[0];\n if (typeof arg0 === \'number\') {\n if (arg0 >= 0 && arg0 <= array.length) {\n array.splice(arg0, 0, arguments[1]); // for add(i, Object)\n } else {\n throw(arg0 + " is not a valid index");\n }\n } else {\n throw(typeof arg0 + " is not a number");\n }\n } else {\n throw("Please use the proper number of parameters.");\n }\n };\n /**\n * @member ArrayList\n * ArrayList.addAll(collection) appends all of the elements in the specified\n * Collection to the end of this list, in the order that they are returned by\n * the specified Collection\'s Iterator.\n *\n * When called as addAll(index, collection) the elements are inserted into\n * this list at the position indicated by index.\n *\n * @param {index} Optional; specifies the position the colletion should be inserted at\n * @param {collection} Any iterable object (ArrayList, HashMap.keySet(), etc.)\n * @throws out of bounds error for negative index, or index greater than list size.\n */\n this.addAll = function(arg1, arg2) {\n // addAll(int, Collection)\n var it;\n if (typeof arg1 === "number") {\n if (arg1 < 0 || arg1 > array.length) {\n throw("Index out of bounds for addAll: " + arg1 + " greater or equal than " + array.length);\n }\n it = new ObjectIterator(arg2);\n while (it.hasNext()) {\n array.splice(arg1++, 0, it.next());\n }\n }\n // addAll(Collection)\n else {\n it = new ObjectIterator(arg1);\n while (it.hasNext()) {\n array.push(it.next());\n }\n }\n };\n /**\n * @member ArrayList\n * ArrayList.set() Replaces the element at the specified position in this list with the specified element.\n *\n * @param {int} index index of element to replace\n * @param {Object} object element to be stored at the specified position\n */\n this.set = function() {\n if (arguments.length === 2) {\n var arg0 = arguments[0];\n if (typeof arg0 === \'number\') {\n if (arg0 >= 0 && arg0 < array.length) {\n array.splice(arg0, 1, arguments[1]);\n } else {\n throw(arg0 + " is not a valid index.");\n }\n } else {\n throw(typeof arg0 + " is not a number");\n }\n } else {\n throw("Please use the proper number of parameters.");\n }\n };\n\n /**\n * @member ArrayList\n * ArrayList.size() Returns the number of elements in this list.\n *\n * @returns {int} the number of elements in this list\n */\n this.size = function() {\n return array.length;\n };\n\n /**\n * @member ArrayList\n * ArrayList.clear() Removes all of the elements from this list. The list will be empty after this call returns.\n */\n this.clear = function() {\n array.length = 0;\n };\n\n /**\n * @member ArrayList\n * ArrayList.remove() Removes an element either based on index, if the argument is a number, or\n * by equality check, if the argument is an object.\n *\n * @param {int|Object} item either the index of the element to be removed, or the element itself.\n *\n * @returns {Object|boolean} If removal is by index, the element that was removed, or null if nothing was removed. If removal is by object, true if removal occurred, otherwise false.\n */\n this.remove = function(item) {\n if (typeof item === \'number\') {\n return array.splice(item, 1)[0];\n }\n item = this.indexOf(item);\n if (item > -1) {\n array.splice(item, 1);\n return true;\n }\n return false;\n };\n\n /**\n * @member ArrayList\n * ArrayList.isEmpty() Tests if this list has no elements.\n *\n * @returns {boolean} true if this list has no elements; false otherwise\n */\n this.isEmpty = function() {\n return !array.length;\n };\n\n /**\n * @member ArrayList\n * ArrayList.clone() Returns a shallow copy of this ArrayList instance. (The elements themselves are not copied.)\n *\n * @returns {ArrayList} a clone of this ArrayList instance\n */\n this.clone = function() {\n return new ArrayList(this);\n };\n\n /**\n * @member ArrayList\n * ArrayList.toArray() Returns an array containing all of the elements in this list in the correct order.\n *\n * @returns {Object[]} Returns an array containing all of the elements in this list in the correct order\n */\n this.toArray = function() {\n return array.slice(0);\n };\n\n this.iterator = function() {\n return new Iterator(array);\n };\n }\n\n return ArrayList;\n }());\n\n /**\n * A HashMap stores a collection of objects, each referenced by a key. This is similar to an Array, only\n * instead of accessing elements with a numeric index, a String is used. (If you are familiar with\n * associative arrays from other languages, this is the same idea.)\n *\n * @param {int} initialCapacity defines the initial capacity of the map, it\'s 16 by default\n * @param {float} loadFactor the load factor for the map, the default is 0.75\n * @param {Map} m gives the new HashMap the same mappings as this Map\n */\n var HashMap = (function() {\n /**\n * @member HashMap\n * A HashMap stores a collection of objects, each referenced by a key. This is similar to an Array, only\n * instead of accessing elements with a numeric index, a String is used. (If you are familiar with\n * associative arrays from other languages, this is the same idea.)\n *\n * @param {int} initialCapacity defines the initial capacity of the map, it\'s 16 by default\n * @param {float} loadFactor the load factor for the map, the default is 0.75\n * @param {Map} m gives the new HashMap the same mappings as this Map\n */\n function HashMap() {\n if (arguments.length === 1 && arguments[0] instanceof HashMap) {\n return arguments[0].clone();\n }\n\n var initialCapacity = arguments.length > 0 ? arguments[0] : 16;\n var loadFactor = arguments.length > 1 ? arguments[1] : 0.75;\n var buckets = [];\n buckets.length = initialCapacity;\n var count = 0;\n var hashMap = this;\n\n function getBucketIndex(key) {\n var index = virtHashCode(key) % buckets.length;\n return index < 0 ? buckets.length + index : index;\n }\n function ensureLoad() {\n if (count <= loadFactor * buckets.length) {\n return;\n }\n var allEntries = [];\n for (var i = 0; i < buckets.length; ++i) {\n if (buckets[i] !== undef) {\n allEntries = allEntries.concat(buckets[i]);\n }\n }\n var newBucketsLength = buckets.length * 2;\n buckets = [];\n buckets.length = newBucketsLength;\n for (var j = 0; j < allEntries.length; ++j) {\n var index = getBucketIndex(allEntries[j].key);\n var bucket = buckets[index];\n if (bucket === undef) {\n buckets[index] = bucket = [];\n }\n bucket.push(allEntries[j]);\n }\n }\n\n function Iterator(conversion, removeItem) {\n var bucketIndex = 0;\n var itemIndex = -1;\n var endOfBuckets = false;\n\n function findNext() {\n while (!endOfBuckets) {\n ++itemIndex;\n if (bucketIndex >= buckets.length) {\n endOfBuckets = true;\n } else if (buckets[bucketIndex] === undef || itemIndex >= buckets[bucketIndex].length) {\n itemIndex = -1;\n ++bucketIndex;\n } else {\n return;\n }\n }\n }\n\n /*\n * @member Iterator\n * Checks if the Iterator has more items\n */\n this.hasNext = function() {\n return !endOfBuckets;\n };\n\n /*\n * @member Iterator\n * Return the next Item\n */\n this.next = function() {\n var result = conversion(buckets[bucketIndex][itemIndex]);\n findNext();\n return result;\n };\n\n /*\n * @member Iterator\n * Remove the current item\n */\n this.remove = function() {\n removeItem(this.next());\n --itemIndex;\n };\n\n findNext();\n }\n\n function Set(conversion, isIn, removeItem) {\n this.clear = function() {\n hashMap.clear();\n };\n\n this.contains = function(o) {\n return isIn(o);\n };\n\n this.containsAll = function(o) {\n var it = o.iterator();\n while (it.hasNext()) {\n if (!this.contains(it.next())) {\n return false;\n }\n }\n return true;\n };\n\n this.isEmpty = function() {\n return hashMap.isEmpty();\n };\n\n this.iterator = function() {\n return new Iterator(conversion, removeItem);\n };\n\n this.remove = function(o) {\n if (this.contains(o)) {\n removeItem(o);\n return true;\n }\n return false;\n };\n\n this.removeAll = function(c) {\n var it = c.iterator();\n var changed = false;\n while (it.hasNext()) {\n var item = it.next();\n if (this.contains(item)) {\n removeItem(item);\n changed = true;\n }\n }\n return true;\n };\n\n this.retainAll = function(c) {\n var it = this.iterator();\n var toRemove = [];\n while (it.hasNext()) {\n var entry = it.next();\n if (!c.contains(entry)) {\n toRemove.push(entry);\n }\n }\n for (var i = 0; i < toRemove.length; ++i) {\n removeItem(toRemove[i]);\n }\n return toRemove.length > 0;\n };\n\n this.size = function() {\n return hashMap.size();\n };\n\n this.toArray = function() {\n var result = [];\n var it = this.iterator();\n while (it.hasNext()) {\n result.push(it.next());\n }\n return result;\n };\n }\n\n function Entry(pair) {\n this._isIn = function(map) {\n return map === hashMap && (pair.removed === undef);\n };\n\n this.equals = function(o) {\n return virtEquals(pair.key, o.getKey());\n };\n\n this.getKey = function() {\n return pair.key;\n };\n\n this.getValue = function() {\n return pair.value;\n };\n\n this.hashCode = function(o) {\n return virtHashCode(pair.key);\n };\n\n this.setValue = function(value) {\n var old = pair.value;\n pair.value = value;\n return old;\n };\n }\n\n this.clear = function() {\n count = 0;\n buckets = [];\n buckets.length = initialCapacity;\n };\n\n this.clone = function() {\n var map = new HashMap();\n map.putAll(this);\n return map;\n };\n\n this.containsKey = function(key) {\n var index = getBucketIndex(key);\n var bucket = buckets[index];\n if (bucket === undef) {\n return false;\n }\n for (var i = 0; i < bucket.length; ++i) {\n if (virtEquals(bucket[i].key, key)) {\n return true;\n }\n }\n return false;\n };\n\n this.containsValue = function(value) {\n for (var i = 0; i < buckets.length; ++i) {\n var bucket = buckets[i];\n if (bucket === undef) {\n continue;\n }\n for (var j = 0; j < bucket.length; ++j) {\n if (virtEquals(bucket[j].value, value)) {\n return true;\n }\n }\n }\n return false;\n };\n\n this.entrySet = function() {\n return new Set(\n\n function(pair) {\n return new Entry(pair);\n },\n\n function(pair) {\n return (pair instanceof Entry) && pair._isIn(hashMap);\n },\n\n function(pair) {\n return hashMap.remove(pair.getKey());\n });\n };\n\n this.get = function(key) {\n var index = getBucketIndex(key);\n var bucket = buckets[index];\n if (bucket === undef) {\n return null;\n }\n for (var i = 0; i < bucket.length; ++i) {\n if (virtEquals(bucket[i].key, key)) {\n return bucket[i].value;\n }\n }\n return null;\n };\n\n this.isEmpty = function() {\n return count === 0;\n };\n\n this.keySet = function() {\n return new Set(\n // get key from pair\n function(pair) {\n return pair.key;\n },\n // is-in test\n function(key) {\n return hashMap.containsKey(key);\n },\n // remove from hashmap by key\n function(key) {\n return hashMap.remove(key);\n }\n );\n };\n\n this.values = function() {\n return new Set(\n // get value from pair\n function(pair) {\n return pair.value;\n },\n // is-in test\n function(value) {\n return hashMap.containsValue(value);\n },\n // remove from hashmap by value\n function(value) {\n return hashMap.removeByValue(value);\n }\n );\n };\n\n this.put = function(key, value) {\n var index = getBucketIndex(key);\n var bucket = buckets[index];\n if (bucket === undef) {\n ++count;\n buckets[index] = [{\n key: key,\n value: value\n }];\n ensureLoad();\n return null;\n }\n for (var i = 0; i < bucket.length; ++i) {\n if (virtEquals(bucket[i].key, key)) {\n var previous = bucket[i].value;\n bucket[i].value = value;\n return previous;\n }\n }\n ++count;\n bucket.push({\n key: key,\n value: value\n });\n ensureLoad();\n return null;\n };\n\n this.putAll = function(m) {\n var it = m.entrySet().iterator();\n while (it.hasNext()) {\n var entry = it.next();\n this.put(entry.getKey(), entry.getValue());\n }\n };\n\n this.remove = function(key) {\n var index = getBucketIndex(key);\n var bucket = buckets[index];\n if (bucket === undef) {\n return null;\n }\n for (var i = 0; i < bucket.length; ++i) {\n if (virtEquals(bucket[i].key, key)) {\n --count;\n var previous = bucket[i].value;\n bucket[i].removed = true;\n if (bucket.length > 1) {\n bucket.splice(i, 1);\n } else {\n buckets[index] = undef;\n }\n return previous;\n }\n }\n return null;\n };\n\n this.removeByValue = function(value) {\n var bucket, i, ilen, pair;\n for (bucket in buckets) {\n if (buckets.hasOwnProperty(bucket)) {\n for (i = 0, ilen = buckets[bucket].length; i < ilen; i++) {\n pair = buckets[bucket][i];\n // removal on values is based on identity, not equality\n if (pair.value === value) {\n buckets[bucket].splice(i, 1);\n return true;\n }\n }\n }\n }\n return false;\n };\n\n this.size = function() {\n return count;\n };\n }\n\n return HashMap;\n }());\n\n // Building defaultScope. Changing of the prototype protects\n // internal Processing code from the changes in defaultScope\n function DefaultScope() {}\n DefaultScope.prototype = PConstants;\n\n var defaultScope = new DefaultScope();\n defaultScope.ArrayList = ArrayList;\n defaultScope.HashMap = HashMap;\n defaultScope.ObjectIterator = ObjectIterator;\n defaultScope.PConstants = PConstants;\n //defaultScope.PImage = PImage; // TODO\n //defaultScope.PShape = PShape; // TODO\n //defaultScope.PShapeSVG = PShapeSVG; // TODO\n\n ////////////////////////////////////////////////////////////////////////////\n // Class inheritance helper methods\n ////////////////////////////////////////////////////////////////////////////\n\n defaultScope.defineProperty = function(obj, name, desc) {\n if("defineProperty" in Object) {\n Object.defineProperty(obj, name, desc);\n } else {\n if (desc.hasOwnProperty("get")) {\n obj.__defineGetter__(name, desc.get);\n }\n if (desc.hasOwnProperty("set")) {\n obj.__defineSetter__(name, desc.set);\n }\n }\n };\n\n function extendClass(subClass, baseClass) {\n function extendGetterSetter(propertyName) {\n defaultScope.defineProperty(subClass, propertyName, {\n get: function() {\n return baseClass[propertyName];\n },\n set: function(v) {\n baseClass[propertyName]=v;\n },\n enumerable: true\n });\n }\n\n var properties = [];\n for (var propertyName in baseClass) {\n if (typeof baseClass[propertyName] === \'function\') {\n // Overriding all non-overriden functions\n if (!subClass.hasOwnProperty(propertyName)) {\n subClass[propertyName] = baseClass[propertyName];\n }\n } else if(propertyName.charAt(0) !== "$" && !(propertyName in subClass)) {\n // Delaying the properties extension due to the IE9 bug (see #918).\n properties.push(propertyName);\n }\n }\n while (properties.length > 0) {\n extendGetterSetter(properties.shift());\n }\n }\n\n defaultScope.extendClassChain = function(base) {\n var path = [base];\n for (var self = base.$upcast; self; self = self.$upcast) {\n extendClass(self, base);\n path.push(self);\n base = self;\n }\n while (path.length > 0) {\n path.pop().$self=base;\n }\n };\n\n defaultScope.extendStaticMembers = function(derived, base) {\n extendClass(derived, base);\n };\n\n defaultScope.extendInterfaceMembers = function(derived, base) {\n extendClass(derived, base);\n };\n\n defaultScope.addMethod = function(object, name, fn, superAccessor) {\n if (object[name]) {\n var args = fn.length,\n oldfn = object[name];\n\n object[name] = function() {\n if (arguments.length === args) {\n return fn.apply(this, arguments);\n }\n return oldfn.apply(this, arguments);\n };\n } else {\n object[name] = fn;\n }\n };\n\n defaultScope.createJavaArray = function(type, bounds) {\n var result = null;\n if (typeof bounds[0] === \'number\') {\n var itemsCount = 0 | bounds[0];\n if (bounds.length <= 1) {\n result = [];\n result.length = itemsCount;\n for (var i = 0; i < itemsCount; ++i) {\n result[i] = 0;\n }\n } else {\n result = [];\n var newBounds = bounds.slice(1);\n for (var j = 0; j < itemsCount; ++j) {\n result.push(defaultScope.createJavaArray(type, newBounds));\n }\n }\n }\n return result;\n };\n\n var colors = {\n aliceblue: "#f0f8ff",\n antiquewhite: "#faebd7",\n aqua: "#00ffff",\n aquamarine: "#7fffd4",\n azure: "#f0ffff",\n beige: "#f5f5dc",\n bisque: "#ffe4c4",\n black: "#000000",\n blanchedalmond: "#ffebcd",\n blue: "#0000ff",\n blueviolet: "#8a2be2",\n brown: "#a52a2a",\n burlywood: "#deb887",\n cadetblue: "#5f9ea0",\n chartreuse: "#7fff00",\n chocolate: "#d2691e",\n coral: "#ff7f50",\n cornflowerblue: "#6495ed",\n cornsilk: "#fff8dc",\n crimson: "#dc143c",\n cyan: "#00ffff",\n darkblue: "#00008b",\n darkcyan: "#008b8b",\n darkgoldenrod: "#b8860b",\n darkgray: "#a9a9a9",\n darkgreen: "#006400",\n darkkhaki: "#bdb76b",\n darkmagenta: "#8b008b",\n darkolivegreen: "#556b2f",\n darkorange: "#ff8c00",\n darkorchid: "#9932cc",\n darkred: "#8b0000",\n darksalmon: "#e9967a",\n darkseagreen: "#8fbc8f",\n darkslateblue: "#483d8b",\n darkslategray: "#2f4f4f",\n darkturquoise: "#00ced1",\n darkviolet: "#9400d3",\n deeppink: "#ff1493",\n deepskyblue: "#00bfff",\n dimgray: "#696969",\n dodgerblue: "#1e90ff",\n firebrick: "#b22222",\n floralwhite: "#fffaf0",\n forestgreen: "#228b22",\n fuchsia: "#ff00ff",\n gainsboro: "#dcdcdc",\n ghostwhite: "#f8f8ff",\n gold: "#ffd700",\n goldenrod: "#daa520",\n gray: "#808080",\n green: "#008000",\n greenyellow: "#adff2f",\n honeydew: "#f0fff0",\n hotpink: "#ff69b4",\n indianred: "#cd5c5c",\n indigo: "#4b0082",\n ivory: "#fffff0",\n khaki: "#f0e68c",\n lavender: "#e6e6fa",\n lavenderblush: "#fff0f5",\n lawngreen: "#7cfc00",\n lemonchiffon: "#fffacd",\n lightblue: "#add8e6",\n lightcoral: "#f08080",\n lightcyan: "#e0ffff",\n lightgoldenrodyellow: "#fafad2",\n lightgrey: "#d3d3d3",\n lightgreen: "#90ee90",\n lightpink: "#ffb6c1",\n lightsalmon: "#ffa07a",\n lightseagreen: "#20b2aa",\n lightskyblue: "#87cefa",\n lightslategray: "#778899",\n lightsteelblue: "#b0c4de",\n lightyellow: "#ffffe0",\n lime: "#00ff00",\n limegreen: "#32cd32",\n linen: "#faf0e6",\n magenta: "#ff00ff",\n maroon: "#800000",\n mediumaquamarine: "#66cdaa",\n mediumblue: "#0000cd",\n mediumorchid: "#ba55d3",\n mediumpurple: "#9370d8",\n mediumseagreen: "#3cb371",\n mediumslateblue: "#7b68ee",\n mediumspringgreen: "#00fa9a",\n mediumturquoise: "#48d1cc",\n mediumvioletred: "#c71585",\n midnightblue: "#191970",\n mintcream: "#f5fffa",\n mistyrose: "#ffe4e1",\n moccasin: "#ffe4b5",\n navajowhite: "#ffdead",\n navy: "#000080",\n oldlace: "#fdf5e6",\n olive: "#808000",\n olivedrab: "#6b8e23",\n orange: "#ffa500",\n orangered: "#ff4500",\n orchid: "#da70d6",\n palegoldenrod: "#eee8aa",\n palegreen: "#98fb98",\n paleturquoise: "#afeeee",\n palevioletred: "#d87093",\n papayawhip: "#ffefd5",\n peachpuff: "#ffdab9",\n peru: "#cd853f",\n pink: "#ffc0cb",\n plum: "#dda0dd",\n powderblue: "#b0e0e6",\n purple: "#800080",\n red: "#ff0000",\n rosybrown: "#bc8f8f",\n royalblue: "#4169e1",\n saddlebrown: "#8b4513",\n salmon: "#fa8072",\n sandybrown: "#f4a460",\n seagreen: "#2e8b57",\n seashell: "#fff5ee",\n sienna: "#a0522d",\n silver: "#c0c0c0",\n skyblue: "#87ceeb",\n slateblue: "#6a5acd",\n slategray: "#708090",\n snow: "#fffafa",\n springgreen: "#00ff7f",\n steelblue: "#4682b4",\n tan: "#d2b48c",\n teal: "#008080",\n thistle: "#d8bfd8",\n tomato: "#ff6347",\n turquoise: "#40e0d0",\n violet: "#ee82ee",\n wheat: "#f5deb3",\n white: "#ffffff",\n whitesmoke: "#f5f5f5",\n yellow: "#ffff00",\n yellowgreen: "#9acd32"\n };\n\n // Unsupported Processing File and I/O operations.\n (function(Processing) {\n var unsupportedP5 = ("open() createOutput() createInput() BufferedReader selectFolder() " +\n "dataPath() createWriter() selectOutput() beginRecord() " +\n "saveStream() endRecord() selectInput() saveBytes() createReader() " +\n "beginRaw() endRaw() PrintWriter delay()").split(" "),\n count = unsupportedP5.length,\n prettyName,\n p5Name;\n\n function createUnsupportedFunc(n) {\n return function() {\n throw "Processing.js does not support " + n + ".";\n };\n }\n\n while (count--) {\n prettyName = unsupportedP5[count];\n p5Name = prettyName.replace("()", "");\n\n Processing[p5Name] = createUnsupportedFunc(prettyName);\n }\n }(defaultScope));\n\n // screenWidth and screenHeight are shared by all instances.\n // and return the width/height of the browser\'s viewport.\n defaultScope.defineProperty(defaultScope, \'screenWidth\',\n { get: function() { return window.innerWidth; } });\n\n defaultScope.defineProperty(defaultScope, \'screenHeight\',\n { get: function() { return window.innerHeight; } });\n\n // Manage multiple Processing instances\n var processingInstances = [];\n var processingInstanceIds = {};\n\n var removeInstance = function(id) {\n processingInstances.splice(processingInstanceIds[id], 1);\n delete processingInstanceIds[id];\n };\n\n var addInstance = function(processing) {\n if (processing.externals.canvas.id === undef || !processing.externals.canvas.id.length) {\n processing.externals.canvas.id = "__processing" + processingInstances.length;\n }\n processingInstanceIds[processing.externals.canvas.id] = processingInstances.length;\n processingInstances.push(processing);\n };\n\n ////////////////////////////////////////////////////////////////////////////\n // LRUCache.JS START\n ////////////////////////////////////////////////////////////////////////////\n\n /**\n * This is a Least Recently Used Cache\n *\n * When the max size is reached, then the least recently used item is dropped.\n *\n * This is tracked by having a "use index", which is a number indicating how\n * recently a given item was accessed. The closer the "use index" is to\n * "mostRecent", the more recently is was used.\n *\n * When an item is accessed (via .get()) it\'s "use index" gets updated to be\n * the new "most recent".\n */\n\n function LRUCache(maxSize) {\n this.maxSize = maxSize;\n this.size = 0;\n this.cache = {}; // key => val\n this.useIndex = {}; // use index => key\n this.useReverse = {}; // key => use index\n // this will be incremented to 0 for the first item added, making\n // leastRecent === mostRecent\n this.mostRecent = -1;\n this.leastRecent = 0;\n }\n\n /**\n * Get a value from the cache, returning undefined for an unknown key\n */\n LRUCache.prototype.get = function(key) {\n key = key + \'\';\n if (!this.cache[key]) {\n return;\n }\n this._makeMostRecent(key);\n return this.cache[key];\n };\n\n /**\n * Set a value in the cache. If the max size is reached, the least recently\n * used item will be popped off.\n */\n LRUCache.prototype.set = function(key, val) {\n key = key + \'\';\n if (!this.cache[key]) {\n this.size += 1;\n }\n this.cache[key] = val;\n this._makeMostRecent(key);\n\n if (this.size > this.maxSize) {\n this._pop();\n }\n };\n\n LRUCache.prototype._makeMostRecent = function (key) {\n var current = this.useReverse[key];\n if (current === this.mostRecent) {\n return;\n } else if (current) {\n delete this.useIndex[current];\n }\n\n this.mostRecent += 1;\n var newIndex = this.mostRecent;\n this.useIndex[newIndex] = key;\n this.useReverse[key] = newIndex;\n }\n\n LRUCache.prototype._pop = function () {\n while (this.leastRecent < this.mostRecent) {\n var oldKey = this.useIndex[this.leastRecent];\n if (!oldKey) {\n this.leastRecent += 1;\n continue;\n }\n\n delete this.useIndex[this.leastRecent];\n delete this.useReverse[oldKey];\n delete this.cache[oldKey];\n this.leastRecent += 1;\n this.size -= 1;\n return;\n }\n }\n\n ////////////////////////////////////////////////////////////////////////////\n // PFONT.JS START\n ////////////////////////////////////////////////////////////////////////////\n\n /**\n * [internal function] computeFontMetrics() calculates various metrics for text\n * placement. Currently this function computes the ascent, descent and leading\n * (from "lead", used for vertical space) values for the currently active font.\n */\n function computeFontMetrics(pfont) {\n var emQuad = 250,\n correctionFactor = pfont.size / emQuad,\n canvas = document.createElement("canvas");\n canvas.width = 2*emQuad;\n canvas.height = 2*emQuad;\n canvas.style.opacity = 0;\n var cfmFont = pfont.getCSSDefinition(emQuad+"px", "normal"),\n ctx = canvas.getContext("2d");\n ctx.font = cfmFont;\n pfont.context2d = ctx;\n\n // Size the canvas using a string with common max-ascent and max-descent letters.\n // Changing the canvas dimensions resets the context, so we must reset the font.\n var protrusions = "dbflkhyjqpg";\n canvas.width = ctx.measureText(protrusions).width;\n ctx.font = cfmFont;\n\n // for text lead values, we meaure a multiline text container.\n var leadDiv = document.createElement("div");\n leadDiv.style.position = "absolute";\n leadDiv.style.opacity = 0;\n leadDiv.style.fontFamily = \'"\' + pfont.name + \'"\';\n leadDiv.style.fontSize = emQuad + "px";\n leadDiv.innerHTML = protrusions + "<br/>" + protrusions;\n document.body.appendChild(leadDiv);\n\n var w = canvas.width,\n h = canvas.height,\n baseline = h/2;\n\n // Set all canvas pixeldata values to 255, with all the content\n // data being 0. This lets us scan for data[i] != 255.\n ctx.fillStyle = "white";\n ctx.fillRect(0, 0, w, h);\n ctx.fillStyle = "black";\n ctx.fillText(protrusions, 0, baseline);\n var pixelData = ctx.getImageData(0, 0, w, h).data;\n\n // canvas pixel data is w*4 by h*4, because R, G, B and A are separate,\n // consecutive values in the array, rather than stored as 32 bit ints.\n var i = 0,\n w4 = w * 4,\n len = pixelData.length;\n\n // Finding the ascent uses a normal, forward scanline\n while (++i < len && pixelData[i] === 255) {\n nop();\n }\n var ascent = Math.round(i / w4);\n\n // Finding the descent uses a reverse scanline\n i = len - 1;\n while (--i > 0 && pixelData[i] === 255) {\n nop();\n }\n var descent = Math.round(i / w4);\n\n // set font metrics\n pfont.ascent = correctionFactor * (baseline - ascent);\n pfont.descent = correctionFactor * (descent - baseline);\n\n // Then we try to get the real value from the browser\n if (document.defaultView.getComputedStyle) {\n var leadDivHeight = document.defaultView.getComputedStyle(leadDiv,null).getPropertyValue("height");\n leadDivHeight = correctionFactor * leadDivHeight.replace("px","");\n if (leadDivHeight >= pfont.size * 2) {\n pfont.leading = Math.round(leadDivHeight/2);\n }\n }\n document.body.removeChild(leadDiv);\n }\n\n // Defines system (non-SVG) font.\n function PFont(name, size) {\n // according to the P5 API, new PFont() is legal (albeit completely useless)\n if (name === undef) {\n name = "";\n }\n this.name = name;\n if (size === undef) {\n size = 0;\n }\n this.size = size;\n this.glyph = false;\n this.ascent = 0;\n this.descent = 0;\n // For leading, the "safe" value uses the standard TEX ratio\n this.leading = 1.2 * size;\n\n // Note that an italic, bold font must used "... Bold Italic"\n // in P5. "... Italic Bold" is treated as normal/normal.\n var illegalIndicator = name.indexOf(" Italic Bold");\n if (illegalIndicator !== -1) {\n name = name.substring(0, illegalIndicator);\n }\n\n // determine font style\n this.style = "normal";\n var italicsIndicator = name.indexOf(" Italic");\n if (italicsIndicator !== -1) {\n name = name.substring(0, italicsIndicator);\n this.style = "italic";\n }\n\n // determine font weight\n this.weight = "normal";\n var boldIndicator = name.indexOf(" Bold");\n if (boldIndicator !== -1) {\n name = name.substring(0, boldIndicator);\n this.weight = "bold";\n }\n\n // determine font-family name\n this.family = "sans-serif";\n if (name !== undef) {\n switch(name) {\n case "sans-serif":\n case "serif":