You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

881 lines
28 KiB

7 years ago
  1. /*! p5.dom.js v0.1.5 November 7, 2014 */
  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/lmccart/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/lmccart/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. var p5DOM = (function(){
  28. // =============================================================================
  29. // p5 additions
  30. // =============================================================================
  31. /**
  32. * Searches the page for an element with given ID and returns it as
  33. * a p5.Element. The DOM node itself can be accessed with .elt.
  34. * Returns null if none found.
  35. *
  36. * @method getElement
  37. * @param {String} id id of element to search for
  38. * @return {Object/p5.Element|Null} p5.Element containing node found
  39. */
  40. p5.prototype.getElement = function (e) {
  41. var res = document.getElementById(e);
  42. if (res) {
  43. return new p5.Element(res);
  44. } else {
  45. return null;
  46. }
  47. };
  48. /**
  49. * Searches the page for elements with given class and returns an
  50. * array of p5.Elements. The DOM nodes themselves can be accessed
  51. * with .elt. Returns an empty array if none found.
  52. *
  53. * @method getElements
  54. * @param {String} class class name of elements to search for
  55. * @return {Array} array of p5.Element wrapped nodes found
  56. */
  57. p5.prototype.getElements = function (e) {
  58. var arr = [];
  59. var res = document.getElementsByClassName(e);
  60. if (res) {
  61. for (var j = 0; j < res.length; j++) {
  62. var obj = new p5.Element(res[j]);
  63. arr.push(obj);
  64. }
  65. }
  66. return arr;
  67. };
  68. /**
  69. * Removes all elements created by p5, except any canvas / graphics
  70. * elements created by createCanvas or createGraphics.
  71. * Event handlers are removed, and element is removed from the DOM.
  72. * @method removeElements
  73. * @example
  74. * <div class='norender'><code>
  75. * function setup() {
  76. * createCanvas(100, 100);
  77. * createDiv('this is some text');
  78. * createP('this is a paragraph');
  79. * }
  80. * function mousePressed() {
  81. * removeElements(); // this will remove the div and p, not canvas
  82. * }
  83. * </code></div>
  84. *
  85. */
  86. p5.prototype.removeElements = function (e) {
  87. for (var i=0; i<this._elements.length; i++) {
  88. if (!(this._elements[i].elt instanceof HTMLCanvasElement)) {
  89. this._elements[i].remove();
  90. }
  91. }
  92. };
  93. /**
  94. * Helpers for create methods.
  95. */
  96. function addElement(elt, pInst, media) {
  97. var node = pInst._userNode ? pInst._userNode : document.body;
  98. node.appendChild(elt);
  99. var c = media ? new p5.MediaElement(elt) : new p5.Element(elt);
  100. pInst._elements.push(c);
  101. return c;
  102. }
  103. /**
  104. * Creates a &lt;div&gt;&lt;/div&gt; element in the DOM with given inner HTML.
  105. * Appends to the container node if one is specified, otherwise
  106. * appends to body.
  107. *
  108. * @method createDiv
  109. * @param {String} html inner HTML for element created
  110. * @return {Object/p5.Element} pointer to p5.Element holding created
  111. * node
  112. */
  113. /**
  114. * Creates a &lt;p&gt;&lt;/p&gt; element in the DOM with given inner HTML. Used
  115. * for paragraph length text.
  116. * Appends to the container node if one is specified, otherwise
  117. * appends to body.
  118. *
  119. * @method createP
  120. * @param {String} html inner HTML for element created
  121. * @return {Object/p5.Element} pointer to p5.Element holding created
  122. * node
  123. */
  124. /**
  125. * Creates a &lt;span&gt;&lt;/span&gt; element in the DOM with given inner HTML.
  126. * Appends to the container node if one is specified, otherwise
  127. * appends to body.
  128. *
  129. * @method createSpan
  130. * @param {String} html inner HTML for element created
  131. * @return {Object/p5.Element} pointer to p5.Element holding created
  132. * node
  133. */
  134. var tags = ['div', 'p', 'span'];
  135. tags.forEach(function(tag) {
  136. var method = 'create' + tag.charAt(0).toUpperCase() + tag.slice(1);
  137. p5.prototype[method] = function(html) {
  138. var elt = document.createElement(tag);
  139. elt.innerHTML = html;
  140. return addElement(elt, this);
  141. }
  142. });
  143. /**
  144. * Creates an &lt;img /&gt; element in the DOM with given src and
  145. * alternate text.
  146. * Appends to the container node if one is specified, otherwise
  147. * appends to body.
  148. *
  149. * @method createImg
  150. * @param {String} src src path or url for image
  151. * @param {String} alt alternate text to be used if image does not
  152. * load
  153. * @return {Object/p5.Element} pointer to p5.Element holding created
  154. * node
  155. */
  156. p5.prototype.createImg = function(src, alt) {
  157. var elt = document.createElement('img');
  158. elt.src = src;
  159. if (typeof alt !== 'undefined') {
  160. elt.alt = alt;
  161. }
  162. return addElement(elt, this);
  163. };
  164. /**
  165. * Creates an &lt;a&gt;&lt;/a&gt; element in the DOM for including a hyperlink.
  166. * Appends to the container node if one is specified, otherwise
  167. * appends to body.
  168. *
  169. * @method createA
  170. * @param {String} href url of page to link to
  171. * @param {String} html inner html of link element to display
  172. * @param {String} [target] target where new link should open,
  173. * could be _blank, _self, _parent, _top.
  174. * @return {Object/p5.Element} pointer to p5.Element holding created
  175. * node
  176. */
  177. p5.prototype.createA = function(href, html, target) {
  178. var elt = document.createElement('a');
  179. elt.href = href;
  180. elt.innerHTML = html;
  181. if (target) elt.target = target;
  182. return addElement(elt, this);
  183. };
  184. /** INPUT **/
  185. /**
  186. * Creates a slider &lt;input&gt;&lt;/input&gt; element in the DOM.
  187. * Use .size() to set the display length of the slider.
  188. * Appends to the container node if one is specified, otherwise
  189. * appends to body.
  190. *
  191. * @method createSlider
  192. * @param {Number} min minimum value of the slider
  193. * @param {Number} max maximum value of the slider
  194. * @param {Number} [value] default value of the slider
  195. * @return {Object/p5.Element} pointer to p5.Element holding created
  196. * node
  197. */
  198. p5.prototype.createSlider = function(min, max, value) {
  199. var elt = document.createElement('input');
  200. elt.type = 'range';
  201. elt.min = min;
  202. elt.max = max;
  203. if (value) elt.value = value;
  204. return addElement(elt, this);
  205. };
  206. /**
  207. * Creates a &lt;button&gt;&lt;/button&gt; element in the DOM.
  208. * Use .size() to set the display size of the button.
  209. * Use .mousePressed() to specify behavior on press.
  210. * Appends to the container node if one is specified, otherwise
  211. * appends to body.
  212. *
  213. * @method createButton
  214. * @param {String} label label displayed on the button
  215. * @param {String} [value] value of the button
  216. * @return {Object/p5.Element} pointer to p5.Element holding created
  217. * node
  218. */
  219. p5.prototype.createButton = function(label, value) {
  220. var elt = document.createElement('button');
  221. elt.innerHTML = label;
  222. elt.value = value;
  223. if (value) elt.value = value;
  224. return addElement(elt, this);
  225. };
  226. /**
  227. * Creates an &lt;input&gt;&lt;/input&gt; element in the DOM for text input.
  228. * Use .size() to set the display length of the box.
  229. * Appends to the container node if one is specified, otherwise
  230. * appends to body.
  231. *
  232. * @method createInput
  233. * @param {Number} [value] default value of the input box
  234. * @return {Object/p5.Element} pointer to p5.Element holding created
  235. * node
  236. */
  237. p5.prototype.createInput = function(value) {
  238. var elt = document.createElement('input');
  239. elt.type = 'text';
  240. if (value) elt.value = value;
  241. return addElement(elt, this);
  242. };
  243. /** VIDEO STUFF **/
  244. function createMedia(pInst, type, src, callback) {
  245. var elt = document.createElement(type);
  246. if (typeof src === 'string') {
  247. src = [src];
  248. }
  249. for (var i=0; i<src.length; i++) {
  250. var source = document.createElement('source');
  251. source.src = src[i];
  252. elt.appendChild(source);
  253. }
  254. if (typeof callback !== 'undefined') {
  255. elt.addEventListener('canplaythrough', function() {
  256. callback();
  257. });
  258. }
  259. var c = addElement(elt, pInst, true);
  260. c.loadedmetadata = false;
  261. // set width and height onload metadata
  262. elt.addEventListener('loadedmetadata', function() {
  263. c.width = elt.videoWidth;
  264. c.height = elt.videoHeight;
  265. c.loadedmetadata = true;
  266. });
  267. return c;
  268. }
  269. /**
  270. * Creates an HTML5 &lt;video&gt; element in the DOM for simple playback
  271. * of audio/video. Shown by default, can be hidden with .hide()
  272. * and drawn into canvas using video(). Appends to the container
  273. * node if one is specified, otherwise appends to body. The first parameter
  274. * can be either a single string path to a video file, or an array of string
  275. * paths to different formats of the same video. This is useful for ensuring
  276. * that your video can play across different browsers, as each supports
  277. * different formats. See <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Supported_media_formats">this
  278. * page for further information about supported formats.
  279. *
  280. * @method createVideo
  281. * @param {String|Array} src path to a video file, or array of paths for
  282. * supporting different browsers
  283. * @param {Object} [callback] callback function to be called upon
  284. * 'canplaythrough' event fire, that is, when the
  285. * browser can play the media, and estimates that
  286. * enough data has been loaded to play the media
  287. * up to its end without having to stop for
  288. * further buffering of content
  289. * @return {Object/p5.Element} pointer to video p5.Element
  290. */
  291. p5.prototype.createVideo = function(src, callback) {
  292. return createMedia(this, 'video', src, callback);
  293. };
  294. /** AUDIO STUFF **/
  295. /**
  296. * Creates a hidden HTML5 &lt;audio&gt; element in the DOM for simple audio
  297. * playback. Appends to the container node if one is specified,
  298. * otherwise appends to body. The first parameter
  299. * can be either a single string path to a audio file, or an array of string
  300. * paths to different formats of the same audio. This is useful for ensuring
  301. * that your audio can play across different browsers, as each supports
  302. * different formats. See <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Supported_media_formats">this
  303. * page for further information about supported formats.
  304. *
  305. * @method createAudio
  306. * @param {String|Array} src path to an audio file, or array of paths for
  307. * supporting different browsers
  308. * @param {Object} [callback] callback function to be called upon
  309. * 'canplaythrough' event fire, that is, when the
  310. * browser can play the media, and estimates that
  311. * enough data has been loaded to play the media
  312. * up to its end without having to stop for
  313. * further buffering of content
  314. * @return {Object/p5.Element} pointer to audio p5.Element
  315. */
  316. p5.prototype.createAudio = function(src, callback) {
  317. return createMedia(this, 'audio', src, callback);
  318. };
  319. /** CAMERA STUFF **/
  320. p5.prototype.VIDEO = 'video';
  321. p5.prototype.AUDIO = 'audio';
  322. navigator.getUserMedia = navigator.getUserMedia ||
  323. navigator.webkitGetUserMedia ||
  324. navigator.mozGetUserMedia ||
  325. navigator.msGetUserMedia;
  326. /**
  327. * Creates a new &lt;video&gt; element that contains the audio/video feed
  328. * from a webcam. This can be drawn onto the canvas using video().
  329. *
  330. * @method createCapture
  331. * @param {String/Constant} type type of capture, either VIDEO or
  332. * AUDIO if none specified, default both
  333. * @return {Object/p5.Element} capture video p5.Element
  334. */
  335. p5.prototype.createCapture = function(type) {
  336. var useVideo, useAudio;
  337. if (!type) {
  338. useVideo = true;
  339. useAudio = true;
  340. } else if (type === p5.prototype.VIDEO) {
  341. useVideo = true;
  342. } else if (type === p5.prototype.AUDIO) {
  343. useAudio = true;
  344. }
  345. if (navigator.getUserMedia) {
  346. var elt = document.createElement('video');
  347. navigator.getUserMedia({video: useVideo, audio: useAudio}, function(stream) {
  348. elt.src = window.URL.createObjectURL(stream);
  349. elt.play();
  350. }, function(e) { console.log(e); });
  351. } else {
  352. throw 'getUserMedia not supported in this browser';
  353. }
  354. return addElement(elt, this);
  355. };
  356. /**
  357. * Creates element with given tag in the DOM with given content.
  358. * Appends to the container node if one is specified, otherwise
  359. * appends to body.
  360. *
  361. * @method createElement
  362. * @param {String} tag tag for the new element
  363. * @param {String} [content] html content to be inserted into the element
  364. * @return {Object/p5.Element} pointer to p5.Element holding created
  365. * node
  366. */
  367. p5.prototype.createElement = function(tag, content) {
  368. var elt = document.createElement(tag);
  369. if (typeof content !== 'undefined') {
  370. elt.innerHTML = content;
  371. }
  372. return addElement(elt, this);
  373. };
  374. // =============================================================================
  375. // p5.Element additions
  376. // =============================================================================
  377. /**
  378. *
  379. * Adds specified class to the element.
  380. *
  381. * @for p5.Element
  382. * @method addClass
  383. * @param {String} class name of class to add
  384. * @return {p5.Element}
  385. */
  386. p5.Element.prototype.addClass = function(c) {
  387. if (this.elt.className) {
  388. // PEND don't add class more than once
  389. //var regex = new RegExp('[^a-zA-Z\d:]?'+c+'[^a-zA-Z\d:]?');
  390. //if (this.elt.className.search(/[^a-zA-Z\d:]?hi[^a-zA-Z\d:]?/) === -1) {
  391. this.elt.className = this.elt.className+' '+c;
  392. //}
  393. } else {
  394. this.elt.className = c;
  395. }
  396. return this;
  397. }
  398. /**
  399. *
  400. * Removes specified class from the element.
  401. *
  402. * @method removeClass
  403. * @param {String} class name of class to remove
  404. * @return {p5.Element}
  405. */
  406. p5.Element.prototype.removeClass = function(c) {
  407. var regex = new RegExp('(?:^|\\s)'+c+'(?!\\S)');
  408. this.elt.className = this.elt.className.replace(regex, '');
  409. this.elt.className = this.elt.className.replace(/^\s+|\s+$/g, ""); //prettify (optional)
  410. return this;
  411. }
  412. /**
  413. *
  414. * Attaches the element as a child to the parent specified.
  415. * Accepts either a string ID, DOM node, or p5.Element
  416. *
  417. * @method child
  418. * @param {String|Object} child the ID, DOM node, or p5.Element
  419. * to add to the current element
  420. * @return {p5.Element}
  421. * @example
  422. * <div class='norender'><code>
  423. * var div0 = createDiv('this is the parent');
  424. * var div1 = createDiv('this is the child');
  425. * div0.child(div1); // use p5.Element
  426. * </code></div>
  427. * <div class='norender'><code>
  428. * var div0 = createDiv('this is the parent');
  429. * var div1 = createDiv('this is the child');
  430. * div1.id('apples');
  431. * div0.child('apples'); // use id
  432. * </code></div>
  433. * <div class='norender'><code>
  434. * var div0 = createDiv('this is the parent');
  435. * var elt = document.getElementById('myChildDiv');
  436. * div0.child(elt); // use element from page
  437. * </code></div>
  438. */
  439. p5.Element.prototype.child = function(c) {
  440. if (typeof c === 'string') {
  441. c = document.getElementById(c);
  442. } else if (c instanceof p5.Element) {
  443. c = c.elt;
  444. }
  445. this.elt.appendChild(c);
  446. return this;
  447. };
  448. /**
  449. *
  450. * If an argument is given, sets the inner HTML of the element,
  451. * replacing any existing html. If no arguments are given, returns
  452. * the inner HTML of the element.
  453. *
  454. * @for p5.Element
  455. * @method html
  456. * @param {String} [html] the HTML to be placed inside the element
  457. * @return {p5.Element|String}
  458. */
  459. p5.Element.prototype.html = function(html) {
  460. if (typeof html !== 'undefined') {
  461. this.elt.innerHTML = html;
  462. return this;
  463. } else {
  464. return this.elt.innerHTML;
  465. }
  466. };
  467. /**
  468. *
  469. * Sets the position of the element relative to (0, 0) of the
  470. * window. Essentially, sets position:absolute and left and top
  471. * properties of style.
  472. *
  473. * @method position
  474. * @param {Number} x x-position relative to upper left of window
  475. * @param {Number} y y-position relative to upper left of window
  476. * @return {p5.Element}
  477. * @example
  478. * <div><code class='norender'>
  479. * function setup() {
  480. * var cnv = createCanvas(100, 100);
  481. * // positions canvas 50px to right and 100px
  482. * // below upper left corner of the window
  483. * cnv.position(50, 100);
  484. * }
  485. * </code></div>
  486. */
  487. p5.Element.prototype.position = function(x, y) {
  488. this.elt.style.position = 'absolute';
  489. this.elt.style.left = x+'px';
  490. this.elt.style.top = y+'px';
  491. return this;
  492. };
  493. /**
  494. *
  495. * Sets the given style (css) property of the element with the given value.
  496. * If no value is specified, returns the value of the given property,
  497. * or undefined if the property is not.
  498. *
  499. * @method style
  500. * @param {String} property property to be set
  501. * @param {String} [value] value to assign to property
  502. * @return {String|p5.Element} value of property, if no value is specified
  503. * or p5.Element
  504. * @example
  505. * <div><code class="norender">
  506. * var myDiv = createDiv("I like pandas.");
  507. * myDiv.style("color", "#ff0000");
  508. * myDiv.style("font-size", "18px");
  509. * </code></div>
  510. */
  511. p5.Element.prototype.style = function(prop, val) {
  512. if (typeof val === 'undefined') {
  513. var attrs = prop.split(';');
  514. for (var i=0; i<attrs.length; i++) {
  515. var parts = attrs[i].split(':');
  516. if (parts[0] && parts[1]) {
  517. this.elt.style[parts[0].trim()] = parts[1].trim();
  518. }
  519. }
  520. console.log(this.elt.style)
  521. } else {
  522. this.elt.style[prop] = val;
  523. }
  524. return this;
  525. };
  526. /**
  527. *
  528. * Adds a new attribute or changes the value of an existing attribute
  529. * on the specified element. If no value is specified, returns the
  530. * value of the given attribute, or null if attribute is not set.
  531. *
  532. * @method attribute
  533. * @param {String} attr attribute to set
  534. * @param {String} [value] value to assign to attribute
  535. * @return {String|p5.Element} value of attribute, if no value is
  536. * specified or p5.Element
  537. * @example
  538. * <div class="norender"><code>
  539. * var myDiv = createDiv("I like pandas.");
  540. *myDiv.attribute("align", "center");
  541. * </code></div>
  542. */
  543. p5.Element.prototype.attribute = function(attr, value) {
  544. if (typeof value === 'undefined') {
  545. return this.elt.getAttribute(attr);
  546. } else {
  547. this.elt.setAttribute(attr, value);
  548. return this;
  549. }
  550. };
  551. /**
  552. * Either returns the value of the element if no arguments
  553. * given, or sets the value of the element.
  554. *
  555. * @method value
  556. * @param {String|Number} [value]
  557. * @return {String|p5.Element} value of element, if no value is
  558. * specified or p5.Element
  559. */
  560. p5.Element.prototype.value = function() {
  561. if (arguments.length > 0) {
  562. this.elt.value = arguments[0];
  563. return this;
  564. } else {
  565. if (this.elt.type === 'range') {
  566. return parseFloat(this.elt.value);
  567. }
  568. else return this.elt.value;
  569. }
  570. };
  571. /**
  572. *
  573. * Shows the current element. Essentially, setting display:block for the style.
  574. *
  575. * @method show
  576. * @return {p5.Element}
  577. */
  578. p5.Element.prototype.show = function() {
  579. this.elt.style.display = 'block';
  580. return this;
  581. };
  582. /**
  583. * Hides the current element. Essentially, setting display:none for the style.
  584. *
  585. * @method hide
  586. * @return {p5.Element}
  587. */
  588. p5.Element.prototype.hide = function() {
  589. this.elt.style.display = 'none';
  590. return this;
  591. };
  592. /**
  593. *
  594. * Sets the width and height of the element. AUTO can be used to
  595. * only adjust one dimension.
  596. *
  597. * @method size
  598. * @param {Number} w width of the element
  599. * @param {Number} h height of the element
  600. * @return {p5.Element}
  601. */
  602. p5.Element.prototype.size = function(w, h) {
  603. var aW = w;
  604. var aH = h;
  605. var AUTO = p5.prototype.AUTO;
  606. if (aW !== AUTO || aH !== AUTO) {
  607. if (aW === AUTO) {
  608. aW = h * this.elt.width / this.elt.height;
  609. } else if (aH === AUTO) {
  610. aH = w * this.elt.height / this.elt.width;
  611. }
  612. // set diff for cnv vs normal div
  613. if (this.elt instanceof HTMLCanvasElement) {
  614. var j = {};
  615. var k = this.elt.getContext('2d');
  616. for (var prop in k) {
  617. j[prop] = k[prop];
  618. }
  619. this.elt.setAttribute('width', aW * this._pInst._pixelDensity);
  620. this.elt.setAttribute('height', aH * this._pInst._pixelDensity);
  621. this.elt.setAttribute('style', 'width:' + aW + 'px !important; height:' + aH + 'px !important;');
  622. this._pInst.scale(this._pInst._pixelDensity, this._pInst._pixelDensity);
  623. for (var prop in j) {
  624. this.elt.getContext('2d')[prop] = j[prop];
  625. }
  626. } else {
  627. this.elt.style.width = aW+'px';
  628. this.elt.style.height = aH+'px';
  629. this.elt.width = aW;
  630. this.elt.height = aH;
  631. }
  632. this.width = this.elt.offsetWidth;
  633. this.height = this.elt.offsetHeight;
  634. if (this._pInst) { // main canvas associated with p5 instance
  635. if (this._pInst._curElement.elt === this.elt) {
  636. this._pInst._setProperty('width', this.elt.offsetWidth);
  637. this._pInst._setProperty('height', this.elt.offsetHeight);
  638. }
  639. }
  640. }
  641. return this;
  642. };
  643. /**
  644. * Removes the element and deregisters all listeners.
  645. * @method remove
  646. * @example
  647. * <div class='norender'><code>
  648. * var myDiv = createDiv('this is some text');
  649. * myDiv.remove();
  650. * </code></div>
  651. */
  652. p5.Element.prototype.remove = function() {
  653. // deregister events
  654. for (var ev in this._events) {
  655. this.elt.removeEventListener(ev, this._events[ev]);
  656. }
  657. if (this.elt.parentNode) {
  658. this.elt.parentNode.removeChild(this.elt);
  659. }
  660. delete(this);
  661. };
  662. // =============================================================================
  663. // p5.MediaElement additions
  664. // =============================================================================
  665. /**
  666. * Extends p5.Element to handle audio and video. In addition to the methods
  667. * of p5.Element, it also contains methods for controlling media. It is not
  668. * called directly, but p5.MediaElements are created by calling createVideo,
  669. * createAudio, and createCapture.
  670. *
  671. * @class p5.MediaElement
  672. * @constructor
  673. * @param {String} elt DOM node that is wrapped
  674. * @param {Object} [pInst] pointer to p5 instance
  675. */
  676. p5.MediaElement = function(elt, pInst) {
  677. p5.Element.call(this, elt, pInst);
  678. };
  679. p5.MediaElement.prototype = Object.create(p5.Element.prototype);
  680. /**
  681. * Play an HTML5 media element.
  682. *
  683. * @method play
  684. * @return {p5.Element}
  685. */
  686. p5.MediaElement.prototype.play = function() {
  687. if (this.elt.currentTime === this.elt.duration) {
  688. this.elt.currentTime = 0;
  689. }
  690. this.elt.play();
  691. return this;
  692. };
  693. /**
  694. * Stops an HTML5 media element (sets current time to zero).
  695. *
  696. * @method stop
  697. * @return {p5.Element}
  698. */
  699. p5.MediaElement.prototype.stop = function() {
  700. this.elt.pause();
  701. this.elt.currentTime = 0;
  702. return this;
  703. };
  704. /**
  705. * Pauses an HTML5 media element.
  706. *
  707. * @method pause
  708. * @return {p5.Element}
  709. */
  710. p5.MediaElement.prototype.pause = function() {
  711. this.elt.pause();
  712. return this;
  713. };
  714. /**
  715. * Set 'loop' to true for an HTML5 media element, and starts playing.
  716. *
  717. * @method loop
  718. * @return {p5.Element}
  719. */
  720. p5.MediaElement.prototype.loop = function() {
  721. this.elt.setAttribute('loop', true);
  722. this.play();
  723. return this;
  724. };
  725. /**
  726. * Set 'loop' to false for an HTML5 media element. Element will stop
  727. * when it reaches the end.
  728. *
  729. * @method noLoop
  730. * @return {p5.Element}
  731. */
  732. p5.MediaElement.prototype.noLoop = function() {
  733. this.elt.setAttribute('loop', false);
  734. return this;
  735. };
  736. /**
  737. * Set HTML5 media element to autoplay or not.
  738. *
  739. * @method autoplay
  740. * @param {Boolean} autoplay whether the element should autoplay
  741. * @return {p5.Element}
  742. */
  743. p5.MediaElement.prototype.autoplay = function(val) {
  744. this.elt.setAttribute('autoplay', val);
  745. return this;
  746. };
  747. /**
  748. * Sets volume for this HTML5 media element. If no argument is given,
  749. * returns the current volume.
  750. *
  751. * @param {Number} [val] volume between 0.0 and 1.0
  752. * @return {Number|p5.MediaElement} current volume or p5.MediaElement
  753. * @method volume
  754. */
  755. p5.MediaElement.prototype.volume = function(val) {
  756. if (typeof val === 'undefined') {
  757. return this.elt.volume;
  758. } else {
  759. this.elt.volume = val;
  760. }
  761. };
  762. /**
  763. * If no arguments are given, returns the current time of the elmeent.
  764. * If an argument is given the current time of the element is set to it.
  765. *
  766. * @method time
  767. * @param {Number} [time] time to jump to (in seconds)
  768. * @return {Number|p5.MediaElement} current time (in seconds)
  769. * or p5.MediaElement
  770. */
  771. p5.MediaElement.prototype.time = function(val) {
  772. if (typeof val === 'undefined') {
  773. return this.elt.currentTime;
  774. } else {
  775. this.elt.currentTime = val;
  776. }
  777. };
  778. /**
  779. * Returns the duration of the HTML5 media element.
  780. *
  781. * @method duration
  782. * @return {Number} duration
  783. */
  784. p5.MediaElement.prototype.duration = function() {
  785. return this.elt.duration;
  786. };
  787. p5.MediaElement.prototype.pixels = [];
  788. p5.MediaElement.prototype.loadPixels = function() {
  789. if (this.loadedmetadata) { // wait for metadata for w/h
  790. if (!this.canvas) {
  791. this.canvas = document.createElement('canvas');
  792. this.canvas.width = this.width;
  793. this.canvas.height = this.height;
  794. this.drawingContext = this.canvas.getContext('2d');
  795. }
  796. this.drawingContext.drawImage(this.elt, 0, 0);
  797. p5.prototype.loadPixels.call(this);
  798. }
  799. return this;
  800. }
  801. p5.MediaElement.prototype.updatePixels = function(x, y, w, h){
  802. if (this.loadedmetadata) { // wait for metadata
  803. p5.prototype.updatePixels.call(this, x, y, w, h);
  804. }
  805. return this;
  806. }
  807. p5.MediaElement.prototype.get = function(x, y, w, h){
  808. if (this.loadedmetadata) { // wait for metadata
  809. return p5.prototype.get.call(this, x, y, w, h);
  810. } else return [0, 0, 0, 255];
  811. };
  812. p5.MediaElement.prototype.set = function(x, y, imgOrCol){
  813. if (this.loadedmetadata) { // wait for metadata
  814. p5.prototype.set.call(this, x, y, imgOrCol);
  815. }
  816. };
  817. })();