├── .gitattributes ├── Back Prop ├── color nn - BP.js ├── index.html └── libs │ ├── matrix.js │ ├── nn.js │ ├── p5.dom.js │ ├── p5.dom.min.js │ ├── p5.js │ ├── p5.min.js │ ├── p5.sound.js │ └── p5.sound.min.js └── Genetic Algorithm ├── color nn - GA.js ├── index.html └── libs ├── p5.dom.js ├── p5.dom.min.js ├── p5.js ├── p5.min.js ├── p5.sound.js └── p5.sound.min.js /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /Back Prop/color nn - BP.js: -------------------------------------------------------------------------------- 1 | // for red, green, and blue color values 2 | let nn; 3 | var r, g, b, diameter, choices = 2; 4 | var button = [[180,300],[540,300]] 5 | let inputs, outputs, targets; 6 | 7 | function setup() { 8 | nn = new NeuralNetwork(3, 20, 2,.5); 9 | 10 | canv = createCanvas(700,500); 11 | canv.parent("sketcher"); 12 | 13 | ColorRandomizer(false,[0,0]); 14 | 15 | 16 | diameter = 200; 17 | } 18 | 19 | function draw() { 20 | background(0); 21 | 22 | textSize(30); 23 | textAlign(CENTER, CENTER); 24 | fill(255); 25 | text("Does White or Black look better over this color?", 350,50); 26 | 27 | DrawDisplayer(0); 28 | DrawDisplayer(1); 29 | 30 | fill(255); 31 | ellipse(button[outputs[0] > outputs[1] ? 0 : 1][0], 150, 50, 50); 32 | 33 | } 34 | 35 | function DrawDisplayer(loc) 36 | { 37 | fill(r, g, b); 38 | ellipse(button[loc][0], button[loc][1], diameter, diameter); 39 | fill(loc*255); 40 | textSize(48); 41 | textAlign(CENTER, CENTER); 42 | var t = (loc==0) ? "BLACK" : "WHITE"; 43 | text(t,button[loc][0], button[loc][1]); 44 | } 45 | 46 | function ColorRandomizer(tr,t) 47 | { 48 | if(tr) 49 | { 50 | // For training 51 | targets = t; 52 | nn.train(inputs, targets); 53 | 54 | // TIPS FROM USING THE TOY NN LIB 55 | // I think the backprop order of operation may need a bit of work? for I can't get the toy NN to converge without overfitting it seems 56 | // however I am out of time & not good enough in JS syntax to add to the high level JS syntax in the lib. xD 57 | // so I suggest using the nn.predict() function on only a small dataset perhaps to get the highest possible accuracy? 58 | // feel free to submit your github request here for the lib! https://github.com/CodingTrain/Toy-Neural-Network-JS 59 | outputs = nn.predict(inputs); 60 | } 61 | GrabRandomColAndGuess(); 62 | } 63 | 64 | function GrabRandomColAndGuess() 65 | { 66 | // Pick colors randomly 67 | r = random(255); 68 | g = random(255); 69 | b = random(255); 70 | 71 | inputs = [r, g, b]; 72 | // for guessing 73 | outputs = nn.predict(inputs); 74 | } 75 | 76 | function keyPressed() { 77 | if (keyCode === RIGHT_ARROW) { 78 | GrabRandomColAndGuess(); 79 | } 80 | } 81 | // When the user clicks the mouse 82 | function mousePressed() { 83 | // Check if mouse is inside the circle 84 | var d = []; 85 | 86 | for ( var i = 0; i < choices; i++) 87 | { 88 | d[i] = dist(mouseX, mouseY, button[i][0], button[i][1]); 89 | } 90 | 91 | if (d[0] < diameter/2) { 92 | // Pick new random color values 93 | ColorRandomizer(true,[1,0]); 94 | } 95 | else if (d[1] < diameter/2) { 96 | // Pick new random color values 97 | ColorRandomizer(true,[0,1]); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /Back Prop/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |

16 |

Press "Right Arrow" to test a new random color!

17 | 18 |
19 |
20 | 21 | -------------------------------------------------------------------------------- /Back Prop/libs/matrix.js: -------------------------------------------------------------------------------- 1 | // let m = new Matrix(3,2); 2 | 3 | 4 | class Matrix { 5 | constructor(rows, cols) { 6 | this.rows = rows; 7 | this.cols = cols; 8 | this.data = Array(this.rows).fill().map(() => Array(this.cols).fill(0)); 9 | } 10 | 11 | static fromArray(arr) { 12 | return new Matrix(arr.length, 1).map((e, i) => arr[i]); 13 | } 14 | 15 | static subtract(a, b) { 16 | if(a.rows!==b.rows || a.cols!==b.cols){ 17 | console.log('Columns and Rows of A must match Columns and Rows of B.'); 18 | return; 19 | } 20 | 21 | // Return a new Matrix a-b 22 | return new Matrix(a.rows, a.cols) 23 | .map((_, i, j) => a.data[i][j] - b.data[i][j]); 24 | } 25 | 26 | toArray() { 27 | let arr = []; 28 | for (let i = 0; i < this.rows; i++) { 29 | for (let j = 0; j < this.cols; j++) { 30 | arr.push(this.data[i][j]); 31 | } 32 | } 33 | return arr; 34 | } 35 | 36 | randomize() { 37 | return this.map(e => Math.random() * 2 - 1); 38 | } 39 | 40 | add(n) { 41 | if (n instanceof Matrix) { 42 | if(this.rows!==n.rows || this.cols!==n.cols){ 43 | console.log('Columns and Rows of A must match Columns and Rows of B.'); 44 | return; 45 | } 46 | return this.map((e, i, j) => e + n.data[i][j]); 47 | } else { 48 | return this.map(e => e + n); 49 | } 50 | } 51 | 52 | static transpose(matrix) { 53 | return new Matrix(matrix.cols, matrix.rows) 54 | .map((_, i, j) => matrix.data[j][i]); 55 | } 56 | 57 | static multiply(a, b) { 58 | // Matrix product 59 | if (a.cols !== b.rows) { 60 | console.log('Columns of A must match rows of B.') 61 | return; 62 | } 63 | 64 | return new Matrix(a.rows, b.cols) 65 | .map((e, i, j) => { 66 | // Dot product of values in col 67 | let sum = 0; 68 | for (let k = 0; k < a.cols; k++) { 69 | sum += a.data[i][k] * b.data[k][j]; 70 | } 71 | return sum; 72 | }); 73 | } 74 | 75 | multiply(n) { 76 | if (n instanceof Matrix) { 77 | if(this.rows!==n.rows || this.cols!==n.cols){ 78 | console.log('Columns and Rows of A must match Columns and Rows of B.'); 79 | return; 80 | } 81 | 82 | // hadamard product 83 | return this.map((e, i, j) => e * n.data[i][j]); 84 | } else { 85 | // Scalar product 86 | return this.map(e => e * n); 87 | } 88 | } 89 | 90 | map(func) { 91 | // Apply a function to every element of matrix 92 | for (let i = 0; i < this.rows; i++) { 93 | for (let j = 0; j < this.cols; j++) { 94 | let val = this.data[i][j]; 95 | this.data[i][j] = func(val, i, j); 96 | } 97 | } 98 | return this; 99 | } 100 | 101 | static map(matrix, func) { 102 | // Apply a function to every element of matrix 103 | return new Matrix(matrix.rows, matrix.cols) 104 | .map((e, i, j) => func(matrix.data[i][j], i, j)); 105 | } 106 | 107 | print() { 108 | console.table(this.data); 109 | return this; 110 | } 111 | 112 | serialize() { 113 | return JSON.stringify(this); 114 | } 115 | 116 | static deserialize(data) { 117 | if(typeof data == 'string') 118 | { 119 | data = JSON.parse(data); 120 | } 121 | let matrix = new Matrix(data.rows, data.cols); 122 | matrix.data = data.data; 123 | return matrix; 124 | } 125 | } 126 | 127 | if (typeof module !== 'undefined') { 128 | module.exports = Matrix; 129 | } -------------------------------------------------------------------------------- /Back Prop/libs/nn.js: -------------------------------------------------------------------------------- 1 | // Other techniques for learning 2 | 3 | class ActivationFunction{ 4 | constructor(func, dfunc){ 5 | this.func = func; 6 | this.dfunc = dfunc; 7 | } 8 | } 9 | 10 | let sigmoid = new ActivationFunction( 11 | x => 1 / (1 + Math.exp(-x)), 12 | y => y * (1- y) 13 | ); 14 | 15 | let tanh = new ActivationFunction( 16 | x => Math.tanh(x), 17 | y => 1-(y*y) 18 | ); 19 | 20 | // returns a softmax probability distribution 21 | function softmax(vec) { 22 | VEC = []; 23 | 24 | // 25 | for (var i = 0; i < vec.length; i++) { 26 | VEC[i] = Math.pow(Math.E, vec[i]) / Summation(vec); 27 | } 28 | 29 | return VEC 30 | } 31 | 32 | // returns the summation of input vector 33 | function Summation(vec) { 34 | final = 0; 35 | 36 | for (var i = 0; i < vec.length; i++) { 37 | final += Math.pow(Math.E, (vec[i])) 38 | } 39 | 40 | return final; 41 | } 42 | 43 | class NeuralNetwork { 44 | constructor(input_nodes, hidden_nodes, output_nodes, learn_rate) { 45 | this.input_nodes = input_nodes; 46 | this.hidden_nodes = hidden_nodes; 47 | this.output_nodes = output_nodes; 48 | 49 | this.weights_ih = new Matrix(this.hidden_nodes, this.input_nodes); 50 | this.weights_ho = new Matrix(this.output_nodes, this.hidden_nodes); 51 | this.weights_ih.randomize(); 52 | this.weights_ho.randomize(); 53 | 54 | this.bias_h = new Matrix(this.hidden_nodes, 1); 55 | this.bias_o = new Matrix(this.output_nodes, 1); 56 | this.bias_h.randomize(); 57 | this.bias_o.randomize(); 58 | this.setLearningRate(learn_rate); 59 | 60 | this.setActivationFunction(); 61 | 62 | } 63 | 64 | predict(input_array) { 65 | 66 | // Generating the Hidden Outputs 67 | let inputs = Matrix.fromArray(input_array); 68 | let hidden = Matrix.multiply(this.weights_ih, inputs); 69 | hidden.add(this.bias_h); 70 | // activation function! 71 | hidden.map(this.activation_function.func); 72 | 73 | // Generating the output's output! 74 | let output = Matrix.multiply(this.weights_ho, hidden); 75 | output.add(this.bias_o); 76 | output.map(this.activation_function.func); 77 | 78 | // Sending back to the caller! 79 | return output.toArray(); 80 | } 81 | 82 | setLearningRate(learning_rate) { 83 | this.learning_rate = learning_rate; 84 | } 85 | 86 | setActivationFunction(func = sigmoid) { 87 | this.activation_function = func; 88 | } 89 | 90 | train(input_array, target_array) { 91 | // Generating the Hidden Outputs 92 | let inputs = Matrix.fromArray(input_array); 93 | let hidden = Matrix.multiply(this.weights_ih, inputs); 94 | hidden.add(this.bias_h); 95 | // activation function! 96 | hidden.map(this.activation_function.func); 97 | 98 | // Generating the output's output! 99 | let outputs = Matrix.multiply(this.weights_ho, hidden); 100 | outputs.add(this.bias_o); 101 | outputs.map(this.activation_function.func); 102 | 103 | // Convert array to matrix object 104 | let targets = Matrix.fromArray(target_array); 105 | 106 | // Calculate the error 107 | // ERROR = TARGETS - OUTPUTS 108 | let output_errors = Matrix.subtract(targets, outputs); 109 | 110 | // let gradient = outputs * (1 - outputs); 111 | // Calculate gradient 112 | let gradients = Matrix.map(outputs, this.activation_function.dfunc); 113 | gradients.multiply(output_errors); 114 | gradients.multiply(this.learning_rate); 115 | 116 | 117 | // Calculate deltas 118 | let hidden_T = Matrix.transpose(hidden); 119 | let weight_ho_deltas = Matrix.multiply(gradients, hidden_T); 120 | 121 | // Adjust the weights by deltas 122 | this.weights_ho.add(weight_ho_deltas); 123 | // Adjust the bias by its deltas (which is just the gradients) 124 | this.bias_o.add(gradients); 125 | 126 | // Calculate the hidden layer errors 127 | let who_t = Matrix.transpose(this.weights_ho); 128 | let hidden_errors = Matrix.multiply(who_t, output_errors); 129 | 130 | // Calculate hidden gradient 131 | let hidden_gradient = Matrix.map(hidden, this.activation_function.dfunc); 132 | hidden_gradient.multiply(hidden_errors); 133 | hidden_gradient.multiply(this.learning_rate); 134 | 135 | // Calcuate input->hidden deltas 136 | let inputs_T = Matrix.transpose(inputs); 137 | let weight_ih_deltas = Matrix.multiply(hidden_gradient, inputs_T); 138 | 139 | this.weights_ih.add(weight_ih_deltas); 140 | // Adjust the bias by its deltas (which is just the gradients) 141 | this.bias_h.add(hidden_gradient); 142 | 143 | // outputs.print(); 144 | // targets.print(); 145 | // error.print(); 146 | } 147 | 148 | serialize() { 149 | return JSON.stringify(this); 150 | } 151 | 152 | static deserialize(data) { 153 | if(typeof data == 'string') 154 | { 155 | data = JSON.parse(data); 156 | } 157 | let nn = new NeuralNetwork(data.input_nodes, data.hidden_nodes, data.output_nodes); 158 | nn.weights_ih = Matrix.deserialize(data.weights_ih); 159 | nn.weights_ho = Matrix.deserialize(data.weights_ho); 160 | nn.bias_h = Matrix.deserialize(data.bias_h); 161 | nn.bias_o = Matrix.deserialize(data.bias_o); 162 | nn.learning_rate = data.learning_rate; 163 | return nn; 164 | } 165 | 166 | } 167 | -------------------------------------------------------------------------------- /Back Prop/libs/p5.dom.js: -------------------------------------------------------------------------------- 1 | /*! p5.dom.js v0.3.4 Jan 19, 2017 */ 2 | /** 3 | *

The web is much more than just canvas and p5.dom makes it easy to interact 4 | * with other HTML5 objects, including text, hyperlink, image, input, video, 5 | * audio, and webcam.

6 | *

There is a set of creation methods, DOM manipulation methods, and 7 | * an extended p5.Element that supports a range of HTML elements. See the 8 | * 9 | * beyond the canvas tutorial for a full overview of how this addon works. 10 | * 11 | *

Methods and properties shown in black are part of the p5.js core, items in 12 | * blue are part of the p5.dom library. You will need to include an extra file 13 | * in order to access the blue functions. See the 14 | * using a library 15 | * section for information on how to include this library. p5.dom comes with 16 | * p5 complete or you can download the single file 17 | * 18 | * here.

19 | *

