1/*! p5.dom.js v0.2.7 January 4, 2016 */ 2/** 3 * <p>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.</p> 6 * <p>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 * <a href="https://github.com/processing/p5.js/wiki/Beyond-the-canvas"> 9 * beyond the canvas tutorial</a> for a full overview of how this addon works. 10 * 11 * <p>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 * <a href="http://p5js.org/libraries/#using-a-library">using a library</a> 15 * section for information on how to include this library. p5.dom comes with 16 * <a href="http://p5js.org/download">p5 complete</a> or you can download the single file 17 * <a href="https://raw.githubusercontent.com/lmccart/p5.js/master/lib/addons/p5.dom.js"> 18 * here</a>.</p> 19 * <p>See <a href="https://github.com/processing/p5.js/wiki/Beyond-the-canvas">tutorial: beyond the canvas</a> 20 * for more info on how to use this libary.</a> 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) { (factory(p5));}); 31 else if (typeof exports === 'object') 32 factory(require('../p5')); 33 else 34 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 * <div ><code class='norender'> 54 * function setup() { 55 * createCanvas(100,100); 56 * //translates canvas 50px down 57 * select('canvas').translate(0,50); 58 * } 59 * </code></div> 60 * <div ><code class='norender'> 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 * </code></div> 68 * 69 */ 70 p5.prototype.select = function (e, p) { 71 var res = null; 72 var container = getContainer(p); 73 if (e[0] === '.'){ 74 e = e.slice(1); 75 res = container.getElementsByClassName(e); 76 if (res.length) { 77 res = res[0]; 78 } else { 79 res = null; 80 } 81 }else if (e[0] === '#'){ 82 e = e.slice(1); 83 res = container.getElementById(e); 84 }else { 85 res = container.getElementsByTagName(e); 86 if (res.length) { 87 res = res[0]; 88 } else { 89 res = null; 90 } 91 } 92 if (res) { 93 return wrapElement(res); 94 } else { 95 return null; 96 } 97 }; 98 99 /** 100 * Searches the page for elements with the given class or tag name (using the '.' prefix 101 * to specify a class and no prefix for a tag) and returns them as p5.Elements 102 * in an array. 103 * The DOM node itself can be accessed with .elt. 104 * Returns an empty array if none found. 105 * You can also specify a container to search within. 106 * 107 * @method selectAll 108 * @param {String} name class or tag name of elements to search for 109 * @param {String} [container] id, p5.Element, or HTML element to search within 110 * @return {Array} Array of p5.Elements containing nodes found 111 * @example 112 * <div ><code class='norender'> 113 * function setup() { 114 * createButton('btn'); 115 * createButton('2nd btn'); 116 * createButton('3rd btn'); 117 * var buttons = selectAll('button'); 118 * 119 * for (var i = 0; i < buttons.length; i++){ 120 * buttons[i].size(100,100); 121 * } 122 * } 123 * </code></div> 124 * <div ><code class='norender'> 125 * // these are all valid calls to selectAll() 126 * var a = selectAll('.moo'); 127 * var b = selectAll('div'); 128 * var c = selectAll('button', '#myContainer'); 129 * var d = select('#container'); 130 * var e = selectAll('p', d); 131 * var f = document.getElementById('beep'); 132 * var g = select('.blah', f); 133 * </code></div> 134 * 135 */ 136 p5.prototype.selectAll = function (e, p) { 137 var arr = []; 138 var res; 139 var container = getContainer(p); 140 if (e[0] === '.'){ 141 e = e.slice(1); 142 res = container.getElementsByClassName(e); 143 } else { 144 res = container.getElementsByTagName(e); 145 } 146 if (res) { 147 for (var j = 0; j < res.length; j++) { 148 var obj = wrapElement(res[j]); 149 arr.push(obj); 150 } 151 } 152 return arr; 153 }; 154 155 /** 156 * Helper function for select and selectAll 157 */ 158 function getContainer(p) { 159 var container = document; 160 if (typeof p === 'string' && p[0] === '#'){ 161 p = p.slice(1); 162 container = document.getElementById(p) || document; 163 } else if (p instanceof p5.Element){ 164 container = p.elt; 165 } else if (p instanceof HTMLElement){ 166 container = p; 167 } 168 return container; 169 } 170 171 /** 172 * Helper function for getElement and getElements. 173 */ 174 function wrapElement(elt) { 175 if(elt.tagName === "INPUT" && elt.type === "checkbox") { 176 var converted = new p5.Element(elt); 177 converted.checked = function(){ 178 if (arguments.length === 0){ 179 return this.elt.checked; 180 } else if(arguments[0]) { 181 this.elt.checked = true; 182 } else { 183 this.elt.checked = false;
184 } 185 return this; 186 }; 187 return converted; 188 } else if (elt.tagName === "VIDEO" || elt.tagName === "AUDIO") { 189 return new p5.MediaElement(elt); 190 } else { 191 return new p5.Element(elt); 192 } 193 } 194 195 /** 196 * Removes all elements created by p5, except any canvas / graphics 197 * elements created by createCanvas or createGraphics. 198 * Event handlers are removed, and element is removed from the DOM. 199 * @method removeElements 200 * @example 201 * <div class='norender'><code> 202 * function setup() { 203 * createCanvas(100, 100); 204 * createDiv('this is some text'); 205 * createP('this is a paragraph'); 206 * } 207 * function mousePressed() { 208 * removeElements(); // this will remove the div and p, not canvas 209 * } 210 * </code></div> 211 * 212 */ 213 p5.prototype.removeElements = function (e) { 214 for (var i=0; i<this._elements.length; i++) { 215 if (!(this._elements[i].elt instanceof HTMLCanvasElement)) { 216 this._elements[i].remove(); 217 } 218 } 219 }; 220 221 /** 222 * Helpers for create methods. 223 */ 224 function addElement(elt, pInst, media) { 225 var node = pInst._userNode ? pInst._userNode : document.body; 226 node.appendChild(elt); 227 var c = media ? new p5.MediaElement(elt) : new p5.Element(elt); 228 pInst._elements.push(c); 229 return c; 230 } 231 232 /** 233 * Creates a <div></div> element in the DOM with given inner HTML. 234 * Appends to the container node if one is specified, otherwise 235 * appends to body. 236 * 237 * @method createDiv 238 * @param {String} html inner HTML for element created 239 * @return {Object/p5.Element} pointer to p5.Element holding created node 240 * @example 241 * <div class='norender'><code> 242 * var myDiv; 243 * function setup() { 244 * myDiv = createDiv('this is some text'); 245 * } 246 * </code></div> 247 */ 248 249 /** 250 * Creates a <p></p> element in the DOM with given inner HTML. Used 251 * for paragraph length text. 252 * Appends to the container node if one is specified, otherwise 253 * appends to body. 254 * 255 * @method createP 256 * @param {String} html inner HTML for element created 257 * @return {Object/p5.Element} pointer to p5.Element holding created node 258 * @example 259 * <div class='norender'><code> 260 * var myP; 261 * function setup() { 262 * myP = createP('this is some text'); 263 * } 264 * </code></div> 265 */ 266 267 /** 268 * Creates a <span></span> element in the DOM with given inner HTML. 269 * Appends to the container node if one is specified, otherwise 270 * appends to body. 271 * 272 * @method createSpan 273 * @param {String} html inner HTML for element created 274 * @return {Object/p5.Element} pointer to p5.Element holding created node 275 * @example 276 * <div class='norender'><code> 277 * var mySpan; 278 * function setup() { 279 * mySpan = createSpan('this is some text'); 280 * } 281 * </code></div> 282 */ 283 var tags = ['div', 'p', 'span'];
284 tags.forEach(function(tag) { 285 var method = 'create' + tag.charAt(0).toUpperCase() + tag.slice(1); 286 p5.prototype[method] = function(html) { 287 var elt = document.createElement(tag); 288 elt.innerHTML = typeof html === undefined ? "" : html; 289 return addElement(elt, this); 290 } 291 }); 292 293 /** 294 * Creates an <img /> element in the DOM with given src and 295 * alternate text. 296 * Appends to the container node if one is specified, otherwise 297 * appends to body. 298 * 299 * @method createImg 300 * @param {String} src src path or url for image 301 * @param {String} [alt] alternate text to be used if image does not load 302 * @param {Function} [successCallback] callback to be called once image data is loaded 303 * @return {Object/p5.Element} pointer to p5.Element holding created node 304 * @example 305 * <div class='norender'><code> 306 * var img; 307 * function setup() { 308 * img = createImg('http://p5js.org/img/asterisk-01.png'); 309 * } 310 * </code></div> 311 */ 312 p5.prototype.createImg = function() { 313 var elt = document.createElement('img'); 314 var args = arguments; 315 var self; 316 var setAttrs = function(){ 317 self.width = elt.offsetWidth; 318 self.height = elt.offsetHeight; 319 if (args.length > 1 && typeof args[1] === 'function'){ 320 self.fn = args[1]; 321 self.fn(); 322 }else if (args.length > 1 && typeof args[2] === 'function'){ 323 self.fn = args[2]; 324 self.fn(); 325 } 326 }; 327 elt.src = args[0]; 328 if (args.length > 1 && typeof args[1] === 'string'){ 329 elt.alt = args[1]; 330 } 331 elt.onload = function(){ 332 setAttrs(); 333 } 334 self = addElement(elt, this); 335 return self; 336 }; 337 338 /** 339 * Creates an <a></a> element in the DOM for including a hyperlink. 340 * Appends to the container node if one is specified, otherwise 341 * appends to body. 342 * 343 * @method createA 344 * @param {String} href url of page to link to 345 * @param {String} html inner html of link element to display 346 * @param {String} [target] target where new link should open, 347 * could be _blank, _self, _parent, _top. 348 * @return {Object/p5.Element} pointer to p5.Element holding created node 349 * @example 350 * <div class='norender'><code> 351 * var myLink; 352 * function setup() { 353 * myLink = createA('http://p5js.org/', 'this is a link'); 354 * } 355 * </code></div> 356 */ 357 p5.prototype.createA = function(href, html, target) { 358 var elt = document.createElement('a'); 359 elt.href = href; 360 elt.innerHTML = html; 361 if (target) elt.target = target; 362 return addElement(elt, this); 363 }; 364 365 /** INPUT **/ 366 367 368 /** 369 * Creates a slider <input></input> element in the DOM. 370 * Use .size() to set the display length of the slider. 371 * Appends to the container node if one is specified, otherwise 372 * appends to body. 373 * 374 * @method createSlider 375 * @param {Number} min minimum value of the slider 376 * @param {Number} max maximum value of the slider 377 * @param {Number} [value] default value of the slider 378 * @return {Object/p5.Element} pointer to p5.Element holding created node 379 * @example 380 * <div><code> 381 * var slider; 382 * function setup() { 383 * slider = createSlider(0, 255, 100); 384 * slider.position(10, 10); 385 * slider.style('width', '80px'); 386 * } 387 * 388 * function draw() { 389 * var val = slider.value(); 390 * background(val); 391 * } 392 * </code></div> 393 */ 394 p5.prototype.createSlider = function(min, max, value, step) { 395 var elt = document.createElement('input'); 396 elt.type = 'range'; 397 elt.min = min; 398 elt.max = max; 399 if (step) elt.step = step; 400 if (typeof(value) === "number") elt.value = value; 401 return addElement(elt, this); 402 }; 403 404 /** 405 * Creates a <button></button> element in the DOM. 406 * Use .size() to set the display size of the button. 407 * Use .mousePressed() to specify behavior on press. 408 * Appends to the container node if one is specified, otherwise 409 * appends to body. 410 * 411 * @method createButton 412 * @param {String} label label displayed on the button 413 * @param {String} [value] value of the button 414 * @return {Object/p5.Element} pointer to p5.Element holding created node 415 * @example 416 * <div class='norender'><code> 417 * var button; 418 * function setup() { 419 * createCanvas(100, 100); 420 * background(0); 421 * button = createButton('click me'); 422 * button.position(19, 19); 423 * button.mousePressed(changeBG); 424 * } 425 * 426 * function changeBG() { 427 * var val = random(255); 428 * background(val); 429 * } 430 * </code></div> 431 */ 432 p5.prototype.createButton = function(label, value) { 433 var elt = document.createElement('button'); 434 elt.innerHTML = label; 435 elt.value = value; 436 if (value) elt.value = value; 437 return addElement(elt, this); 438 }; 439 440 /** 441 * Creates a checkbox <input></input> element in the DOM. 442 * Calling .checked() on a checkbox returns if it is checked or not 443 * 444 * @method createCheckbox 445 * @param {String} [label] label displayed after checkbox 446 * @param {boolean} [value] value of the checkbox; checked is true, unchecked is false.Unchecked if no value given 447 * @return {Object/p5.Element} pointer to p5.Element holding created node 448 * @example 449 * <div class='norender'><code> 450 * var checkbox; 451 * 452 * function setup() { 453 * checkbox = createCheckbox('label', false); 454 * checkbox.changed(myCheckedEvent); 455 * } 456 * 457 * function myCheckedEvent() { 458 * if (this.checked()) { 459 * console.log("Unchecking!"); 460 * } else { 461 * console.log("Checking!"); 462 * } 463 * 464 * </code></div> 465 */ 466 p5.prototype.createCheckbox = function() { 467 var elt = document.createElement('input'); 468 elt.type = 'checkbox'; 469 //checkbox must be wrapped in p5.Element before label so that label appears after 470 var self = addElement(elt, this); 471 self.checked = function(){ 472 if (arguments.length === 0){ 473 return self.elt.checked; 474 }else if(arguments[0]){ 475 self.elt.checked = true; 476 }else{ 477 self.elt.checked = false;
478 } 479 return self; 480 }; 481 this.value = function(val){ 482 self.value = val; 483 return this; 484 }; 485 if (arguments[0]){ 486 var ran = Math.random().toString(36).slice(2); 487 var label = document.createElement('label'); 488 elt.setAttribute('id', ran); 489 label.htmlFor = ran; 490 self.value(arguments[0]); 491 label.appendChild(document.createTextNode(arguments[0])); 492 addElement(label, this); 493 } 494 if (arguments[1]){ 495 elt.checked = true; 496 } 497 return self; 498 }; 499 500 /** 501 * Creates a dropdown menu <select></select> element in the DOM. 502 * @method createSelect 503 * @param {boolean} [multiple] [true if dropdown should support multiple selections] 504 * @return {Object/p5.Element} pointer to p5.Element holding created node 505 * @example 506 * <div><code> 507 * var sel; 508 * 509 * function setup() { 510 * textAlign(CENTER); 511 * background(200); 512 * sel = createSelect(); 513 * sel.position(10, 10); 514 * sel.option('pear'); 515 * sel.option('kiwi'); 516 * sel.option('grape'); 517 * sel.changed(mySelectEvent); 518 * } 519 * 520 * function mySelectEvent() { 521 * var item = sel.value(); 522 * background(200); 523 * text("it's a "+item+"!", 50, 50); 524 * } 525 * </code></div> 526 */ 527 p5.prototype.createSelect = function(mult) { 528 var elt = document.createElement('select'); 529 if (mult){ 530 elt.setAttribute('multiple', 'true'); 531 } 532 var self = addElement(elt, this); 533 self.option = function(name, value){ 534 var opt = document.createElement('option'); 535 opt.innerHTML = name; 536 if (arguments.length > 1) 537 opt.value = value; 538 else 539 opt.value = name; 540 elt.appendChild(opt); 541 }; 542 self.selected = function(value){ 543 var arr = []; 544 if (arguments.length > 0){ 545 for (var i = 0; i < this.elt.length; i++){ 546 if (value.toString() === this.elt[i].value){ 547 this.elt.selectedIndex = i; 548 } 549 } 550 return this; 551 }else{ 552 if (mult){ 553 for (var i = 0; i < this.elt.selectedOptions.length; i++){ 554 arr.push(this.elt.selectedOptions[i].value); 555 } 556 return arr; 557 }else{ 558 return this.elt.value; 559 } 560 } 561 }; 562 return self; 563 }; 564 565 /** 566 * Creates a radio button <input></input> element in the DOM. 567 * 568 * @method createRadio 569 * @param {String} [divId] the id and name of the created div and input field respectively 570 * @return {Object/p5.Element} pointer to p5.Element holding created node 571 */ 572 p5.prototype.createRadio = function() { 573 var radios = document.querySelectorAll("input[type=radio]"); 574 var count = 0; 575 if(radios.length > 1){ 576 console.log(radios,radios[0].name); 577 var length = radios.length; 578 var prev=radios[0].name; 579 var current = radios[1].name; 580 count=1; 581 for(var i = 1; i < length; i++ ){ 582 current = radios[i].name; 583 if(prev != current){ 584 count++; 585 } 586 prev = current; 587 } 588 } 589 else if (radios.length == 1){ 590 count = 1; 591 } 592 var elt = document.createElement('div'); 593 var self = addElement(elt, this); 594 var times = -1; 595 self.option = function(name, value){ 596 var opt = document.createElement('input'); 597 opt.type = 'radio'; 598 opt.innerHTML = name; 599 if (arguments.length > 1) 600 opt.value = value; 601 else 602 opt.value = name; 603 opt.setAttribute('name',"defaultradio"+count); 604 elt.appendChild(opt); 605 if (name){ 606 times++; 607 var ran = Math.random().toString(36).slice(2); 608 var label = document.createElement('label'); 609 opt.setAttribute('id', "defaultradio"+count+"-"+times); 610 label.htmlFor = "defaultradio"+count+"-"+times; 611 label.appendChild(document.createTextNode(name)); 612 elt.appendChild(label); 613 } 614 return opt; 615 }; 616 self.selected = function(){ 617 var length = this.elt.childNodes.length; 618 if(arguments[0]) { 619 for (var i = 0; i < length; i+=2){ 620 if(this.elt.childNodes[i].value == arguments[0]) 621 this.elt.childNodes[i].checked = true; 622 } 623 return this; 624 } else { 625 for (var i = 0; i < length; i+=2){ 626 if(this.elt.childNodes[i].checked == true) 627 return this.elt.childNodes[i].value; 628 } 629 } 630 }; 631 self.value = function(){ 632 var length = this.elt.childNodes.length; 633 if(arguments[0]) { 634 for (var i = 0; i < length; i+=2){ 635 if(this.elt.childNodes[i].value == arguments[0]) 636 this.elt.childNodes[i].checked = true; 637 } 638 return this; 639 } else { 640 for (var i = 0; i < length; i+=2){ 641 if(this.elt.childNodes[i].checked == true) 642 return this.elt.childNodes[i].value; 643 } 644 return ""; 645 } 646 }; 647 return self 648 }; 649 650 /** 651 * Creates an <input></input> element in the DOM for text input. 652 * Use .size() to set the display length of the box. 653 * Appends to the container node if one is specified, otherwise 654 * appends to body. 655 * 656 * @method createInput 657 * @param {Number} [value] default value of the input box 658 * @return {Object/p5.Element} pointer to p5.Element holding created node 659 * @example 660 * <div class='norender'><code> 661 * function setup(){ 662 * var inp = createInput(''); 663 * inp.input(myInputEvent); 664 * } 665 * 666 * function myInputEvent(){ 667 * console.log('you are typing: ', this.value()); 668 * } 669 * 670 * </code></div> 671 */ 672 p5.prototype.createInput = function(value) { 673 var elt = document.createElement('input'); 674 elt.type = 'text'; 675 if (value) elt.value = value; 676 return addElement(elt, this); 677 }; 678 679 /** 680 * Creates an <input></input> element in the DOM of type 'file'. 681 * This allows users to select local files for use in a sketch. 682 * 683 * @method createFileInput 684 * @param {Function} [callback] callback function for when a file loaded 685 * @param {String} [multiple] optional to allow multiple files selected 686 * @return {Object/p5.Element} pointer to p5.Element holding created DOM element 687 */ 688 p5.prototype.createFileInput = function(callback, multiple) { 689 690 // Is the file stuff supported? 691 if (window.File && window.FileReader && window.FileList && window.Blob) { 692 // Yup, we're ok and make an input file selector 693 var elt = document.createElement('input'); 694 elt.type = 'file'; 695 696 // If we get a second argument that evaluates to true 697 // then we are looking for multiple files 698 if (multiple) { 699 // Anything gets the job done 700 elt.multiple = 'multiple'; 701 } 702 703 // Function to handle when a file is selected 704 // We're simplifying life and assuming that we always 705 // want to load every selected file 706 function handleFileSelect(evt) { 707 // These are the files 708 var files = evt.target.files; 709 // Load each one and trigger a callback 710 for (var i = 0; i < files.length; i++) { 711 var f = files[i]; 712 var reader = new FileReader(); 713 function makeLoader(theFile) { 714 // Making a p5.File object 715 var p5file = new p5.File(theFile); 716 return function(e) { 717 p5file.data = e.target.result; 718 callback(p5file); 719 }; 720 }; 721 reader.onload = makeLoader(f); 722 723 // Text or data? 724 // This should likely be improved 725 if (f.type.indexOf('text') > -1) { 726 reader.readAsText(f); 727 } else {
728 reader.readAsDataURL(f); 729 } 730 } 731 } 732 733 // Now let's handle when a file was selected 734 elt.addEventListener('change', handleFileSelect, false); 735 return addElement(elt, this); 736 } else { 737 console.log('The File APIs are not fully supported in this browser. Cannot create element.'); 738 } 739 }; 740 741 742 /** VIDEO STUFF **/ 743 744 function createMedia(pInst, type, src, callback) { 745 var elt = document.createElement(type); 746 747 // allow src to be empty 748 var src = src || ''; 749 if (typeof src === 'string') { 750 src = [src]; 751 } 752 for (var i=0; i<src.length; i++) { 753 var source = document.createElement('source'); 754 source.src = src[i]; 755 elt.appendChild(source); 756 } 757 if (typeof callback !== 'undefined') { 758 var callbackHandler = function() { 759 callback(); 760 elt.removeEventListener('canplaythrough', callbackHandler); 761 } 762 elt.addEventListener('canplaythrough', callbackHandler); 763 } 764 765 var c = addElement(elt, pInst, true); 766 c.loadedmetadata = false; 767 // set width and height onload metadata 768 elt.addEventListener('loadedmetadata', function() { 769 c.width = elt.videoWidth; 770 c.height = elt.videoHeight; 771 c.loadedmetadata = true; 772 }); 773 774 return c; 775 } 776 /** 777 * Creates an HTML5 <video> element in the DOM for simple playback 778 * of audio/video. Shown by default, can be hidden with .hide() 779 * and drawn into canvas using video(). Appends to the container 780 * node if one is specified, otherwise appends to body. The first parameter 781 * can be either a single string path to a video file, or an array of string 782 * paths to different formats of the same video. This is useful for ensuring 783 * that your video can play across different browsers, as each supports 784 * different formats. See <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Supported_media_formats">this 785 * page</a> for further information about supported formats. 786 * 787 * @method createVideo 788 * @param {String|Array} src path to a video file, or array of paths for 789 * supporting different browsers 790 * @param {Object} [callback] callback function to be called upon 791 * 'canplaythrough' event fire, that is, when the 792 * browser can play the media, and estimates that 793 * enough data has been loaded to play the media 794 * up to its end without having to stop for 795 * further buffering of content 796 * @return {Object/p5.Element} pointer to video p5.Element 797 */ 798 p5.prototype.createVideo = function(src, callback) { 799 return createMedia(this, 'video', src, callback); 800 }; 801 802 /** AUDIO STUFF **/ 803 804 /** 805 * Creates a hidden HTML5 <audio> element in the DOM for simple audio 806 * playback. Appends to the container node if one is specified, 807 * otherwise appends to body. The first parameter 808 * can be either a single string path to a audio file, or an array of string 809 * paths to different formats of the same audio. This is useful for ensuring 810 * that your audio can play across different browsers, as each supports 811 * different formats. See <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Supported_media_formats">this 812 * page for further information about supported formats. 813 * 814 * @method createAudio 815 * @param {String|Array} src path to an audio file, or array of paths for 816 * supporting different browsers 817 * @param {Object} [callback] callback function to be called upon 818 * 'canplaythrough' event fire, that is, when the 819 * browser can play the media, and estimates that 820 * enough data has been loaded to play the media 821 * up to its end without having to stop for 822 * further buffering of content 823 * @return {Object/p5.Element} pointer to audio p5.Element 824 */ 825 p5.prototype.createAudio = function(src, callback) { 826 return createMedia(this, 'audio', src, callback); 827 }; 828 829 830 /** CAMERA STUFF **/ 831 832 p5.prototype.VIDEO = 'video'; 833 p5.prototype.AUDIO = 'audio'; 834 835 navigator.getUserMedia = navigator.getUserMedia || 836 navigator.webkitGetUserMedia || 837 navigator.mozGetUserMedia || 838 navigator.msGetUserMedia; 839 840 /** 841 * Creates a new <video> element that contains the audio/video feed 842 * from a webcam. This can be drawn onto the canvas using video(). More 843 * specific properties of the stream can be passing in a Constraints object. 844 * See the 845 * <a href="http://w3c.github.io/mediacapture-main/getusermedia.html">W3C 846 * spec</a> for possible properties. Note that not all of these are supported 847 * by all browsers. 848 * 849 * @method createCapture 850 * @param {String|Constant|Object} type type of capture, either VIDEO or 851 * AUDIO if none specified, default both, 852 * or a Constraints boject 853 * @param {Function} callback function to be called once 854 * stream has loaded 855 * @return {Object/p5.Element} capture video p5.Element 856 * @example 857 * <div class='norender'><code> 858 * var capture; 859 * 860 * function setup() { 861 * createCanvas(480, 120); 862 * capture = createCapture(VIDEO); 863 * } 864 * 865 * function draw() { 866 * image(capture, 0, 0, width, width*capture.height/capture.width); 867 * filter(INVERT); 868 * } 869 * </code></div> 870 * <div class='norender'><code> 871 * function setup() { 872 * createCanvas(480, 120); 873 * var constraints = { 874 * video: { 875 * mandatory: { 876 * minWidth: 1280, 877 * minHeight: 720 878 * }, 879 * optional: [ 880 * { maxFrameRate: 10 } 881 * ] 882 * }, 883 * audio: true 884 * }; 885 * createCapture(constraints, function(stream) { 886 * console.log(stream); 887 * }); 888 * } 889 * </code></div> 890 */ 891 p5.prototype.createCapture = function() { 892 var useVideo = true; 893 var useAudio = true; 894 var constraints; 895 var cb; 896 for (var i=0; i<arguments.length; i++) { 897 if (arguments[i] === p5.prototype.VIDEO) { 898 useAudio = false;
899 } else if (arguments[i] === p5.prototype.AUDIO) { 900 useVideo = false; 901 } else if (typeof arguments[i] === 'object') { 902 constraints = arguments[i]; 903 } else if (typeof arguments[i] === 'function') { 904 cb = arguments[i]; 905 } 906 } 907 908 if (navigator.getUserMedia) { 909 var elt = document.createElement('video'); 910 911 if (!constraints) { 912 constraints = {video: useVideo, audio: useAudio}; 913 } 914 915 navigator.getUserMedia(constraints, function(stream) { 916 elt.src = window.URL.createObjectURL(stream); 917 elt.onloadedmetadata = function(e) { 918 elt.play(); 919 if (cb) { 920 cb(stream); 921 } 922 }; 923 }, function(e) { console.log(e); }); 924 } else { 925 throw 'getUserMedia not supported in this browser'; 926 } 927 var c = addElement(elt, this, true); 928 c.loadedmetadata = false; 929 // set width and height onload metadata 930 elt.addEventListener('loadedmetadata', function() { 931 c.width = elt.videoWidth; 932 c.height = elt.videoHeight; 933 c.loadedmetadata = true; 934 }); 935 return c; 936 }; 937 938 /** 939 * Creates element with given tag in the DOM with given content. 940 * Appends to the container node if one is specified, otherwise 941 * appends to body. 942 * 943 * @method createElement 944 * @param {String} tag tag for the new element 945 * @param {String} [content] html content to be inserted into the element 946 * @return {Object/p5.Element} pointer to p5.Element holding created node 947 * @example 948 * <div class='norender'><code> 949 * var h2 = createElement('h2','im an h2 p5.element!'); 950 * </code></div> 951 */ 952 p5.prototype.createElement = function(tag, content) { 953 var elt = document.createElement(tag); 954 if (typeof content !== 'undefined') { 955 elt.innerHTML = content; 956 } 957 return addElement(elt, this); 958 }; 959 960 961// ============================================================================= 962// p5.Element additions 963// ============================================================================= 964 /** 965 * 966 * Adds specified class to the element. 967 * 968 * @for p5.Element 969 * @method addClass 970 * @param {String} class name of class to add 971 * @return {Object/p5.Element} 972 * @example 973 * <div class='norender'><code> 974 * var div = createDiv('div'); 975 * div.addClass('myClass'); 976 * </code></div> 977 */ 978 p5.Element.prototype.addClass = function(c) { 979 if (this.elt.className) { 980 // PEND don't add class more than once 981 //var regex = new RegExp('[^a-zA-Z\d:]?'+c+'[^a-zA-Z\d:]?'); 982 //if (this.elt.className.search(/[^a-zA-Z\d:]?hi[^a-zA-Z\d:]?/) === -1) { 983 this.elt.className = this.elt.className+' '+c; 984 //} 985 } else { 986 this.elt.className = c; 987 } 988 return this; 989 } 990 991 /** 992 * 993 * Removes specified class from the element. 994 * 995 * @method removeClass 996 * @param {String} class name of class to remove 997 * @return {Object/p5.Element} 998 */ 999 p5.Element.prototype.removeClass = function(c) { 1000 var regex = new RegExp('(?:^|\\s)'+c+'(?!\\S)'); 1001 this.elt.className = this.elt.className.replace(regex, ''); 1002 this.elt.className = this.elt.className.replace(/^\s+|\s+$/g, ""); //prettify (optional) 1003 return this; 1004 } 1005 1006 /** 1007 * 1008 * Attaches the element as a child to the parent specified. 1009 * Accepts either a string ID, DOM node, or p5.Element. 1010 * If no argument is specified, an array of children DOM nodes is returned. 1011 * 1012 * @method child 1013 * @param {String|Object|p5.Element} [child] the ID, DOM node, or p5.Element 1014 * to add to the current element 1015 * @return {p5.Element} 1016 * @example 1017 * <div class='norender'><code> 1018 * var div0 = createDiv('this is the parent'); 1019 * var div1 = createDiv('this is the child'); 1020 * div0.child(div1); // use p5.Element 1021 * </code></div> 1022 * <div class='norender'><code> 1023 * var div0 = createDiv('this is the parent'); 1024 * var div1 = createDiv('this is the child'); 1025 * div1.id('apples'); 1026 * div0.child('apples'); // use id 1027 * </code></div> 1028 * <div class='norender'><code> 1029 * var div0 = createDiv('this is the parent'); 1030 * var elt = document.getElementById('myChildDiv'); 1031 * div0.child(elt); // use element from page 1032 * </code></div> 1033 */ 1034 p5.Element.prototype.child = function(c) { 1035 if (c === null){ 1036 return this.elt.childNodes 1037 } 1038 if (typeof c === 'string') { 1039 if (c[0] === '#') { 1040 c = c.substring(1); 1041 } 1042 c = document.getElementById(c); 1043 } else if (c instanceof p5.Element) { 1044 c = c.elt;
1045 } 1046 this.elt.appendChild(c); 1047 return this; 1048 }; 1049 1050 /** 1051 * Centers a p5 Element either vertically, horizontally, 1052 * or both, relative to its parent or according to 1053 * the body if the Element has no parent. If no argument is passed 1054 * the Element is aligned both vertically and horizontally. 1055 * 1056 * @param {String} align passing 'vertical', 'horizontal' aligns element accordingly 1057 * @return {Object/p5.Element} pointer to p5.Element 1058 * @example 1059 * <div><code> 1060 * function setup() { 1061 * var div = createDiv('').size(10,10); 1062 * div.style('background-color','orange'); 1063 * div.center(); 1064 * 1065 * } 1066 * </code></div> 1067 */ 1068 p5.Element.prototype.center = function(align) { 1069 var style = this.elt.style.display; 1070 var hidden = this.elt.style.display === 'none'; 1071 var parentHidden = this.parent().style.display === 'none'; 1072 var pos = { x : this.elt.offsetLeft, y : this.elt.offsetTop }; 1073 1074 if (hidden) this.show(); 1075 1076 this.elt.style.display = 'block'; 1077 this.position(0,0); 1078 1079 if (parentHidden) this.parent().style.display = 'block'; 1080 1081 var wOffset = Math.abs(this.parent().offsetWidth - this.elt.offsetWidth); 1082 var hOffset = Math.abs(this.parent().offsetHeight - this.elt.offsetHeight); 1083 var y = pos.y; 1084 var x = pos.x; 1085 1086 if (align === 'both' || align === undefined){ 1087 this.position(wOffset/2, hOffset/2); 1088 }else if (align === 'horizontal'){ 1089 this.position(wOffset/2, y); 1090 }else if (align === 'vertical'){ 1091 this.position(x, hOffset/2); 1092 } 1093 1094 this.style('display', style); 1095 1096 if (hidden) this.hide(); 1097 1098 if (parentHidden) this.parent().style.display = 'none'; 1099 1100 return this; 1101 }; 1102 1103 /** 1104 * 1105 * If an argument is given, sets the inner HTML of the element, 1106 * replacing any existing html. If no arguments are given, returns 1107 * the inner HTML of the element. 1108 * 1109 * @for p5.Element 1110 * @method html 1111 * @param {String} [html] the HTML to be placed inside the element 1112 * @return {Object/p5.Element|String} 1113 * @example 1114 * <div class='norender'><code> 1115 * var div = createDiv('').size(100,100); 1116 * div.style('background-color','orange'); 1117 * div.html('hi'); 1118 * </code></div> 1119 */ 1120 p5.Element.prototype.html = function(html) { 1121 if (typeof html !== 'undefined') { 1122 this.elt.innerHTML = html; 1123 return this; 1124 } else { 1125 return this.elt.innerHTML; 1126 } 1127 }; 1128 1129 /** 1130 * 1131 * Sets the position of the element relative to (0, 0) of the
1132 * window. Essentially, sets position:absolute and left and top 1133 * properties of style. If no arguments given returns the x and y position 1134 * of the element in an object. 1135 * 1136 * @method position 1137 * @param {Number} [x] x-position relative to upper left of window 1138 * @param {Number} [y] y-position relative to upper left of window 1139 * @return {Object/p5.Element} 1140 * @example 1141 * <div><code class='norender'> 1142 * function setup() { 1143 * var cnv = createCanvas(100, 100); 1144 * // positions canvas 50px to the right and 100px 1145 * // below upper left corner of the window 1146 * cnv.position(50, 100); 1147 * } 1148 * </code></div> 1149 */ 1150 p5.Element.prototype.position = function() { 1151 if (arguments.length === 0){ 1152 return { 'x' : this.elt.offsetLeft , 'y' : this.elt.offsetTop }; 1153 }else{ 1154 this.elt.style.position = 'absolute'; 1155 this.elt.style.left = arguments[0]+'px'; 1156 this.elt.style.top = arguments[1]+'px'; 1157 this.x = arguments[0]; 1158 this.y = arguments[1]; 1159 return this; 1160 } 1161 }; 1162 1163 /** 1164 * Translates an element with css transforms in either 2d (if 2 arguments given) 1165 * or 3d (if 3 arguments given) space. 1166 * @method translate 1167 * @param {Number} x x-position in px 1168 * @param {Number} y y-position in px 1169 * @param {Number} [z] z-position in px 1170 * @param {Number} [perspective] sets the perspective of the parent element in px, 1171 * default value set to 1000px 1172 * @return {Object/p5.Element} 1173 * @example 1174 * <div ><code class='norender'> 1175 * function setup() { 1176 * var cnv = createCanvas(100,100); 1177 * //translates canvas 50px down 1178 * cnv.translate(0,50); 1179 * } 1180 * </code></div> 1181 */ 1182 p5.Element.prototype.translate = function(){ 1183 this.elt.style.position = 'absolute'; 1184 // save out initial non-translate transform styling 1185 var transform = ''; 1186 if (this.elt.style.transform) { 1187 transform = this.elt.style.transform.replace(/translate3d\(.*\)/g, ''); 1188 transform = transform.replace(/translate[X-Z]?\(.*\)/g, ''); 1189 } 1190 if (arguments.length === 2) { 1191 this.elt.style.transform = 'translate('+arguments[0]+'px, '+arguments[1]+'px)'; 1192 } else if (arguments.length > 2) { 1193 this.elt.style.transform = 'translate3d('+arguments[0]+'px,'+arguments[1]+'px,'+arguments[2]+'px)'; 1194 if (arguments.length === 3) { 1195 this.elt.parentElement.style.perspective = '1000px'; 1196 } else { 1197 this.elt.parentElement.style.perspective = arguments[3]+'px'; 1198 } 1199 } 1200 // add any extra transform styling back on end 1201 this.elt.style.transform += transform; 1202 return this; 1203 }; 1204 1205 /** 1206 * Rotates an element with css transforms in either 2d (if 2 arguments given) 1207 * or 3d (if 3 arguments given) space. 1208 * @method rotate 1209 * @param {Number} x amount of degrees to rotate the element along the x-axis in deg 1210 * @param {Number} [y] amount of degrees to rotate the element along the y-axis in deg 1211 * @param {Number} [z] amount of degrees to rotate the element along the z-axis in deg 1212 * @return {Object/p5.Element} 1213 * @example 1214 * <div><code> 1215 * var x = 0, 1216 * y = 0, 1217 * z = 0; 1218 * 1219 * function draw(){ 1220 * x+=.5 % 360; 1221 * y+=.5 % 360; 1222 * z+=.5 % 360; 1223 * //rotates p5.js logo .5 degrees on every axis each frame. 1224 * select('canvas').rotate(x,y,z); 1225 * } 1226 * </code></div> 1227 */ 1228 p5.Element.prototype.rotate = function(){ 1229 // save out initial non-rotate transform styling 1230 var transform = ''; 1231 if (this.elt.style.transform) { 1232 var transform = this.elt.style.transform.replace(/rotate3d\(.*\)/g, ''); 1233 transform = transform.replace(/rotate[X-Z]?\(.*\)/g, ''); 1234 } 1235 1236 if (arguments.length === 1){ 1237 this.elt.style.transform = 'rotate('+arguments[0]+'deg)'; 1238 }else if (arguments.length === 2){ 1239 this.elt.style.transform = 'rotate('+arguments[0]+'deg, '+arguments[1]+'deg)'; 1240 }else if (arguments.length === 3){ 1241 this.elt.style.transform = 'rotateX('+arguments[0]+'deg)'; 1242 this.elt.style.transform += 'rotateY('+arguments[1]+'deg)'; 1243 this.elt.style.transform += 'rotateZ('+arguments[2]+'deg)'; 1244 } 1245 // add remaining transform back on 1246 this.elt.style.transform += transform; 1247 return this; 1248 }; 1249 1250 /** 1251 * Sets the given style (css) property (1st arg) of the element with the 1252 * given value (2nd arg). If a single argument is given, .style() 1253 * returns the value of the given property; however, if the single argument 1254 * is given in css syntax ('text-align:center'), .style() sets the css 1255 * appropriatly. .style() also handles 2d and 3d css transforms. If 1256 * the 1st arg is 'rotate', 'translate', or 'position', the following arguments 1257 * accept Numbers as values. ('translate', 10, 100, 50); 1258 * 1259 * @method style 1260 * @param {String} property property to be set 1261 * @param {String|Number|p5.Color} [value] value to assign to property 1262 * @param {String|Number} [value] value to assign to property (rotate/translate) 1263 * @param {String|Number} [value] value to assign to property (rotate/translate) 1264 * @param {String|Number} [value] value to assign to property (translate) 1265 * @return {String|Object/p5.Element} value of property, if no value is specified 1266 * or p5.Element 1267 * @example 1268 * <div><code class="norender"> 1269 * var myDiv = createDiv("I like pandas."); 1270 * myDiv.style("font-size", "18px"); 1271 * myDiv.style("color", "#ff0000"); 1272 * </code></div> 1273 * <div><code class="norender"> 1274 * var col = color(25,23,200,50); 1275 * var button = createButton("button"); 1276 * button.style("background-color", col); 1277 * button.position(10, 10); 1278 * </code></div> 1279 * <div><code class="norender"> 1280 * var myDiv = createDiv("I like lizards."); 1281 * myDiv.style("position", 20, 20); 1282 * myDiv.style("rotate", 45); 1283 * </code></div> 1284 * <div><code class="norender"> 1285 * var myDiv; 1286 * function setup() { 1287 * background(200); 1288 * myDiv = createDiv("I like gray."); 1289 * myDiv.position(20, 20); 1290 * } 1291 * 1292 * function draw() { 1293 * myDiv.style("font-size", mouseX+"px"); 1294 * } 1295 * </code></div> 1296 */ 1297 p5.Element.prototype.style = function(prop, val) { 1298 var self = this; 1299 1300 if (val instanceof p5.Color) { 1301 val = 'rgba(' + val.levels[0] + ',' + val.levels[1] + ',' + val.levels[2] + ',' + val.levels[3]/255 + ')' 1302 } 1303 1304 if (typeof val === 'undefined') { 1305 if (prop.indexOf(':') === -1) { 1306 var styles = window.getComputedStyle(self.elt); 1307 var style = styles.getPropertyValue(prop); 1308 return style; 1309 } else { 1310 var attrs = prop.split(';'); 1311 for (var i = 0; i < attrs.length; i++) { 1312 var parts = attrs[i].split(':'); 1313 if (parts[0] && parts[1]) { 1314 this.elt.style[parts[0].trim()] = parts[1].trim(); 1315 } 1316 } 1317 } 1318 } else { 1319 if (prop === 'rotate' || prop === 'translate' || prop === 'position'){ 1320 var trans = Array.prototype.shift.apply(arguments); 1321 this[trans].apply(this, arguments); 1322 } else { 1323 this.elt.style[prop] = val; 1324 if (prop === 'width' || prop === 'height' || prop === 'left' || prop === 'top') { 1325 var numVal = val.replace(/\D+/g, ''); 1326 this[prop] = parseInt(numVal, 10); // pend: is this necessary? 1327 } 1328 } 1329 } 1330 return this; 1331 }; 1332 1333 1334 /** 1335 *
1336 * Adds a new attribute or changes the value of an existing attribute 1337 * on the specified element. If no value is specified, returns the 1338 * value of the given attribute, or null if attribute is not set. 1339 * 1340 * @method attribute 1341 * @param {String} attr attribute to set 1342 * @param {String} [value] value to assign to attribute 1343 * @return {String|Object/p5.Element} value of attribute, if no value is 1344 * specified or p5.Element 1345 * @example 1346 * <div class="norender"><code> 1347 * var myDiv = createDiv("I like pandas."); 1348 *myDiv.attribute("align", "center"); 1349 * </code></div> 1350 */ 1351 p5.Element.prototype.attribute = function(attr, value) { 1352 if (typeof value === 'undefined') { 1353 return this.elt.getAttribute(attr); 1354 } else { 1355 this.elt.setAttribute(attr, value); 1356 return this; 1357 } 1358 }; 1359 1360 1361 /** 1362 * Either returns the value of the element if no arguments 1363 * given, or sets the value of the element. 1364 * 1365 * @method value 1366 * @param {String|Number} [value] 1367 * @return {String|Object/p5.Element} value of element if no value is specified or p5.Element 1368 */ 1369 p5.Element.prototype.value = function() { 1370 if (arguments.length > 0) { 1371 this.elt.value = arguments[0]; 1372 return this; 1373 } else { 1374 if (this.elt.type === 'range') { 1375 return parseFloat(this.elt.value); 1376 } 1377 else return this.elt.value; 1378 } 1379 }; 1380 1381 /** 1382 * 1383 * Shows the current element. Essentially, setting display:block for the style. 1384 * 1385 * @method show 1386 * @return {Object/p5.Element} 1387 */ 1388 p5.Element.prototype.show = function() { 1389 this.elt.style.display = 'block'; 1390 return this; 1391 }; 1392 1393 /** 1394 * Hides the current element. Essentially, setting display:none for the style. 1395 * 1396 * @method hide 1397 * @return {Object/p5.Element} 1398 */ 1399 p5.Element.prototype.hide = function() { 1400 this.elt.style.display = 'none'; 1401 return this; 1402 }; 1403 1404 /** 1405 * 1406 * Sets the width and height of the element. AUTO can be used to 1407 * only adjust one dimension. If no arguments given returns the width and height 1408 * of the element in an object. 1409 * 1410 * @method size 1411 * @param {Number} [w] width of the element 1412 * @param {Number} [h] height of the element 1413 * @return {Object/p5.Element} 1414 */ 1415 p5.Element.prototype.size = function(w, h) { 1416 if (arguments.length === 0){ 1417 return { 'width' : this.elt.offsetWidth , 'height' : this.elt.offsetHeight }; 1418 }else{ 1419 var aW = w; 1420 var aH = h; 1421 var AUTO = p5.prototype.AUTO; 1422 if (aW !== AUTO || aH !== AUTO) { 1423 if (aW === AUTO) { 1424 aW = h * this.width / this.height; 1425 } else if (aH === AUTO) { 1426 aH = w * this.height / this.width; 1427 } 1428 // set diff for cnv vs normal div 1429 if (this.elt instanceof HTMLCanvasElement) { 1430 var j = {}; 1431 var k = this.elt.getContext('2d'); 1432 for (var prop in k) { 1433 j[prop] = k[prop]; 1434 } 1435 this.elt.setAttribute('width', aW * this._pInst._pixelDensity); 1436 this.elt.setAttribute('height', aH * this._pInst._pixelDensity); 1437 this.elt.setAttribute('style', 'width:' + aW + 'px; height:' + aH + 'px'); 1438 this._pInst.scale(this._pInst._pixelDensity, this._pInst._pixelDensity); 1439 for (var prop in j) { 1440 this.elt.getContext('2d')[prop] = j[prop]; 1441 } 1442 } else { 1443 this.elt.style.width = aW+'px'; 1444 this.elt.style.height = aH+'px'; 1445 this.elt.width = aW; 1446 this.elt.height = aH; 1447 this.width = aW; 1448 this.height = aH; 1449 } 1450 1451 this.width = this.elt.offsetWidth; 1452 this.height = this.elt.offsetHeight; 1453 1454 if (this._pInst) { // main canvas associated with p5 instance 1455 if (this._pInst._curElement.elt === this.elt) { 1456 this._pInst._setProperty('width', this.elt.offsetWidth); 1457 this._pInst._setProperty('height', this.elt.offsetHeight); 1458 } 1459 } 1460 } 1461 return this; 1462 } 1463 }; 1464 1465 /** 1466 * Removes the element and deregisters all listeners. 1467 * @method remove 1468 * @example 1469 * <div class='norender'><code> 1470 * var myDiv = createDiv('this is some text'); 1471 * myDiv.remove(); 1472 * </code></div> 1473 */ 1474 p5.Element.prototype.remove = function() { 1475 // deregister events 1476 for (var ev in this._events) { 1477 this.elt.removeEventListener(ev, this._events[ev]); 1478 } 1479 if (this.elt.parentNode) { 1480 this.elt.parentNode.removeChild(this.elt); 1481 } 1482 delete(this); 1483 }; 1484 1485 1486 1487// ============================================================================= 1488// p5.MediaElement additions 1489// ============================================================================= 1490 1491 1492 /** 1493 * Extends p5.Element to handle audio and video. In addition to the methods 1494 * of p5.Element, it also contains methods for controlling media. It is not 1495 * called directly, but p5.MediaElements are created by calling createVideo, 1496 * createAudio, and createCapture. 1497 * 1498 * @class p5.MediaElement 1499 * @constructor 1500 * @param {String} elt DOM node that is wrapped 1501 * @param {Object} [pInst] pointer to p5 instance 1502 */ 1503 p5.MediaElement = function(elt, pInst) { 1504 p5.Element.call(this, elt, pInst); 1505 1506 var self = this; 1507 this.elt.crossOrigin = 'anonymous'; 1508 1509 this._prevTime = 0; 1510 this._cueIDCounter = 0; 1511 this._cues = []; 1512 this._pixelDensity = 1; 1513 1514 /** 1515 * Path to the media element source. 1516 * 1517 * @property src 1518 * @return {String} src 1519 */ 1520 Object.defineProperty(self, 'src', { 1521 get: function() { 1522 var firstChildSrc = self.elt.children[0].src; 1523 var srcVal = self.elt.src === window.location.href ? '' : self.elt.src; 1524 var ret = firstChildSrc === window.location.href ? srcVal : firstChildSrc; 1525 return ret; 1526 }, 1527 set: function(newValue) { 1528 for (var i = 0; i < self.elt.children.length; i++) { 1529 self.elt.removeChild(self.elt.children[i]); 1530 } 1531 var source = document.createElement('source'); 1532 source.src = newValue; 1533 elt.appendChild(source); 1534 self.elt.src = newValue; 1535 }, 1536 }); 1537 1538 // private _onended callback, set by the method: onended(callback) 1539 self._onended = function() {};
1540 self.elt.onended = function() { 1541 self._onended(self); 1542 } 1543 }; 1544 p5.MediaElement.prototype = Object.create(p5.Element.prototype); 1545 1546 1547 1548 1549 /** 1550 * Play an HTML5 media element. 1551 * 1552 * @method play 1553 * @return {Object/p5.Element} 1554 */ 1555 p5.MediaElement.prototype.play = function() { 1556 if (this.elt.currentTime === this.elt.duration) { 1557 this.elt.currentTime = 0; 1558 } 1559 1560 if (this.elt.readyState > 1) { 1561 this.elt.play(); 1562 } else { 1563 // in Chrome, playback cannot resume after being stopped and must reload 1564 this.elt.load(); 1565 this.elt.play(); 1566 } 1567 return this; 1568 }; 1569 1570 /** 1571 * Stops an HTML5 media element (sets current time to zero). 1572 * 1573 * @method stop 1574 * @return {Object/p5.Element} 1575 */ 1576 p5.MediaElement.prototype.stop = function() { 1577 this.elt.pause(); 1578 this.elt.currentTime = 0; 1579 return this; 1580 }; 1581 1582 /** 1583 * Pauses an HTML5 media element. 1584 * 1585 * @method pause 1586 * @return {Object/p5.Element} 1587 */ 1588 p5.MediaElement.prototype.pause = function() { 1589 this.elt.pause(); 1590 return this; 1591 }; 1592 1593 /** 1594 * Set 'loop' to true for an HTML5 media element, and starts playing. 1595 * 1596 * @method loop 1597 * @return {Object/p5.Element} 1598 */ 1599 p5.MediaElement.prototype.loop = function() { 1600 this.elt.setAttribute('loop', true); 1601 this.play(); 1602 return this; 1603 }; 1604 /** 1605 * Set 'loop' to false for an HTML5 media element. Element will stop 1606 * when it reaches the end. 1607 * 1608 * @method noLoop 1609 * @return {Object/p5.Element} 1610 */ 1611 p5.MediaElement.prototype.noLoop = function() { 1612 this.elt.setAttribute('loop', false); 1613 return this; 1614 }; 1615 1616 1617 /** 1618 * Set HTML5 media element to autoplay or not. 1619 * 1620 * @method autoplay 1621 * @param {Boolean} autoplay whether the element should autoplay 1622 * @return {Object/p5.Element} 1623 */ 1624 p5.MediaElement.prototype.autoplay = function(val) { 1625 this.elt.setAttribute('autoplay', val); 1626 return this; 1627 }; 1628 1629 /** 1630 * Sets volume for this HTML5 media element. If no argument is given, 1631 * returns the current volume. 1632 * 1633 * @param {Number} [val] volume between 0.0 and 1.0 1634 * @return {Number|p5.MediaElement} current volume or p5.MediaElement 1635 * @method volume 1636 */ 1637 p5.MediaElement.prototype.volume = function(val) { 1638 if (typeof val === 'undefined') { 1639 return this.elt.volume; 1640 } else { 1641 this.elt.volume = val; 1642 } 1643 }; 1644 1645 /** 1646 * If no arguments are given, returns the current time of the element. 1647 * If an argument is given the current time of the element is set to it. 1648 * 1649 * @method time 1650 * @param {Number} [time] time to jump to (in seconds) 1651 * @return {Number|Object/p5.MediaElement} current time (in seconds) 1652 * or p5.MediaElement 1653 */ 1654 p5.MediaElement.prototype.time = function(val) { 1655 if (typeof val === 'undefined') { 1656 return this.elt.currentTime; 1657 } else { 1658 this.elt.currentTime = val; 1659 } 1660 }; 1661 1662 /** 1663 * Returns the duration of the HTML5 media element. 1664 * 1665 * @method duration 1666 * @return {Number} duration 1667 */ 1668 p5.MediaElement.prototype.duration = function() { 1669 return this.elt.duration; 1670 }; 1671 p5.MediaElement.prototype.pixels = []; 1672 p5.MediaElement.prototype.loadPixels = function() { 1673 if (this.loadedmetadata) { // wait for metadata for w/h 1674 if (!this.canvas) { 1675 this.canvas = document.createElement('canvas'); 1676 this.drawingContext = this.canvas.getContext('2d'); 1677 } 1678 if (this.canvas.width !== this.elt.width) { 1679 this.canvas.width = this.elt.videoWidth; 1680 this.canvas.height = this.elt.videoHeight; 1681 this.width = this.canvas.width; 1682 this.height = this.canvas.height; 1683 } 1684 this.drawingContext.drawImage(this.elt, 0, 0, this.canvas.width, this.canvas.height); 1685 p5.Renderer2D.prototype.loadPixels.call(this); 1686 } 1687 return this; 1688 } 1689 p5.MediaElement.prototype.updatePixels = function(x, y, w, h){ 1690 if (this.loadedmetadata) { // wait for metadata 1691 p5.Renderer2D.prototype.updatePixels.call(this, x, y, w, h); 1692 } 1693 return this; 1694 } 1695 p5.MediaElement.prototype.get = function(x, y, w, h){ 1696 if (this.loadedmetadata) { // wait for metadata 1697 return p5.Renderer2D.prototype.get.call(this, x, y, w, h); 1698 } else return [0, 0, 0, 255]; 1699 }; 1700 p5.MediaElement.prototype.set = function(x, y, imgOrCol){ 1701 if (this.loadedmetadata) { // wait for metadata 1702 p5.Renderer2D.prototype.set.call(this, x, y, imgOrCol); 1703 } 1704 }; 1705 /** 1706 * Schedule an event to be called when the audio or video 1707 * element reaches the end. If the element is looping, 1708 * this will not be called. The element is passed in 1709 * as the argument to the onended callback. 1710 * 1711 * @method onended 1712 * @param {Function} callback function to call when the 1713 * soundfile has ended. The 1714 * media element will be passed 1715 * in as the argument to the 1716 * callback. 1717 * @return {Object/p5.MediaElement} 1718 * @example 1719 * <div><code> 1720 * function setup() { 1721 * audioEl = createAudio('assets/beat.mp3'); 1722 * audioEl.showControls(true); 1723 * audioEl.onended(sayDone); 1724 * } 1725 * 1726 * function sayDone(elt) { 1727 * alert('done playing ' + elt.src ); 1728 * } 1729 * </code></div> 1730 */ 1731 p5.MediaElement.prototype.onended = function(callback) { 1732 this._onended = callback; 1733 return this; 1734 }; 1735 1736 1737 /*** CONNECT TO WEB AUDIO API / p5.sound.js ***/ 1738 1739 /** 1740 * Send the audio output of this element to a specified audioNode or 1741 * p5.sound object. If no element is provided, connects to p5's master 1742 * output. That connection is established when this method is first called. 1743 * All connections are removed by the .disconnect() method. 1744 * 1745 * This method is meant to be used with the p5.sound.js addon library. 1746 * 1747 * @method connect 1748 * @param {AudioNode|p5.sound object} audioNode AudioNode from the Web Audio API, 1749 * or an object from the p5.sound library 1750 */ 1751 p5.MediaElement.prototype.connect = function(obj) { 1752 var audioContext, masterOutput; 1753 1754 // if p5.sound exists, same audio context 1755 if (typeof p5.prototype.getAudioContext === 'function') { 1756 audioContext = p5.prototype.getAudioContext(); 1757 masterOutput = p5.soundOut.input; 1758 } else {
1759 try { 1760 audioContext = obj.context; 1761 masterOutput = audioContext.destination 1762 } catch(e) { 1763 throw 'connect() is meant to be used with Web Audio API or p5.sound.js' 1764 } 1765 } 1766 1767 // create a Web Audio MediaElementAudioSourceNode if none already exists 1768 if (!this.audioSourceNode) { 1769 this.audioSourceNode = audioContext.createMediaElementSource(this.elt); 1770 1771 // connect to master output when this method is first called 1772 this.audioSourceNode.connect(masterOutput); 1773 } 1774 1775 // connect to object if provided 1776 if (obj) { 1777 if (obj.input) { 1778 this.audioSourceNode.connect(obj.input); 1779 } else { 1780 this.audioSourceNode.connect(obj); 1781 } 1782 } 1783 1784 // otherwise connect to master output of p5.sound / AudioContext 1785 else { 1786 this.audioSourceNode.connect(masterOutput); 1787 } 1788 1789 }; 1790 1791 /** 1792 * Disconnect all Web Audio routing, including to master output. 1793 * This is useful if you want to re-route the output through 1794 * audio effects, for example. 1795 * 1796 * @method disconnect 1797 */ 1798 p5.MediaElement.prototype.disconnect = function() { 1799 if (this.audioSourceNode) { 1800 this.audioSourceNode.disconnect(); 1801 } else { 1802 throw 'nothing to disconnect'; 1803 } 1804 }; 1805 1806 1807 /*** SHOW / HIDE CONTROLS ***/ 1808 1809 /** 1810 * Show the default MediaElement controls, as determined by the web browser. 1811 * 1812 * @method showControls 1813 */ 1814 p5.MediaElement.prototype.showControls = function() { 1815 // must set style for the element to show on the page 1816 this.elt.style['text-align'] = 'inherit'; 1817 this.elt.controls = true; 1818 }; 1819 1820 /** 1821 * Hide the default mediaElement controls. 1822 * 1823 * @method hideControls 1824 */ 1825 p5.MediaElement.prototype.hideControls = function() { 1826 this.elt.controls = false; 1827 }; 1828 1829 /*** SCHEDULE EVENTS ***/ 1830 1831 /** 1832 * Schedule events to trigger every time a MediaElement 1833 * (audio/video) reaches a playback cue point. 1834 * 1835 * Accepts a callback function, a time (in seconds) at which to trigger 1836 * the callback, and an optional parameter for the callback. 1837 * 1838 * Time will be passed as the first parameter to the callback function, 1839 * and param will be the second parameter. 1840 * 1841 * 1842 * @method addCue 1843 * @param {Number} time Time in seconds, relative to this media 1844 * element's playback. For example, to trigger 1845 * an event every time playback reaches two 1846 * seconds, pass in the number 2. This will be 1847 * passed as the first parameter to 1848 * the callback function. 1849 * @param {Function} callback Name of a function that will be 1850 * called at the given time. The callback will 1851 * receive time and (optionally) param as its 1852 * two parameters. 1853 * @param {Object} [value] An object to be passed as the 1854 * second parameter to the 1855 * callback function. 1856 * @return {Number} id ID of this cue, 1857 * useful for removeCue(id) 1858 * @example 1859 * <div><code> 1860 * function setup() { 1861 * background(255,255,255); 1862 * 1863 * audioEl = createAudio('assets/beat.mp3'); 1864 * audioEl.showControls(); 1865 * 1866 * // schedule three calls to changeBackground 1867 * audioEl.addCue(0.5, changeBackground, color(255,0,0) ); 1868 * audioEl.addCue(1.0, changeBackground, color(0,255,0) ); 1869 * audioEl.addCue(2.5, changeBackground, color(0,0,255) ); 1870 * audioEl.addCue(3.0, changeBackground, color(0,255,255) ); 1871 * audioEl.addCue(4.2, changeBackground, color(255,255,0) ); 1872 * audioEl.addCue(5.0, changeBackground, color(255,255,0) ); 1873 * } 1874 * 1875 * function changeBackground(val) { 1876 * background(val); 1877 * } 1878 * </code></div> 1879 */ 1880 p5.MediaElement.prototype.addCue = function(time, callback, val) { 1881 var id = this._cueIDCounter++; 1882 1883 var cue = new Cue(callback, time, id, val); 1884 this._cues.push(cue); 1885 1886 if (!this.elt.ontimeupdate) { 1887 this.elt.ontimeupdate = this._onTimeUpdate.bind(this); 1888 } 1889 1890 return id; 1891 }; 1892 1893 /** 1894 * Remove a callback based on its ID. The ID is returned by the 1895 * addCue method. 1896 * 1897 * @method removeCue 1898 * @param {Number} id ID of the cue, as returned by addCue 1899 */ 1900 p5.MediaElement.prototype.removeCue = function(id) { 1901 for (var i = 0; i < this._cues.length; i++) { 1902 var cue = this._cues[i]; 1903 if (cue.id === id) { 1904 this.cues.splice(i, 1); 1905 } 1906 } 1907 1908 if (this._cues.length === 0) { 1909 this.elt.ontimeupdate = null 1910 } 1911 }; 1912 1913 /** 1914 * Remove all of the callbacks that had originally been scheduled 1915 * via the addCue method. 1916 * 1917 * @method clearCues 1918 */ 1919 p5.MediaElement.prototype.clearCues = function() { 1920 this._cues = []; 1921 this.elt.ontimeupdate = null; 1922 }; 1923 1924 // private method that checks for cues to be fired if events 1925 // have been scheduled using addCue(callback, time).
1926 p5.MediaElement.prototype._onTimeUpdate = function() { 1927 var playbackTime = this.time(); 1928 1929 for (var i = 0 ; i < this._cues.length; i++) { 1930 var callbackTime = this._cues[i].time; 1931 var val = this._cues[i].val; 1932 1933 1934 if (this._prevTime < callbackTime && callbackTime <= playbackTime) { 1935 1936 // pass the scheduled callbackTime as parameter to the callback 1937 this._cues[i].callback(val); 1938 } 1939 1940 } 1941 1942 this._prevTime = playbackTime; 1943 }; 1944 1945 1946 // Cue inspired by JavaScript setTimeout, and the 1947 // Tone.js Transport Timeline Event, MIT License Yotam Mann 2015 tonejs.org 1948 var Cue = function(callback, time, id, val) { 1949 this.callback = callback; 1950 this.time = time; 1951 this.id = id; 1952 this.val = val; 1953 }; 1954 1955// ============================================================================= 1956// p5.File 1957// ============================================================================= 1958 1959 1960 /** 1961 * Base class for a file 1962 * Using this for createFileInput 1963 * 1964 * @class p5.File 1965 * @constructor 1966 * @param {File} file File that is wrapped 1967 * @param {Object} [pInst] pointer to p5 instance 1968 */ 1969 p5.File = function(file, pInst) { 1970 /** 1971 * Underlying File object. All normal File methods can be called on this. 1972 * 1973 * @property file 1974 */ 1975 this.file = file; 1976 1977 this._pInst = pInst; 1978 1979 // Splitting out the file type into two components 1980 // This makes determining if image or text etc simpler 1981 var typeList = file.type.split('/'); 1982 /** 1983 * File type (image, text, etc.) 1984 * 1985 * @property type 1986 */ 1987 this.type = typeList[0]; 1988 /** 1989 * File subtype (usually the file extension jpg, png, xml, etc.) 1990 * 1991 * @property subtype 1992 */ 1993 this.subtype = typeList[1]; 1994 /** 1995 * File name 1996 * 1997 * @property name 1998 */ 1999 this.name = file.name; 2000 /** 2001 * File size 2002 * 2003 * @property size 2004 */ 2005 this.size = file.size; 2006 2007 /** 2008 * URL string containing image data. 2009 * 2010 * @property data 2011 */ 2012 this.data = undefined; 2013 }; 2014 2015}));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.