/*! p5.dom.js v0.3.1 Jan 3, 2017 */ /** *

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.

*

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

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 * using a library * section for information on how to include this library. p5.dom comes with * p5 complete or you can download the single file * * here.

*

See tutorial: beyond the canvas * for more info on how to use this libary. * * @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 *

* function setup() { * createCanvas(100,100); * //translates canvas 50px down * select('canvas').position(100, 100); * } *
*
* // 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); *
* */ 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 *
* 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); * } * } *
*
* // 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); *
* */ 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 { 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 *
* 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 * } *
* */ p5.prototype.removeElements = function (e) { for (var i=0; i * var myDiv; * function setup() { * myDiv = createDiv('this is some text'); * } * */ /** * Creates a <p></p> 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 *
* var myP; * function setup() { * myP = createP('this is some text'); * } *
*/ /** * Creates a <span></span> 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 *
* var mySpan; * function setup() { * mySpan = createSpan('this is some text'); * } *
*/ 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 <img> 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 *
* var img; * function setup() { * img = createImg('http://p5js.org/img/asterisk-01.png'); * } *
*/ 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 <a></a> 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 *
* var myLink; * function setup() { * myLink = createA('http://p5js.org/', 'this is a link'); * } *
*/ 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 <input></input> 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 *
* 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); * } *
* *
* 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); * } *
*/ 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 <button></button> 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 *
* 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); * } *
*/ p5.prototype.createButton = function(label, value) { var elt = document.createElement('button'); elt.innerHTML = label; elt.value = value; if (value) elt.value = value; return addElement(elt, this); }; /** * Creates a checkbox <input></input> 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 *
* var checkbox; * * function setup() { * checkbox = createCheckbox('label', false); * checkbox.changed(myCheckedEvent); * } * * function myCheckedEvent() { * if (this.checked()) { * console.log("Checking!"); * } else { * console.log("Unchecking!"); * } * } *
*/ 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 <select></select> element in the DOM. * @method createSelect * @param {boolean} [multiple] [true if dropdown should support multiple selections] * @return {Object/p5.Element} pointer to p5.Element holding created node * @example *
* 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); * } *
*/ p5.prototype.createSelect = function(mult) { var elt = document.createElement('select'); if (mult){ elt.setAttribute('multiple', 'true'); } var self = addElement(elt, this); self.option = function(name, value){ 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 (mult){ 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 <input></input> 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 *
* 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); * } *
*
* 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); * } * } *
*/ 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 <input></input> 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 *
* function setup(){ * var inp = createInput(''); * inp.input(myInputEvent); * } * * function myInputEvent(){ * console.log('you are typing: ', this.value()); * } * *
*/ 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 <input></input> 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