PageSourceSearch

http://kelseyrogers.github.io/VizualizationTech/Project%203%20-%20Attractors:Repellers/libraries/p5.dom.js

js kelseyrogers.github.io collected 2026-10-03 09:27:11 UTC 63,473 bytes, 2,015 lines download raw bytes

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 &lt;div&gt;&lt;/div&gt; 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 &lt;p&gt;&lt;/p&gt; 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 &lt;span&gt;&lt;/span&gt; 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 &lt;img /&gt; 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 &lt;a&gt;&lt;/a&gt; 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 &lt;input&gt;&lt;/input&gt; 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 &lt;button&gt;&lt;/button&gt; 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 &lt;input&gt;&lt;/input&gt; 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 &lt;select&gt;&lt;/select&gt; 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 &lt;input&gt;&lt;/input&gt; 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 &lt;input&gt;&lt;/input&gt; 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 &lt;input&gt;&lt;/input&gt; 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 &lt;video&gt; 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 &lt;audio&gt; 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 &lt;video&gt; 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.