See tutorial: beyond the canvas 20 | * for more info on how to use this libary. 21 | * 22 | * @module p5.dom 23 | * @submodule p5.dom 24 | * @for p5.dom 25 | * @main 26 | */ 27 | 28 | (function(root, factory) { 29 | if (typeof define === 'function' && define.amd) 30 | define('p5.dom', ['p5'], function(p5) { 31 | factory(p5); 32 | }); 33 | else if (typeof exports === 'object') factory(require('../p5')); 34 | else factory(root['p5']); 35 | })(this, function(p5) { 36 | // ============================================================================= 37 | // p5 additions 38 | // ============================================================================= 39 | 40 | /** 41 | * Searches the page for an element with the given ID, class, or tag name (using the '#' or '.' 42 | * prefixes to specify an ID or class respectively, and none for a tag) and returns it as 43 | * a p5.Element. If a class or tag name is given with more than 1 element, 44 | * only the first element will be returned. 45 | * The DOM node itself can be accessed with .elt. 46 | * Returns null if none found. You can also specify a container to search within. 47 | * 48 | * @method select 49 | * @param {String} name id, class, or tag name of element to search for 50 | * @param {String} [container] id, p5.Element, or HTML element to search within 51 | * @return {Object|p5.Element|Null} p5.Element containing node found 52 | * @example 53 | *

54 | * function setup() { 55 | * createCanvas(100, 100); 56 | * //translates canvas 50px down 57 | * select('canvas').position(100, 100); 58 | * } 59 | *
60 | *
61 | * // these are all valid calls to select() 62 | * var a = select('#moo'); 63 | * var b = select('#blah', '#myContainer'); 64 | * var c = select('#foo', b); 65 | * var d = document.getElementById('beep'); 66 | * var e = select('p', d); 67 | * [a, b, c, d, e]; // unused 68 | *
69 | * 70 | */ 71 | p5.prototype.select = function(e, p) { 72 | p5._validateParameters('select', arguments); 73 | var res = null; 74 | var container = getContainer(p); 75 | if (e[0] === '.') { 76 | e = e.slice(1); 77 | res = container.getElementsByClassName(e); 78 | if (res.length) { 79 | res = res[0]; 80 | } else { 81 | res = null; 82 | } 83 | } else if (e[0] === '#') { 84 | e = e.slice(1); 85 | res = container.getElementById(e); 86 | } else { 87 | res = container.getElementsByTagName(e); 88 | if (res.length) { 89 | res = res[0]; 90 | } else { 91 | res = null; 92 | } 93 | } 94 | if (res) { 95 | return this._wrapElement(res); 96 | } else { 97 | return null; 98 | } 99 | }; 100 | 101 | /** 102 | * Searches the page for elements with the given class or tag name (using the '.' prefix 103 | * to specify a class and no prefix for a tag) and returns them as p5.Elements 104 | * in an array. 105 | * The DOM node itself can be accessed with .elt. 106 | * Returns an empty array if none found. 107 | * You can also specify a container to search within. 108 | * 109 | * @method selectAll 110 | * @param {String} name class or tag name of elements to search for 111 | * @param {String} [container] id, p5.Element, or HTML element to search within 112 | * @return {Array} Array of p5.Elements containing nodes found 113 | * @example 114 | *
115 | * function setup() { 116 | * createButton('btn'); 117 | * createButton('2nd btn'); 118 | * createButton('3rd btn'); 119 | * var buttons = selectAll('button'); 120 | * 121 | * for (var i = 0; i < buttons.length; i++) { 122 | * buttons[i].size(100, 100); 123 | * } 124 | * } 125 | *
126 | *
127 | * // these are all valid calls to selectAll() 128 | * var a = selectAll('.moo'); 129 | * a = selectAll('div'); 130 | * a = selectAll('button', '#myContainer'); 131 | * 132 | * var d = select('#container'); 133 | * a = selectAll('p', d); 134 | * 135 | * var f = document.getElementById('beep'); 136 | * a = select('.blah', f); 137 | * 138 | * a; // unused 139 | *
140 | * 141 | */ 142 | p5.prototype.selectAll = function(e, p) { 143 | p5._validateParameters('selectAll', arguments); 144 | var arr = []; 145 | var res; 146 | var container = getContainer(p); 147 | if (e[0] === '.') { 148 | e = e.slice(1); 149 | res = container.getElementsByClassName(e); 150 | } else { 151 | res = container.getElementsByTagName(e); 152 | } 153 | if (res) { 154 | for (var j = 0; j < res.length; j++) { 155 | var obj = this._wrapElement(res[j]); 156 | arr.push(obj); 157 | } 158 | } 159 | return arr; 160 | }; 161 | 162 | /** 163 | * Helper function for select and selectAll 164 | */ 165 | function getContainer(p) { 166 | var container = document; 167 | if (typeof p === 'string' && p[0] === '#') { 168 | p = p.slice(1); 169 | container = document.getElementById(p) || document; 170 | } else if (p instanceof p5.Element) { 171 | container = p.elt; 172 | } else if (p instanceof HTMLElement) { 173 | container = p; 174 | } 175 | return container; 176 | } 177 | 178 | /** 179 | * Helper function for getElement and getElements. 180 | */ 181 | p5.prototype._wrapElement = function(elt) { 182 | var children = Array.prototype.slice.call(elt.children); 183 | if (elt.tagName === 'INPUT' && elt.type === 'checkbox') { 184 | var converted = new p5.Element(elt); 185 | converted.checked = function() { 186 | if (arguments.length === 0) { 187 | return this.elt.checked; 188 | } else if (arguments[0]) { 189 | this.elt.checked = true; 190 | } else { 191 | this.elt.checked = false; 192 | } 193 | return this; 194 | }; 195 | return converted; 196 | } else if (elt.tagName === 'VIDEO' || elt.tagName === 'AUDIO') { 197 | return new p5.MediaElement(elt); 198 | } else if (elt.tagName === 'SELECT') { 199 | return this.createSelect(new p5.Element(elt)); 200 | } else if ( 201 | children.length > 0 && 202 | children.every(function(c) { 203 | return c.tagName === 'INPUT' || c.tagName === 'LABEL'; 204 | }) 205 | ) { 206 | return this.createRadio(new p5.Element(elt)); 207 | } else { 208 | return new p5.Element(elt); 209 | } 210 | }; 211 | 212 | /** 213 | * Removes all elements created by p5, except any canvas / graphics 214 | * elements created by createCanvas or createGraphics. 215 | * Event handlers are removed, and element is removed from the DOM. 216 | * @method removeElements 217 | * @example 218 | *
219 | * function setup() { 220 | * createCanvas(100, 100); 221 | * createDiv('this is some text'); 222 | * createP('this is a paragraph'); 223 | * } 224 | * function mousePressed() { 225 | * removeElements(); // this will remove the div and p, not canvas 226 | * } 227 | *
228 | * 229 | */ 230 | p5.prototype.removeElements = function(e) { 231 | p5._validateParameters('removeElements', arguments); 232 | for (var i = 0; i < this._elements.length; i++) { 233 | if (!(this._elements[i].elt instanceof HTMLCanvasElement)) { 234 | this._elements[i].remove(); 235 | } 236 | } 237 | }; 238 | 239 | /** 240 | * Helpers for create methods. 241 | */ 242 | function addElement(elt, pInst, media) { 243 | var node = pInst._userNode ? pInst._userNode : document.body; 244 | node.appendChild(elt); 245 | var c = media ? new p5.MediaElement(elt) : new p5.Element(elt); 246 | pInst._elements.push(c); 247 | return c; 248 | } 249 | 250 | /** 251 | * Creates a <div></div> element in the DOM with given inner HTML. 252 | * Appends to the container node if one is specified, otherwise 253 | * appends to body. 254 | * 255 | * @method createDiv 256 | * @param {String} [html] inner HTML for element created 257 | * @return {Object|p5.Element} pointer to p5.Element holding created node 258 | * @example 259 | *
260 | * createDiv('this is some text'); 261 | *
262 | */ 263 | 264 | /** 265 | * Creates a <p></p> element in the DOM with given inner HTML. Used 266 | * for paragraph length text. 267 | * Appends to the container node if one is specified, otherwise 268 | * appends to body. 269 | * 270 | * @method createP 271 | * @param {String} [html] inner HTML for element created 272 | * @return {Object|p5.Element} pointer to p5.Element holding created node 273 | * @example 274 | *
275 | * createP('this is some text'); 276 | *
277 | */ 278 | 279 | /** 280 | * Creates a <span></span> element in the DOM with given inner HTML. 281 | * Appends to the container node if one is specified, otherwise 282 | * appends to body. 283 | * 284 | * @method createSpan 285 | * @param {String} [html] inner HTML for element created 286 | * @return {Object|p5.Element} pointer to p5.Element holding created node 287 | * @example 288 | *
289 | * createSpan('this is some text'); 290 | *
291 | */ 292 | var tags = ['div', 'p', 'span']; 293 | tags.forEach(function(tag) { 294 | var method = 'create' + tag.charAt(0).toUpperCase() + tag.slice(1); 295 | p5.prototype[method] = function(html) { 296 | var elt = document.createElement(tag); 297 | elt.innerHTML = typeof html === undefined ? '' : html; 298 | return addElement(elt, this); 299 | }; 300 | }); 301 | 302 | /** 303 | * Creates an <img> element in the DOM with given src and 304 | * alternate text. 305 | * Appends to the container node if one is specified, otherwise 306 | * appends to body. 307 | * 308 | * @method createImg 309 | * @param {String} src src path or url for image 310 | * @param {String} [alt] alternate text to be used if image does not load 311 | * @param {Function} [successCallback] callback to be called once image data is loaded 312 | * @return {Object|p5.Element} pointer to p5.Element holding created node 313 | * @example 314 | *
315 | * createImg('http://p5js.org/img/asterisk-01.png'); 316 | *
317 | */ 318 | p5.prototype.createImg = function() { 319 | p5._validateParameters('createImg', arguments); 320 | var elt = document.createElement('img'); 321 | var args = arguments; 322 | var self; 323 | var setAttrs = function() { 324 | self.width = elt.offsetWidth || elt.width; 325 | self.height = elt.offsetHeight || elt.height; 326 | if (args.length > 1 && typeof args[1] === 'function') { 327 | self.fn = args[1]; 328 | self.fn(); 329 | } else if (args.length > 1 && typeof args[2] === 'function') { 330 | self.fn = args[2]; 331 | self.fn(); 332 | } 333 | }; 334 | elt.src = args[0]; 335 | if (args.length > 1 && typeof args[1] === 'string') { 336 | elt.alt = args[1]; 337 | } 338 | elt.onload = function() { 339 | setAttrs(); 340 | }; 341 | self = addElement(elt, this); 342 | return self; 343 | }; 344 | 345 | /** 346 | * Creates an <a></a> element in the DOM for including a hyperlink. 347 | * Appends to the container node if one is specified, otherwise 348 | * appends to body. 349 | * 350 | * @method createA 351 | * @param {String} href url of page to link to 352 | * @param {String} html inner html of link element to display 353 | * @param {String} [target] target where new link should open, 354 | * could be _blank, _self, _parent, _top. 355 | * @return {Object|p5.Element} pointer to p5.Element holding created node 356 | * @example 357 | *
358 | * createA('http://p5js.org/', 'this is a link'); 359 | *
360 | */ 361 | p5.prototype.createA = function(href, html, target) { 362 | p5._validateParameters('createA', arguments); 363 | var elt = document.createElement('a'); 364 | elt.href = href; 365 | elt.innerHTML = html; 366 | if (target) elt.target = target; 367 | return addElement(elt, this); 368 | }; 369 | 370 | /** INPUT **/ 371 | 372 | /** 373 | * Creates a slider <input></input> element in the DOM. 374 | * Use .size() to set the display length of the slider. 375 | * Appends to the container node if one is specified, otherwise 376 | * appends to body. 377 | * 378 | * @method createSlider 379 | * @param {Number} min minimum value of the slider 380 | * @param {Number} max maximum value of the slider 381 | * @param {Number} [value] default value of the slider 382 | * @param {Number} [step] step size for each tick of the slider (if step is set to 0, the slider will move continuously from the minimum to the maximum value) 383 | * @return {Object|p5.Element} pointer to p5.Element holding created node 384 | * @example 385 | *
386 | * var slider; 387 | * function setup() { 388 | * slider = createSlider(0, 255, 100); 389 | * slider.position(10, 10); 390 | * slider.style('width', '80px'); 391 | * } 392 | * 393 | * function draw() { 394 | * var val = slider.value(); 395 | * background(val); 396 | * } 397 | *
398 | * 399 | *
400 | * var slider; 401 | * function setup() { 402 | * colorMode(HSB); 403 | * slider = createSlider(0, 360, 60, 40); 404 | * slider.position(10, 10); 405 | * slider.style('width', '80px'); 406 | * } 407 | * 408 | * function draw() { 409 | * var val = slider.value(); 410 | * background(val, 100, 100, 1); 411 | * } 412 | *
413 | */ 414 | p5.prototype.createSlider = function(min, max, value, step) { 415 | p5._validateParameters('createSlider', arguments); 416 | var elt = document.createElement('input'); 417 | elt.type = 'range'; 418 | elt.min = min; 419 | elt.max = max; 420 | if (step === 0) { 421 | elt.step = 0.000000000000000001; // smallest valid step 422 | } else if (step) { 423 | elt.step = step; 424 | } 425 | if (typeof value === 'number') elt.value = value; 426 | return addElement(elt, this); 427 | }; 428 | 429 | /** 430 | * Creates a <button></button> element in the DOM. 431 | * Use .size() to set the display size of the button. 432 | * Use .mousePressed() to specify behavior on press. 433 | * Appends to the container node if one is specified, otherwise 434 | * appends to body. 435 | * 436 | * @method createButton 437 | * @param {String} label label displayed on the button 438 | * @param {String} [value] value of the button 439 | * @return {Object|p5.Element} pointer to p5.Element holding created node 440 | * @example 441 | *
442 | * var button; 443 | * function setup() { 444 | * createCanvas(100, 100); 445 | * background(0); 446 | * button = createButton('click me'); 447 | * button.position(19, 19); 448 | * button.mousePressed(changeBG); 449 | * } 450 | * 451 | * function changeBG() { 452 | * var val = random(255); 453 | * background(val); 454 | * } 455 | *
456 | */ 457 | p5.prototype.createButton = function(label, value) { 458 | p5._validateParameters('createButton', arguments); 459 | var elt = document.createElement('button'); 460 | elt.innerHTML = label; 461 | if (value) elt.value = value; 462 | return addElement(elt, this); 463 | }; 464 | 465 | /** 466 | * Creates a checkbox <input></input> element in the DOM. 467 | * Calling .checked() on a checkbox returns if it is checked or not 468 | * 469 | * @method createCheckbox 470 | * @param {String} [label] label displayed after checkbox 471 | * @param {boolean} [value] value of the checkbox; checked is true, unchecked is false 472 | * @return {Object|p5.Element} pointer to p5.Element holding created node 473 | * @example 474 | *
475 | * var checkbox; 476 | * 477 | * function setup() { 478 | * checkbox = createCheckbox('label', false); 479 | * checkbox.changed(myCheckedEvent); 480 | * } 481 | * 482 | * function myCheckedEvent() { 483 | * if (this.checked()) { 484 | * console.log('Checking!'); 485 | * } else { 486 | * console.log('Unchecking!'); 487 | * } 488 | * } 489 | *
490 | */ 491 | p5.prototype.createCheckbox = function() { 492 | p5._validateParameters('createCheckbox', arguments); 493 | var elt = document.createElement('div'); 494 | var checkbox = document.createElement('input'); 495 | checkbox.type = 'checkbox'; 496 | elt.appendChild(checkbox); 497 | //checkbox must be wrapped in p5.Element before label so that label appears after 498 | var self = addElement(elt, this); 499 | self.checked = function() { 500 | var cb = self.elt.getElementsByTagName('input')[0]; 501 | if (cb) { 502 | if (arguments.length === 0) { 503 | return cb.checked; 504 | } else if (arguments[0]) { 505 | cb.checked = true; 506 | } else { 507 | cb.checked = false; 508 | } 509 | } 510 | return self; 511 | }; 512 | this.value = function(val) { 513 | self.value = val; 514 | return this; 515 | }; 516 | if (arguments[0]) { 517 | var ran = Math.random() 518 | .toString(36) 519 | .slice(2); 520 | var label = document.createElement('label'); 521 | checkbox.setAttribute('id', ran); 522 | label.htmlFor = ran; 523 | self.value(arguments[0]); 524 | label.appendChild(document.createTextNode(arguments[0])); 525 | elt.appendChild(label); 526 | } 527 | if (arguments[1]) { 528 | checkbox.checked = true; 529 | } 530 | return self; 531 | }; 532 | 533 | /** 534 | * Creates a dropdown menu <select></select> element in the DOM. 535 | * It also helps to assign select-box methods to p5.Element when selecting existing select box 536 | * @method createSelect 537 | * @param {boolean} [multiple] true if dropdown should support multiple selections 538 | * @return {p5.Element} 539 | * @example 540 | *
541 | * var sel; 542 | * 543 | * function setup() { 544 | * textAlign(CENTER); 545 | * background(200); 546 | * sel = createSelect(); 547 | * sel.position(10, 10); 548 | * sel.option('pear'); 549 | * sel.option('kiwi'); 550 | * sel.option('grape'); 551 | * sel.changed(mySelectEvent); 552 | * } 553 | * 554 | * function mySelectEvent() { 555 | * var item = sel.value(); 556 | * background(200); 557 | * text('it is a' + item + '!', 50, 50); 558 | * } 559 | *
560 | */ 561 | /** 562 | * @method createSelect 563 | * @param {Object} existing DOM select element 564 | * @return {p5.Element} 565 | */ 566 | 567 | p5.prototype.createSelect = function() { 568 | p5._validateParameters('createSelect', arguments); 569 | var elt, self; 570 | var arg = arguments[0]; 571 | if (typeof arg === 'object' && arg.elt.nodeName === 'SELECT') { 572 | self = arg; 573 | elt = this.elt = arg.elt; 574 | } else { 575 | elt = document.createElement('select'); 576 | if (arg && typeof arg === 'boolean') { 577 | elt.setAttribute('multiple', 'true'); 578 | } 579 | self = addElement(elt, this); 580 | } 581 | self.option = function(name, value) { 582 | var index; 583 | //see if there is already an option with this name 584 | for (var i = 0; i < this.elt.length; i++) { 585 | if (this.elt[i].innerHTML === name) { 586 | index = i; 587 | break; 588 | } 589 | } 590 | //if there is an option with this name we will modify it 591 | if (index !== undefined) { 592 | //if the user passed in false then delete that option 593 | if (value === false) { 594 | this.elt.remove(index); 595 | } else { 596 | //otherwise if the name and value are the same then change both 597 | if (this.elt[index].innerHTML === this.elt[index].value) { 598 | this.elt[index].innerHTML = this.elt[index].value = value; 599 | //otherwise just change the value 600 | } else { 601 | this.elt[index].value = value; 602 | } 603 | } 604 | } else { 605 | //if it doesn't exist make it 606 | var opt = document.createElement('option'); 607 | opt.innerHTML = name; 608 | if (arguments.length > 1) opt.value = value; 609 | else opt.value = name; 610 | elt.appendChild(opt); 611 | } 612 | }; 613 | self.selected = function(value) { 614 | var arr = [], 615 | i; 616 | if (arguments.length > 0) { 617 | for (i = 0; i < this.elt.length; i++) { 618 | if (value.toString() === this.elt[i].value) { 619 | this.elt.selectedIndex = i; 620 | } 621 | } 622 | return this; 623 | } else { 624 | if (this.elt.getAttribute('multiple')) { 625 | for (i = 0; i < this.elt.selectedOptions.length; i++) { 626 | arr.push(this.elt.selectedOptions[i].value); 627 | } 628 | return arr; 629 | } else { 630 | return this.elt.value; 631 | } 632 | } 633 | }; 634 | return self; 635 | }; 636 | 637 | /** 638 | * Creates a radio button <input></input> element in the DOM. 639 | * The .option() method can be used to set options for the radio after it is 640 | * created. The .value() method will return the currently selected option. 641 | * 642 | * @method createRadio 643 | * @param {String} [divId] the id and name of the created div and input field respectively 644 | * @return {Object|p5.Element} pointer to p5.Element holding created node 645 | * @example 646 | *
647 | * var radio; 648 | * 649 | * function setup() { 650 | * radio = createRadio(); 651 | * radio.option('black'); 652 | * radio.option('white'); 653 | * radio.option('gray'); 654 | * radio.style('width', '60px'); 655 | * textAlign(CENTER); 656 | * fill(255, 0, 0); 657 | * } 658 | * 659 | * function draw() { 660 | * var val = radio.value(); 661 | * background(val); 662 | * text(val, width / 2, height / 2); 663 | * } 664 | *
665 | *
666 | * var radio; 667 | * 668 | * function setup() { 669 | * radio = createRadio(); 670 | * radio.option('apple', 1); 671 | * radio.option('bread', 2); 672 | * radio.option('juice', 3); 673 | * radio.style('width', '60px'); 674 | * textAlign(CENTER); 675 | * } 676 | * 677 | * function draw() { 678 | * background(200); 679 | * var val = radio.value(); 680 | * if (val) { 681 | * text('item cost is $' + val, width / 2, height / 2); 682 | * } 683 | * } 684 | *
685 | */ 686 | p5.prototype.createRadio = function(existing_radios) { 687 | p5._validateParameters('createRadio', arguments); 688 | // do some prep by counting number of radios on page 689 | var radios = document.querySelectorAll('input[type=radio]'); 690 | var count = 0; 691 | if (radios.length > 1) { 692 | var length = radios.length; 693 | var prev = radios[0].name; 694 | var current = radios[1].name; 695 | count = 1; 696 | for (var i = 1; i < length; i++) { 697 | current = radios[i].name; 698 | if (prev !== current) { 699 | count++; 700 | } 701 | prev = current; 702 | } 703 | } else if (radios.length === 1) { 704 | count = 1; 705 | } 706 | // see if we got an existing set of radios from callee 707 | var elt, self; 708 | if (typeof existing_radios === 'object') { 709 | // use existing elements 710 | self = existing_radios; 711 | elt = this.elt = existing_radios.elt; 712 | } else { 713 | // create a set of radio buttons 714 | elt = document.createElement('div'); 715 | self = addElement(elt, this); 716 | } 717 | // setup member functions 718 | self._getInputChildrenArray = function() { 719 | return Array.prototype.slice.call(this.elt.children).filter(function(c) { 720 | return c.tagName === 'INPUT'; 721 | }); 722 | }; 723 | 724 | var times = -1; 725 | self.option = function(name, value) { 726 | var opt = document.createElement('input'); 727 | opt.type = 'radio'; 728 | opt.innerHTML = name; 729 | if (value) opt.value = value; 730 | else opt.value = name; 731 | opt.setAttribute('name', 'defaultradio' + count); 732 | elt.appendChild(opt); 733 | if (name) { 734 | times++; 735 | var label = document.createElement('label'); 736 | opt.setAttribute('id', 'defaultradio' + count + '-' + times); 737 | label.htmlFor = 'defaultradio' + count + '-' + times; 738 | label.appendChild(document.createTextNode(name)); 739 | elt.appendChild(label); 740 | } 741 | return opt; 742 | }; 743 | self.selected = function(value) { 744 | var i; 745 | var inputChildren = self._getInputChildrenArray(); 746 | if (value) { 747 | for (i = 0; i < inputChildren.length; i++) { 748 | if (inputChildren[i].value === value) inputChildren[i].checked = true; 749 | } 750 | return this; 751 | } else { 752 | for (i = 0; i < inputChildren.length; i++) { 753 | if (inputChildren[i].checked === true) return inputChildren[i].value; 754 | } 755 | } 756 | }; 757 | self.value = function(value) { 758 | var i; 759 | var inputChildren = self._getInputChildrenArray(); 760 | if (value) { 761 | for (i = 0; i < inputChildren.length; i++) { 762 | if (inputChildren[i].value === value) inputChildren[i].checked = true; 763 | } 764 | return this; 765 | } else { 766 | for (i = 0; i < inputChildren.length; i++) { 767 | if (inputChildren[i].checked === true) return inputChildren[i].value; 768 | } 769 | return ''; 770 | } 771 | }; 772 | return self; 773 | }; 774 | 775 | /** 776 | * Creates an <input></input> element in the DOM for text input. 777 | * Use .size() to set the display length of the box. 778 | * Appends to the container node if one is specified, otherwise 779 | * appends to body. 780 | * 781 | * @method createInput 782 | * @param {String} [value] default value of the input box 783 | * @param {String} [type] type of text, ie text, password etc. Defaults to text 784 | * @return {Object|p5.Element} pointer to p5.Element holding created node 785 | * @example 786 | *
787 | * function setup() { 788 | * var inp = createInput(''); 789 | * inp.input(myInputEvent); 790 | * } 791 | * 792 | * function myInputEvent() { 793 | * console.log('you are typing: ', this.value()); 794 | * } 795 | *
796 | */ 797 | p5.prototype.createInput = function(value, type) { 798 | p5._validateParameters('createInput', arguments); 799 | var elt = document.createElement('input'); 800 | elt.type = type ? type : 'text'; 801 | if (value) elt.value = value; 802 | return addElement(elt, this); 803 | }; 804 | 805 | /** 806 | * Creates an <input></input> element in the DOM of type 'file'. 807 | * This allows users to select local files for use in a sketch. 808 | * 809 | * @method createFileInput 810 | * @param {Function} [callback] callback function for when a file loaded 811 | * @param {String} [multiple] optional to allow multiple files selected 812 | * @return {Object|p5.Element} pointer to p5.Element holding created DOM element 813 | * @example 814 | * var input; 815 | * var img; 816 | * 817 | * function setup() { 818 | * input = createFileInput(handleFile); 819 | * input.position(0, 0); 820 | * } 821 | * 822 | * function draw() { 823 | * if (img) { 824 | * image(img, 0, 0, width, height); 825 | * } 826 | * } 827 | * 828 | * function handleFile(file) { 829 | * print(file); 830 | * if (file.type === 'image') { 831 | * img = createImg(file.data); 832 | * img.hide(); 833 | * } 834 | * } 835 | */ 836 | p5.prototype.createFileInput = function(callback, multiple) { 837 | p5._validateParameters('createFileInput', arguments); 838 | // Function to handle when a file is selected 839 | // We're simplifying life and assuming that we always 840 | // want to load every selected file 841 | function handleFileSelect(evt) { 842 | function makeLoader(theFile) { 843 | // Making a p5.File object 844 | var p5file = new p5.File(theFile); 845 | return function(e) { 846 | p5file.data = e.target.result; 847 | callback(p5file); 848 | }; 849 | } 850 | // These are the files 851 | var files = evt.target.files; 852 | // Load each one and trigger a callback 853 | for (var i = 0; i < files.length; i++) { 854 | var f = files[i]; 855 | var reader = new FileReader(); 856 | 857 | reader.onload = makeLoader(f); 858 | 859 | // Text or data? 860 | // This should likely be improved 861 | if (f.type.indexOf('text') > -1) { 862 | reader.readAsText(f); 863 | } else { 864 | reader.readAsDataURL(f); 865 | } 866 | } 867 | } 868 | // Is the file stuff supported? 869 | if (window.File && window.FileReader && window.FileList && window.Blob) { 870 | // Yup, we're ok and make an input file selector 871 | var elt = document.createElement('input'); 872 | elt.type = 'file'; 873 | 874 | // If we get a second argument that evaluates to true 875 | // then we are looking for multiple files 876 | if (multiple) { 877 | // Anything gets the job done 878 | elt.multiple = 'multiple'; 879 | } 880 | 881 | // Now let's handle when a file was selected 882 | elt.addEventListener('change', handleFileSelect, false); 883 | return addElement(elt, this); 884 | } else { 885 | console.log( 886 | 'The File APIs are not fully supported in this browser. Cannot create element.' 887 | ); 888 | } 889 | }; 890 | 891 | /** VIDEO STUFF **/ 892 | 893 | function createMedia(pInst, type, src, callback) { 894 | var elt = document.createElement(type); 895 | 896 | // allow src to be empty 897 | src = src || ''; 898 | if (typeof src === 'string') { 899 | src = [src]; 900 | } 901 | for (var i = 0; i < src.length; i++) { 902 | var source = document.createElement('source'); 903 | source.src = src[i]; 904 | elt.appendChild(source); 905 | } 906 | if (typeof callback !== 'undefined') { 907 | var callbackHandler = function() { 908 | callback(); 909 | elt.removeEventListener('canplaythrough', callbackHandler); 910 | }; 911 | elt.addEventListener('canplaythrough', callbackHandler); 912 | } 913 | 914 | var c = addElement(elt, pInst, true); 915 | c.loadedmetadata = false; 916 | // set width and height onload metadata 917 | elt.addEventListener('loadedmetadata', function() { 918 | c.width = elt.videoWidth; 919 | c.height = elt.videoHeight; 920 | // set elt width and height if not set 921 | if (c.elt.width === 0) c.elt.width = elt.videoWidth; 922 | if (c.elt.height === 0) c.elt.height = elt.videoHeight; 923 | c.loadedmetadata = true; 924 | }); 925 | 926 | return c; 927 | } 928 | /** 929 | * Creates an HTML5 <video> element in the DOM for simple playback 930 | * of audio/video. Shown by default, can be hidden with .hide() 931 | * and drawn into canvas using video(). Appends to the container 932 | * node if one is specified, otherwise appends to body. The first parameter 933 | * can be either a single string path to a video file, or an array of string 934 | * paths to different formats of the same video. This is useful for ensuring 935 | * that your video can play across different browsers, as each supports 936 | * different formats. See this 937 | * page for further information about supported formats. 938 | * 939 | * @method createVideo 940 | * @param {String|Array} src path to a video file, or array of paths for 941 | * supporting different browsers 942 | * @param {Object} [callback] callback function to be called upon 943 | * 'canplaythrough' event fire, that is, when the 944 | * browser can play the media, and estimates that 945 | * enough data has been loaded to play the media 946 | * up to its end without having to stop for 947 | * further buffering of content 948 | * @return {p5.MediaElement|p5.Element} pointer to video p5.Element 949 | * @example 950 | *
951 | * var vid; 952 | * function setup() { 953 | * vid = createVideo(['small.mp4', 'small.ogv', 'small.webm'], vidLoad); 954 | * } 955 | * 956 | * // This function is called when the video loads 957 | * function vidLoad() { 958 | * vid.play(); 959 | * } 960 | *
961 | */ 962 | p5.prototype.createVideo = function(src, callback) { 963 | p5._validateParameters('createVideo', arguments); 964 | return createMedia(this, 'video', src, callback); 965 | }; 966 | 967 | /** AUDIO STUFF **/ 968 | 969 | /** 970 | * Creates a hidden HTML5 <audio> element in the DOM for simple audio 971 | * playback. Appends to the container node if one is specified, 972 | * otherwise appends to body. The first parameter 973 | * can be either a single string path to a audio file, or an array of string 974 | * paths to different formats of the same audio. This is useful for ensuring 975 | * that your audio can play across different browsers, as each supports 976 | * different formats. See this 977 | * page for further information about supported formats. 978 | * 979 | * @method createAudio 980 | * @param {String|String[]} [src] path to an audio file, or array of paths 981 | * for supporting different browsers 982 | * @param {Object} [callback] callback function to be called upon 983 | * 'canplaythrough' event fire, that is, when the 984 | * browser can play the media, and estimates that 985 | * enough data has been loaded to play the media 986 | * up to its end without having to stop for 987 | * further buffering of content 988 | * @return {p5.MediaElement|p5.Element} pointer to audio p5.Element /** 989 | * @example 990 | *
991 | * var ele; 992 | * function setup() { 993 | * ele = createAudio('assets/beat.mp3'); 994 | * 995 | * // here we set the element to autoplay 996 | * // The element will play as soon 997 | * // as it is able to do so. 998 | * ele.autoplay(true); 999 | * } 1000 | *
1001 | */ 1002 | p5.prototype.createAudio = function(src, callback) { 1003 | p5._validateParameters('createAudio', arguments); 1004 | return createMedia(this, 'audio', src, callback); 1005 | }; 1006 | 1007 | /** CAMERA STUFF **/ 1008 | 1009 | p5.prototype.VIDEO = 'video'; 1010 | p5.prototype.AUDIO = 'audio'; 1011 | 1012 | // from: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia 1013 | // Older browsers might not implement mediaDevices at all, so we set an empty object first 1014 | if (navigator.mediaDevices === undefined) { 1015 | navigator.mediaDevices = {}; 1016 | } 1017 | 1018 | // Some browsers partially implement mediaDevices. We can't just assign an object 1019 | // with getUserMedia as it would overwrite existing properties. 1020 | // Here, we will just add the getUserMedia property if it's missing. 1021 | if (navigator.mediaDevices.getUserMedia === undefined) { 1022 | navigator.mediaDevices.getUserMedia = function(constraints) { 1023 | // First get ahold of the legacy getUserMedia, if present 1024 | var getUserMedia = 1025 | navigator.webkitGetUserMedia || navigator.mozGetUserMedia; 1026 | 1027 | // Some browsers just don't implement it - return a rejected promise with an error 1028 | // to keep a consistent interface 1029 | if (!getUserMedia) { 1030 | return Promise.reject( 1031 | new Error('getUserMedia is not implemented in this browser') 1032 | ); 1033 | } 1034 | 1035 | // Otherwise, wrap the call to the old navigator.getUserMedia with a Promise 1036 | return new Promise(function(resolve, reject) { 1037 | getUserMedia.call(navigator, constraints, resolve, reject); 1038 | }); 1039 | }; 1040 | } 1041 | 1042 | /** 1043 | *

Creates a new <video> element that contains the audio/video feed 1044 | * from a webcam. This can be drawn onto the canvas using video().

1045 | *

More specific properties of the feed can be passing in a Constraints object. 1046 | * See the 1047 | * W3C 1048 | * spec for possible properties. Note that not all of these are supported 1049 | * by all browsers.

1050 | *

Security note: A new browser security specification requires that getUserMedia, 1051 | * which is behind createCapture(), only works when you're running the code locally, 1052 | * or on HTTPS. Learn more here 1053 | * and here.

1054 | * 1055 | * @method createCapture 1056 | * @param {String|Constant|Object} type type of capture, either VIDEO or 1057 | * AUDIO if none specified, default both, 1058 | * or a Constraints object 1059 | * @param {Function} callback function to be called once 1060 | * stream has loaded 1061 | * @return {Object|p5.Element} capture video p5.Element 1062 | * @example 1063 | *
1064 | * var capture; 1065 | * 1066 | * function setup() { 1067 | * createCanvas(480, 120); 1068 | * capture = createCapture(VIDEO); 1069 | * } 1070 | * 1071 | * function draw() { 1072 | * image(capture, 0, 0, width, width * capture.height / capture.width); 1073 | * filter(INVERT); 1074 | * } 1075 | *
1076 | *
1077 | * function setup() { 1078 | * createCanvas(480, 120); 1079 | * var constraints = { 1080 | * video: { 1081 | * mandatory: { 1082 | * minWidth: 1280, 1083 | * minHeight: 720 1084 | * }, 1085 | * optional: [{ maxFrameRate: 10 }] 1086 | * }, 1087 | * audio: true 1088 | * }; 1089 | * createCapture(constraints, function(stream) { 1090 | * console.log(stream); 1091 | * }); 1092 | * } 1093 | *
1094 | */ 1095 | p5.prototype.createCapture = function() { 1096 | p5._validateParameters('createCapture', arguments); 1097 | var useVideo = true; 1098 | var useAudio = true; 1099 | var constraints; 1100 | var cb; 1101 | for (var i = 0; i < arguments.length; i++) { 1102 | if (arguments[i] === p5.prototype.VIDEO) { 1103 | useAudio = false; 1104 | } else if (arguments[i] === p5.prototype.AUDIO) { 1105 | useVideo = false; 1106 | } else if (typeof arguments[i] === 'object') { 1107 | constraints = arguments[i]; 1108 | } else if (typeof arguments[i] === 'function') { 1109 | cb = arguments[i]; 1110 | } 1111 | } 1112 | if (navigator.getUserMedia) { 1113 | var elt = document.createElement('video'); 1114 | 1115 | if (!constraints) { 1116 | constraints = { video: useVideo, audio: useAudio }; 1117 | } 1118 | 1119 | navigator.mediaDevices.getUserMedia(constraints).then( 1120 | function(stream) { 1121 | try { 1122 | if ('srcObject' in elt) { 1123 | elt.srcObject = stream; 1124 | } else { 1125 | elt.src = window.URL.createObjectURL(stream); 1126 | } 1127 | } catch (err) { 1128 | elt.src = stream; 1129 | } 1130 | if (cb) { 1131 | cb(stream); 1132 | } 1133 | }, 1134 | function(e) { 1135 | console.log(e); 1136 | } 1137 | ); 1138 | } else { 1139 | throw 'getUserMedia not supported in this browser'; 1140 | } 1141 | var c = addElement(elt, this, true); 1142 | c.loadedmetadata = false; 1143 | // set width and height onload metadata 1144 | elt.addEventListener('loadedmetadata', function() { 1145 | elt.play(); 1146 | if (elt.width) { 1147 | c.width = elt.videoWidth = elt.width; 1148 | c.height = elt.videoHeight = elt.height; 1149 | } else { 1150 | c.width = c.elt.width = elt.videoWidth; 1151 | c.height = c.elt.height = elt.videoHeight; 1152 | } 1153 | c.loadedmetadata = true; 1154 | }); 1155 | return c; 1156 | }; 1157 | 1158 | /** 1159 | * Creates element with given tag in the DOM with given content. 1160 | * Appends to the container node if one is specified, otherwise 1161 | * appends to body. 1162 | * 1163 | * @method createElement 1164 | * @param {String} tag tag for the new element 1165 | * @param {String} [content] html content to be inserted into the element 1166 | * @return {Object|p5.Element} pointer to p5.Element holding created node 1167 | * @example 1168 | *
1169 | * createElement('h2', 'im an h2 p5.element!'); 1170 | *
1171 | */ 1172 | p5.prototype.createElement = function(tag, content) { 1173 | p5._validateParameters('createElement', arguments); 1174 | var elt = document.createElement(tag); 1175 | if (typeof content !== 'undefined') { 1176 | elt.innerHTML = content; 1177 | } 1178 | return addElement(elt, this); 1179 | }; 1180 | 1181 | // ============================================================================= 1182 | // p5.Element additions 1183 | // ============================================================================= 1184 | /** 1185 | * 1186 | * Adds specified class to the element. 1187 | * 1188 | * @for p5.Element 1189 | * @method addClass 1190 | * @param {String} class name of class to add 1191 | * @return {Object|p5.Element} 1192 | * @example 1193 | *
1194 | * var div = createDiv('div'); 1195 | * div.addClass('myClass'); 1196 | *
1197 | */ 1198 | p5.Element.prototype.addClass = function(c) { 1199 | if (this.elt.className) { 1200 | // PEND don't add class more than once 1201 | //var regex = new RegExp('[^a-zA-Z\d:]?'+c+'[^a-zA-Z\d:]?'); 1202 | //if (this.elt.className.search(/[^a-zA-Z\d:]?hi[^a-zA-Z\d:]?/) === -1) { 1203 | this.elt.className = this.elt.className + ' ' + c; 1204 | //} 1205 | } else { 1206 | this.elt.className = c; 1207 | } 1208 | return this; 1209 | }; 1210 | 1211 | /** 1212 | * 1213 | * Removes specified class from the element. 1214 | * 1215 | * @method removeClass 1216 | * @param {String} class name of class to remove 1217 | * @return {Object|p5.Element} * @example 1218 | *
1219 | * // In this example, a class is set when the div is created 1220 | * // and removed when mouse is pressed. This could link up 1221 | * // with a CSS style rule to toggle style properties. 1222 | * 1223 | * var div; 1224 | * 1225 | * function setup() { 1226 | * div = createDiv('div'); 1227 | * div.addClass('myClass'); 1228 | * } 1229 | * 1230 | * function mousePressed() { 1231 | * div.removeClass('myClass'); 1232 | * } 1233 | *
1234 | */ 1235 | p5.Element.prototype.removeClass = function(c) { 1236 | var regex = new RegExp('(?:^|\\s)' + c + '(?!\\S)'); 1237 | this.elt.className = this.elt.className.replace(regex, ''); 1238 | this.elt.className = this.elt.className.replace(/^\s+|\s+$/g, ''); //prettify (optional) 1239 | return this; 1240 | }; 1241 | 1242 | /** 1243 | * 1244 | * Attaches the element as a child to the parent specified. 1245 | * Accepts either a string ID, DOM node, or p5.Element. 1246 | * If no argument is specified, an array of children DOM nodes is returned. 1247 | * 1248 | * @method child 1249 | * @param {String|Object|p5.Element} [child] the ID, DOM node, or p5.Element 1250 | * to add to the current element 1251 | * @return {p5.Element} 1252 | * @example 1253 | *
1254 | * var div0 = createDiv('this is the parent'); 1255 | * var div1 = createDiv('this is the child'); 1256 | * div0.child(div1); // use p5.Element 1257 | *
1258 | *
1259 | * var div0 = createDiv('this is the parent'); 1260 | * var div1 = createDiv('this is the child'); 1261 | * div1.id('apples'); 1262 | * div0.child('apples'); // use id 1263 | *
1264 | *
1265 | * var div0 = createDiv('this is the parent'); 1266 | * var elt = document.getElementById('myChildDiv'); 1267 | * div0.child(elt); // use element from page 1268 | *
1269 | */ 1270 | p5.Element.prototype.child = function(c) { 1271 | if (typeof c === 'undefined') { 1272 | return this.elt.childNodes; 1273 | } 1274 | if (typeof c === 'string') { 1275 | if (c[0] === '#') { 1276 | c = c.substring(1); 1277 | } 1278 | c = document.getElementById(c); 1279 | } else if (c instanceof p5.Element) { 1280 | c = c.elt; 1281 | } 1282 | this.elt.appendChild(c); 1283 | return this; 1284 | }; 1285 | 1286 | /** 1287 | * Centers a p5 Element either vertically, horizontally, 1288 | * or both, relative to its parent or according to 1289 | * the body if the Element has no parent. If no argument is passed 1290 | * the Element is aligned both vertically and horizontally. 1291 | * 1292 | * @method center 1293 | * @param {String} [align] passing 'vertical', 'horizontal' aligns element accordingly 1294 | * @return {Object|p5.Element} pointer to p5.Element 1295 | * @example 1296 | *
1297 | * function setup() { 1298 | * var div = createDiv('').size(10, 10); 1299 | * div.style('background-color', 'orange'); 1300 | * div.center(); 1301 | * } 1302 | *
1303 | */ 1304 | p5.Element.prototype.center = function(align) { 1305 | var style = this.elt.style.display; 1306 | var hidden = this.elt.style.display === 'none'; 1307 | var parentHidden = this.parent().style.display === 'none'; 1308 | var pos = { x: this.elt.offsetLeft, y: this.elt.offsetTop }; 1309 | 1310 | if (hidden) this.show(); 1311 | 1312 | this.elt.style.display = 'block'; 1313 | this.position(0, 0); 1314 | 1315 | if (parentHidden) this.parent().style.display = 'block'; 1316 | 1317 | var wOffset = Math.abs(this.parent().offsetWidth - this.elt.offsetWidth); 1318 | var hOffset = Math.abs(this.parent().offsetHeight - this.elt.offsetHeight); 1319 | var y = pos.y; 1320 | var x = pos.x; 1321 | 1322 | if (align === 'both' || align === undefined) { 1323 | this.position(wOffset / 2, hOffset / 2); 1324 | } else if (align === 'horizontal') { 1325 | this.position(wOffset / 2, y); 1326 | } else if (align === 'vertical') { 1327 | this.position(x, hOffset / 2); 1328 | } 1329 | 1330 | this.style('display', style); 1331 | 1332 | if (hidden) this.hide(); 1333 | 1334 | if (parentHidden) this.parent().style.display = 'none'; 1335 | 1336 | return this; 1337 | }; 1338 | 1339 | /** 1340 | * 1341 | * If an argument is given, sets the inner HTML of the element, 1342 | * replacing any existing html. If true is included as a second 1343 | * argument, html is appended instead of replacing existing html. 1344 | * If no arguments are given, returns 1345 | * the inner HTML of the element. 1346 | * 1347 | * @for p5.Element 1348 | * @method html 1349 | * @param {String} [html] the HTML to be placed inside the element 1350 | * @param {boolean} [append] whether to append HTML to existing 1351 | * @return {Object|p5.Element|String} 1352 | * @example 1353 | *
1354 | * var div = createDiv('').size(100, 100); 1355 | * div.html('hi'); 1356 | *
1357 | *
1358 | * var div = createDiv('Hello ').size(100, 100); 1359 | * div.html('World', true); 1360 | *
1361 | */ 1362 | p5.Element.prototype.html = function() { 1363 | if (arguments.length === 0) { 1364 | return this.elt.innerHTML; 1365 | } else if (arguments[1]) { 1366 | this.elt.innerHTML += arguments[0]; 1367 | return this; 1368 | } else { 1369 | this.elt.innerHTML = arguments[0]; 1370 | return this; 1371 | } 1372 | }; 1373 | 1374 | /** 1375 | * 1376 | * Sets the position of the element relative to (0, 0) of the 1377 | * window. Essentially, sets position:absolute and left and top 1378 | * properties of style. If no arguments given returns the x and y position 1379 | * of the element in an object. 1380 | * 1381 | * @method position 1382 | * @param {Number} [x] x-position relative to upper left of window 1383 | * @param {Number} [y] y-position relative to upper left of window 1384 | * @return {Object|p5.Element} 1385 | * @example 1386 | *
1387 | * function setup() { 1388 | * var cnv = createCanvas(100, 100); 1389 | * // positions canvas 50px to the right and 100px 1390 | * // below upper left corner of the window 1391 | * cnv.position(50, 100); 1392 | * } 1393 | *
1394 | */ 1395 | p5.Element.prototype.position = function() { 1396 | if (arguments.length === 0) { 1397 | return { x: this.elt.offsetLeft, y: this.elt.offsetTop }; 1398 | } else { 1399 | this.elt.style.position = 'absolute'; 1400 | this.elt.style.left = arguments[0] + 'px'; 1401 | this.elt.style.top = arguments[1] + 'px'; 1402 | this.x = arguments[0]; 1403 | this.y = arguments[1]; 1404 | return this; 1405 | } 1406 | }; 1407 | 1408 | /* Helper method called by p5.Element.style() */ 1409 | p5.Element.prototype._translate = function() { 1410 | this.elt.style.position = 'absolute'; 1411 | // save out initial non-translate transform styling 1412 | var transform = ''; 1413 | if (this.elt.style.transform) { 1414 | transform = this.elt.style.transform.replace(/translate3d\(.*\)/g, ''); 1415 | transform = transform.replace(/translate[X-Z]?\(.*\)/g, ''); 1416 | } 1417 | if (arguments.length === 2) { 1418 | this.elt.style.transform = 1419 | 'translate(' + arguments[0] + 'px, ' + arguments[1] + 'px)'; 1420 | } else if (arguments.length > 2) { 1421 | this.elt.style.transform = 1422 | 'translate3d(' + 1423 | arguments[0] + 1424 | 'px,' + 1425 | arguments[1] + 1426 | 'px,' + 1427 | arguments[2] + 1428 | 'px)'; 1429 | if (arguments.length === 3) { 1430 | this.elt.parentElement.style.perspective = '1000px'; 1431 | } else { 1432 | this.elt.parentElement.style.perspective = arguments[3] + 'px'; 1433 | } 1434 | } 1435 | // add any extra transform styling back on end 1436 | this.elt.style.transform += transform; 1437 | return this; 1438 | }; 1439 | 1440 | /* Helper method called by p5.Element.style() */ 1441 | p5.Element.prototype._rotate = function() { 1442 | // save out initial non-rotate transform styling 1443 | var transform = ''; 1444 | if (this.elt.style.transform) { 1445 | transform = this.elt.style.transform.replace(/rotate3d\(.*\)/g, ''); 1446 | transform = transform.replace(/rotate[X-Z]?\(.*\)/g, ''); 1447 | } 1448 | 1449 | if (arguments.length === 1) { 1450 | this.elt.style.transform = 'rotate(' + arguments[0] + 'deg)'; 1451 | } else if (arguments.length === 2) { 1452 | this.elt.style.transform = 1453 | 'rotate(' + arguments[0] + 'deg, ' + arguments[1] + 'deg)'; 1454 | } else if (arguments.length === 3) { 1455 | this.elt.style.transform = 'rotateX(' + arguments[0] + 'deg)'; 1456 | this.elt.style.transform += 'rotateY(' + arguments[1] + 'deg)'; 1457 | this.elt.style.transform += 'rotateZ(' + arguments[2] + 'deg)'; 1458 | } 1459 | // add remaining transform back on 1460 | this.elt.style.transform += transform; 1461 | return this; 1462 | }; 1463 | 1464 | /** 1465 | * Sets the given style (css) property (1st arg) of the element with the 1466 | * given value (2nd arg). If a single argument is given, .style() 1467 | * returns the value of the given property; however, if the single argument 1468 | * is given in css syntax ('text-align:center'), .style() sets the css 1469 | * appropriatly. .style() also handles 2d and 3d css transforms. If 1470 | * the 1st arg is 'rotate', 'translate', or 'position', the following arguments 1471 | * accept Numbers as values. ('translate', 10, 100, 50); 1472 | * 1473 | * @method style 1474 | * @param {String} property property to be set 1475 | * @param {String|Number|p5.Color} [value] value to assign to property (only String|Number for rotate/translate) 1476 | * @param {String|Number|p5.Color} [value2] position can take a 2nd value 1477 | * @param {String|Number|p5.Color} [value3] translate can take a 2nd & 3rd value 1478 | * @return {String|Object|p5.Element} value of property, if no value is specified 1479 | * or p5.Element 1480 | * @example 1481 | *
1482 | * var myDiv = createDiv('I like pandas.'); 1483 | * myDiv.style('font-size', '18px'); 1484 | * myDiv.style('color', '#ff0000'); 1485 | *
1486 | *
1487 | * var col = color(25, 23, 200, 50); 1488 | * var button = createButton('button'); 1489 | * button.style('background-color', col); 1490 | * button.position(10, 10); 1491 | *
1492 | *
1493 | * var myDiv = createDiv('I like lizards.'); 1494 | * myDiv.style('position', 20, 20); 1495 | * myDiv.style('rotate', 45); 1496 | *
1497 | *
1498 | * var myDiv; 1499 | * function setup() { 1500 | * background(200); 1501 | * myDiv = createDiv('I like gray.'); 1502 | * myDiv.position(20, 20); 1503 | * } 1504 | * 1505 | * function draw() { 1506 | * myDiv.style('font-size', mouseX + 'px'); 1507 | * } 1508 | *
1509 | */ 1510 | p5.Element.prototype.style = function(prop, val) { 1511 | var self = this; 1512 | 1513 | if (val instanceof p5.Color) { 1514 | val = 1515 | 'rgba(' + 1516 | val.levels[0] + 1517 | ',' + 1518 | val.levels[1] + 1519 | ',' + 1520 | val.levels[2] + 1521 | ',' + 1522 | val.levels[3] / 255 + 1523 | ')'; 1524 | } 1525 | 1526 | if (typeof val === 'undefined') { 1527 | if (prop.indexOf(':') === -1) { 1528 | var styles = window.getComputedStyle(self.elt); 1529 | var style = styles.getPropertyValue(prop); 1530 | return style; 1531 | } else { 1532 | var attrs = prop.split(';'); 1533 | for (var i = 0; i < attrs.length; i++) { 1534 | var parts = attrs[i].split(':'); 1535 | if (parts[0] && parts[1]) { 1536 | this.elt.style[parts[0].trim()] = parts[1].trim(); 1537 | } 1538 | } 1539 | } 1540 | } else { 1541 | if (prop === 'rotate' || prop === 'translate' || prop === 'position') { 1542 | var trans = Array.prototype.shift.apply(arguments); 1543 | var f = this[trans] || this['_' + trans]; 1544 | f.apply(this, arguments); 1545 | } else { 1546 | this.elt.style[prop] = val; 1547 | if ( 1548 | prop === 'width' || 1549 | prop === 'height' || 1550 | prop === 'left' || 1551 | prop === 'top' 1552 | ) { 1553 | var numVal = val.replace(/\D+/g, ''); 1554 | this[prop] = parseInt(numVal, 10); // pend: is this necessary? 1555 | } 1556 | } 1557 | } 1558 | return this; 1559 | }; 1560 | 1561 | /** 1562 | * 1563 | * Adds a new attribute or changes the value of an existing attribute 1564 | * on the specified element. If no value is specified, returns the 1565 | * value of the given attribute, or null if attribute is not set. 1566 | * 1567 | * @method attribute 1568 | * @param {String} attr attribute to set 1569 | * @param {String} [value] value to assign to attribute 1570 | * @return {String|Object|p5.Element} value of attribute, if no value is 1571 | * specified or p5.Element 1572 | * @example 1573 | *
1574 | * var myDiv = createDiv('I like pandas.'); 1575 | * myDiv.attribute('align', 'center'); 1576 | *
1577 | */ 1578 | p5.Element.prototype.attribute = function(attr, value) { 1579 | //handling for checkboxes and radios to ensure options get 1580 | //attributes not divs 1581 | if ( 1582 | this.elt.firstChild != null && 1583 | (this.elt.firstChild.type === 'checkbox' || 1584 | this.elt.firstChild.type === 'radio') 1585 | ) { 1586 | if (typeof value === 'undefined') { 1587 | return this.elt.firstChild.getAttribute(attr); 1588 | } else { 1589 | for (var i = 0; i < this.elt.childNodes.length; i++) { 1590 | this.elt.childNodes[i].setAttribute(attr, value); 1591 | } 1592 | } 1593 | } else if (typeof value === 'undefined') { 1594 | return this.elt.getAttribute(attr); 1595 | } else { 1596 | this.elt.setAttribute(attr, value); 1597 | return this; 1598 | } 1599 | }; 1600 | 1601 | /** 1602 | * 1603 | * Removes an attribute on the specified element. 1604 | * 1605 | * @method removeAttribute 1606 | * @param {String} attr attribute to remove 1607 | * @return {Object|p5.Element} 1608 | * 1609 | * @example 1610 | *
1611 | * var button; 1612 | * var checkbox; 1613 | * 1614 | * function setup() { 1615 | * checkbox = createCheckbox('enable', true); 1616 | * checkbox.changed(enableButton); 1617 | * button = createButton('button'); 1618 | * button.position(10, 10); 1619 | * } 1620 | * 1621 | * function enableButton() { 1622 | * if (this.checked()) { 1623 | * // Re-enable the button 1624 | * button.removeAttribute('disabled'); 1625 | * } else { 1626 | * // Disable the button 1627 | * button.attribute('disabled', ''); 1628 | * } 1629 | * } 1630 | *
1631 | */ 1632 | p5.Element.prototype.removeAttribute = function(attr) { 1633 | if ( 1634 | this.elt.firstChild != null && 1635 | (this.elt.firstChild.type === 'checkbox' || 1636 | this.elt.firstChild.type === 'radio') 1637 | ) { 1638 | for (var i = 0; i < this.elt.childNodes.length; i++) { 1639 | this.elt.childNodes[i].removeAttribute(attr); 1640 | } 1641 | } 1642 | this.elt.removeAttribute(attr); 1643 | return this; 1644 | }; 1645 | 1646 | /** 1647 | * Either returns the value of the element if no arguments 1648 | * given, or sets the value of the element. 1649 | * 1650 | * @method value 1651 | * @param {String|Number} [value] 1652 | * @return {String|Object|p5.Element} value of element if no value is specified or p5.Element 1653 | * @example 1654 | *
1655 | * // gets the value 1656 | * var inp; 1657 | * function setup() { 1658 | * inp = createInput(''); 1659 | * } 1660 | * 1661 | * function mousePressed() { 1662 | * print(inp.value()); 1663 | * } 1664 | *
1665 | *
1666 | * // sets the value 1667 | * var inp; 1668 | * function setup() { 1669 | * inp = createInput('myValue'); 1670 | * } 1671 | * 1672 | * function mousePressed() { 1673 | * inp.value('myValue'); 1674 | * } 1675 | *
1676 | */ 1677 | p5.Element.prototype.value = function() { 1678 | if (arguments.length > 0) { 1679 | this.elt.value = arguments[0]; 1680 | return this; 1681 | } else { 1682 | if (this.elt.type === 'range') { 1683 | return parseFloat(this.elt.value); 1684 | } else return this.elt.value; 1685 | } 1686 | }; 1687 | 1688 | /** 1689 | * 1690 | * Shows the current element. Essentially, setting display:block for the style. 1691 | * 1692 | * @method show 1693 | * @return {Object|p5.Element} 1694 | * @example 1695 | *
1696 | * var div = createDiv('div'); 1697 | * div.style('display', 'none'); 1698 | * div.show(); // turns display to block 1699 | *
1700 | */ 1701 | p5.Element.prototype.show = function() { 1702 | this.elt.style.display = 'block'; 1703 | return this; 1704 | }; 1705 | 1706 | /** 1707 | * Hides the current element. Essentially, setting display:none for the style. 1708 | * 1709 | * @method hide 1710 | * @return {Object|p5.Element} 1711 | * @example 1712 | *
1713 | * var div = createDiv('this is a div'); 1714 | * div.hide(); 1715 | *
1716 | */ 1717 | p5.Element.prototype.hide = function() { 1718 | this.elt.style.display = 'none'; 1719 | return this; 1720 | }; 1721 | 1722 | /** 1723 | * 1724 | * Sets the width and height of the element. AUTO can be used to 1725 | * only adjust one dimension. If no arguments given returns the width and height 1726 | * of the element in an object. 1727 | * 1728 | * @method size 1729 | * @param {Number|Constant} [w] width of the element, either AUTO, or a number 1730 | * @param {Number|Constant} [h] height of the element, either AUTO, or a number 1731 | * @return {Object|p5.Element} 1732 | * @example 1733 | *
1734 | * var div = createDiv('this is a div'); 1735 | * div.size(100, 100); 1736 | *
1737 | */ 1738 | p5.Element.prototype.size = function(w, h) { 1739 | if (arguments.length === 0) { 1740 | return { width: this.elt.offsetWidth, height: this.elt.offsetHeight }; 1741 | } else { 1742 | var aW = w; 1743 | var aH = h; 1744 | var AUTO = p5.prototype.AUTO; 1745 | if (aW !== AUTO || aH !== AUTO) { 1746 | if (aW === AUTO) { 1747 | aW = h * this.width / this.height; 1748 | } else if (aH === AUTO) { 1749 | aH = w * this.height / this.width; 1750 | } 1751 | // set diff for cnv vs normal div 1752 | if (this.elt instanceof HTMLCanvasElement) { 1753 | var j = {}; 1754 | var k = this.elt.getContext('2d'); 1755 | var prop; 1756 | for (prop in k) { 1757 | j[prop] = k[prop]; 1758 | } 1759 | this.elt.setAttribute('width', aW * this._pInst._pixelDensity); 1760 | this.elt.setAttribute('height', aH * this._pInst._pixelDensity); 1761 | this.elt.setAttribute( 1762 | 'style', 1763 | 'width:' + aW + 'px; height:' + aH + 'px' 1764 | ); 1765 | this._pInst.scale( 1766 | this._pInst._pixelDensity, 1767 | this._pInst._pixelDensity 1768 | ); 1769 | for (prop in j) { 1770 | this.elt.getContext('2d')[prop] = j[prop]; 1771 | } 1772 | } else { 1773 | this.elt.style.width = aW + 'px'; 1774 | this.elt.style.height = aH + 'px'; 1775 | this.elt.width = aW; 1776 | this.elt.height = aH; 1777 | this.width = aW; 1778 | this.height = aH; 1779 | } 1780 | 1781 | this.width = this.elt.offsetWidth; 1782 | this.height = this.elt.offsetHeight; 1783 | 1784 | if (this._pInst) { 1785 | // main canvas associated with p5 instance 1786 | if (this._pInst._curElement.elt === this.elt) { 1787 | this._pInst._setProperty('width', this.elt.offsetWidth); 1788 | this._pInst._setProperty('height', this.elt.offsetHeight); 1789 | } 1790 | } 1791 | } 1792 | return this; 1793 | } 1794 | }; 1795 | 1796 | /** 1797 | * Removes the element and deregisters all listeners. 1798 | * @method remove 1799 | * @example 1800 | *
1801 | * var myDiv = createDiv('this is some text'); 1802 | * myDiv.remove(); 1803 | *
1804 | */ 1805 | p5.Element.prototype.remove = function() { 1806 | // deregister events 1807 | for (var ev in this._events) { 1808 | this.elt.removeEventListener(ev, this._events[ev]); 1809 | } 1810 | if (this.elt.parentNode) { 1811 | this.elt.parentNode.removeChild(this.elt); 1812 | } 1813 | delete this; 1814 | }; 1815 | 1816 | // ============================================================================= 1817 | // p5.MediaElement additions 1818 | // ============================================================================= 1819 | 1820 | /** 1821 | * Extends p5.Element to handle audio and video. In addition to the methods 1822 | * of p5.Element, it also contains methods for controlling media. It is not 1823 | * called directly, but p5.MediaElements are created by calling createVideo, 1824 | * createAudio, and createCapture. 1825 | * 1826 | * @class p5.MediaElement 1827 | * @constructor 1828 | * @param {String} elt DOM node that is wrapped 1829 | */ 1830 | p5.MediaElement = function(elt, pInst) { 1831 | p5.Element.call(this, elt, pInst); 1832 | 1833 | var self = this; 1834 | this.elt.crossOrigin = 'anonymous'; 1835 | 1836 | this._prevTime = 0; 1837 | this._cueIDCounter = 0; 1838 | this._cues = []; 1839 | this._pixelDensity = 1; 1840 | this._modified = false; 1841 | 1842 | /** 1843 | * Path to the media element source. 1844 | * 1845 | * @property src 1846 | * @return {String} src 1847 | * @example 1848 | *
1849 | * var ele; 1850 | * 1851 | * function setup() { 1852 | * background(250); 1853 | * 1854 | * //p5.MediaElement objects are usually created 1855 | * //by calling the createAudio(), createVideo(), 1856 | * //and createCapture() functions. 1857 | * 1858 | * //In this example we create 1859 | * //a new p5.MediaElement via createAudio(). 1860 | * ele = createAudio('assets/beat.mp3'); 1861 | * 1862 | * //We'll set up our example so that 1863 | * //when you click on the text, 1864 | * //an alert box displays the MediaElement's 1865 | * //src field. 1866 | * textAlign(CENTER); 1867 | * text('Click Me!', width / 2, height / 2); 1868 | * } 1869 | * 1870 | * function mouseClicked() { 1871 | * //here we test if the mouse is over the 1872 | * //canvas element when it's clicked 1873 | * if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) { 1874 | * //Show our p5.MediaElement's src field 1875 | * alert(ele.src); 1876 | * } 1877 | * } 1878 | *
1879 | */ 1880 | Object.defineProperty(self, 'src', { 1881 | get: function() { 1882 | var firstChildSrc = self.elt.children[0].src; 1883 | var srcVal = self.elt.src === window.location.href ? '' : self.elt.src; 1884 | var ret = 1885 | firstChildSrc === window.location.href ? srcVal : firstChildSrc; 1886 | return ret; 1887 | }, 1888 | set: function(newValue) { 1889 | for (var i = 0; i < self.elt.children.length; i++) { 1890 | self.elt.removeChild(self.elt.children[i]); 1891 | } 1892 | var source = document.createElement('source'); 1893 | source.src = newValue; 1894 | elt.appendChild(source); 1895 | self.elt.src = newValue; 1896 | self.modified = true; 1897 | } 1898 | }); 1899 | 1900 | // private _onended callback, set by the method: onended(callback) 1901 | self._onended = function() {}; 1902 | self.elt.onended = function() { 1903 | self._onended(self); 1904 | }; 1905 | }; 1906 | p5.MediaElement.prototype = Object.create(p5.Element.prototype); 1907 | 1908 | /** 1909 | * Play an HTML5 media element. 1910 | * 1911 | * @method play 1912 | * @return {Object|p5.Element} 1913 | * @example 1914 | *
1915 | * var ele; 1916 | * 1917 | * function setup() { 1918 | * //p5.MediaElement objects are usually created 1919 | * //by calling the createAudio(), createVideo(), 1920 | * //and createCapture() functions. 1921 | * 1922 | * //In this example we create 1923 | * //a new p5.MediaElement via createAudio(). 1924 | * ele = createAudio('assets/beat.mp3'); 1925 | * 1926 | * background(250); 1927 | * textAlign(CENTER); 1928 | * text('Click to Play!', width / 2, height / 2); 1929 | * } 1930 | * 1931 | * function mouseClicked() { 1932 | * //here we test if the mouse is over the 1933 | * //canvas element when it's clicked 1934 | * if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) { 1935 | * //Here we call the play() function on 1936 | * //the p5.MediaElement we created above. 1937 | * //This will start the audio sample. 1938 | * ele.play(); 1939 | * 1940 | * background(200); 1941 | * text('You clicked Play!', width / 2, height / 2); 1942 | * } 1943 | * } 1944 | *
1945 | */ 1946 | p5.MediaElement.prototype.play = function() { 1947 | if (this.elt.currentTime === this.elt.duration) { 1948 | this.elt.currentTime = 0; 1949 | } 1950 | 1951 | if (this.elt.readyState > 1) { 1952 | this.elt.play(); 1953 | } else { 1954 | // in Chrome, playback cannot resume after being stopped and must reload 1955 | this.elt.load(); 1956 | this.elt.play(); 1957 | } 1958 | return this; 1959 | }; 1960 | 1961 | /** 1962 | * Stops an HTML5 media element (sets current time to zero). 1963 | * 1964 | * @method stop 1965 | * @return {Object|p5.Element} 1966 | * @example 1967 | *
1968 | * //This example both starts 1969 | * //and stops a sound sample 1970 | * //when the user clicks the canvas 1971 | * 1972 | * //We will store the p5.MediaElement 1973 | * //object in here 1974 | * var ele; 1975 | * 1976 | * //while our audio is playing, 1977 | * //this will be set to true 1978 | * var sampleIsPlaying = false; 1979 | * 1980 | * function setup() { 1981 | * //Here we create a p5.MediaElement object 1982 | * //using the createAudio() function. 1983 | * ele = createAudio('assets/beat.mp3'); 1984 | * background(200); 1985 | * textAlign(CENTER); 1986 | * text('Click to play!', width / 2, height / 2); 1987 | * } 1988 | * 1989 | * function mouseClicked() { 1990 | * //here we test if the mouse is over the 1991 | * //canvas element when it's clicked 1992 | * if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) { 1993 | * background(200); 1994 | * 1995 | * if (sampleIsPlaying) { 1996 | * //if the sample is currently playing 1997 | * //calling the stop() function on 1998 | * //our p5.MediaElement will stop 1999 | * //it and reset its current 2000 | * //time to 0 (i.e. it will start 2001 | * //at the beginning the next time 2002 | * //you play it) 2003 | * ele.stop(); 2004 | * 2005 | * sampleIsPlaying = false; 2006 | * text('Click to play!', width / 2, height / 2); 2007 | * } else { 2008 | * //loop our sound element until we 2009 | * //call ele.stop() on it. 2010 | * ele.loop(); 2011 | * 2012 | * sampleIsPlaying = true; 2013 | * text('Click to stop!', width / 2, height / 2); 2014 | * } 2015 | * } 2016 | * } 2017 | *
2018 | */ 2019 | p5.MediaElement.prototype.stop = function() { 2020 | this.elt.pause(); 2021 | this.elt.currentTime = 0; 2022 | return this; 2023 | }; 2024 | 2025 | /** 2026 | * Pauses an HTML5 media element. 2027 | * 2028 | * @method pause 2029 | * @return {Object|p5.Element} 2030 | * @example 2031 | *
2032 | * //This example both starts 2033 | * //and pauses a sound sample 2034 | * //when the user clicks the canvas 2035 | * 2036 | * //We will store the p5.MediaElement 2037 | * //object in here 2038 | * var ele; 2039 | * 2040 | * //while our audio is playing, 2041 | * //this will be set to true 2042 | * var sampleIsPlaying = false; 2043 | * 2044 | * function setup() { 2045 | * //Here we create a p5.MediaElement object 2046 | * //using the createAudio() function. 2047 | * ele = createAudio('assets/lucky_dragons.mp3'); 2048 | * background(200); 2049 | * textAlign(CENTER); 2050 | * text('Click to play!', width / 2, height / 2); 2051 | * } 2052 | * 2053 | * function mouseClicked() { 2054 | * //here we test if the mouse is over the 2055 | * //canvas element when it's clicked 2056 | * if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) { 2057 | * background(200); 2058 | * 2059 | * if (sampleIsPlaying) { 2060 | * //Calling pause() on our 2061 | * //p5.MediaElement will stop it 2062 | * //playing, but when we call the 2063 | * //loop() or play() functions 2064 | * //the sample will start from 2065 | * //where we paused it. 2066 | * ele.pause(); 2067 | * 2068 | * sampleIsPlaying = false; 2069 | * text('Click to resume!', width / 2, height / 2); 2070 | * } else { 2071 | * //loop our sound element until we 2072 | * //call ele.pause() on it. 2073 | * ele.loop(); 2074 | * 2075 | * sampleIsPlaying = true; 2076 | * text('Click to pause!', width / 2, height / 2); 2077 | * } 2078 | * } 2079 | * } 2080 | *
2081 | */ 2082 | p5.MediaElement.prototype.pause = function() { 2083 | this.elt.pause(); 2084 | return this; 2085 | }; 2086 | 2087 | /** 2088 | * Set 'loop' to true for an HTML5 media element, and starts playing. 2089 | * 2090 | * @method loop 2091 | * @return {Object|p5.Element} 2092 | * @example 2093 | *
2094 | * //Clicking the canvas will loop 2095 | * //the audio sample until the user 2096 | * //clicks again to stop it 2097 | * 2098 | * //We will store the p5.MediaElement 2099 | * //object in here 2100 | * var ele; 2101 | * 2102 | * //while our audio is playing, 2103 | * //this will be set to true 2104 | * var sampleIsLooping = false; 2105 | * 2106 | * function setup() { 2107 | * //Here we create a p5.MediaElement object 2108 | * //using the createAudio() function. 2109 | * ele = createAudio('assets/lucky_dragons.mp3'); 2110 | * background(200); 2111 | * textAlign(CENTER); 2112 | * text('Click to loop!', width / 2, height / 2); 2113 | * } 2114 | * 2115 | * function mouseClicked() { 2116 | * //here we test if the mouse is over the 2117 | * //canvas element when it's clicked 2118 | * if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) { 2119 | * background(200); 2120 | * 2121 | * if (!sampleIsLooping) { 2122 | * //loop our sound element until we 2123 | * //call ele.stop() on it. 2124 | * ele.loop(); 2125 | * 2126 | * sampleIsLooping = true; 2127 | * text('Click to stop!', width / 2, height / 2); 2128 | * } else { 2129 | * ele.stop(); 2130 | * 2131 | * sampleIsLooping = false; 2132 | * text('Click to loop!', width / 2, height / 2); 2133 | * } 2134 | * } 2135 | * } 2136 | *
2137 | */ 2138 | p5.MediaElement.prototype.loop = function() { 2139 | this.elt.setAttribute('loop', true); 2140 | this.play(); 2141 | return this; 2142 | }; 2143 | /** 2144 | * Set 'loop' to false for an HTML5 media element. Element will stop 2145 | * when it reaches the end. 2146 | * 2147 | * @method noLoop 2148 | * @return {Object|p5.Element} 2149 | * @example 2150 | *
2151 | * //This example both starts 2152 | * //and stops loop of sound sample 2153 | * //when the user clicks the canvas 2154 | * 2155 | * //We will store the p5.MediaElement 2156 | * //object in here 2157 | * var ele; 2158 | * //while our audio is playing, 2159 | * //this will be set to true 2160 | * var sampleIsPlaying = false; 2161 | * 2162 | * function setup() { 2163 | * //Here we create a p5.MediaElement object 2164 | * //using the createAudio() function. 2165 | * ele = createAudio('assets/beat.mp3'); 2166 | * background(200); 2167 | * textAlign(CENTER); 2168 | * text('Click to play!', width / 2, height / 2); 2169 | * } 2170 | * 2171 | * function mouseClicked() { 2172 | * //here we test if the mouse is over the 2173 | * //canvas element when it's clicked 2174 | * if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) { 2175 | * background(200); 2176 | * 2177 | * if (sampleIsPlaying) { 2178 | * ele.noLoop(); 2179 | * text('No more Loops!', width / 2, height / 2); 2180 | * } else { 2181 | * ele.loop(); 2182 | * sampleIsPlaying = true; 2183 | * text('Click to stop looping!', width / 2, height / 2); 2184 | * } 2185 | * } 2186 | * } 2187 | *
2188 | * 2189 | */ 2190 | p5.MediaElement.prototype.noLoop = function() { 2191 | this.elt.setAttribute('loop', false); 2192 | return this; 2193 | }; 2194 | 2195 | /** 2196 | * Set HTML5 media element to autoplay or not. 2197 | * 2198 | * @method autoplay 2199 | * @param {Boolean} autoplay whether the element should autoplay 2200 | * @return {Object|p5.Element} 2201 | */ 2202 | p5.MediaElement.prototype.autoplay = function(val) { 2203 | this.elt.setAttribute('autoplay', val); 2204 | return this; 2205 | }; 2206 | 2207 | /** 2208 | * Sets volume for this HTML5 media element. If no argument is given, 2209 | * returns the current volume. 2210 | * 2211 | * @param {Number} [val] volume between 0.0 and 1.0 2212 | * @return {Number|p5.MediaElement} current volume or p5.MediaElement 2213 | * @method volume 2214 | * 2215 | * @example 2216 | *
2217 | * var ele; 2218 | * function setup() { 2219 | * // p5.MediaElement objects are usually created 2220 | * // by calling the createAudio(), createVideo(), 2221 | * // and createCapture() functions. 2222 | * // In this example we create 2223 | * // a new p5.MediaElement via createAudio(). 2224 | * ele = createAudio('assets/lucky_dragons.mp3'); 2225 | * background(250); 2226 | * textAlign(CENTER); 2227 | * text('Click to Play!', width / 2, height / 2); 2228 | * } 2229 | * function mouseClicked() { 2230 | * // Here we call the volume() function 2231 | * // on the sound element to set its volume 2232 | * // Volume must be between 0.0 and 1.0 2233 | * ele.volume(0.2); 2234 | * ele.play(); 2235 | * background(200); 2236 | * text('You clicked Play!', width / 2, height / 2); 2237 | * } 2238 | *
2239 | *
2240 | * var audio; 2241 | * var counter = 0; 2242 | * 2243 | * function loaded() { 2244 | * audio.play(); 2245 | * } 2246 | * 2247 | * function setup() { 2248 | * audio = createAudio('assets/lucky_dragons.mp3', loaded); 2249 | * textAlign(CENTER); 2250 | * } 2251 | * 2252 | * function draw() { 2253 | * if (counter === 0) { 2254 | * background(0, 255, 0); 2255 | * text('volume(0.9)', width / 2, height / 2); 2256 | * } else if (counter === 1) { 2257 | * background(255, 255, 0); 2258 | * text('volume(0.5)', width / 2, height / 2); 2259 | * } else if (counter === 2) { 2260 | * background(255, 0, 0); 2261 | * text('volume(0.1)', width / 2, height / 2); 2262 | * } 2263 | * } 2264 | * 2265 | * function mousePressed() { 2266 | * counter++; 2267 | * if (counter === 0) { 2268 | * audio.volume(0.9); 2269 | * } else if (counter === 1) { 2270 | * audio.volume(0.5); 2271 | * } else if (counter === 2) { 2272 | * audio.volume(0.1); 2273 | * } else { 2274 | * counter = 0; 2275 | * audio.volume(0.9); 2276 | * } 2277 | * } 2278 | * 2279 | *
2280 | */ 2281 | p5.MediaElement.prototype.volume = function(val) { 2282 | if (typeof val === 'undefined') { 2283 | return this.elt.volume; 2284 | } else { 2285 | this.elt.volume = val; 2286 | } 2287 | }; 2288 | 2289 | /** 2290 | * If no arguments are given, returns the current playback speed of the 2291 | * element. The speed parameter sets the speed where 2.0 will play the 2292 | * element twice as fast, 0.5 will play at half the speed, and -1 will play 2293 | * the element in normal speed in reverse.(Note that not all browsers support 2294 | * backward playback and even if they do, playback might not be smooth.) 2295 | * 2296 | * @method speed 2297 | * @param {Number} [speed] speed multiplier for element playback 2298 | * @return {Number|Object|p5.MediaElement} current playback speed or p5.MediaElement 2299 | * @example 2300 | *
2301 | * //Clicking the canvas will loop 2302 | * //the audio sample until the user 2303 | * //clicks again to stop it 2304 | * 2305 | * //We will store the p5.MediaElement 2306 | * //object in here 2307 | * var ele; 2308 | * var button; 2309 | * 2310 | * function setup() { 2311 | * createCanvas(710, 400); 2312 | * //Here we create a p5.MediaElement object 2313 | * //using the createAudio() function. 2314 | * ele = createAudio('assets/beat.mp3'); 2315 | * ele.loop(); 2316 | * background(200); 2317 | * 2318 | * button = createButton('2x speed'); 2319 | * button.position(100, 68); 2320 | * button.mousePressed(twice_speed); 2321 | * 2322 | * button = createButton('half speed'); 2323 | * button.position(200, 68); 2324 | * button.mousePressed(half_speed); 2325 | * 2326 | * button = createButton('reverse play'); 2327 | * button.position(300, 68); 2328 | * button.mousePressed(reverse_speed); 2329 | * 2330 | * button = createButton('STOP'); 2331 | * button.position(400, 68); 2332 | * button.mousePressed(stop_song); 2333 | * 2334 | * button = createButton('PLAY!'); 2335 | * button.position(500, 68); 2336 | * button.mousePressed(play_speed); 2337 | * } 2338 | * 2339 | * function twice_speed() { 2340 | * ele.speed(2); 2341 | * } 2342 | * 2343 | * function half_speed() { 2344 | * ele.speed(0.5); 2345 | * } 2346 | * 2347 | * function reverse_speed() { 2348 | * ele.speed(-1); 2349 | * } 2350 | * 2351 | * function stop_song() { 2352 | * ele.stop(); 2353 | * } 2354 | * 2355 | * function play_speed() { 2356 | * ele.play(); 2357 | * } 2358 | *
2359 | */ 2360 | p5.MediaElement.prototype.speed = function(val) { 2361 | if (typeof val === 'undefined') { 2362 | return this.elt.playbackRate; 2363 | } else { 2364 | this.elt.playbackRate = val; 2365 | } 2366 | }; 2367 | 2368 | /** 2369 | * If no arguments are given, returns the current time of the element. 2370 | * If an argument is given the current time of the element is set to it. 2371 | * 2372 | * @method time 2373 | * @param {Number} [time] time to jump to (in seconds) 2374 | * @return {Number|Object|p5.MediaElement} current time (in seconds) 2375 | * or p5.MediaElement 2376 | * @example 2377 | *
2378 | * var ele; 2379 | * var beginning = true; 2380 | * function setup() { 2381 | * //p5.MediaElement objects are usually created 2382 | * //by calling the createAudio(), createVideo(), 2383 | * //and createCapture() functions. 2384 | * 2385 | * //In this example we create 2386 | * //a new p5.MediaElement via createAudio(). 2387 | * ele = createAudio('assets/lucky_dragons.mp3'); 2388 | * background(250); 2389 | * textAlign(CENTER); 2390 | * text('start at beginning', width / 2, height / 2); 2391 | * } 2392 | * 2393 | * // this function fires with click anywhere 2394 | * function mousePressed() { 2395 | * if (beginning === true) { 2396 | * // here we start the sound at the beginning 2397 | * // time(0) is not necessary here 2398 | * // as this produces the same result as 2399 | * // play() 2400 | * ele.play().time(0); 2401 | * background(200); 2402 | * text('jump 2 sec in', width / 2, height / 2); 2403 | * beginning = false; 2404 | * } else { 2405 | * // here we jump 2 seconds into the sound 2406 | * ele.play().time(2); 2407 | * background(250); 2408 | * text('start at beginning', width / 2, height / 2); 2409 | * beginning = true; 2410 | * } 2411 | * } 2412 | *
2413 | */ 2414 | 2415 | p5.MediaElement.prototype.time = function(val) { 2416 | if (typeof val === 'undefined') { 2417 | return this.elt.currentTime; 2418 | } else { 2419 | this.elt.currentTime = val; 2420 | } 2421 | }; 2422 | 2423 | /** 2424 | * Returns the duration of the HTML5 media element. 2425 | * 2426 | * @method duration 2427 | * @return {Number} duration 2428 | * 2429 | * @example 2430 | *
2431 | * var ele; 2432 | * function setup() { 2433 | * //p5.MediaElement objects are usually created 2434 | * //by calling the createAudio(), createVideo(), 2435 | * //and createCapture() functions. 2436 | * //In this example we create 2437 | * //a new p5.MediaElement via createAudio(). 2438 | * ele = createAudio('assets/doorbell.mp3'); 2439 | * background(250); 2440 | * textAlign(CENTER); 2441 | * text('Click to know the duration!', 10, 25, 70, 80); 2442 | * } 2443 | * function mouseClicked() { 2444 | * ele.play(); 2445 | * background(200); 2446 | * //ele.duration dislpays the duration 2447 | * text(ele.duration() + ' seconds', width / 2, height / 2); 2448 | * } 2449 | *
2450 | */ 2451 | p5.MediaElement.prototype.duration = function() { 2452 | return this.elt.duration; 2453 | }; 2454 | p5.MediaElement.prototype.pixels = []; 2455 | p5.MediaElement.prototype.loadPixels = function() { 2456 | if (!this.canvas) { 2457 | this.canvas = document.createElement('canvas'); 2458 | this.drawingContext = this.canvas.getContext('2d'); 2459 | } 2460 | if (this.loadedmetadata) { 2461 | // wait for metadata for w/h 2462 | if (this.canvas.width !== this.elt.width) { 2463 | this.canvas.width = this.elt.width; 2464 | this.canvas.height = this.elt.height; 2465 | this.width = this.canvas.width; 2466 | this.height = this.canvas.height; 2467 | } 2468 | this.drawingContext.drawImage( 2469 | this.elt, 2470 | 0, 2471 | 0, 2472 | this.canvas.width, 2473 | this.canvas.height 2474 | ); 2475 | p5.Renderer2D.prototype.loadPixels.call(this); 2476 | } 2477 | this.setModified(true); 2478 | return this; 2479 | }; 2480 | p5.MediaElement.prototype.updatePixels = function(x, y, w, h) { 2481 | if (this.loadedmetadata) { 2482 | // wait for metadata 2483 | p5.Renderer2D.prototype.updatePixels.call(this, x, y, w, h); 2484 | } 2485 | this.setModified(true); 2486 | return this; 2487 | }; 2488 | p5.MediaElement.prototype.get = function(x, y, w, h) { 2489 | if (this.loadedmetadata) { 2490 | // wait for metadata 2491 | return p5.Renderer2D.prototype.get.call(this, x, y, w, h); 2492 | } else if (typeof x === 'undefined') { 2493 | return new p5.Image(1, 1); 2494 | } else if (w > 1) { 2495 | return new p5.Image(x, y, w, h); 2496 | } else { 2497 | return [0, 0, 0, 255]; 2498 | } 2499 | }; 2500 | p5.MediaElement.prototype.set = function(x, y, imgOrCol) { 2501 | if (this.loadedmetadata) { 2502 | // wait for metadata 2503 | p5.Renderer2D.prototype.set.call(this, x, y, imgOrCol); 2504 | this.setModified(true); 2505 | } 2506 | }; 2507 | p5.MediaElement.prototype.copy = function() { 2508 | p5.Renderer2D.prototype.copy.apply(this, arguments); 2509 | }; 2510 | p5.MediaElement.prototype.mask = function() { 2511 | this.loadPixels(); 2512 | this.setModified(true); 2513 | p5.Image.prototype.mask.apply(this, arguments); 2514 | }; 2515 | /** 2516 | * helper method for web GL mode to figure out if the element 2517 | * has been modified and might need to be re-uploaded to texture 2518 | * memory between frames. 2519 | * @method isModified 2520 | * @private 2521 | * @return {boolean} a boolean indicating whether or not the 2522 | * image has been updated or modified since last texture upload. 2523 | */ 2524 | p5.MediaElement.prototype.isModified = function() { 2525 | return this._modified; 2526 | }; 2527 | /** 2528 | * helper method for web GL mode to indicate that an element has been 2529 | * changed or unchanged since last upload. gl texture upload will 2530 | * set this value to false after uploading the texture; or might set 2531 | * it to true if metadata has become available but there is no actual 2532 | * texture data available yet.. 2533 | * @method setModified 2534 | * @param {boolean} val sets whether or not the element has been 2535 | * modified. 2536 | * @private 2537 | */ 2538 | p5.MediaElement.prototype.setModified = function(value) { 2539 | this._modified = value; 2540 | }; 2541 | /** 2542 | * Schedule an event to be called when the audio or video 2543 | * element reaches the end. If the element is looping, 2544 | * this will not be called. The element is passed in 2545 | * as the argument to the onended callback. 2546 | * 2547 | * @method onended 2548 | * @param {Function} callback function to call when the 2549 | * soundfile has ended. The 2550 | * media element will be passed 2551 | * in as the argument to the 2552 | * callback. 2553 | * @return {Object|p5.MediaElement} 2554 | * @example 2555 | *
2556 | * function setup() { 2557 | * var audioEl = createAudio('assets/beat.mp3'); 2558 | * audioEl.showControls(); 2559 | * audioEl.onended(sayDone); 2560 | * } 2561 | * 2562 | * function sayDone(elt) { 2563 | * alert('done playing ' + elt.src); 2564 | * } 2565 | *
2566 | */ 2567 | p5.MediaElement.prototype.onended = function(callback) { 2568 | this._onended = callback; 2569 | return this; 2570 | }; 2571 | 2572 | /*** CONNECT TO WEB AUDIO API / p5.sound.js ***/ 2573 | 2574 | /** 2575 | * Send the audio output of this element to a specified audioNode or 2576 | * p5.sound object. If no element is provided, connects to p5's master 2577 | * output. That connection is established when this method is first called. 2578 | * All connections are removed by the .disconnect() method. 2579 | * 2580 | * This method is meant to be used with the p5.sound.js addon library. 2581 | * 2582 | * @method connect 2583 | * @param {AudioNode|Object} audioNode AudioNode from the Web Audio API, 2584 | * or an object from the p5.sound library 2585 | */ 2586 | p5.MediaElement.prototype.connect = function(obj) { 2587 | var audioContext, masterOutput; 2588 | 2589 | // if p5.sound exists, same audio context 2590 | if (typeof p5.prototype.getAudioContext === 'function') { 2591 | audioContext = p5.prototype.getAudioContext(); 2592 | masterOutput = p5.soundOut.input; 2593 | } else { 2594 | try { 2595 | audioContext = obj.context; 2596 | masterOutput = audioContext.destination; 2597 | } catch (e) { 2598 | throw 'connect() is meant to be used with Web Audio API or p5.sound.js'; 2599 | } 2600 | } 2601 | 2602 | // create a Web Audio MediaElementAudioSourceNode if none already exists 2603 | if (!this.audioSourceNode) { 2604 | this.audioSourceNode = audioContext.createMediaElementSource(this.elt); 2605 | 2606 | // connect to master output when this method is first called 2607 | this.audioSourceNode.connect(masterOutput); 2608 | } 2609 | 2610 | // connect to object if provided 2611 | if (obj) { 2612 | if (obj.input) { 2613 | this.audioSourceNode.connect(obj.input); 2614 | } else { 2615 | this.audioSourceNode.connect(obj); 2616 | } 2617 | } else { 2618 | // otherwise connect to master output of p5.sound / AudioContext 2619 | this.audioSourceNode.connect(masterOutput); 2620 | } 2621 | }; 2622 | 2623 | /** 2624 | * Disconnect all Web Audio routing, including to master output. 2625 | * This is useful if you want to re-route the output through 2626 | * audio effects, for example. 2627 | * 2628 | * @method disconnect 2629 | */ 2630 | p5.MediaElement.prototype.disconnect = function() { 2631 | if (this.audioSourceNode) { 2632 | this.audioSourceNode.disconnect(); 2633 | } else { 2634 | throw 'nothing to disconnect'; 2635 | } 2636 | }; 2637 | 2638 | /*** SHOW / HIDE CONTROLS ***/ 2639 | 2640 | /** 2641 | * Show the default MediaElement controls, as determined by the web browser. 2642 | * 2643 | * @method showControls 2644 | * @example 2645 | *
2646 | * var ele; 2647 | * function setup() { 2648 | * //p5.MediaElement objects are usually created 2649 | * //by calling the createAudio(), createVideo(), 2650 | * //and createCapture() functions. 2651 | * //In this example we create 2652 | * //a new p5.MediaElement via createAudio() 2653 | * ele = createAudio('assets/lucky_dragons.mp3'); 2654 | * background(200); 2655 | * textAlign(CENTER); 2656 | * text('Click to Show Controls!', 10, 25, 70, 80); 2657 | * } 2658 | * function mousePressed() { 2659 | * ele.showControls(); 2660 | * background(200); 2661 | * text('Controls Shown', width / 2, height / 2); 2662 | * } 2663 | *
2664 | */ 2665 | p5.MediaElement.prototype.showControls = function() { 2666 | // must set style for the element to show on the page 2667 | this.elt.style['text-align'] = 'inherit'; 2668 | this.elt.controls = true; 2669 | }; 2670 | 2671 | /** 2672 | * Hide the default mediaElement controls. 2673 | * @method hideControls 2674 | * @example 2675 | *
2676 | * var ele; 2677 | * function setup() { 2678 | * //p5.MediaElement objects are usually created 2679 | * //by calling the createAudio(), createVideo(), 2680 | * //and createCapture() functions. 2681 | * //In this example we create 2682 | * //a new p5.MediaElement via createAudio() 2683 | * ele = createAudio('assets/lucky_dragons.mp3'); 2684 | * ele.showControls(); 2685 | * background(200); 2686 | * textAlign(CENTER); 2687 | * text('Click to hide Controls!', 10, 25, 70, 80); 2688 | * } 2689 | * function mousePressed() { 2690 | * ele.hideControls(); 2691 | * background(200); 2692 | * text('Controls hidden', width / 2, height / 2); 2693 | * } 2694 | *
2695 | */ 2696 | p5.MediaElement.prototype.hideControls = function() { 2697 | this.elt.controls = false; 2698 | }; 2699 | 2700 | /*** SCHEDULE EVENTS ***/ 2701 | 2702 | // Cue inspired by JavaScript setTimeout, and the 2703 | // Tone.js Transport Timeline Event, MIT License Yotam Mann 2015 tonejs.org 2704 | var Cue = function(callback, time, id, val) { 2705 | this.callback = callback; 2706 | this.time = time; 2707 | this.id = id; 2708 | this.val = val; 2709 | }; 2710 | 2711 | /** 2712 | * Schedule events to trigger every time a MediaElement 2713 | * (audio/video) reaches a playback cue point. 2714 | * 2715 | * Accepts a callback function, a time (in seconds) at which to trigger 2716 | * the callback, and an optional parameter for the callback. 2717 | * 2718 | * Time will be passed as the first parameter to the callback function, 2719 | * and param will be the second parameter. 2720 | * 2721 | * 2722 | * @method addCue 2723 | * @param {Number} time Time in seconds, relative to this media 2724 | * element's playback. For example, to trigger 2725 | * an event every time playback reaches two 2726 | * seconds, pass in the number 2. This will be 2727 | * passed as the first parameter to 2728 | * the callback function. 2729 | * @param {Function} callback Name of a function that will be 2730 | * called at the given time. The callback will 2731 | * receive time and (optionally) param as its 2732 | * two parameters. 2733 | * @param {Object} [value] An object to be passed as the 2734 | * second parameter to the 2735 | * callback function. 2736 | * @return {Number} id ID of this cue, 2737 | * useful for removeCue(id) 2738 | * @example 2739 | *
2740 | * function setup() { 2741 | * background(255, 255, 255); 2742 | * 2743 | * var audioEl = createAudio('assets/beat.mp3'); 2744 | * audioEl.showControls(); 2745 | * 2746 | * // schedule three calls to changeBackground 2747 | * audioEl.addCue(0.5, changeBackground, color(255, 0, 0)); 2748 | * audioEl.addCue(1.0, changeBackground, color(0, 255, 0)); 2749 | * audioEl.addCue(2.5, changeBackground, color(0, 0, 255)); 2750 | * audioEl.addCue(3.0, changeBackground, color(0, 255, 255)); 2751 | * audioEl.addCue(4.2, changeBackground, color(255, 255, 0)); 2752 | * audioEl.addCue(5.0, changeBackground, color(255, 255, 0)); 2753 | * } 2754 | * 2755 | * function changeBackground(val) { 2756 | * background(val); 2757 | * } 2758 | *
2759 | */ 2760 | p5.MediaElement.prototype.addCue = function(time, callback, val) { 2761 | var id = this._cueIDCounter++; 2762 | 2763 | var cue = new Cue(callback, time, id, val); 2764 | this._cues.push(cue); 2765 | 2766 | if (!this.elt.ontimeupdate) { 2767 | this.elt.ontimeupdate = this._onTimeUpdate.bind(this); 2768 | } 2769 | 2770 | return id; 2771 | }; 2772 | 2773 | /** 2774 | * Remove a callback based on its ID. The ID is returned by the 2775 | * addCue method. 2776 | * @method removeCue 2777 | * @param {Number} id ID of the cue, as returned by addCue 2778 | * @example 2779 | *
2780 | * var audioEl, id1, id2; 2781 | * function setup() { 2782 | * background(255, 255, 255); 2783 | * audioEl = createAudio('assets/beat.mp3'); 2784 | * audioEl.showControls(); 2785 | * // schedule five calls to changeBackground 2786 | * id1 = audioEl.addCue(0.5, changeBackground, color(255, 0, 0)); 2787 | * audioEl.addCue(1.0, changeBackground, color(0, 255, 0)); 2788 | * audioEl.addCue(2.5, changeBackground, color(0, 0, 255)); 2789 | * audioEl.addCue(3.0, changeBackground, color(0, 255, 255)); 2790 | * id2 = audioEl.addCue(4.2, changeBackground, color(255, 255, 0)); 2791 | * text('Click to remove first and last Cue!', 10, 25, 70, 80); 2792 | * } 2793 | * function mousePressed() { 2794 | * audioEl.removeCue(id1); 2795 | * audioEl.removeCue(id2); 2796 | * } 2797 | * function changeBackground(val) { 2798 | * background(val); 2799 | * } 2800 | *
2801 | */ 2802 | p5.MediaElement.prototype.removeCue = function(id) { 2803 | for (var i = 0; i < this._cues.length; i++) { 2804 | if (this._cues[i].id === id) { 2805 | console.log(id); 2806 | this._cues.splice(i, 1); 2807 | } 2808 | } 2809 | 2810 | if (this._cues.length === 0) { 2811 | this.elt.ontimeupdate = null; 2812 | } 2813 | }; 2814 | 2815 | /** 2816 | * Remove all of the callbacks that had originally been scheduled 2817 | * via the addCue method. 2818 | * @method clearCues 2819 | * @param {Number} id ID of the cue, as returned by addCue 2820 | * @example 2821 | *
2822 | * var audioEl; 2823 | * function setup() { 2824 | * background(255, 255, 255); 2825 | * audioEl = createAudio('assets/beat.mp3'); 2826 | * //Show the default MediaElement controls, as determined by the web browser 2827 | * audioEl.showControls(); 2828 | * // schedule calls to changeBackground 2829 | * background(200); 2830 | * text('Click to change Cue!', 10, 25, 70, 80); 2831 | * audioEl.addCue(0.5, changeBackground, color(255, 0, 0)); 2832 | * audioEl.addCue(1.0, changeBackground, color(0, 255, 0)); 2833 | * audioEl.addCue(2.5, changeBackground, color(0, 0, 255)); 2834 | * audioEl.addCue(3.0, changeBackground, color(0, 255, 255)); 2835 | * audioEl.addCue(4.2, changeBackground, color(255, 255, 0)); 2836 | * } 2837 | * function mousePressed() { 2838 | * // here we clear the scheduled callbacks 2839 | * audioEl.clearCues(); 2840 | * // then we add some more callbacks 2841 | * audioEl.addCue(1, changeBackground, color(2, 2, 2)); 2842 | * audioEl.addCue(3, changeBackground, color(255, 255, 0)); 2843 | * } 2844 | * function changeBackground(val) { 2845 | * background(val); 2846 | * } 2847 | *
2848 | */ 2849 | p5.MediaElement.prototype.clearCues = function() { 2850 | this._cues = []; 2851 | this.elt.ontimeupdate = null; 2852 | }; 2853 | 2854 | // private method that checks for cues to be fired if events 2855 | // have been scheduled using addCue(callback, time). 2856 | p5.MediaElement.prototype._onTimeUpdate = function() { 2857 | var playbackTime = this.time(); 2858 | 2859 | for (var i = 0; i < this._cues.length; i++) { 2860 | var callbackTime = this._cues[i].time; 2861 | var val = this._cues[i].val; 2862 | 2863 | if (this._prevTime < callbackTime && callbackTime <= playbackTime) { 2864 | // pass the scheduled callbackTime as parameter to the callback 2865 | this._cues[i].callback(val); 2866 | } 2867 | } 2868 | 2869 | this._prevTime = playbackTime; 2870 | }; 2871 | 2872 | // ============================================================================= 2873 | // p5.File 2874 | // ============================================================================= 2875 | 2876 | /** 2877 | * Base class for a file 2878 | * Using this for createFileInput 2879 | * 2880 | * @class p5.File 2881 | * @constructor 2882 | * @param {File} file File that is wrapped 2883 | */ 2884 | p5.File = function(file, pInst) { 2885 | /** 2886 | * Underlying File object. All normal File methods can be called on this. 2887 | * 2888 | * @property file 2889 | */ 2890 | this.file = file; 2891 | 2892 | this._pInst = pInst; 2893 | 2894 | // Splitting out the file type into two components 2895 | // This makes determining if image or text etc simpler 2896 | var typeList = file.type.split('/'); 2897 | /** 2898 | * File type (image, text, etc.) 2899 | * 2900 | * @property type 2901 | */ 2902 | this.type = typeList[0]; 2903 | /** 2904 | * File subtype (usually the file extension jpg, png, xml, etc.) 2905 | * 2906 | * @property subtype 2907 | */ 2908 | this.subtype = typeList[1]; 2909 | /** 2910 | * File name 2911 | * 2912 | * @property name 2913 | */ 2914 | this.name = file.name; 2915 | /** 2916 | * File size 2917 | * 2918 | * @property size 2919 | */ 2920 | this.size = file.size; 2921 | 2922 | /** 2923 | * URL string containing image data. 2924 | * 2925 | * @property data 2926 | */ 2927 | this.data = undefined; 2928 | }; 2929 | }); 2930 | -------------------------------------------------------------------------------- /Back Prop/libs/p5.dom.min.js: -------------------------------------------------------------------------------- 1 | /*! p5.js v0.6.0 January 19, 2018 */ !function(a,b){"function"==typeof define&&define.amd?define("p5.dom",["p5"],function(a){b(a)}):b("object"==typeof exports?require("../p5"):a.p5)}(this,function(a){function b(b){var c=document;return"string"==typeof b&&"#"===b[0]?(b=b.slice(1),c=document.getElementById(b)||document):b instanceof a.Element?c=b.elt:b instanceof HTMLElement&&(c=b),c}function c(b,c,d){(c._userNode?c._userNode:document.body).appendChild(b);var e=d?new a.MediaElement(b):new a.Element(b);return c._elements.push(e),e}function d(a,b,d,e){var f=document.createElement(b);"string"==typeof(d=d||"")&&(d=[d]);for(var g=0;g0&&c.every(function(a){return"INPUT"===a.tagName||"LABEL"===a.tagName})?this.createRadio(new a.Element(b)):new a.Element(b)},a.prototype.removeElements=function(b){a._validateParameters("removeElements",arguments);for(var c=0;c1&&"function"==typeof e[1]?(b.fn=e[1],b.fn()):e.length>1&&"function"==typeof e[2]&&(b.fn=e[2],b.fn())};return d.src=e[0],e.length>1&&"string"==typeof e[1]&&(d.alt=e[1]),d.onload=function(){f()},b=c(d,this)},a.prototype.createA=function(b,d,e){a._validateParameters("createA",arguments);var f=document.createElement("a");return f.href=b,f.innerHTML=d,e&&(f.target=e),c(f,this)},a.prototype.createSlider=function(b,d,e,f){a._validateParameters("createSlider",arguments);var g=document.createElement("input");return g.type="range",g.min=b,g.max=d,0===f?g.step=1e-18:f&&(g.step=f),"number"==typeof e&&(g.value=e),c(g,this)},a.prototype.createButton=function(b,d){a._validateParameters("createButton",arguments);var e=document.createElement("button");return e.innerHTML=b,d&&(e.value=d),c(e,this)},a.prototype.createCheckbox=function(){a._validateParameters("createCheckbox",arguments);var b=document.createElement("div"),d=document.createElement("input");d.type="checkbox",b.appendChild(d);var e=c(b,this);if(e.checked=function(){var a=e.elt.getElementsByTagName("input")[0];if(a){if(0===arguments.length)return a.checked;arguments[0]?a.checked=!0:a.checked=!1}return e},this.value=function(a){return e.value=a,this},arguments[0]){var f=Math.random().toString(36).slice(2),g=document.createElement("label");d.setAttribute("id",f),g.htmlFor=f,e.value(arguments[0]),g.appendChild(document.createTextNode(arguments[0])),b.appendChild(g)}return arguments[1]&&(d.checked=!0),e},a.prototype.createSelect=function(){a._validateParameters("createSelect",arguments);var b,d,e=arguments[0];return"object"==typeof e&&"SELECT"===e.elt.nodeName?(d=e,b=this.elt=e.elt):(b=document.createElement("select"),e&&"boolean"==typeof e&&b.setAttribute("multiple","true"),d=c(b,this)),d.option=function(a,c){for(var d,e=0;e1?f.value=c:f.value=a,b.appendChild(f)}},d.selected=function(a){var b,c=[];if(arguments.length>0){for(b=0;b1){var f=d.length,g=d[0].name,h=d[1].name;e=1;for(var i=1;i-1?h.readAsText(g):h.readAsDataURL(g)}}if(a._validateParameters("createFileInput",arguments),window.File&&window.FileReader&&window.FileList&&window.Blob){var f=document.createElement("input");return f.type="file",d&&(f.multiple="multiple"),f.addEventListener("change",e,!1),c(f,this)}console.log("The File APIs are not fully supported in this browser. Cannot create element.")},a.prototype.createVideo=function(b,c){return a._validateParameters("createVideo",arguments),d(this,"video",b,c)},a.prototype.createAudio=function(b,c){return a._validateParameters("createAudio",arguments),d(this,"audio",b,c)},a.prototype.VIDEO="video",a.prototype.AUDIO="audio",void 0===navigator.mediaDevices&&(navigator.mediaDevices={}),void 0===navigator.mediaDevices.getUserMedia&&(navigator.mediaDevices.getUserMedia=function(a){var b=navigator.webkitGetUserMedia||navigator.mozGetUserMedia;return b?new Promise(function(c,d){b.call(navigator,a,c,d)}):Promise.reject(new Error("getUserMedia is not implemented in this browser"))}),a.prototype.createCapture=function(){a._validateParameters("createCapture",arguments);for(var b,d,e=!0,f=!0,g=0;g2&&(this.elt.style.transform="translate3d("+arguments[0]+"px,"+arguments[1]+"px,"+arguments[2]+"px)",3===arguments.length?this.elt.parentElement.style.perspective="1000px":this.elt.parentElement.style.perspective=arguments[3]+"px"),this.elt.style.transform+=a,this},a.Element.prototype._rotate=function(){var a="";return this.elt.style.transform&&(a=this.elt.style.transform.replace(/rotate3d\(.*\)/g,""),a=a.replace(/rotate[X-Z]?\(.*\)/g,"")),1===arguments.length?this.elt.style.transform="rotate("+arguments[0]+"deg)":2===arguments.length?this.elt.style.transform="rotate("+arguments[0]+"deg, "+arguments[1]+"deg)":3===arguments.length&&(this.elt.style.transform="rotateX("+arguments[0]+"deg)",this.elt.style.transform+="rotateY("+arguments[1]+"deg)",this.elt.style.transform+="rotateZ("+arguments[2]+"deg)"),this.elt.style.transform+=a,this},a.Element.prototype.style=function(b,c){var d=this;if(c instanceof a.Color&&(c="rgba("+c.levels[0]+","+c.levels[1]+","+c.levels[2]+","+c.levels[3]/255+")"),void 0===c){if(-1===b.indexOf(":"))return window.getComputedStyle(d.elt).getPropertyValue(b);for(var e=b.split(";"),f=0;f0?(this.elt.value=arguments[0],this):"range"===this.elt.type?parseFloat(this.elt.value):this.elt.value},a.Element.prototype.show=function(){return this.elt.style.display="block",this},a.Element.prototype.hide=function(){return this.elt.style.display="none",this},a.Element.prototype.size=function(b,c){if(0===arguments.length)return{width:this.elt.offsetWidth,height:this.elt.offsetHeight};var d=b,e=c,f=a.prototype.AUTO;if(d!==f||e!==f){if(d===f?d=c*this.width/this.height:e===f&&(e=b*this.height/this.width),this.elt instanceof HTMLCanvasElement){var g,h={},i=this.elt.getContext("2d");for(g in i)h[g]=i[g];this.elt.setAttribute("width",d*this._pInst._pixelDensity),this.elt.setAttribute("height",e*this._pInst._pixelDensity),this.elt.setAttribute("style","width:"+d+"px; height:"+e+"px"),this._pInst.scale(this._pInst._pixelDensity,this._pInst._pixelDensity);for(g in h)this.elt.getContext("2d")[g]=h[g]}else this.elt.style.width=d+"px",this.elt.style.height=e+"px",this.elt.width=d,this.elt.height=e,this.width=d,this.height=e;this.width=this.elt.offsetWidth,this.height=this.elt.offsetHeight,this._pInst&&this._pInst._curElement.elt===this.elt&&(this._pInst._setProperty("width",this.elt.offsetWidth),this._pInst._setProperty("height",this.elt.offsetHeight))}return this},a.Element.prototype.remove=function(){for(var a in this._events)this.elt.removeEventListener(a,this._events[a]);this.elt.parentNode&&this.elt.parentNode.removeChild(this.elt)},a.MediaElement=function(b,c){a.Element.call(this,b,c);var d=this;this.elt.crossOrigin="anonymous",this._prevTime=0,this._cueIDCounter=0,this._cues=[],this._pixelDensity=1,this._modified=!1,Object.defineProperty(d,"src",{get:function(){var a=d.elt.children[0].src,b=d.elt.src===window.location.href?"":d.elt.src;return a===window.location.href?b:a},set:function(a){for(var c=0;c1?this.elt.play():(this.elt.load(),this.elt.play()),this},a.MediaElement.prototype.stop=function(){return this.elt.pause(),this.elt.currentTime=0,this},a.MediaElement.prototype.pause=function(){return this.elt.pause(),this},a.MediaElement.prototype.loop=function(){return this.elt.setAttribute("loop",!0),this.play(),this},a.MediaElement.prototype.noLoop=function(){return this.elt.setAttribute("loop",!1),this},a.MediaElement.prototype.autoplay=function(a){return this.elt.setAttribute("autoplay",a),this},a.MediaElement.prototype.volume=function(a){if(void 0===a)return this.elt.volume;this.elt.volume=a},a.MediaElement.prototype.speed=function(a){if(void 0===a)return this.elt.playbackRate;this.elt.playbackRate=a},a.MediaElement.prototype.time=function(a){if(void 0===a)return this.elt.currentTime;this.elt.currentTime=a},a.MediaElement.prototype.duration=function(){return this.elt.duration},a.MediaElement.prototype.pixels=[],a.MediaElement.prototype.loadPixels=function(){return this.canvas||(this.canvas=document.createElement("canvas"),this.drawingContext=this.canvas.getContext("2d")),this.loadedmetadata&&(this.canvas.width!==this.elt.width&&(this.canvas.width=this.elt.width,this.canvas.height=this.elt.height,this.width=this.canvas.width,this.height=this.canvas.height),this.drawingContext.drawImage(this.elt,0,0,this.canvas.width,this.canvas.height),a.Renderer2D.prototype.loadPixels.call(this)),this.setModified(!0),this},a.MediaElement.prototype.updatePixels=function(b,c,d,e){return this.loadedmetadata&&a.Renderer2D.prototype.updatePixels.call(this,b,c,d,e),this.setModified(!0),this},a.MediaElement.prototype.get=function(b,c,d,e){return this.loadedmetadata?a.Renderer2D.prototype.get.call(this,b,c,d,e):void 0===b?new a.Image(1,1):d>1?new a.Image(b,c,d,e):[0,0,0,255]},a.MediaElement.prototype.set=function(b,c,d){this.loadedmetadata&&(a.Renderer2D.prototype.set.call(this,b,c,d),this.setModified(!0))},a.MediaElement.prototype.copy=function(){a.Renderer2D.prototype.copy.apply(this,arguments)},a.MediaElement.prototype.mask=function(){this.loadPixels(),this.setModified(!0),a.Image.prototype.mask.apply(this,arguments)},a.MediaElement.prototype.isModified=function(){return this._modified},a.MediaElement.prototype.setModified=function(a){this._modified=a},a.MediaElement.prototype.onended=function(a){return this._onended=a,this},a.MediaElement.prototype.connect=function(b){var c,d;if("function"==typeof a.prototype.getAudioContext)c=a.prototype.getAudioContext(),d=a.soundOut.input;else try{c=b.context,d=c.destination}catch(a){throw"connect() is meant to be used with Web Audio API or p5.sound.js"}this.audioSourceNode||(this.audioSourceNode=c.createMediaElementSource(this.elt),this.audioSourceNode.connect(d)),b?b.input?this.audioSourceNode.connect(b.input):this.audioSourceNode.connect(b):this.audioSourceNode.connect(d)},a.MediaElement.prototype.disconnect=function(){if(!this.audioSourceNode)throw"nothing to disconnect";this.audioSourceNode.disconnect()},a.MediaElement.prototype.showControls=function(){this.elt.style["text-align"]="inherit",this.elt.controls=!0},a.MediaElement.prototype.hideControls=function(){this.elt.controls=!1};var e=function(a,b,c,d){this.callback=a,this.time=b,this.id=c,this.val=d};a.MediaElement.prototype.addCue=function(a,b,c){var d=this._cueIDCounter++,f=new e(b,a,d,c);return this._cues.push(f),this.elt.ontimeupdate||(this.elt.ontimeupdate=this._onTimeUpdate.bind(this)),d},a.MediaElement.prototype.removeCue=function(a){for(var b=0;b