UNPKG

p5

Version:

[![Build Status](https://travis-ci.org/processing/p5.js.svg?branch=master)](https://travis-ci.org/processing/p5.js) [![npm version](https://badge.fury.io/js/p5.svg)](https://www.npmjs.com/package/p5)

1,550 lines (1,477 loc) 70.3 kB
/*! p5.dom.js v0.3.4 Aug 11, 2017 */ /** * <p>The web is much more than just canvas and p5.dom makes it easy to interact * with other HTML5 objects, including text, hyperlink, image, input, video, * audio, and webcam.</p> * <p>There is a set of creation methods, DOM manipulation methods, and * an extended p5.Element that supports a range of HTML elements. See the * <a href="https://github.com/processing/p5.js/wiki/Beyond-the-canvas"> * beyond the canvas tutorial</a> for a full overview of how this addon works. * * <p>Methods and properties shown in black are part of the p5.js core, items in * blue are part of the p5.dom library. You will need to include an extra file * in order to access the blue functions. See the * <a href="http://p5js.org/libraries/#using-a-library">using a library</a> * section for information on how to include this library. p5.dom comes with * <a href="http://p5js.org/download">p5 complete</a> or you can download the single file * <a href="https://raw.githubusercontent.com/lmccart/p5.js/master/lib/addons/p5.dom.js"> * here</a>.</p> * <p>See <a href="https://github.com/processing/p5.js/wiki/Beyond-the-canvas">tutorial: beyond the canvas</a> * for more info on how to use this libary.</a> * * @module p5.dom * @submodule p5.dom * @for p5.dom * @main */ (function (root, factory) { if (typeof define === 'function' && define.amd) define('p5.dom', ['p5'], function (p5) { (factory(p5));}); else if (typeof exports === 'object') factory(require('../p5')); else factory(root['p5']); }(this, function (p5) { // ============================================================================= // p5 additions // ============================================================================= /** * Searches the page for an element with the given ID, class, or tag name (using the '#' or '.' * prefixes to specify an ID or class respectively, and none for a tag) and returns it as * a p5.Element. If a class or tag name is given with more than 1 element, * only the first element will be returned. * The DOM node itself can be accessed with .elt. * Returns null if none found. You can also specify a container to search within. * * @method select * @param {String} name id, class, or tag name of element to search for * @param {String} [container] id, p5.Element, or HTML element to search within * @return {Object|p5.Element|Null} p5.Element containing node found * @example * <div ><code class='norender'> * function setup() { * createCanvas(100,100); * //translates canvas 50px down * select('canvas').position(100, 100); * } * </code></div> * <div ><code class='norender'> * // these are all valid calls to select() * var a = select('#moo'); * var b = select('#blah', '#myContainer'); * var c = select('#foo', b); * var d = document.getElementById('beep'); * var e = select('p', d); * </code></div> * */ p5.prototype.select = function (e, p) { var res = null; var container = getContainer(p); if (e[0] === '.'){ e = e.slice(1); res = container.getElementsByClassName(e); if (res.length) { res = res[0]; } else { res = null; } }else if (e[0] === '#'){ e = e.slice(1); res = container.getElementById(e); }else { res = container.getElementsByTagName(e); if (res.length) { res = res[0]; } else { res = null; } } if (res) { return wrapElement(res); } else { return null; } }; /** * Searches the page for elements with the given class or tag name (using the '.' prefix * to specify a class and no prefix for a tag) and returns them as p5.Elements * in an array. * The DOM node itself can be accessed with .elt. * Returns an empty array if none found. * You can also specify a container to search within. * * @method selectAll * @param {String} name class or tag name of elements to search for * @param {String} [container] id, p5.Element, or HTML element to search within * @return {Array} Array of p5.Elements containing nodes found * @example * <div class='norender'><code> * function setup() { * createButton('btn'); * createButton('2nd btn'); * createButton('3rd btn'); * var buttons = selectAll('button'); * * for (var i = 0; i < buttons.length; i++){ * buttons[i].size(100,100); * } * } * </code></div> * <div class='norender'><code> * // these are all valid calls to selectAll() * var a = selectAll('.moo'); * var b = selectAll('div'); * var c = selectAll('button', '#myContainer'); * var d = select('#container'); * var e = selectAll('p', d); * var f = document.getElementById('beep'); * var g = select('.blah', f); * </code></div> * */ p5.prototype.selectAll = function (e, p) { var arr = []; var res; var container = getContainer(p); if (e[0] === '.'){ e = e.slice(1); res = container.getElementsByClassName(e); } else { res = container.getElementsByTagName(e); } if (res) { for (var j = 0; j < res.length; j++) { var obj = wrapElement(res[j]); arr.push(obj); } } return arr; }; /** * Helper function for select and selectAll */ function getContainer(p) { var container = document; if (typeof p === 'string' && p[0] === '#'){ p = p.slice(1); container = document.getElementById(p) || document; } else if (p instanceof p5.Element){ container = p.elt; } else if (p instanceof HTMLElement){ container = p; } return container; } /** * Helper function for getElement and getElements. */ function wrapElement(elt) { if(elt.tagName === "INPUT" && elt.type === "checkbox") { var converted = new p5.Element(elt); converted.checked = function(){ if (arguments.length === 0){ return this.elt.checked; } else if(arguments[0]) { this.elt.checked = true; } else { this.elt.checked = false; } return this; }; return converted; } else if (elt.tagName === "VIDEO" || elt.tagName === "AUDIO") { return new p5.MediaElement(elt); } else if ( elt.tagName === "SELECT" ){ return createSelect( new p5.Element(elt) ); } else { return new p5.Element(elt); } } /** * Removes all elements created by p5, except any canvas / graphics * elements created by createCanvas or createGraphics. * Event handlers are removed, and element is removed from the DOM. * @method removeElements * @example * <div class='norender'><code> * function setup() { * createCanvas(100, 100); * createDiv('this is some text'); * createP('this is a paragraph'); * } * function mousePressed() { * removeElements(); // this will remove the div and p, not canvas * } * </code></div> * */ p5.prototype.removeElements = function (e) { for (var i=0; i<this._elements.length; i++) { if (!(this._elements[i].elt instanceof HTMLCanvasElement)) { this._elements[i].remove(); } } }; /** * Helpers for create methods. */ function addElement(elt, pInst, media) { var node = pInst._userNode ? pInst._userNode : document.body; node.appendChild(elt); var c = media ? new p5.MediaElement(elt) : new p5.Element(elt); pInst._elements.push(c); return c; } /** * Creates a &lt;div&gt;&lt;/div&gt; element in the DOM with given inner HTML. * Appends to the container node if one is specified, otherwise * appends to body. * * @method createDiv * @param {String} [html] inner HTML for element created * @return {Object|p5.Element} pointer to p5.Element holding created node * @example * <div class='norender'><code> * var myDiv; * function setup() { * myDiv = createDiv('this is some text'); * } * </code></div> */ /** * Creates a &lt;p&gt;&lt;/p&gt; element in the DOM with given inner HTML. Used * for paragraph length text. * Appends to the container node if one is specified, otherwise * appends to body. * * @method createP * @param {String} [html] inner HTML for element created * @return {Object|p5.Element} pointer to p5.Element holding created node * @example * <div class='norender'><code> * var myP; * function setup() { * myP = createP('this is some text'); * } * </code></div> */ /** * Creates a &lt;span&gt;&lt;/span&gt; element in the DOM with given inner HTML. * Appends to the container node if one is specified, otherwise * appends to body. * * @method createSpan * @param {String} [html] inner HTML for element created * @return {Object|p5.Element} pointer to p5.Element holding created node * @example * <div class='norender'><code> * var mySpan; * function setup() { * mySpan = createSpan('this is some text'); * } * </code></div> */ var tags = ['div', 'p', 'span']; tags.forEach(function(tag) { var method = 'create' + tag.charAt(0).toUpperCase() + tag.slice(1); p5.prototype[method] = function(html) { var elt = document.createElement(tag); elt.innerHTML = typeof html === undefined ? "" : html; return addElement(elt, this); } }); /** * Creates an &lt;img&gt; element in the DOM with given src and * alternate text. * Appends to the container node if one is specified, otherwise * appends to body. * * @method createImg * @param {String} src src path or url for image * @param {String} [alt] alternate text to be used if image does not load * @param {Function} [successCallback] callback to be called once image data is loaded * @return {Object|p5.Element} pointer to p5.Element holding created node * @example * <div class='norender'><code> * var img; * function setup() { * img = createImg('http://p5js.org/img/asterisk-01.png'); * } * </code></div> */ p5.prototype.createImg = function() { var elt = document.createElement('img'); var args = arguments; var self; var setAttrs = function(){ self.width = elt.offsetWidth || elt.width; self.height = elt.offsetHeight || elt.height; if (args.length > 1 && typeof args[1] === 'function'){ self.fn = args[1]; self.fn(); }else if (args.length > 1 && typeof args[2] === 'function'){ self.fn = args[2]; self.fn(); } }; elt.src = args[0]; if (args.length > 1 && typeof args[1] === 'string'){ elt.alt = args[1]; } elt.onload = function(){ setAttrs(); } self = addElement(elt, this); return self; }; /** * Creates an &lt;a&gt;&lt;/a&gt; element in the DOM for including a hyperlink. * Appends to the container node if one is specified, otherwise * appends to body. * * @method createA * @param {String} href url of page to link to * @param {String} html inner html of link element to display * @param {String} [target] target where new link should open, * could be _blank, _self, _parent, _top. * @return {Object|p5.Element} pointer to p5.Element holding created node * @example * <div class='norender'><code> * var myLink; * function setup() { * myLink = createA('http://p5js.org/', 'this is a link'); * } * </code></div> */ p5.prototype.createA = function(href, html, target) { var elt = document.createElement('a'); elt.href = href; elt.innerHTML = html; if (target) elt.target = target; return addElement(elt, this); }; /** INPUT **/ /** * Creates a slider &lt;input&gt;&lt;/input&gt; element in the DOM. * Use .size() to set the display length of the slider. * Appends to the container node if one is specified, otherwise * appends to body. * * @method createSlider * @param {Number} min minimum value of the slider * @param {Number} max maximum value of the slider * @param {Number} [value] default value of the slider * @param {Number} [step] step size for each tick of the slider (if step is set to 0, the slider will move continuously from the minimum to the maximum value) * @return {Object|p5.Element} pointer to p5.Element holding created node * @example * <div><code> * var slider; * function setup() { * slider = createSlider(0, 255, 100); * slider.position(10, 10); * slider.style('width', '80px'); * } * * function draw() { * var val = slider.value(); * background(val); * } * </code></div> * * <div><code> * var slider; * function setup() { * colorMode(HSB); * slider = createSlider(0, 360, 60, 40); * slider.position(10, 10); * slider.style('width', '80px'); * } * * function draw() { * var val = slider.value(); * background(val, 100, 100, 1); * } * </code></div> */ p5.prototype.createSlider = function(min, max, value, step) { var elt = document.createElement('input'); elt.type = 'range'; elt.min = min; elt.max = max; if (step === 0) { elt.step = .000000000000000001; // smallest valid step } else if (step) { elt.step = step; } if (typeof(value) === "number") elt.value = value; return addElement(elt, this); }; /** * Creates a &lt;button&gt;&lt;/button&gt; element in the DOM. * Use .size() to set the display size of the button. * Use .mousePressed() to specify behavior on press. * Appends to the container node if one is specified, otherwise * appends to body. * * @method createButton * @param {String} label label displayed on the button * @param {String} [value] value of the button * @return {Object|p5.Element} pointer to p5.Element holding created node * @example * <div class='norender'><code> * var button; * function setup() { * createCanvas(100, 100); * background(0); * button = createButton('click me'); * button.position(19, 19); * button.mousePressed(changeBG); * } * * function changeBG() { * var val = random(255); * background(val); * } * </code></div> */ p5.prototype.createButton = function(label, value) { var elt = document.createElement('button'); elt.innerHTML = label; if (value) elt.value = value; return addElement(elt, this); }; /** * Creates a checkbox &lt;input&gt;&lt;/input&gt; element in the DOM. * Calling .checked() on a checkbox returns if it is checked or not * * @method createCheckbox * @param {String} [label] label displayed after checkbox * @param {boolean} [value] value of the checkbox; checked is true, unchecked is false.Unchecked if no value given * @return {Object|p5.Element} pointer to p5.Element holding created node * @example * <div class='norender'><code> * var checkbox; * * function setup() { * checkbox = createCheckbox('label', false); * checkbox.changed(myCheckedEvent); * } * * function myCheckedEvent() { * if (this.checked()) { * console.log("Checking!"); * } else { * console.log("Unchecking!"); * } * } * </code></div> */ p5.prototype.createCheckbox = function() { var elt = document.createElement('div'); var checkbox = document.createElement('input'); checkbox.type = 'checkbox'; elt.appendChild(checkbox); //checkbox must be wrapped in p5.Element before label so that label appears after var self = addElement(elt, this); self.checked = function(){ var cb = self.elt.getElementsByTagName('input')[0]; if (cb) { if (arguments.length === 0){ return cb.checked; }else if(arguments[0]){ cb.checked = true; }else{ cb.checked = false; } } return self; }; this.value = function(val){ self.value = val; return this; }; if (arguments[0]){ var ran = Math.random().toString(36).slice(2); var label = document.createElement('label'); checkbox.setAttribute('id', ran); label.htmlFor = ran; self.value(arguments[0]); label.appendChild(document.createTextNode(arguments[0])); elt.appendChild(label); } if (arguments[1]){ checkbox.checked = true; } return self; }; /** * Creates a dropdown menu &lt;select&gt;&lt;/select&gt; element in the DOM. * It also helps to assign select-box methods to p5.Element when selecting existing select box * @method createSelect * @param {boolean} [multiple] true if dropdown should support multiple selections * @return {p5.Element} * @example * <div><code> * var sel; * * function setup() { * textAlign(CENTER); * background(200); * sel = createSelect(); * sel.position(10, 10); * sel.option('pear'); * sel.option('kiwi'); * sel.option('grape'); * sel.changed(mySelectEvent); * } * * function mySelectEvent() { * var item = sel.value(); * background(200); * text("it's a "+item+"!", 50, 50); * } * </code></div> */ /** * @method createSelect * @param {Object} existing DOM select element * @return {p5.Element} */ p5.prototype.createSelect = function() { var elt, self; var arg = arguments[0]; if( typeof arg === 'object' && arg.elt.nodeName === 'SELECT' ) { self = arg; elt = this.elt = arg.elt; } else { elt = document.createElement('select'); if( arg && typeof arg === 'boolean' ) { elt.setAttribute('multiple', 'true'); } self = addElement(elt, this); } self.option = function(name, value) { var index; //see if there is already an option with this name for (var i = 0; i < this.elt.length; i++) { if(this.elt[i].innerHTML == name) { index = i; break; } } //if there is an option with this name we will modify it if(index !== undefined) { //if the user passed in false then delete that option if(value === false) { this.elt.remove(index); } else { //otherwise if the name and value are the same then change both if(this.elt[index].innerHTML == this.elt[index].value) { this.elt[index].innerHTML = this.elt[index].value = value; //otherwise just change the value } else { this.elt[index].value = value; } } } //if it doesn't exist make it else { var opt = document.createElement('option'); opt.innerHTML = name; if (arguments.length > 1) opt.value = value; else opt.value = name; elt.appendChild(opt); } }; self.selected = function(value) { var arr = []; if (arguments.length > 0) { for (var i = 0; i < this.elt.length; i++) { if (value.toString() === this.elt[i].value) { this.elt.selectedIndex = i; } } return this; } else { if (arg) { for (var i = 0; i < this.elt.selectedOptions.length; i++) { arr.push(this.elt.selectedOptions[i].value); } return arr; } else { return this.elt.value; } } }; return self; }; /** * Creates a radio button &lt;input&gt;&lt;/input&gt; element in the DOM. * The .option() method can be used to set options for the radio after it is * created. The .value() method will return the currently selected option. * * @method createRadio * @param {String} [divId] the id and name of the created div and input field respectively * @return {Object|p5.Element} pointer to p5.Element holding created node * @example * <div><code> * var radio; * * function setup() { * radio = createRadio(); * radio.option("black"); * radio.option("white"); * radio.option("gray"); * radio.style('width', '60px'); * textAlign(CENTER); * fill(255, 0, 0); * } * * function draw() { * var val = radio.value(); * background(val); * text(val, width/2, height/2); * } * </code></div> * <div><code> * var radio; * * function setup() { * radio = createRadio(); * radio.option('apple', 1); * radio.option('bread', 2); * radio.option('juice', 3); * radio.style('width', '60px'); * textAlign(CENTER); * } * * function draw() { * background(200); * var val = radio.value(); * if (val) { * text('item cost is $'+val, width/2, height/2); * } * } * </code></div> */ p5.prototype.createRadio = function() { var radios = document.querySelectorAll("input[type=radio]"); var count = 0; if(radios.length > 1){ var length = radios.length; var prev=radios[0].name; var current = radios[1].name; count = 1; for(var i = 1; i < length; i++) { current = radios[i].name; if(prev != current){ count++; } prev = current; } } else if (radios.length == 1){ count = 1; } var elt = document.createElement('div'); var self = addElement(elt, this); var times = -1; self.option = function(name, value){ var opt = document.createElement('input'); opt.type = 'radio'; opt.innerHTML = name; if (arguments.length > 1) opt.value = value; else opt.value = name; opt.setAttribute('name',"defaultradio"+count); elt.appendChild(opt); if (name){ times++; var ran = Math.random().toString(36).slice(2); var label = document.createElement('label'); opt.setAttribute('id', "defaultradio"+count+"-"+times); label.htmlFor = "defaultradio"+count+"-"+times; label.appendChild(document.createTextNode(name)); elt.appendChild(label); } return opt; }; self.selected = function(){ var length = this.elt.childNodes.length; if(arguments.length == 1) { for (var i = 0; i < length; i+=2){ if(this.elt.childNodes[i].value == arguments[0]) this.elt.childNodes[i].checked = true; } return this; } else { for (var i = 0; i < length; i+=2){ if(this.elt.childNodes[i].checked == true) return this.elt.childNodes[i].value; } } }; self.value = function(){ var length = this.elt.childNodes.length; if(arguments.length == 1) { for (var i = 0; i < length; i+=2){ if(this.elt.childNodes[i].value == arguments[0]) this.elt.childNodes[i].checked = true; } return this; } else { for (var i = 0; i < length; i+=2){ if(this.elt.childNodes[i].checked == true) return this.elt.childNodes[i].value; } return ""; } }; return self }; /** * Creates an &lt;input&gt;&lt;/input&gt; element in the DOM for text input. * Use .size() to set the display length of the box. * Appends to the container node if one is specified, otherwise * appends to body. * * @method createInput * @param {Number} [value] default value of the input box * @param {String} [type] type of text, ie text, password etc. Defaults to text * @return {Object|p5.Element} pointer to p5.Element holding created node * @example * <div class='norender'><code> * function setup(){ * var inp = createInput(''); * inp.input(myInputEvent); * } * * function myInputEvent(){ * console.log('you are typing: ', this.value()); * } * * </code></div> */ p5.prototype.createInput = function(value, type) { var elt = document.createElement('input'); elt.type = type ? type : 'text'; if (value) elt.value = value; return addElement(elt, this); }; /** * Creates an &lt;input&gt;&lt;/input&gt; element in the DOM of type 'file'. * This allows users to select local files for use in a sketch. * * @method createFileInput * @param {Function} [callback] callback function for when a file loaded * @param {String} [multiple] optional to allow multiple files selected * @return {Object|p5.Element} pointer to p5.Element holding created DOM element * @example * var input; * var img; * * function setup() { * input = createFileInput(handleFile); * input.position(0, 0); * } * * function draw() { * if (img) { * image(img, 0, 0, width, height); * } * } * * function handleFile(file) { * print(file); * if (file.type === 'image') { * img = createImg(file.data); * img.hide(); * } * } */ p5.prototype.createFileInput = function(callback, multiple) { // Is the file stuff supported? if (window.File && window.FileReader && window.FileList && window.Blob) { // Yup, we're ok and make an input file selector var elt = document.createElement('input'); elt.type = 'file'; // If we get a second argument that evaluates to true // then we are looking for multiple files if (multiple) { // Anything gets the job done elt.multiple = 'multiple'; } // Function to handle when a file is selected // We're simplifying life and assuming that we always // want to load every selected file function handleFileSelect(evt) { // These are the files var files = evt.target.files; // Load each one and trigger a callback for (var i = 0; i < files.length; i++) { var f = files[i]; var reader = new FileReader(); function makeLoader(theFile) { // Making a p5.File object var p5file = new p5.File(theFile); return function(e) { p5file.data = e.target.result; callback(p5file); }; }; reader.onload = makeLoader(f); // Text or data? // This should likely be improved if (f.type.indexOf('text') > -1) { reader.readAsText(f); } else { reader.readAsDataURL(f); } } } // Now let's handle when a file was selected elt.addEventListener('change', handleFileSelect, false); return addElement(elt, this); } else { console.log('The File APIs are not fully supported in this browser. Cannot create element.'); } }; /** VIDEO STUFF **/ function createMedia(pInst, type, src, callback) { var elt = document.createElement(type); // allow src to be empty var src = src || ''; if (typeof src === 'string') { src = [src]; } for (var i=0; i<src.length; i++) { var source = document.createElement('source'); source.src = src[i]; elt.appendChild(source); } if (typeof callback !== 'undefined') { var callbackHandler = function() { callback(); elt.removeEventListener('canplaythrough', callbackHandler); } elt.addEventListener('canplaythrough', callbackHandler); } var c = addElement(elt, pInst, true); c.loadedmetadata = false; // set width and height onload metadata elt.addEventListener('loadedmetadata', function() { c.width = elt.videoWidth; c.height = elt.videoHeight; // set elt width and height if not set if (c.elt.width === 0) c.elt.width = elt.videoWidth; if (c.elt.height === 0) c.elt.height = elt.videoHeight; c.loadedmetadata = true; }); return c; } /** * Creates an HTML5 &lt;video&gt; element in the DOM for simple playback * of audio/video. Shown by default, can be hidden with .hide() * and drawn into canvas using video(). Appends to the container * node if one is specified, otherwise appends to body. The first parameter * can be either a single string path to a video file, or an array of string * paths to different formats of the same video. This is useful for ensuring * that your video can play across different browsers, as each supports * different formats. See <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Supported_media_formats">this * page</a> for further information about supported formats. * * @method createVideo * @param {String|Array} src path to a video file, or array of paths for * supporting different browsers * @param {Object} [callback] callback function to be called upon * 'canplaythrough' event fire, that is, when the * browser can play the media, and estimates that * enough data has been loaded to play the media * up to its end without having to stop for * further buffering of content * @return {p5.MediaElement|p5.Element} pointer to video p5.Element */ p5.prototype.createVideo = function(src, callback) { return createMedia(this, 'video', src, callback); }; /** AUDIO STUFF **/ /** * Creates a hidden HTML5 &lt;audio&gt; element in the DOM for simple audio * playback. Appends to the container node if one is specified, * otherwise appends to body. The first parameter * can be either a single string path to a audio file, or an array of string * paths to different formats of the same audio. This is useful for ensuring * that your audio can play across different browsers, as each supports * different formats. See <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Supported_media_formats">this * page for further information about supported formats</a>. * * @method createAudio * @param {String|Array} src path to an audio file, or array of paths for * supporting different browsers * @param {Object} [callback] callback function to be called upon * 'canplaythrough' event fire, that is, when the * browser can play the media, and estimates that * enough data has been loaded to play the media * up to its end without having to stop for * further buffering of content * @return {p5.MediaElement|p5.Element} pointer to audio p5.Element */ p5.prototype.createAudio = function(src, callback) { return createMedia(this, 'audio', src, callback); }; /** CAMERA STUFF **/ p5.prototype.VIDEO = 'video'; p5.prototype.AUDIO = 'audio'; navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia; /** * <p>Creates a new &lt;video&gt; element that contains the audio/video feed * from a webcam. This can be drawn onto the canvas using video().</p> * <p>More specific properties of the feed can be passing in a Constraints object. * See the * <a href="http://w3c.github.io/mediacapture-main/getusermedia.html#media-track-constraints"> W3C * spec</a> for possible properties. Note that not all of these are supported * by all browsers.</p> * <p>Security note: A new browser security specification requires that getUserMedia, * which is behind createCapture(), only works when you're running the code locally, * or on HTTPS. Learn more <a href="http://stackoverflow.com/questions/34197653/getusermedia-in-chrome-47-without-using-https">here</a> * and <a href="https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia">here</a>.</p> * * @method createCapture * @param {String|Constant|Object} type type of capture, either VIDEO or * AUDIO if none specified, default both, * or a Constraints object * @param {Function} callback function to be called once * stream has loaded * @return {Object|p5.Element} capture video p5.Element * @example * <div class='norender'><code> * var capture; * * function setup() { * createCanvas(480, 120); * capture = createCapture(VIDEO); * } * * function draw() { * image(capture, 0, 0, width, width*capture.height/capture.width); * filter(INVERT); * } * </code></div> * <div class='norender'><code> * function setup() { * createCanvas(480, 120); * var constraints = { * video: { * mandatory: { * minWidth: 1280, * minHeight: 720 * }, * optional: [ * { maxFrameRate: 10 } * ] * }, * audio: true * }; * createCapture(constraints, function(stream) { * console.log(stream); * }); * } * </code></div> */ p5.prototype.createCapture = function() { var useVideo = true; var useAudio = true; var constraints; var cb; for (var i=0; i<arguments.length; i++) { if (arguments[i] === p5.prototype.VIDEO) { useAudio = false; } else if (arguments[i] === p5.prototype.AUDIO) { useVideo = false; } else if (typeof arguments[i] === 'object') { constraints = arguments[i]; } else if (typeof arguments[i] === 'function') { cb = arguments[i]; } } if (navigator.getUserMedia) { var elt = document.createElement('video'); if (!constraints) { constraints = {video: useVideo, audio: useAudio}; } navigator.getUserMedia(constraints, function(stream) { elt.src = window.URL.createObjectURL(stream); if (cb) { cb(stream); } }, function(e) { console.log(e); }); } else { throw 'getUserMedia not supported in this browser'; } var c = addElement(elt, this, true); c.loadedmetadata = false; // set width and height onload metadata elt.addEventListener('loadedmetadata', function() { elt.play(); if (elt.width) { c.width = elt.videoWidth = elt.width; c.height = elt.videoHeight = elt.height; } else { c.width = c.elt.width = elt.videoWidth; c.height = c.elt.height = elt.videoHeight; } c.loadedmetadata = true; }); return c; }; /** * Creates element with given tag in the DOM with given content. * Appends to the container node if one is specified, otherwise * appends to body. * * @method createElement * @param {String} tag tag for the new element * @param {String} [content] html content to be inserted into the element * @return {Object|p5.Element} pointer to p5.Element holding created node * @example * <div class='norender'><code> * var h2 = createElement('h2','im an h2 p5.element!'); * </code></div> */ p5.prototype.createElement = function(tag, content) { var elt = document.createElement(tag); if (typeof content !== 'undefined') { elt.innerHTML = content; } return addElement(elt, this); }; // ============================================================================= // p5.Element additions // ============================================================================= /** * * Adds specified class to the element. * * @for p5.Element * @method addClass * @param {String} class name of class to add * @return {Object|p5.Element} * @example * <div class='norender'><code> * var div = createDiv('div'); * div.addClass('myClass'); * </code></div> */ p5.Element.prototype.addClass = function(c) { if (this.elt.className) { // PEND don't add class more than once //var regex = new RegExp('[^a-zA-Z\d:]?'+c+'[^a-zA-Z\d:]?'); //if (this.elt.className.search(/[^a-zA-Z\d:]?hi[^a-zA-Z\d:]?/) === -1) { this.elt.className = this.elt.className+' '+c; //} } else { this.elt.className = c; } return this; } /** * * Removes specified class from the element. * * @method removeClass * @param {String} class name of class to remove * @return {Object|p5.Element} */ p5.Element.prototype.removeClass = function(c) { var regex = new RegExp('(?:^|\\s)'+c+'(?!\\S)'); this.elt.className = this.elt.className.replace(regex, ''); this.elt.className = this.elt.className.replace(/^\s+|\s+$/g, ""); //prettify (optional) return this; } /** * * Attaches the element as a child to the parent specified. * Accepts either a string ID, DOM node, or p5.Element. * If no argument is specified, an array of children DOM nodes is returned. * * @method child * @param {String|Object|p5.Element} [child] the ID, DOM node, or p5.Element * to add to the current element * @return {p5.Element} * @example * <div class='norender'><code> * var div0 = createDiv('this is the parent'); * var div1 = createDiv('this is the child'); * div0.child(div1); // use p5.Element * </code></div> * <div class='norender'><code> * var div0 = createDiv('this is the parent'); * var div1 = createDiv('this is the child'); * div1.id('apples'); * div0.child('apples'); // use id * </code></div> * <div class='norender'><code> * var div0 = createDiv('this is the parent'); * var elt = document.getElementById('myChildDiv'); * div0.child(elt); // use element from page * </code></div> */ p5.Element.prototype.child = function(c) { if (typeof c === 'undefined'){ return this.elt.childNodes } if (typeof c === 'string') { if (c[0] === '#') { c = c.substring(1); } c = document.getElementById(c); } else if (c instanceof p5.Element) { c = c.elt; } this.elt.appendChild(c); return this; }; /** * Centers a p5 Element either vertically, horizontally, * or both, relative to its parent or according to * the body if the Element has no parent. If no argument is passed * the Element is aligned both vertically and horizontally. * * @param {String} align passing 'vertical', 'horizontal' aligns element accordingly * @return {Object|p5.Element} pointer to p5.Element * @example * <div><code> * function setup() { * var div = createDiv('').size(10,10); * div.style('background-color','orange'); * div.center(); * * } * </code></div> */ p5.Element.prototype.center = function(align) { var style = this.elt.style.display; var hidden = this.elt.style.display === 'none'; var parentHidden = this.parent().style.display === 'none'; var pos = { x : this.elt.offsetLeft, y : this.elt.offsetTop }; if (hidden) this.show(); this.elt.style.display = 'block'; this.position(0,0); if (parentHidden) this.parent().style.display = 'block'; var wOffset = Math.abs(this.parent().offsetWidth - this.elt.offsetWidth); var hOffset = Math.abs(this.parent().offsetHeight - this.elt.offsetHeight); var y = pos.y; var x = pos.x; if (align === 'both' || align === undefined){ this.position(wOffset/2, hOffset/2); }else if (align === 'horizontal'){ this.position(wOffset/2, y); }else if (align === 'vertical'){ this.position(x, hOffset/2); } this.style('display', style); if (hidden) this.hide(); if (parentHidden) this.parent().style.display = 'none'; return this; }; /** * * If an argument is given, sets the inner HTML of the element, * replacing any existing html. If true is included as a second * argument, html is appended instead of replacing existing html. * If no arguments are given, returns * the inner HTML of the element. * * @for p5.Element * @method html * @param {String} [html] the HTML to be placed inside the element * @param {boolean} [append] whether to append HTML to existing * @return {Object|p5.Element|String} * @example * <div class='norender'><code> * var div = createDiv('').size(100,100); * div.html('hi'); * </code></div> * <div class='norender'><code> * var div = createDiv('Hello ').size(100,100); * div.html('World', true); * </code></div> */ p5.Element.prototype.html = function() { if (arguments.length === 0) { return this.elt.innerHTML; } else if (arguments[1]) { this.elt.innerHTML += arguments[0]; return this; } else { this.elt.innerHTML = arguments[0]; return this; } }; /** * * Sets the position of the element relative to (0, 0) of the * window. Essentially, sets position:absolute and left and top * properties of style. If no arguments given returns the x and y position * of the element in an object. * * @method position * @param {Number} [x] x-position relative to upper left of window * @param {Number} [y] y-position relative to upper left of window * @return {Object|p5.Element} * @example * <div><code class='norender'> * function setup() { * var cnv = createCanvas(100, 100); * // positions canvas 50px to the right and 100px * // below upper left corner of the window * cnv.position(50, 100); * } * </code></div> */ p5.Element.prototype.position = function() { if (arguments.length === 0){ return { 'x' : this.elt.offsetLeft , 'y' : this.elt.offsetTop }; }else{ this.elt.style.position = 'absolute'; this.elt.style.left = arguments[0]+'px'; this.elt.style.top = arguments[1]+'px'; this.x = arguments[0]; this.y = arguments[1]; return this; } }; /* Helper method called by p5.Element.style() */ p5.Element.prototype._translate = function(){ this.elt.style.position = 'absolute'; // save out initial non-translate transform styling var transform = ''; if (this.elt.style.transform) { transform = this.elt.style.transform.replace(/translate3d\(.*\)/g, ''); transform = transform.replace(/translate[X-Z]?\(.*\)/g, ''); } if (arguments.length === 2) { this.elt.style.transform = 'translate('+arguments[0]+'px, '+arguments[1]+'px)'; } else if (arguments.length > 2) { this.elt.style.transform = 'translate3d('+arguments[0]+'px,'+arguments[1]+'px,'+arguments[2]+'px)'; if (arguments.length === 3) { this.elt.parentElement.style.perspective = '1000px'; } else { this.elt.parentElement.style.perspective = arguments[3]+'px'; } } // add any extra transform styling back on end this.elt.style.transform += transform; return this; }; /* Helper method called by p5.Element.style() */ p5.Element.prototype._rotate = function(){ // save out initial non-rotate transform styling var transform = ''; if (this.elt.style.transform) { var transform = this.elt.style.transform.replace(/rotate3d\(.*\)/g, ''); transform = transform.replace(/rotate[X-Z]?\(.*\)/g, ''); } if (arguments.length === 1){ this.elt.style.transform = 'rotate('+arguments[0]+'deg)'; }else if (arguments.length === 2){ this.elt.style.transform = 'rotate('+arguments[0]+'deg, '+arguments[1]+'deg)'; }else if (arguments.length === 3){ this.elt.style.transform = 'rotateX('+arguments[0]+'deg)'; this.elt.style.transform += 'rotateY('+arguments[1]+'deg)'; this.elt.style.transform += 'rotateZ('+arguments[2]+'deg)'; } // add remaining transform back on this.elt.style.transform += transform; return this; }; /** * Sets the given style (css) property (1st arg) of the element with the * given value (2nd arg). If a single argument is given, .style() * returns the value of the given property; however, if the single argument * is given in css syntax ('text-align:center'), .style() sets the css * appropriatly. .style() also handles 2d and 3d css transforms. If * the 1st arg is 'rotate', 'translate', or 'position', the following arguments * accept Numbers as values. ('translate', 10, 100, 50); * * @method style * @param {String} property property to be set * @param {String|Number|p5.Color} [value] value to assign to property (only String|Number for rotate/translate) * @return {String|Object|p5.Element} value of property, if no value is specified * or p5.Element * @example * <div><code class="norender"> * var myDiv = createDiv("I like pandas."); * myDiv.style("font-size", "18px"); * myDiv.style("color", "#ff0000"); * </code></div> * <div><code class="norender"> * var col = color(25,23,200,50); * var button = createButton("button"); * button.style("background-color", col); * button.position(10, 10); * </code></div> * <div><code class="norender"> * var myDiv = createDiv("I like lizards."); * myDiv.style("position", 20, 20); * myDiv.style("rotate", 45); * </code></div> * <div><code class="norender"> * var myDiv; * function setup() { * background(200); * myDiv = createDiv("I like gray."); * myDiv.position(20, 20); * } * * function draw() { * myDiv.style("font-size", mouseX+"px"); * } * </code></div> */ p5.Element.prototype.style = function(prop, val) { var self = this; if (val instanceof p5.Color) { val = 'rgba(' + val.levels[0] + ',' + val.levels[1] + ',' + val.levels[2] + ',' + val.levels[3]/255 + ')' } if (typeof val === 'undefined') { if (prop.indexOf(':') === -1) { var styles = window.getComputedStyle(self.elt); var style = styles.getPropertyValue(prop); return style; } else { var attrs = prop.split(';'); for (var i = 0; i < attrs.length; i++) { var parts = attrs[i].split(':'); if (parts[0] && parts[1]) { this.elt.style[parts[0].trim()] = parts[1].trim(); } } } } else { if (prop === 'rotate' || prop === 'translate' || prop === 'position'){ var trans = Array.prototype.shift.apply(arguments); var f = this[trans] || this['_'+trans]; f.apply(this, arguments); } else { this.elt.style[prop] = val; if (prop === 'width' || prop === 'height' || prop === 'left' || prop === 'top') { var numVal = val.replace(/\D+/g, ''); this[prop] = parseInt(numVal, 10); // pend: is this necessary? } } } return this; }; /** * * Adds a new attribute or changes the value of an existing attribute * on the specified element. If no value is specified, returns the * value of the given attribute, or null if attribute is not set. * * @method attribute * @param {String} attr attribute to set * @param {String} [value] value to assign to attribute * @return {String|Object|p5.Element} value of attribute, if no value is * specified or p5.Element * @example * <div class="norender"><code> * var myDiv = createDiv("I like pandas."); * myDiv.attribute("align", "center"); * </code></div> */ p5.Element.prototype.attribute = function(attr, value) { //handling for checkboxes and radios to ensure options get //attributes not divs if(this.elt.firstChild != null && (this.elt.firstChild.type === 'checkbox' || this.elt.firstChild.type === 'radio')) { if(typeof value === 'undefined') { return this.elt.firstChild.getAttribute(attr); } else { for(var i=0; i<this.elt.childNodes.length; i++) { this.elt.childNodes[i].setAttribute(attr, value); } } } else if (typeof value === 'undefined') { return this.elt.getAttribute(attr); } else { this.elt.setAttribute(attr, value); return this; } }; /** * * Removes an attribute on the specified element. * * @method removeAttribute * @param {String} attr attribute to remove * @return {Object|p5.Element} * * @example * <div><code> * var button; * var checkbox; * * function setup() { * checkbox = createCheckbox('enable', true); * checkbox.changed(enableButton); * button = createButton('button'); * button.position(10, 10); * } * * function enableButton() { * if( this.checked() ) { * // Re-enable the button * button.removeAttribute('disabled'); * } else { * // Disable the button * button.attribute('disabled',''); * } * } * </code></div> */ p5.Element.prototype.removeAttribute = function(attr) { if(this.elt.firstChild != null && (this.elt.firstChild.type === 'checkbox' || this.elt.firstChild.type === 'radio')) { for(var i=0; i<this.elt.childNodes.length; i++) { this.elt.childNodes[i].removeAttribute(attr); } } this.elt.removeAttribute(attr); return this; }; /** * Either returns the value of the element if no arguments * given, or sets the value of the element. * * @method value * @param {String|Number} [value] * @return {String|Object|p5.Element} value of element if no value is specified or p5.Element * @example * <div class='norender'><code> * // gets the value * var inp; * function setup() { * inp = createInput(''); * } * * function mousePressed() { * print(inp.value()); * } * </code></div> * <div class='norender'><code> * // sets the value * var inp; * function setup() { *