diff --git a/preview/index.html b/preview/index.html index deb1e21e11..ac5bedefcc 100644 --- a/preview/index.html +++ b/preview/index.html @@ -20,8 +20,12 @@ import p5 from '../src/app.js'; const sketch = function (p) { + let g, f; + p.setup = function () { p.createCanvas(200, 200); + g = p.createGraphics(200, 200); + f = p.createGraphics(200, 200, p.WEBGL); }; p.draw = function () { @@ -31,6 +35,10 @@ p.fill('white'); p.textSize(30); p.text('hello', 10, 30); + + // f.fill('red'); + f.sphere(); + p.image(f, 0, 0); }; }; diff --git a/src/app.js b/src/app.js index 210f0c444d..85c99e2e11 100644 --- a/src/app.js +++ b/src/app.js @@ -5,9 +5,6 @@ import './core/friendly_errors/validate_params'; import './core/friendly_errors/file_errors'; import './core/friendly_errors/fes_core'; import './core/friendly_errors/sketch_reader'; -import './core/p5.Element'; -// import './core/p5.Graphics'; -// import './core/rendering'; import shape from './shape'; shape(p5); diff --git a/src/core/main.js b/src/core/main.js index 3b58026346..6914a754da 100644 --- a/src/core/main.js +++ b/src/core/main.js @@ -665,7 +665,7 @@ import rendering from './rendering'; import renderer from './p5.Renderer'; import renderer2D from './p5.Renderer2D'; import graphics from './p5.Graphics'; -import element from './p5.Element'; +// import element from './p5.Element'; p5.registerAddon(transform); p5.registerAddon(structure); @@ -674,6 +674,6 @@ p5.registerAddon(rendering); p5.registerAddon(renderer); p5.registerAddon(renderer2D); p5.registerAddon(graphics); -p5.registerAddon(element); +// p5.registerAddon(element); export default p5; diff --git a/src/core/p5.Element.js b/src/core/p5.Element.js deleted file mode 100644 index fdfa402a65..0000000000 --- a/src/core/p5.Element.js +++ /dev/null @@ -1,998 +0,0 @@ -/** - * @module DOM - * @submodule DOM - * @for p5.Element - */ - -class Element { - constructor(elt, pInst) { - this.elt = elt; - this._pInst = this._pixelsState = pInst; - this._events = {}; - this.width = this.elt.offsetWidth; - this.height = this.elt.offsetHeight; - } - - /** - * Attaches the element to a parent element. - * - * For example, a `<div></div>` element may be used as a box to - * hold two pieces of text, a header and a paragraph. The - * `<div></div>` is the parent element of both the header and - * paragraph. - * - * The parameter `parent` can have one of three types. `parent` can be a - * string with the parent element's ID, as in - * `myElement.parent('container')`. It can also be another - * p5.Element object, as in - * `myElement.parent(myDiv)`. Finally, `parent` can be an `HTMLElement` - * object, as in `myElement.parent(anotherElement)`. - * - * Calling `myElement.parent()` without an argument returns the element's - * parent. - * - * @param {String|p5.Element|Object} parent ID, p5.Element, - * or HTMLElement of desired parent element. - * @chainable - * - * @example - *
- * - * function setup() { - * background(200); - * - * // Create a div element. - * let div = createDiv(); - * - * // Place the div in the top-left corner. - * div.position(10, 20); - * - * // Set its width and height. - * div.size(80, 60); - * - * // Set its background color to white - * div.style('background-color', 'white'); - * - * // Align any text to the center. - * div.style('text-align', 'center'); - * - * // Set its ID to "container". - * div.id('container'); - * - * // Create a paragraph element. - * let p = createP('p5*js'); - * - * // Make the div its parent - * // using its ID "container". - * p.parent('container'); - * - * describe('The text "p5*js" written in black at the center of a white rectangle. The rectangle is inside a gray square.'); - * } - * - *
- * - *
- * - * function setup() { - * background(200); - * - * // Create rectangular div element. - * let div = createDiv(); - * - * // Place the div in the top-left corner. - * div.position(10, 20); - * - * // Set its width and height. - * div.size(80, 60); - * - * // Set its background color and align - * // any text to the center. - * div.style('background-color', 'white'); - * div.style('text-align', 'center'); - * - * // Create a paragraph element. - * let p = createP('p5*js'); - * - * // Make the div its parent. - * p.parent(div); - * - * describe('The text "p5*js" written in black at the center of a white rectangle. The rectangle is inside a gray square.'); - * } - * - *
- * - *
- * - * function setup() { - * background(200); - * - * // Create rectangular div element. - * let div = createDiv(); - * - * // Place the div in the top-left corner. - * div.position(10, 20); - * - * // Set its width and height. - * div.size(80, 60); - * - * // Set its background color and align - * // any text to the center. - * div.style('background-color', 'white'); - * div.style('text-align', 'center'); - * - * // Create a paragraph element. - * let p = createP('p5*js'); - * - * // Make the div its parent - * // using the underlying - * // HTMLElement. - * p.parent(div.elt); - * - * describe('The text "p5*js" written in black at the center of a white rectangle. The rectangle is inside a gray square.'); - * } - * - *
- */ - /** - * @return {p5.Element} - */ - parent(p) { - if (typeof p === 'undefined') { - return this.elt.parentNode; - } - - if (typeof p === 'string') { - if (p[0] === '#') { - p = p.substring(1); - } - p = document.getElementById(p); - } else if (p instanceof Element) { - p = p.elt; - } - p.appendChild(this.elt); - return this; - } - - /** - * Sets the element's ID using a given string. - * - * Calling `myElement.id()` without an argument returns its ID as a string. - * - * @param {String} id ID of the element. - * @chainable - * - * @example - *
- * - * function setup() { - * // Create a canvas element and - * // assign it to cnv. - * let cnv = createCanvas(100, 100); - * - * background(200); - * - * // Set the canvas' ID - * // to "mycanvas". - * cnv.id('mycanvas'); - * - * // Get the canvas' ID. - * let id = cnv.id(); - * text(id, 24, 54); - * - * describe('The text "mycanvas" written in black on a gray background.'); - * } - * - *
- */ - /** - * @return {String} ID of the element. - */ - id(id) { - if (typeof id === 'undefined') { - return this.elt.id; - } - - this.elt.id = id; - this.width = this.elt.offsetWidth; - this.height = this.elt.offsetHeight; - return this; - } - - /** - * Adds a - * class attribute - * to the element using a given string. - * - * Calling `myElement.class()` without an argument returns a string with its current classes. - * - * @param {String} class class to add. - * @chainable - * - * @example - *
- * - * function setup() { - * // Create a canvas element and - * // assign it to cnv. - * let cnv = createCanvas(100, 100); - * - * background(200); - * - * // Add the class "small" to the - * // canvas element. - * cnv.class('small'); - * - * // Get the canvas element's class - * // and display it. - * let c = cnv.class(); - * text(c, 35, 54); - * - * describe('The word "small" written in black on a gray canvas.'); - * - * } - * - *
- */ - /** - * @return {String} element's classes, if any. - */ - class(c) { - if (typeof c === 'undefined') { - return this.elt.className; - } - - this.elt.className = c; - return this; - } - - /** - * Calls a function when the mouse is pressed over the element. - * - * Calling `myElement.mousePressed(false)` disables the function. - * - * Note: Some mobile browsers may also trigger this event when the element - * receives a quick tap. - * - * @param {Function|Boolean} fxn function to call when the mouse is - * pressed over the element. - * `false` disables the function. - * @chainable - * - * @example - *
- * - * function setup() { - * // Create a canvas element and - * // assign it to cnv. - * let cnv = createCanvas(100, 100); - * - * background(200); - * - * // Call randomColor() when the canvas - * // is pressed. - * cnv.mousePressed(randomColor); - * - * describe('A gray square changes color when the mouse is pressed.'); - * } - * - * // Paint the background either - * // red, yellow, blue, or green. - * function randomColor() { - * let c = random(['red', 'yellow', 'blue', 'green']); - * background(c); - * } - * - *
- */ - mousePressed(fxn) { - // Prepend the mouse property setters to the event-listener. - // This is required so that mouseButton is set correctly prior to calling the callback (fxn). - // For details, see https://github.com/processing/p5.js/issues/3087. - const eventPrependedFxn = function (event) { - this._pInst.mouseIsPressed = true; - this._pInst._setMouseButton(event); - // Pass along the return-value of the callback: - return fxn.call(this, event); - }; - // Pass along the event-prepended form of the callback. - Element._adjustListener('mousedown', eventPrependedFxn, this); - return this; - } - - /** - * Calls a function when the mouse is pressed twice over the element. - * - * Calling `myElement.doubleClicked(false)` disables the function. - * - * @param {Function|Boolean} fxn function to call when the mouse is - * double clicked over the element. - * `false` disables the function. - * @chainable - * - * @example - *
- * - * function setup() { - * // Create a canvas element and - * // assign it to cnv. - * let cnv = createCanvas(100, 100); - * - * background(200); - * - * // Call randomColor() when the - * // canvas is double-clicked. - * cnv.doubleClicked(randomColor); - * - * describe('A gray square changes color when the user double-clicks the canvas.'); - * } - * - * // Paint the background either - * // red, yellow, blue, or green. - * function randomColor() { - * let c = random(['red', 'yellow', 'blue', 'green']); - * background(c); - * } - * - *
- */ - doubleClicked(fxn) { - Element._adjustListener('dblclick', fxn, this); - return this; - } - - /** - * Calls a function when the mouse wheel scrolls over the element. - * - * The callback function, `fxn`, is passed an `event` object. `event` has - * two numeric properties, `deltaY` and `deltaX`. `event.deltaY` is - * negative if the mouse wheel rotates away from the user. It's positive if - * the mouse wheel rotates toward the user. `event.deltaX` is positive if - * the mouse wheel moves to the right. It's negative if the mouse wheel moves - * to the left. - * - * Calling `myElement.mouseWheel(false)` disables the function. - * - * @param {Function|Boolean} fxn function to call when the mouse wheel is - * scrolled over the element. - * `false` disables the function. - * @chainable - * - * @example - *
- * - * function setup() { - * // Create a canvas element and - * // assign it to cnv. - * let cnv = createCanvas(100, 100); - * - * background(200); - * - * // Call randomColor() when the - * // mouse wheel moves. - * cnv.mouseWheel(randomColor); - * - * describe('A gray square changes color when the user scrolls the mouse wheel over the canvas.'); - * } - * - * // Paint the background either - * // red, yellow, blue, or green. - * function randomColor() { - * let c = random(['red', 'yellow', 'blue', 'green']); - * background(c); - * } - * - *
- * - *
- * - * function setup() { - * // Create a canvas element and - * // assign it to cnv. - * let cnv = createCanvas(100, 100); - * - * background(200); - * - * // Call changeBackground() when the - * // mouse wheel moves. - * cnv.mouseWheel(changeBackground); - * - * describe('A gray square. When the mouse wheel scrolls over the square, it changes color and displays shapes.'); - * } - * - * function changeBackground(event) { - * // Change the background color - * // based on deltaY. - * if (event.deltaY > 0) { - * background('deeppink'); - * } else if (event.deltaY < 0) { - * background('cornflowerblue'); - * } else { - * background(200); - * } - * - * // Draw a shape based on deltaX. - * if (event.deltaX > 0) { - * circle(50, 50, 20); - * } else if (event.deltaX < 0) { - * square(40, 40, 20); - * } - * } - * - *
- */ - mouseWheel(fxn) { - Element._adjustListener('wheel', fxn, this); - return this; - } - - /** - * Calls a function when the mouse is released over the element. - * - * Calling `myElement.mouseReleased(false)` disables the function. - * - * Note: Some mobile browsers may also trigger this event when the element - * receives a quick tap. - * - * @param {Function|Boolean} fxn function to call when the mouse is - * pressed over the element. - * `false` disables the function. - * @chainable - * - * @example - *
- * - * function setup() { - * // Create a canvas element and - * // assign it to cnv. - * let cnv = createCanvas(100, 100); - * - * background(200); - * - * // Call randomColor() when a - * // mouse press ends. - * cnv.mouseReleased(randomColor); - * - * describe('A gray square changes color when the user releases a mouse press.'); - * } - * - * // Paint the background either - * // red, yellow, blue, or green. - * function randomColor() { - * let c = random(['red', 'yellow', 'blue', 'green']); - * background(c); - * } - * - *
- */ - mouseReleased(fxn) { - Element._adjustListener('mouseup', fxn, this); - return this; - } - - /** - * Calls a function when the mouse is pressed and released over the element. - * - * Calling `myElement.mouseReleased(false)` disables the function. - * - * Note: Some mobile browsers may also trigger this event when the element - * receives a quick tap. - * - * @param {Function|Boolean} fxn function to call when the mouse is - * pressed and released over the element. - * `false` disables the function. - * @chainable - * - * @example - *
- * - * function setup() { - * // Create a canvas element and - * // assign it to cnv. - * let cnv = createCanvas(100, 100); - * - * background(200); - * - * // Call randomColor() when a - * // mouse press ends. - * cnv.mouseClicked(randomColor); - * - * describe('A gray square changes color when the user releases a mouse press.'); - * } - * - * // Paint the background either - * // red, yellow, blue, or green. - * function randomColor() { - * let c = random(['red', 'yellow', 'blue', 'green']); - * background(c); - * } - * - *
- */ - mouseClicked(fxn) { - Element._adjustListener('click', fxn, this); - return this; - } - - /** - * Calls a function when the mouse moves over the element. - * - * Calling `myElement.mouseMoved(false)` disables the function. - * - * @param {Function|Boolean} fxn function to call when the mouse - * moves over the element. - * `false` disables the function. - * @chainable - * - * @example - *
- * - * function setup() { - * // Create a canvas element and - * // assign it to cnv. - * let cnv = createCanvas(100, 100); - * - * background(200); - * - * // Call randomColor() when the - * // mouse moves. - * cnv.mouseMoved(randomColor); - * - * describe('A gray square changes color when the mouse moves over the canvas.'); - * } - * - * // Paint the background either - * // red, yellow, blue, or green. - * function randomColor() { - * let c = random(['red', 'yellow', 'blue', 'green']); - * background(c); - * } - * - *
- */ - mouseMoved(fxn) { - Element._adjustListener('mousemove', fxn, this); - return this; - } - - /** - * Calls a function when the mouse moves onto the element. - * - * Calling `myElement.mouseOver(false)` disables the function. - * - * @param {Function|Boolean} fxn function to call when the mouse - * moves onto the element. - * `false` disables the function. - * @chainable - * - * @example - *
- * - * function setup() { - * // Create a canvas element and - * // assign it to cnv. - * let cnv = createCanvas(100, 100); - * - * background(200); - * - * // Call randomColor() when the - * // mouse moves onto the canvas. - * cnv.mouseOver(randomColor); - * - * describe('A gray square changes color when the mouse moves onto the canvas.'); - * } - * - * // Paint the background either - * // red, yellow, blue, or green. - * function randomColor() { - * let c = random(['red', 'yellow', 'blue', 'green']); - * background(c); - * } - * - *
- */ - mouseOver(fxn) { - Element._adjustListener('mouseover', fxn, this); - return this; - } - - /** - * Calls a function when the mouse moves off the element. - * - * Calling `myElement.mouseOut(false)` disables the function. - * - * @param {Function|Boolean} fxn function to call when the mouse - * moves off the element. - * `false` disables the function. - * @chainable - * - * @example - *
- * - * function setup() { - * // Create a canvas element and - * // assign it to cnv. - * let cnv = createCanvas(100, 100); - * - * background(200); - * - * // Call randomColor() when the - * // mouse moves off the canvas. - * cnv.mouseOut(randomColor); - * - * describe('A gray square changes color when the mouse moves off the canvas.'); - * } - * - * // Paint the background either - * // red, yellow, blue, or green. - * function randomColor() { - * let c = random(['red', 'yellow', 'blue', 'green']); - * background(c); - * } - * - *
- */ - mouseOut(fxn) { - Element._adjustListener('mouseout', fxn, this); - return this; - } - - /** - * Calls a function when the element is touched. - * - * Calling `myElement.touchStarted(false)` disables the function. - * - * Note: Touch functions only work on mobile devices. - * - * @param {Function|Boolean} fxn function to call when the touch - * starts. - * `false` disables the function. - * @chainable - * - * @example - *
- * - * function setup() { - * // Create a canvas element and - * // assign it to cnv. - * let cnv = createCanvas(100, 100); - * - * background(200); - * - * // Call randomColor() when the - * // user touches the canvas. - * cnv.touchStarted(randomColor); - * - * describe('A gray square changes color when the user touches the canvas.'); - * } - * - * // Paint the background either - * // red, yellow, blue, or green. - * function randomColor() { - * let c = random(['red', 'yellow', 'blue', 'green']); - * background(c); - * } - * - *
- */ - touchStarted(fxn) { - Element._adjustListener('touchstart', fxn, this); - return this; - } - - /** - * Calls a function when the user touches the element and moves. - * - * Calling `myElement.touchMoved(false)` disables the function. - * - * Note: Touch functions only work on mobile devices. - * - * @param {Function|Boolean} fxn function to call when the touch - * moves over the element. - * `false` disables the function. - * @chainable - * @example - *
- * - * function setup() { - * // Create a canvas element and - * // assign it to cnv. - * let cnv = createCanvas(100, 100); - * - * background(200); - * - * // Call randomColor() when the - * // user touches the canvas - * // and moves. - * cnv.touchMoved(randomColor); - * - * describe('A gray square changes color when the user touches the canvas and moves.'); - * } - * - * // Paint the background either - * // red, yellow, blue, or green. - * function randomColor() { - * let c = random(['red', 'yellow', 'blue', 'green']); - * background(c); - * } - * - *
- */ - touchMoved(fxn) { - Element._adjustListener('touchmove', fxn, this); - return this; - } - - /** - * Calls a function when the user stops touching the element. - * - * Calling `myElement.touchMoved(false)` disables the function. - * - * Note: Touch functions only work on mobile devices. - * - * @param {Function|Boolean} fxn function to call when the touch - * ends. - * `false` disables the function. - * @chainable - * @example - *
- * - * function setup() { - * // Create a canvas element and - * // assign it to cnv. - * let cnv = createCanvas(100, 100); - * - * background(200); - * - * // Call randomColor() when the - * // user touches the canvas, - * // then lifts their finger. - * cnv.touchEnded(randomColor); - * - * describe('A gray square changes color when the user touches the canvas, then lifts their finger.'); - * } - * - * // Paint the background either - * // red, yellow, blue, or green. - * function randomColor() { - * let c = random(['red', 'yellow', 'blue', 'green']); - * background(c); - * } - * - *
- */ - touchEnded(fxn) { - Element._adjustListener('touchend', fxn, this); - return this; - } - - /** - * Calls a function when a file is dragged over the element. - * - * Calling `myElement.dragOver(false)` disables the function. - * - * @param {Function|Boolean} fxn function to call when the file is - * dragged over the element. - * `false` disables the function. - * @chainable - * - * @example - *
- * - * // Drag a file over the canvas to test. - * - * function setup() { - * // Create a canvas element and - * // assign it to cnv. - * let cnv = createCanvas(100, 100); - * - * background(200); - * - * // Call helloFile() when a - * // file is dragged over - * // the canvas. - * cnv.dragOver(helloFile); - * - * describe('A gray square. The text "hello, file" appears when a file is dragged over the square.'); - * } - * - * function helloFile() { - * text('hello, file', 50, 50); - * } - * - *
- */ - dragOver(fxn) { - Element._adjustListener('dragover', fxn, this); - return this; - } - - /** - * Calls a function when a file is dragged off the element. - * - * Calling `myElement.dragLeave(false)` disables the function. - * - * @param {Function|Boolean} fxn function to call when the file is - * dragged off the element. - * `false` disables the function. - * @chainable - * @example - *
- * - * // Drag a file over, then off - * // the canvas to test. - * - * function setup() { - * // Create a canvas element and - * // assign it to cnv. - * let cnv = createCanvas(100, 100); - * - * background(200); - * - * // Call byeFile() when a - * // file is dragged over, - * // then off the canvas. - * cnv.dragLeave(byeFile); - * - * describe('A gray square. The text "bye, file" appears when a file is dragged over, then off the square.'); - * } - * - * function byeFile() { - * text('bye, file', 50, 50); - * } - * - *
- */ - dragLeave(fxn) { - Element._adjustListener('dragleave', fxn, this); - return this; - } - - - /** - * - * @private - * @static - * @param {String} ev - * @param {Boolean|Function} fxn - * @param {Element} ctx - * @chainable - * @alt - * General handler for event attaching and detaching - */ - static _adjustListener(ev, fxn, ctx) { - if (fxn === false) { - Element._detachListener(ev, ctx); - } else { - Element._attachListener(ev, fxn, ctx); - } - return this; - } - /** - * - * @private - * @static - * @param {String} ev - * @param {Function} fxn - * @param {Element} ctx - */ - static _attachListener(ev, fxn, ctx) { - // detach the old listener if there was one - if (ctx._events[ev]) { - Element._detachListener(ev, ctx); - } - const f = fxn.bind(ctx); - ctx.elt.addEventListener(ev, f, false); - ctx._events[ev] = f; - } - /** - * - * @private - * @static - * @param {String} ev - * @param {Element} ctx - */ - static _detachListener(ev, ctx) { - const f = ctx._events[ev]; - ctx.elt.removeEventListener(ev, f, false); - ctx._events[ev] = null; - } -}; - -function element(p5, fn){ - /** - * A class to describe an - * HTML element. - * - * Sketches can use many elements. Common elements include the drawing canvas, - * buttons, sliders, webcam feeds, and so on. - * - * All elements share the methods of the `p5.Element` class. They're created - * with functions such as createCanvas() and - * createButton(). - * - * @class p5.Element - * @param {HTMLElement} elt wrapped DOM element. - * @param {p5} [pInst] pointer to p5 instance. - * - * @example - *
- * - * function setup() { - * createCanvas(100, 100); - * - * background(200); - * - * // Create a button element and - * // place it beneath the canvas. - * let btn = createButton('change'); - * btn.position(0, 100); - * - * // Call randomColor() when - * // the button is pressed. - * btn.mousePressed(randomColor); - * - * describe('A gray square with a button that says "change" beneath it. The square changes color when the user presses the button.'); - * } - * - * // Paint the background either - * // red, yellow, blue, or green. - * function randomColor() { - * let c = random(['red', 'yellow', 'blue', 'green']); - * background(c); - * } - * - *
- */ - p5.Element = Element; - - /** - * The element's underlying `HTMLElement` object. - * - * The - * HTMLElement - * object's properties and methods can be used directly. - * - * @example - *
- * - * function setup() { - * // Create a canvas element and - * // assign it to cnv. - * let cnv = createCanvas(100, 100); - * - * background(200); - * - * // Set the border style for the - * // canvas. - * cnv.elt.style.border = '5px dashed deeppink'; - * - * describe('A gray square with a pink border drawn with dashed lines.'); - * } - * - *
- * - * @property elt - * @for p5.Element - * @name elt - * @readOnly - */ - - /** - * A `Number` property that stores the element's width. - * - * @type {Number} - * @property width - * @for p5.Element - */ - - /** - * A `Number` property that stores the element's height. - * - * @type {Number} - * @property height - * @for p5.Element - */ -} - -export default element; -export { Element }; diff --git a/src/core/p5.Graphics.js b/src/core/p5.Graphics.js index b86e094f90..65315ba989 100644 --- a/src/core/p5.Graphics.js +++ b/src/core/p5.Graphics.js @@ -30,39 +30,6 @@ class Graphics { this._pInst = pInst; this._renderer = new renderers[r](this._pInst, w, h, false, canvas); - // Attach renderer methods - // for(const p of Object.getOwnPropertyNames(p5.renderers[r].prototype)) { - // if( - // p !== 'constructor' && - // p[0] !== '_' && - // !(p in this) && - // typeof this._renderer[p] === 'function' - // ){ - // this[p] = this._renderer[p].bind(this._renderer); - // } - // } - - // Attach renderer properties - for (const p in this._renderer) { - if(p[0] === '_' || typeof this._renderer[p] === 'function') continue; - Object.defineProperty(this, p, { - get(){ - return this._renderer?.[p]; - } - }) - } - - // bind methods and props of p5 to the new object - // for (const p in p5.prototype) { - // if (!this[p]) { - // // console.log(p); - // if (typeof p5.prototype[p] === 'function') { - // this[p] = p5.prototype[p].bind(this); - // } else if(p !== 'deltaTime') { - // this[p] = p5.prototype[p]; - // } - // } - // } p5.prototype._initializeInstanceVariables.apply(this); this._renderer._applyDefaults(); @@ -73,21 +40,25 @@ class Graphics { return this._pInst.deltaTime; } - // get canvas(){ - // return this._renderer.canvas; - // } + get canvas(){ + return this._renderer?.canvas; + } - // get drawingContext(){ - // return this._renderer.drawingContext; - // } + get drawingContext(){ + return this._renderer.drawingContext; + } - // get width(){ - // return this._renderer.width; - // } + get width(){ + return this._renderer?.width; + } + + get height(){ + return this._renderer?.height; + } - // get height(){ - // return this._renderer.height; - // } + get pixels(){ + return this._renderer?.pixels; + } pixelDensity(val){ let returnValue; diff --git a/src/core/p5.Renderer2D.js b/src/core/p5.Renderer2D.js index 634f11727e..75cf1b953d 100644 --- a/src/core/p5.Renderer2D.js +++ b/src/core/p5.Renderer2D.js @@ -1,10 +1,10 @@ import * as constants from './constants'; import p5 from './main'; import { Renderer } from './p5.Renderer'; -import { Element } from './p5.Element'; import { Graphics } from './p5.Graphics'; import { Image } from '../image/p5.Image'; -import { MediaElement } from '../dom/media_element'; +import { Element } from '../dom/p5.Element'; +import { MediaElement } from '../dom/p5.MediaElement'; const styleEmpty = 'rgba(0,0,0,0)'; // const alphaThreshold = 0.00125; // minimum visible diff --git a/src/dom/dom.js b/src/dom/dom.js index d7a4eeedad..dfea7a84f2 100644 --- a/src/dom/dom.js +++ b/src/dom/dom.js @@ -16,6 +16,10 @@ * @requires p5 */ +import { Element } from './p5.Element'; +import { MediaElement } from './p5.MediaElement'; +import { File } from './p5.File'; + function dom(p5, fn){ /** * Searches the page for the first element that matches the given @@ -223,7 +227,7 @@ function dom(p5, fn){ fn._wrapElement = function (elt) { const children = Array.prototype.slice.call(elt.children); if (elt.tagName === 'INPUT' && elt.type === 'checkbox') { - let converted = new p5.Element(elt, this); + let converted = new Element(elt, this); converted.checked = function (...args) { if (args.length === 0) { return this.elt.checked; @@ -236,9 +240,9 @@ function dom(p5, fn){ }; return converted; } else if (elt.tagName === 'VIDEO' || elt.tagName === 'AUDIO') { - return new p5.MediaElement(elt, this); + return new MediaElement(elt, this); } else if (elt.tagName === 'SELECT') { - return this.createSelect(new p5.Element(elt, this)); + return this.createSelect(new Element(elt, this)); } else if ( children.length > 0 && children.every(function (c) { @@ -246,9 +250,9 @@ function dom(p5, fn){ }) && (elt.tagName === 'DIV' || elt.tagName === 'SPAN') ) { - return this.createRadio(new p5.Element(elt, this)); + return this.createRadio(new Element(elt, this)); } else { - return new p5.Element(elt, this); + return new Element(elt, this); } }; @@ -326,79 +330,6 @@ function dom(p5, fn){ removeableElements.map(el => el.remove()); }; - /** - * Calls a function when the element receives input. - * - * `myElement.input()` is often used to with text inputs and sliders. Calling - * `myElement.input(false)` disables the function. - * - * @method input - * @param {Function|Boolean} fxn function to call when input is detected within - * the element. - * `false` disables the function. - * @chainable - * - * @example - *
- * - * let slider; - * - * function setup() { - * createCanvas(100, 100); - * - * background(200); - * - * // Create a slider and place it beneath the canvas. - * slider = createSlider(0, 255, 200); - * slider.position(0, 100); - * - * // Call repaint() when the slider changes. - * slider.input(repaint); - * - * describe('A gray square with a range slider underneath it. The background changes shades of gray when the slider is moved.'); - * } - * - * // Paint the background using slider's value. - * function repaint() { - * let g = slider.value(); - * background(g); - * } - * - *
- * - *
- * - * let input; - * - * function setup() { - * createCanvas(100, 100); - * - * background(200); - * - * // Create an input and place it beneath the canvas. - * input = createInput(''); - * input.position(0, 100); - * - * // Call repaint() when input is detected. - * input.input(repaint); - * - * describe('A gray square with a text input bar beneath it. Any text written in the input appears in the middle of the square.'); - * } - * - * // Paint the background gray and display the input's value. - * function repaint() { - * background(200); - * let msg = input.value(); - * text(msg, 5, 50); - * } - * - *
- */ - p5.Element.prototype.input = function (fxn) { - p5.Element._adjustListener('input', fxn, this); - return this; - }; - /** * Helpers for create methods. */ @@ -406,8 +337,8 @@ function dom(p5, fn){ const node = pInst._userNode ? pInst._userNode : document.body; node.appendChild(elt); const c = media - ? new p5.MediaElement(elt, pInst) - : new p5.Element(elt, pInst); + ? new MediaElement(elt, pInst) + : new Element(elt, pInst); pInst._elements.push(c); return c; } @@ -708,8 +639,7 @@ function dom(p5, fn){ return addElement(elt, this); }; - /** INPUT **/ - + /* INPUT */ /** * Creates a slider `<input></input>` element. * @@ -1952,7 +1882,7 @@ function dom(p5, fn){ const handleFileSelect = function (event) { for (const file of event.target.files) { - p5.File._load(file, callback); + File._load(file, callback); } }; diff --git a/src/dom/index.js b/src/dom/index.js index 39c2528376..2bb5771c1e 100644 --- a/src/dom/index.js +++ b/src/dom/index.js @@ -1,6 +1,6 @@ import dom from './dom'; -import element from './element'; -import media from './media_element'; +import element from './p5.Element'; +import media from './p5.MediaElement'; import file from './p5.File'; export default function(p5){ @@ -8,4 +8,4 @@ export default function(p5){ p5.registerAddon(element); p5.registerAddon(media); p5.registerAddon(file); -} \ No newline at end of file +} diff --git a/src/dom/element.js b/src/dom/p5.Element.js similarity index 59% rename from src/dom/element.js rename to src/dom/p5.Element.js index b10aafba8d..3a896af89a 100644 --- a/src/dom/element.js +++ b/src/dom/p5.Element.js @@ -1,250 +1,270 @@ -function element(p5, fn){ - // ============================================================================= - // p5.Element additions - // ============================================================================= +/** + * @module DOM + * @submodule DOM + * @for p5.Element + */ + +import { File } from './p5.File'; +import { Color } from '../color/p5.Color'; +import * as constants from '../core/constants'; +class Element { /** - * Calls a function when the element changes. + * A `Number` property that stores the element's width. * - * Calling `myElement.changed(false)` disables the function. + * @type {Number} + * @property width + * @for p5.Element + */ + width; + /** + * A `Number` property that stores the element's height. * - * @method changed - * @param {Function|Boolean} fxn function to call when the element changes. - * `false` disables the function. - * @chainable + * @type {Number} + * @property height + * @for p5.Element + */ + height; + /** + * The element's underlying `HTMLElement` object. + * + * The + * HTMLElement + * object's properties and methods can be used directly. * * @example *
* - * let dropdown; - * * function setup() { - * createCanvas(100, 100); + * // Create a canvas element and + * // assign it to cnv. + * let cnv = createCanvas(100, 100); * * background(200); * - * // Create a dropdown menu and add a few color options. - * dropdown = createSelect(); - * dropdown.position(0, 0); - * dropdown.option('red'); - * dropdown.option('green'); - * dropdown.option('blue'); - * - * // Call paintBackground() when the color option changes. - * dropdown.changed(paintBackground); - * - * describe('A gray square with a dropdown menu at the top. The square changes color when an option is selected.'); - * } + * // Set the border style for the + * // canvas. + * cnv.elt.style.border = '5px dashed deeppink'; * - * // Paint the background with the selected color. - * function paintBackground() { - * let c = dropdown.value(); - * background(c); + * describe('A gray square with a pink border drawn with dashed lines.'); * } * *
* - *
- * - * let checkbox; - * - * function setup() { - * createCanvas(100, 100); - * - * background(200); - * - * // Create a checkbox and place it beneath the canvas. - * checkbox = createCheckbox(' circle'); - * checkbox.position(0, 100); - * - * // Call repaint() when the checkbox changes. - * checkbox.changed(repaint); - * - * describe('A gray square with a checkbox underneath it that says "circle". A white circle appears when the box is checked and disappears otherwise.'); - * } - * - * // Paint the background gray and determine whether to draw a circle. - * function repaint() { - * background(200); - * if (checkbox.checked() === true) { - * circle(50, 50, 30); - * } - * } - * - *
+ * @property elt + * @for p5.Element + * @name elt + * @readOnly */ - p5.Element.prototype.changed = function (fxn) { - p5.Element._adjustListener('change', fxn, this); - return this; - }; + elt; + + constructor(elt, pInst) { + this.elt = elt; + this._pInst = this._pixelsState = pInst; + this._events = {}; + this.width = this.elt.offsetWidth; + this.height = this.elt.offsetHeight; + } /** + * Removes the element, stops all audio/video streams, and removes all + * callback functions. * - * Adds a class to the element. - * - * @for p5.Element - * @method addClass - * @param {String} class name of class to add. - * @chainable + * @method remove * * @example - *
+ *
* + * let p; + * * function setup() { * createCanvas(100, 100); * * background(200); * - * // Create a div element. - * let div = createDiv('div'); + * // Create a paragraph element. + * p = createP('p5*js'); + * p.position(10, 10); * - * // Add a class to the div. - * div.addClass('myClass'); + * describe('The text "p5*js" written at the center of a gray square. '); + * } * - * describe('A gray square.'); + * // Remove the paragraph when the user double-clicks. + * function doubleClicked() { + * p.remove(); * } * *
*/ - p5.Element.prototype.addClass = function (c) { - if (this.elt.className) { - if (!this.hasClass(c)) { - this.elt.className = this.elt.className + ' ' + c; + remove() { + // stop all audios/videos and detach all devices like microphone/camera etc + // used as input/output for audios/videos. + // if (this instanceof p5.MediaElement) { + if(this.stop){ + this.stop(); + const sources = this.elt.srcObject; + if (sources !== null) { + const tracks = sources.getTracks(); + tracks.forEach(track => { + track.stop(); + }); } - } else { - this.elt.className = c; } - return this; - }; + + // delete the reference in this._pInst._elements + const index = this._pInst._elements.indexOf(this); + if (index !== -1) { + this._pInst._elements.splice(index, 1); + } + + // deregister events + for (let ev in this._events) { + this.elt.removeEventListener(ev, this._events[ev]); + } + if (this.elt && this.elt.parentNode) { + this.elt.parentNode.removeChild(this.elt); + } + } /** - * Removes a class from the element. + * Attaches the element to a parent element. * - * @method removeClass - * @param {String} class name of class to remove. + * For example, a `<div></div>` element may be used as a box to + * hold two pieces of text, a header and a paragraph. The + * `<div></div>` is the parent element of both the header and + * paragraph. + * + * The parameter `parent` can have one of three types. `parent` can be a + * string with the parent element's ID, as in + * `myElement.parent('container')`. It can also be another + * p5.Element object, as in + * `myElement.parent(myDiv)`. Finally, `parent` can be an `HTMLElement` + * object, as in `myElement.parent(anotherElement)`. + * + * Calling `myElement.parent()` without an argument returns the element's + * parent. + * + * @param {String|p5.Element|Object} parent ID, p5.Element, + * or HTMLElement of desired parent element. * @chainable * * @example - *
+ *
* - * // In this example, a class is set when the div is created - * // and removed when mouse is pressed. This could link up - * // with a CSS style rule to toggle style properties. + * function setup() { + * background(200); * - * let div; + * // Create a div element. + * let div = createDiv(); * - * function setup() { - * createCanvas(100, 100); + * // Place the div in the top-left corner. + * div.position(10, 20); * - * background(200); + * // Set its width and height. + * div.size(80, 60); * - * // Create a div element. - * div = createDiv('div'); + * // Set its background color to white + * div.style('background-color', 'white'); * - * // Add a class to the div. - * div.addClass('myClass'); + * // Align any text to the center. + * div.style('text-align', 'center'); * - * describe('A gray square.'); - * } + * // Set its ID to "container". + * div.id('container'); * - * // Remove 'myClass' from the div when the user presses the mouse. - * function mousePressed() { - * div.removeClass('myClass'); + * // Create a paragraph element. + * let p = createP('p5*js'); + * + * // Make the div its parent + * // using its ID "container". + * p.parent('container'); + * + * describe('The text "p5*js" written in black at the center of a white rectangle. The rectangle is inside a gray square.'); * } * *
- */ - p5.Element.prototype.removeClass = function (c) { - // Note: Removing a class that does not exist does NOT throw an error in classList.remove method - this.elt.classList.remove(c); - return this; - }; - - /** - * Checks if a class is already applied to element. - * - * @method hasClass - * @returns {boolean} a boolean value if element has specified class. - * @param c {String} name of class to check. * - * @example - *
+ *
* - * let div; + * function setup() { + * background(200); * - * function setup() { - * createCanvas(100, 100); + * // Create rectangular div element. + * let div = createDiv(); * - * background(200); + * // Place the div in the top-left corner. + * div.position(10, 20); * - * // Create a div element. - * div = createDiv('div'); + * // Set its width and height. + * div.size(80, 60); * - * // Add the class 'show' to the div. - * div.addClass('show'); + * // Set its background color and align + * // any text to the center. + * div.style('background-color', 'white'); + * div.style('text-align', 'center'); * - * describe('A gray square.'); - * } + * // Create a paragraph element. + * let p = createP('p5*js'); * - * // Toggle the class 'show' when the mouse is pressed. - * function mousePressed() { - * if (div.hasClass('show')) { - * div.addClass('show'); - * } else { - * div.removeClass('show'); - * } + * // Make the div its parent. + * p.parent(div); + * + * describe('The text "p5*js" written in black at the center of a white rectangle. The rectangle is inside a gray square.'); * } * *
- */ - p5.Element.prototype.hasClass = function (c) { - return this.elt.classList.contains(c); - }; - - /** - * Toggles whether a class is applied to the element. - * - * @method toggleClass - * @param c {String} class name to toggle. - * @chainable * - * @example - *
+ *
* - * let div; + * function setup() { + * background(200); * - * function setup() { - * createCanvas(100, 100); + * // Create rectangular div element. + * let div = createDiv(); * - * background(200); + * // Place the div in the top-left corner. + * div.position(10, 20); * - * // Create a div element. - * div = createDiv('div'); + * // Set its width and height. + * div.size(80, 60); * - * // Add the 'show' class to the div. - * div.addClass('show'); + * // Set its background color and align + * // any text to the center. + * div.style('background-color', 'white'); + * div.style('text-align', 'center'); * - * describe('A gray square.'); - * } + * // Create a paragraph element. + * let p = createP('p5*js'); * - * // Toggle the 'show' class when the mouse is pressed. - * function mousePressed() { - * div.toggleClass('show'); + * // Make the div its parent + * // using the underlying + * // HTMLElement. + * p.parent(div.elt); + * + * describe('The text "p5*js" written in black at the center of a white rectangle. The rectangle is inside a gray square.'); * } * *
*/ - p5.Element.prototype.toggleClass = function (c) { - // classList also has a toggle() method, but we cannot use that yet as support is unclear. - // See https://github.com/processing/p5.js/issues/3631 - // this.elt.classList.toggle(c); - if (this.elt.classList.contains(c)) { - this.elt.classList.remove(c); - } else { - this.elt.classList.add(c); + /** + * @return {p5.Element} + */ + parent(p) { + if (typeof p === 'undefined') { + return this.elt.parentNode; + } + + if (typeof p === 'string') { + if (p[0] === '#') { + p = p.substring(1); + } + p = document.getElementById(p); + } else if (p instanceof Element) { + p = p.elt; } + p.appendChild(this.elt); return this; - }; + } /** * Attaches the element as a child of another element. @@ -332,7 +352,7 @@ function element(p5, fn){ * to add to the current element * @chainable */ - p5.Element.prototype.child = function (childNode) { + child(childNode) { if (typeof childNode === 'undefined') { return this.elt.childNodes; } @@ -341,7 +361,7 @@ function element(p5, fn){ childNode = childNode.substring(1); } childNode = document.getElementById(childNode); - } else if (childNode instanceof p5.Element) { + } else if (childNode instanceof Element) { childNode = childNode.elt; } @@ -349,90 +369,24 @@ function element(p5, fn){ this.elt.appendChild(childNode); } return this; - }; + } /** - * Centers the element either vertically, horizontally, or both. + * Sets the inner HTML of the element, replacing any existing HTML. * - * `center()` will center the element relative to its parent or according to - * the page's body if the element has no parent. + * The second parameter, `append`, is optional. If `true` is passed, as in + * `myElement.html('hi', true)`, the HTML is appended instead of replacing + * existing HTML. * - * If no argument is passed, as in `myElement.center()` the element is aligned - * both vertically and horizontally. + * If no arguments are passed, as in `myElement.html()`, the element's inner + * HTML is returned. * - * @method center - * @param {String} [align] passing 'vertical', 'horizontal' aligns element accordingly - * @chainable + * @for p5.Element + * @method html + * @returns {String} the inner HTML of the element * * @example - *
- * - * function setup() { - * createCanvas(100, 100); - * - * background(200); - * - * // Create the div element and style it. - * let div = createDiv(''); - * div.size(10, 10); - * div.style('background-color', 'orange'); - * - * // Center the div relative to the page's body. - * div.center(); - * - * describe('A gray square and an orange rectangle. The rectangle is at the center of the page.'); - * } - * - *
- */ - p5.Element.prototype.center = function (align) { - const style = this.elt.style.display; - const hidden = this.elt.style.display === 'none'; - const parentHidden = this.parent().style.display === 'none'; - const pos = { x: this.elt.offsetLeft, y: this.elt.offsetTop }; - - if (hidden) this.show(); - if (parentHidden) this.parent().show(); - this.elt.style.display = 'block'; - - this.position(0, 0); - const wOffset = Math.abs(this.parent().offsetWidth - this.elt.offsetWidth); - const hOffset = Math.abs(this.parent().offsetHeight - this.elt.offsetHeight); - - if (align === 'both' || align === undefined) { - this.position( - wOffset / 2 + this.parent().offsetLeft, - hOffset / 2 + this.parent().offsetTop - ); - } else if (align === 'horizontal') { - this.position(wOffset / 2 + this.parent().offsetLeft, pos.y); - } else if (align === 'vertical') { - this.position(pos.x, hOffset / 2 + this.parent().offsetTop); - } - - this.style('display', style); - if (hidden) this.hide(); - if (parentHidden) this.parent().hide(); - - return this; - }; - - /** - * Sets the inner HTML of the element, replacing any existing HTML. - * - * The second parameter, `append`, is optional. If `true` is passed, as in - * `myElement.html('hi', true)`, the HTML is appended instead of replacing - * existing HTML. - * - * If no arguments are passed, as in `myElement.html()`, the element's inner - * HTML is returned. - * - * @for p5.Element - * @method html - * @returns {String} the inner HTML of the element - * - * @example - *
+ *
* * function setup() { * createCanvas(100, 100); @@ -492,7 +446,7 @@ function element(p5, fn){ * @param {Boolean} [append] whether to append HTML to existing * @chainable */ - p5.Element.prototype.html = function (...args) { + html(...args) { if (args.length === 0) { return this.elt.innerHTML; } else if (args[1]) { @@ -502,817 +456,1834 @@ function element(p5, fn){ this.elt.innerHTML = args[0]; return this; } - }; + } /** - * Sets the element's position. + * Sets the element's ID using a given string. * - * The first two parameters, `x` and `y`, set the element's position relative - * to the top-left corner of the web page. - * - * The third parameter, `positionType`, is optional. It sets the element's - * positioning scheme. - * `positionType` is a string that can be either `'static'`, `'fixed'`, - * `'relative'`, `'sticky'`, `'initial'`, or `'inherit'`. + * Calling `myElement.id()` without an argument returns its ID as a string. * - * If no arguments passed, as in `myElement.position()`, the method returns - * the element's position in an object, as in `{ x: 0, y: 0 }`. - * - * @method position - * @returns {Object} object of form `{ x: 0, y: 0 }` containing the element's position. + * @param {String} id ID of the element. + * @chainable * * @example *
- * + * * function setup() { + * // Create a canvas element and + * // assign it to cnv. * let cnv = createCanvas(100, 100); * * background(200); * - * // Positions the canvas 50px to the right and 100px - * // below the top-left corner of the window. - * cnv.position(50, 100); + * // Set the canvas' ID + * // to "mycanvas". + * cnv.id('mycanvas'); * - * describe('A gray square that is 50 pixels to the right and 100 pixels down from the top-left corner of the web page.'); + * // Get the canvas' ID. + * let id = cnv.id(); + * text(id, 24, 54); + * + * describe('The text "mycanvas" written in black on a gray background.'); * } * *
+ */ + /** + * @return {String} ID of the element. + */ + id(id) { + if (typeof id === 'undefined') { + return this.elt.id; + } + + this.elt.id = id; + this.width = this.elt.offsetWidth; + this.height = this.elt.offsetHeight; + return this; + } + + /** + * Adds a + * class attribute + * to the element using a given string. + * + * Calling `myElement.class()` without an argument returns a string with its current classes. + * + * @param {String} class class to add. + * @chainable * + * @example *
- * + * * function setup() { + * // Create a canvas element and + * // assign it to cnv. * let cnv = createCanvas(100, 100); * * background(200); * - * // Positions the canvas at the top-left corner - * // of the window with a 'fixed' position type. - * cnv.position(0, 0, 'fixed'); + * // Add the class "small" to the + * // canvas element. + * cnv.class('small'); + * + * // Get the canvas element's class + * // and display it. + * let c = cnv.class(); + * text(c, 35, 54); + * + * describe('The word "small" written in black on a gray canvas.'); * - * describe('A gray square in the top-left corner of the web page.'); * } * *
*/ /** - * @method position - * @param {Number} [x] x-position relative to top-left of window (optional) - * @param {Number} [y] y-position relative to top-left of window (optional) - * @param {String} [positionType] it can be static, fixed, relative, sticky, initial or inherit (optional) - * @chainable + * @return {String} element's classes, if any. */ - p5.Element.prototype.position = function (...args) { - if (args.length === 0) { - return { x: this.elt.offsetLeft, y: this.elt.offsetTop }; - } else { - let positionType = 'absolute'; - if ( - args[2] === 'static' || - args[2] === 'fixed' || - args[2] === 'relative' || - args[2] === 'sticky' || - args[2] === 'initial' || - args[2] === 'inherit' - ) { - positionType = args[2]; - } - this.elt.style.position = positionType; - this.elt.style.left = args[0] + 'px'; - this.elt.style.top = args[1] + 'px'; - this.x = args[0]; - this.y = args[1]; - return this; - } - }; - - /* Helper method called by p5.Element.style() */ - p5.Element.prototype._translate = function (...args) { - this.elt.style.position = 'absolute'; - // save out initial non-translate transform styling - let transform = ''; - if (this.elt.style.transform) { - transform = this.elt.style.transform.replace(/translate3d\(.*\)/g, ''); - transform = transform.replace(/translate[X-Z]?\(.*\)/g, ''); - } - if (args.length === 2) { - this.elt.style.transform = - 'translate(' + args[0] + 'px, ' + args[1] + 'px)'; - } else if (args.length > 2) { - this.elt.style.transform = - 'translate3d(' + - args[0] + - 'px,' + - args[1] + - 'px,' + - args[2] + - 'px)'; - if (args.length === 3) { - this.elt.parentElement.style.perspective = '1000px'; - } else { - this.elt.parentElement.style.perspective = args[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 (...args) { - // save out initial non-rotate transform styling - let transform = ''; - if (this.elt.style.transform) { - transform = this.elt.style.transform.replace(/rotate3d\(.*\)/g, ''); - transform = transform.replace(/rotate[X-Z]?\(.*\)/g, ''); + class(c) { + if (typeof c === 'undefined') { + return this.elt.className; } - if (args.length === 1) { - this.elt.style.transform = 'rotate(' + args[0] + 'deg)'; - } else if (args.length === 2) { - this.elt.style.transform = - 'rotate(' + args[0] + 'deg, ' + args[1] + 'deg)'; - } else if (args.length === 3) { - this.elt.style.transform = 'rotateX(' + args[0] + 'deg)'; - this.elt.style.transform += 'rotateY(' + args[1] + 'deg)'; - this.elt.style.transform += 'rotateZ(' + args[2] + 'deg)'; - } - // add remaining transform back on - this.elt.style.transform += transform; + this.elt.className = c; return this; - }; + } /** - * Applies a style to the element by adding a - * CSS declaration. * - * The first parameter, `property`, is a string. If the name of a style - * property is passed, as in `myElement.style('color')`, the method returns - * the current value as a string or `null` if it hasn't been set. If a - * `property:style` string is passed, as in - * `myElement.style('color:deeppink')`, the method sets the style `property` - * to `value`. - * - * The second parameter, `value`, is optional. It sets the property's value. - * `value` can be a string, as in - * `myElement.style('color', 'deeppink')`, or a - * p5.Color object, as in - * `myElement.style('color', myColor)`. + * Adds a class to the element. * - * @method style - * @param {String} property style property to set. - * @returns {String} value of the property. + * @for p5.Element + * @method addClass + * @param {String} class name of class to add. + * @chainable * * @example - *
+ *
* * function setup() { * createCanvas(100, 100); * * background(200); * - * // Create a paragraph element and set its font color to "deeppink". - * let p = createP('p5*js'); - * p.position(25, 20); - * p.style('color', 'deeppink'); + * // Create a div element. + * let div = createDiv('div'); * - * describe('The text p5*js written in pink on a gray background.'); + * // Add a class to the div. + * div.addClass('myClass'); + * + * describe('A gray square.'); * } * *
+ */ + addClass(c) { + if (this.elt.className) { + if (!this.hasClass(c)) { + this.elt.className = this.elt.className + ' ' + c; + } + } else { + this.elt.className = c; + } + return this; + } + + /** + * Removes a class from the element. * - *
+ * @method removeClass + * @param {String} class name of class to remove. + * @chainable + * + * @example + *
* + * // In this example, a class is set when the div is created + * // and removed when mouse is pressed. This could link up + * // with a CSS style rule to toggle style properties. + * + * let div; + * * function setup() { * createCanvas(100, 100); * * background(200); * - * // Create a p5.Color object. - * let c = color('deeppink'); + * // Create a div element. + * div = createDiv('div'); * - * // Create a paragraph element and set its font color using a p5.Color object. - * let p = createP('p5*js'); - * p.position(25, 20); - * p.style('color', c); + * // Add a class to the div. + * div.addClass('myClass'); * - * describe('The text p5*js written in pink on a gray background.'); + * describe('A gray square.'); * } - * + * + * // Remove 'myClass' from the div when the user presses the mouse. + * function mousePressed() { + * div.removeClass('myClass'); + * } + * *
+ */ + removeClass(c) { + // Note: Removing a class that does not exist does NOT throw an error in classList.remove method + this.elt.classList.remove(c); + return this; + } + + /** + * Checks if a class is already applied to element. * - *
+ * @method hasClass + * @returns {boolean} a boolean value if element has specified class. + * @param c {String} name of class to check. + * + * @example + *
* + * let div; + * * function setup() { * createCanvas(100, 100); * * background(200); * - * // Create a paragraph element and set its font color to "deeppink" - * // using property:value syntax. - * let p = createP('p5*js'); - * p.position(25, 20); - * p.style('color:deeppink'); + * // Create a div element. + * div = createDiv('div'); * - * describe('The text p5*js written in pink on a gray background.'); + * // Add the class 'show' to the div. + * div.addClass('show'); + * + * describe('A gray square.'); + * } + * + * // Toggle the class 'show' when the mouse is pressed. + * function mousePressed() { + * if (div.hasClass('show')) { + * div.addClass('show'); + * } else { + * div.removeClass('show'); + * } * } * *
+ */ + hasClass(c) { + return this.elt.classList.contains(c); + } + + /** + * Toggles whether a class is applied to the element. * - *
+ * @method toggleClass + * @param c {String} class name to toggle. + * @chainable + * + * @example + *
* + * let div; + * * function setup() { * createCanvas(100, 100); * * background(200); * - * // Create an empty paragraph element and set its font color to "deeppink". - * let p = createP(); - * p.position(5, 5); - * p.style('color', 'deeppink'); + * // Create a div element. + * div = createDiv('div'); * - * // Get the element's color as an RGB color string. - * let c = p.style('color'); + * // Add the 'show' class to the div. + * div.addClass('show'); * - * // Set the element's inner HTML using the RGB color string. - * p.html(c); + * describe('A gray square.'); + * } * - * describe('The text "rgb(255, 20, 147)" written in pink on a gray background.'); + * // Toggle the 'show' class when the mouse is pressed. + * function mousePressed() { + * div.toggleClass('show'); * } * *
*/ + toggleClass(c) { + // classList also has a toggle() method, but we cannot use that yet as support is unclear. + // See https://github.com/processing/p5.js/issues/3631 + // this.elt.classList.toggle(c); + if (this.elt.classList.contains(c)) { + this.elt.classList.remove(c); + } else { + this.elt.classList.add(c); + } + return this; + } + /** - * @method style - * @param {String} property - * @param {String|p5.Color} value value to assign to the property. - * @return {String} value of the property. + * Centers the element either vertically, horizontally, or both. + * + * `center()` will center the element relative to its parent or according to + * the page's body if the element has no parent. + * + * If no argument is passed, as in `myElement.center()` the element is aligned + * both vertically and horizontally. + * + * @method center + * @param {String} [align] passing 'vertical', 'horizontal' aligns element accordingly * @chainable + * + * @example + *
+ * + * function setup() { + * createCanvas(100, 100); + * + * background(200); + * + * // Create the div element and style it. + * let div = createDiv(''); + * div.size(10, 10); + * div.style('background-color', 'orange'); + * + * // Center the div relative to the page's body. + * div.center(); + * + * describe('A gray square and an orange rectangle. The rectangle is at the center of the page.'); + * } + * + *
*/ - p5.Element.prototype.style = function (prop, val) { - const self = this; + center(align) { + const style = this.elt.style.display; + const hidden = this.elt.style.display === 'none'; + const parentHidden = this.parent().style.display === 'none'; + const pos = { x: this.elt.offsetLeft, y: this.elt.offsetTop }; - if (val instanceof p5.Color) { - val = - 'rgba(' + - val.levels[0] + - ',' + - val.levels[1] + - ',' + - val.levels[2] + - ',' + - val.levels[3] / 255 + - ')'; - } + if (hidden) this.show(); + if (parentHidden) this.parent().show(); + this.elt.style.display = 'block'; - if (typeof val === 'undefined') { - if (prop.indexOf(':') === -1) { - // no value set, so assume requesting a value - let styles = window.getComputedStyle(self.elt); - let style = styles.getPropertyValue(prop); - return style; - } else { - // value set using `:` in a single line string - const attrs = prop.split(';'); - for (let i = 0; i < attrs.length; i++) { - const parts = attrs[i].split(':'); - if (parts[0] && parts[1]) { - this.elt.style[parts[0].trim()] = parts[1].trim(); - } - } - } - } else { - // input provided as key,val pair - this.elt.style[prop] = val; - if ( - prop === 'width' || - prop === 'height' || - prop === 'left' || - prop === 'top' - ) { - let styles = window.getComputedStyle(self.elt); - let styleVal = styles.getPropertyValue(prop); - let numVal = styleVal.replace(/[^\d.]/g, ''); - this[prop] = Math.round(parseFloat(numVal, 10)); - } + this.position(0, 0); + const wOffset = Math.abs(this.parent().offsetWidth - this.elt.offsetWidth); + const hOffset = Math.abs(this.parent().offsetHeight - this.elt.offsetHeight); + + if (align === 'both' || align === undefined) { + this.position( + wOffset / 2 + this.parent().offsetLeft, + hOffset / 2 + this.parent().offsetTop + ); + } else if (align === 'horizontal') { + this.position(wOffset / 2 + this.parent().offsetLeft, pos.y); + } else if (align === 'vertical') { + this.position(pos.x, hOffset / 2 + this.parent().offsetTop); } + + this.style('display', style); + if (hidden) this.hide(); + if (parentHidden) this.parent().hide(); + return this; - }; + } /** - * Adds an - * attribute - * to the element. + * Sets the element's position. * - * This method is useful for advanced tasks. Most commonly-used attributes, - * such as `id`, can be set with their dedicated methods. For example, - * `nextButton.id('next')` sets an element's `id` attribute. Calling - * `nextButton.attribute('id', 'next')` has the same effect. + * The first two parameters, `x` and `y`, set the element's position relative + * to the top-left corner of the web page. * - * The first parameter, `attr`, is the attribute's name as a string. Calling - * `myElement.attribute('align')` returns the attribute's current value as a - * string or `null` if it hasn't been set. + * The third parameter, `positionType`, is optional. It sets the element's + * positioning scheme. + * `positionType` is a string that can be either `'static'`, `'fixed'`, + * `'relative'`, `'sticky'`, `'initial'`, or `'inherit'`. + * + * If no arguments passed, as in `myElement.position()`, the method returns + * the element's position in an object, as in `{ x: 0, y: 0 }`. + * + * @method position + * @returns {Object} object of form `{ x: 0, y: 0 }` containing the element's position. + * + * @example + *
+ * + * function setup() { + * let cnv = createCanvas(100, 100); + * + * background(200); + * + * // Positions the canvas 50px to the right and 100px + * // below the top-left corner of the window. + * cnv.position(50, 100); + * + * describe('A gray square that is 50 pixels to the right and 100 pixels down from the top-left corner of the web page.'); + * } + * + *
+ * + *
+ * + * function setup() { + * let cnv = createCanvas(100, 100); + * + * background(200); + * + * // Positions the canvas at the top-left corner + * // of the window with a 'fixed' position type. + * cnv.position(0, 0, 'fixed'); + * + * describe('A gray square in the top-left corner of the web page.'); + * } + * + *
+ */ + /** + * @method position + * @param {Number} [x] x-position relative to top-left of window (optional) + * @param {Number} [y] y-position relative to top-left of window (optional) + * @param {String} [positionType] it can be static, fixed, relative, sticky, initial or inherit (optional) + * @chainable + */ + position(...args) { + if (args.length === 0) { + return { x: this.elt.offsetLeft, y: this.elt.offsetTop }; + } else { + let positionType = 'absolute'; + if ( + args[2] === 'static' || + args[2] === 'fixed' || + args[2] === 'relative' || + args[2] === 'sticky' || + args[2] === 'initial' || + args[2] === 'inherit' + ) { + positionType = args[2]; + } + this.elt.style.position = positionType; + this.elt.style.left = args[0] + 'px'; + this.elt.style.top = args[1] + 'px'; + this.x = args[0]; + this.y = args[1]; + return this; + } + } + + /** + * Shows the current element. + * + * @method show + * @chainable + * + * @example + *
+ * + * let p; + * + * function setup() { + * createCanvas(100, 100); + * + * background(200); + * + * // Create a paragraph element and hide it. + * p = createP('p5*js'); + * p.position(10, 10); + * p.hide(); + * + * describe('A gray square. The text "p5*js" appears when the user double-clicks the square.'); + * } + * + * // Show the paragraph when the user double-clicks. + * function doubleClicked() { + * p.show(); + * } + * + *
+ */ + show() { + this.elt.style.display = 'block'; + return this; + } + + /** + * Hides the current element. + * + * @method hide + * @chainable + * + * @example + * let p; + * + * function setup() { + * createCanvas(100, 100); + * + * background(200); + * + * // Create a paragraph element. + * p = createP('p5*js'); + * p.position(10, 10); + * + * describe('The text "p5*js" at the center of a gray square. The text disappears when the user double-clicks the square.'); + * } + * + * // Hide the paragraph when the user double-clicks. + * function doubleClicked() { + * p.hide(); + * } + *
+ *
+ */ + hide() { + this.elt.style.display = 'none'; + return this; + } + + /** + * Sets the element's width and height. + * + * Calling `myElement.size()` without an argument returns the element's size + * as an object with the properties `width` and `height`. For example, + * `{ width: 20, height: 10 }`. + * + * The first parameter, `width`, is optional. It's a number used to set the + * element's width. Calling `myElement.size(10)` + * + * The second parameter, 'height`, is also optional. It's a + * number used to set the element's height. For example, calling + * `myElement.size(20, 10)` sets the element's width to 20 pixels and height + * to 10 pixels. + * + * The constant `AUTO` can be used to adjust one dimension at a time while + * maintaining the aspect ratio, which is `width / height`. For example, + * consider an element that's 200 pixels wide and 100 pixels tall. Calling + * `myElement.size(20, AUTO)` sets the width to 20 pixels and height to 10 + * pixels. + * + * Note: In the case of elements that need to load data, such as images, wait + * to call `myElement.size()` until after the data loads. + * + * @method size + * @return {Object} width and height of the element in an object. + * + * @example + *
+ * + * function setup() { + * createCanvas(100, 100); + * + * background(200); + * + * // Create a pink div element and place it at the top-left corner. + * let div = createDiv(); + * div.position(10, 10); + * div.style('background-color', 'deeppink'); + * + * // Set the div's width to 80 pixels and height to 20 pixels. + * div.size(80, 20); + * + * describe('A gray square with a pink rectangle near its top.'); + * } + * + *
+ * + *
+ * + * function setup() { + * createCanvas(100, 100); + * + * background(200); + * + * // Create a pink div element and place it at the top-left corner. + * let div = createDiv(); + * div.position(10, 10); + * div.style('background-color', 'deeppink'); + * + * // Set the div's width to 80 pixels and height to 40 pixels. + * div.size(80, 40); + * + * // Get the div's size as an object. + * let s = div.size(); + * + * // Display the div's dimensions. + * div.html(`${s.width} x ${s.height}`); + * + * describe('A gray square with a pink rectangle near its top. The text "80 x 40" is written within the rectangle.'); + * } + * + *
+ * + *
+ * + * let img1; + * let img2; + * + * function setup() { + * createCanvas(100, 100); + * + * background(200); + * + * // Load an image of an astronaut on the moon + * // and place it at the top-left of the canvas. + * img1 = createImg( + * 'assets/moonwalk.jpg', + * 'An astronaut walking on the moon', + * '' + * ); + * img1.position(0, 0); + * + * // Load an image of an astronaut on the moon + * // and place it at the top-left of the canvas. + * // Resize the image once it's loaded. + * img2 = createImg( + * 'assets/moonwalk.jpg', + * 'An astronaut walking on the moon', + * '', + * resizeImage + * ); + * img2.position(0, 0); + * + * describe('A gray square two copies of a space image at the top-left. The copy in front is smaller.'); + * } + * + * // Resize img2 and keep its aspect ratio. + * function resizeImage() { + * img2.size(50, AUTO); + * } + * + *
+ */ + /** + * @method size + * @param {(Number|AUTO)} [w] width of the element, either AUTO, or a number. + * @param {(Number|AUTO)} [h] height of the element, either AUTO, or a number. + * @chainable + */ + size(w, h) { + if (arguments.length === 0) { + return { width: this.elt.offsetWidth, height: this.elt.offsetHeight }; + } else { + let aW = w; + let aH = h; + const AUTO = constants.AUTO; + if (aW !== AUTO || aH !== AUTO) { + if (aW === AUTO) { + aW = h * this.width / this.height; + } else if (aH === AUTO) { + aH = w * this.height / this.width; + } + // set diff for cnv vs normal div + if (this.elt instanceof HTMLCanvasElement) { + const j = {}; + const k = this.elt.getContext('2d'); + let prop; + for (prop in k) { + j[prop] = k[prop]; + } + this.elt.setAttribute('width', aW * this._pInst._pixelDensity); + this.elt.setAttribute('height', aH * this._pInst._pixelDensity); + this.elt.style.width = aW + 'px'; + this.elt.style.height = aH + 'px'; + this._pInst.scale(this._pInst._pixelDensity, this._pInst._pixelDensity); + for (prop in j) { + this.elt.getContext('2d')[prop] = j[prop]; + } + } else { + this.elt.style.width = aW + 'px'; + this.elt.style.height = aH + 'px'; + this.elt.width = aW; + this.elt.height = aH; + } + this.width = aW; + this.height = aH; + if (this._pInst && this._pInst._curElement) { + // main canvas associated with p5 instance + if (this._pInst._curElement.elt === this.elt) { + this._pInst.width = aW; + this._pInst.height = aH; + } + } + } + return this; + } + } + + /** + * Applies a style to the element by adding a + * CSS declaration. + * + * The first parameter, `property`, is a string. If the name of a style + * property is passed, as in `myElement.style('color')`, the method returns + * the current value as a string or `null` if it hasn't been set. If a + * `property:style` string is passed, as in + * `myElement.style('color:deeppink')`, the method sets the style `property` + * to `value`. + * + * The second parameter, `value`, is optional. It sets the property's value. + * `value` can be a string, as in + * `myElement.style('color', 'deeppink')`, or a + * p5.Color object, as in + * `myElement.style('color', myColor)`. + * + * @method style + * @param {String} property style property to set. + * @returns {String} value of the property. + * + * @example + *
+ * + * function setup() { + * createCanvas(100, 100); + * + * background(200); + * + * // Create a paragraph element and set its font color to "deeppink". + * let p = createP('p5*js'); + * p.position(25, 20); + * p.style('color', 'deeppink'); + * + * describe('The text p5*js written in pink on a gray background.'); + * } + * + *
+ * + *
+ * + * function setup() { + * createCanvas(100, 100); + * + * background(200); + * + * // Create a p5.Color object. + * let c = color('deeppink'); + * + * // Create a paragraph element and set its font color using a p5.Color object. + * let p = createP('p5*js'); + * p.position(25, 20); + * p.style('color', c); + * + * describe('The text p5*js written in pink on a gray background.'); + * } + * + *
+ * + *
+ * + * function setup() { + * createCanvas(100, 100); + * + * background(200); + * + * // Create a paragraph element and set its font color to "deeppink" + * // using property:value syntax. + * let p = createP('p5*js'); + * p.position(25, 20); + * p.style('color:deeppink'); + * + * describe('The text p5*js written in pink on a gray background.'); + * } + * + *
+ * + *
+ * + * function setup() { + * createCanvas(100, 100); + * + * background(200); + * + * // Create an empty paragraph element and set its font color to "deeppink". + * let p = createP(); + * p.position(5, 5); + * p.style('color', 'deeppink'); + * + * // Get the element's color as an RGB color string. + * let c = p.style('color'); + * + * // Set the element's inner HTML using the RGB color string. + * p.html(c); + * + * describe('The text "rgb(255, 20, 147)" written in pink on a gray background.'); + * } + * + *
+ */ + /** + * @method style + * @param {String} property + * @param {String|p5.Color} value value to assign to the property. + * @return {String} value of the property. + * @chainable + */ + style(prop, val) { + const self = this; + + if (val instanceof Color) { + val = + 'rgba(' + + val.levels[0] + + ',' + + val.levels[1] + + ',' + + val.levels[2] + + ',' + + val.levels[3] / 255 + + ')'; + } + + if (typeof val === 'undefined') { + if (prop.indexOf(':') === -1) { + // no value set, so assume requesting a value + let styles = window.getComputedStyle(self.elt); + let style = styles.getPropertyValue(prop); + return style; + } else { + // value set using `:` in a single line string + const attrs = prop.split(';'); + for (let i = 0; i < attrs.length; i++) { + const parts = attrs[i].split(':'); + if (parts[0] && parts[1]) { + this.elt.style[parts[0].trim()] = parts[1].trim(); + } + } + } + } else { + // input provided as key,val pair + this.elt.style[prop] = val; + if ( + prop === 'width' || + prop === 'height' || + prop === 'left' || + prop === 'top' + ) { + let styles = window.getComputedStyle(self.elt); + let styleVal = styles.getPropertyValue(prop); + let numVal = styleVal.replace(/[^\d.]/g, ''); + this[prop] = Math.round(parseFloat(numVal, 10)); + } + } + return this; + } + + /* Helper method called by p5.Element.style() */ + _translate(...args) { + this.elt.style.position = 'absolute'; + // save out initial non-translate transform styling + let transform = ''; + if (this.elt.style.transform) { + transform = this.elt.style.transform.replace(/translate3d\(.*\)/g, ''); + transform = transform.replace(/translate[X-Z]?\(.*\)/g, ''); + } + if (args.length === 2) { + this.elt.style.transform = + 'translate(' + args[0] + 'px, ' + args[1] + 'px)'; + } else if (args.length > 2) { + this.elt.style.transform = + 'translate3d(' + + args[0] + + 'px,' + + args[1] + + 'px,' + + args[2] + + 'px)'; + if (args.length === 3) { + this.elt.parentElement.style.perspective = '1000px'; + } else { + this.elt.parentElement.style.perspective = args[3] + 'px'; + } + } + // add any extra transform styling back on end + this.elt.style.transform += transform; + return this; + } + + /* Helper method called by p5.Element.style() */ + _rotate(...args) { + // save out initial non-rotate transform styling + let transform = ''; + if (this.elt.style.transform) { + transform = this.elt.style.transform.replace(/rotate3d\(.*\)/g, ''); + transform = transform.replace(/rotate[X-Z]?\(.*\)/g, ''); + } + + if (args.length === 1) { + this.elt.style.transform = 'rotate(' + args[0] + 'deg)'; + } else if (args.length === 2) { + this.elt.style.transform = + 'rotate(' + args[0] + 'deg, ' + args[1] + 'deg)'; + } else if (args.length === 3) { + this.elt.style.transform = 'rotateX(' + args[0] + 'deg)'; + this.elt.style.transform += 'rotateY(' + args[1] + 'deg)'; + this.elt.style.transform += 'rotateZ(' + args[2] + 'deg)'; + } + // add remaining transform back on + this.elt.style.transform += transform; + return this; + } + + /** + * Adds an + * attribute + * to the element. + * + * This method is useful for advanced tasks. Most commonly-used attributes, + * such as `id`, can be set with their dedicated methods. For example, + * `nextButton.id('next')` sets an element's `id` attribute. Calling + * `nextButton.attribute('id', 'next')` has the same effect. + * + * The first parameter, `attr`, is the attribute's name as a string. Calling + * `myElement.attribute('align')` returns the attribute's current value as a + * string or `null` if it hasn't been set. + * + * The second parameter, `value`, is optional. It's a string used to set the + * attribute's value. For example, calling + * `myElement.attribute('align', 'center')` sets the element's horizontal + * alignment to `center`. + * + * @method attribute + * @return {String} value of the attribute. + * + * @example + *
+ * + * function setup() { + * createCanvas(100, 100); + * + * // Create a container div element and place it at the top-left corner. + * let container = createDiv(); + * container.position(0, 0); + * + * // Create a paragraph element and place it within the container. + * // Set its horizontal alignment to "left". + * let p1 = createP('hi'); + * p1.parent(container); + * p1.attribute('align', 'left'); + * + * // Create a paragraph element and place it within the container. + * // Set its horizontal alignment to "center". + * let p2 = createP('hi'); + * p2.parent(container); + * p2.attribute('align', 'center'); + * + * // Create a paragraph element and place it within the container. + * // Set its horizontal alignment to "right". + * let p3 = createP('hi'); + * p3.parent(container); + * p3.attribute('align', 'right'); + * + * describe('A gray square with the text "hi" written on three separate lines, each placed further to the right.'); + * } + * + *
+ */ + /** + * @method attribute + * @param {String} attr attribute to set. + * @param {String} value value to assign to the attribute. + * @chainable + */ + attribute(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 (let 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 from the element. + * + * The parameter `attr` is the attribute's name as a string. For example, + * calling `myElement.removeAttribute('align')` removes its `align` + * attribute if it's been set. + * + * @method removeAttribute + * @param {String} attr attribute to remove. + * @chainable + * + * @example + *
+ * + * let p; + * + * function setup() { + * createCanvas(100, 100); + * + * background(200); + * + * // Create a paragraph element and place it in the center of the canvas. + * // Set its "align" attribute to "center". + * p = createP('hi'); + * p.position(0, 20); + * p.attribute('align', 'center'); + * + * describe('The text "hi" written in black at the center of a gray square. The text moves to the left edge when double-clicked.'); + * } + * + * // Remove the 'align' attribute when the user double-clicks the paragraph. + * function doubleClicked() { + * p.removeAttribute('align'); + * } + * + *
+ */ + removeAttribute(attr) { + if ( + this.elt.firstChild != null && + (this.elt.firstChild.type === 'checkbox' || + this.elt.firstChild.type === 'radio') + ) { + for (let i = 0; i < this.elt.childNodes.length; i++) { + this.elt.childNodes[i].removeAttribute(attr); + } + } + this.elt.removeAttribute(attr); + return this; + } + + /** + * Returns or sets the element's value. + * + * Calling `myElement.value()` returns the element's current value. + * + * The parameter, `value`, is an optional number or string. If provided, + * as in `myElement.value(123)`, it's used to set the element's value. + * + * @method value + * @return {String|Number} value of the element. + * + * @example + *
+ * + * let input; + * + * function setup() { + * createCanvas(100, 100); + * + * // Create a text input and place it beneath the canvas. + * // Set its default value to "hello". + * input = createInput('hello'); + * input.position(0, 100); + * + * describe('The text from an input box is displayed on a gray square.'); + * } + * + * function draw() { + * background(200); + * + * // Use the input's value to display a message. + * let msg = input.value(); + * text(msg, 0, 55); + * } + * + *
+ * + *
+ * + * let input; + * + * function setup() { + * createCanvas(100, 100); + * + * // Create a text input and place it beneath the canvas. + * // Set its default value to "hello". + * input = createInput('hello'); + * input.position(0, 100); + * + * describe('The text from an input box is displayed on a gray square. The text resets to "hello" when the user double-clicks the square.'); + * } + * + * function draw() { + * background(200); + * + * // Use the input's value to display a message. + * let msg = input.value(); + * text(msg, 0, 55); + * } + * + * // Reset the input's value. + * function doubleClicked() { + * input.value('hello'); + * } + * + *
+ */ + /** + * @method value + * @param {String|Number} value + * @chainable + */ + value(...args) { + if (args.length > 0) { + this.elt.value = args[0]; + return this; + } else { + if (this.elt.type === 'range') { + return parseFloat(this.elt.value); + } else return this.elt.value; + } + } + + /** + * Calls a function when the mouse is pressed over the element. + * + * Calling `myElement.mousePressed(false)` disables the function. + * + * Note: Some mobile browsers may also trigger this event when the element + * receives a quick tap. + * + * @param {Function|Boolean} fxn function to call when the mouse is + * pressed over the element. + * `false` disables the function. + * @chainable + * + * @example + *
+ * + * function setup() { + * // Create a canvas element and + * // assign it to cnv. + * let cnv = createCanvas(100, 100); + * + * background(200); + * + * // Call randomColor() when the canvas + * // is pressed. + * cnv.mousePressed(randomColor); + * + * describe('A gray square changes color when the mouse is pressed.'); + * } + * + * // Paint the background either + * // red, yellow, blue, or green. + * function randomColor() { + * let c = random(['red', 'yellow', 'blue', 'green']); + * background(c); + * } + * + *
+ */ + mousePressed(fxn) { + // Prepend the mouse property setters to the event-listener. + // This is required so that mouseButton is set correctly prior to calling the callback (fxn). + // For details, see https://github.com/processing/p5.js/issues/3087. + const eventPrependedFxn = function (event) { + this._pInst.mouseIsPressed = true; + this._pInst._setMouseButton(event); + // Pass along the return-value of the callback: + return fxn.call(this, event); + }; + // Pass along the event-prepended form of the callback. + Element._adjustListener('mousedown', eventPrependedFxn, this); + return this; + } + + /** + * Calls a function when the mouse is pressed twice over the element. + * + * Calling `myElement.doubleClicked(false)` disables the function. + * + * @param {Function|Boolean} fxn function to call when the mouse is + * double clicked over the element. + * `false` disables the function. + * @chainable + * + * @example + *
+ * + * function setup() { + * // Create a canvas element and + * // assign it to cnv. + * let cnv = createCanvas(100, 100); + * + * background(200); + * + * // Call randomColor() when the + * // canvas is double-clicked. + * cnv.doubleClicked(randomColor); + * + * describe('A gray square changes color when the user double-clicks the canvas.'); + * } + * + * // Paint the background either + * // red, yellow, blue, or green. + * function randomColor() { + * let c = random(['red', 'yellow', 'blue', 'green']); + * background(c); + * } + * + *
+ */ + doubleClicked(fxn) { + Element._adjustListener('dblclick', fxn, this); + return this; + } + + /** + * Calls a function when the mouse wheel scrolls over the element. + * + * The callback function, `fxn`, is passed an `event` object. `event` has + * two numeric properties, `deltaY` and `deltaX`. `event.deltaY` is + * negative if the mouse wheel rotates away from the user. It's positive if + * the mouse wheel rotates toward the user. `event.deltaX` is positive if + * the mouse wheel moves to the right. It's negative if the mouse wheel moves + * to the left. + * + * Calling `myElement.mouseWheel(false)` disables the function. + * + * @param {Function|Boolean} fxn function to call when the mouse wheel is + * scrolled over the element. + * `false` disables the function. + * @chainable + * + * @example + *
+ * + * function setup() { + * // Create a canvas element and + * // assign it to cnv. + * let cnv = createCanvas(100, 100); + * + * background(200); + * + * // Call randomColor() when the + * // mouse wheel moves. + * cnv.mouseWheel(randomColor); + * + * describe('A gray square changes color when the user scrolls the mouse wheel over the canvas.'); + * } + * + * // Paint the background either + * // red, yellow, blue, or green. + * function randomColor() { + * let c = random(['red', 'yellow', 'blue', 'green']); + * background(c); + * } + * + *
+ * + *
+ * + * function setup() { + * // Create a canvas element and + * // assign it to cnv. + * let cnv = createCanvas(100, 100); + * + * background(200); + * + * // Call changeBackground() when the + * // mouse wheel moves. + * cnv.mouseWheel(changeBackground); + * + * describe('A gray square. When the mouse wheel scrolls over the square, it changes color and displays shapes.'); + * } + * + * function changeBackground(event) { + * // Change the background color + * // based on deltaY. + * if (event.deltaY > 0) { + * background('deeppink'); + * } else if (event.deltaY < 0) { + * background('cornflowerblue'); + * } else { + * background(200); + * } + * + * // Draw a shape based on deltaX. + * if (event.deltaX > 0) { + * circle(50, 50, 20); + * } else if (event.deltaX < 0) { + * square(40, 40, 20); + * } + * } + * + *
+ */ + mouseWheel(fxn) { + Element._adjustListener('wheel', fxn, this); + return this; + } + + /** + * Calls a function when the mouse is released over the element. + * + * Calling `myElement.mouseReleased(false)` disables the function. + * + * Note: Some mobile browsers may also trigger this event when the element + * receives a quick tap. + * + * @param {Function|Boolean} fxn function to call when the mouse is + * pressed over the element. + * `false` disables the function. + * @chainable + * + * @example + *
+ * + * function setup() { + * // Create a canvas element and + * // assign it to cnv. + * let cnv = createCanvas(100, 100); + * + * background(200); + * + * // Call randomColor() when a + * // mouse press ends. + * cnv.mouseReleased(randomColor); + * + * describe('A gray square changes color when the user releases a mouse press.'); + * } + * + * // Paint the background either + * // red, yellow, blue, or green. + * function randomColor() { + * let c = random(['red', 'yellow', 'blue', 'green']); + * background(c); + * } + * + *
+ */ + mouseReleased(fxn) { + Element._adjustListener('mouseup', fxn, this); + return this; + } + + /** + * Calls a function when the mouse is pressed and released over the element. + * + * Calling `myElement.mouseReleased(false)` disables the function. + * + * Note: Some mobile browsers may also trigger this event when the element + * receives a quick tap. + * + * @param {Function|Boolean} fxn function to call when the mouse is + * pressed and released over the element. + * `false` disables the function. + * @chainable + * + * @example + *
+ * + * function setup() { + * // Create a canvas element and + * // assign it to cnv. + * let cnv = createCanvas(100, 100); + * + * background(200); + * + * // Call randomColor() when a + * // mouse press ends. + * cnv.mouseClicked(randomColor); + * + * describe('A gray square changes color when the user releases a mouse press.'); + * } * - * The second parameter, `value`, is optional. It's a string used to set the - * attribute's value. For example, calling - * `myElement.attribute('align', 'center')` sets the element's horizontal - * alignment to `center`. + * // Paint the background either + * // red, yellow, blue, or green. + * function randomColor() { + * let c = random(['red', 'yellow', 'blue', 'green']); + * background(c); + * } + * + *
+ */ + mouseClicked(fxn) { + Element._adjustListener('click', fxn, this); + return this; + } + + /** + * Calls a function when the mouse moves over the element. * - * @method attribute - * @return {String} value of the attribute. + * Calling `myElement.mouseMoved(false)` disables the function. + * + * @param {Function|Boolean} fxn function to call when the mouse + * moves over the element. + * `false` disables the function. + * @chainable * * @example *
* * function setup() { - * createCanvas(100, 100); - * - * // Create a container div element and place it at the top-left corner. - * let container = createDiv(); - * container.position(0, 0); + * // Create a canvas element and + * // assign it to cnv. + * let cnv = createCanvas(100, 100); * - * // Create a paragraph element and place it within the container. - * // Set its horizontal alignment to "left". - * let p1 = createP('hi'); - * p1.parent(container); - * p1.attribute('align', 'left'); + * background(200); * - * // Create a paragraph element and place it within the container. - * // Set its horizontal alignment to "center". - * let p2 = createP('hi'); - * p2.parent(container); - * p2.attribute('align', 'center'); + * // Call randomColor() when the + * // mouse moves. + * cnv.mouseMoved(randomColor); * - * // Create a paragraph element and place it within the container. - * // Set its horizontal alignment to "right". - * let p3 = createP('hi'); - * p3.parent(container); - * p3.attribute('align', 'right'); + * describe('A gray square changes color when the mouse moves over the canvas.'); + * } * - * describe('A gray square with the text "hi" written on three separate lines, each placed further to the right.'); + * // Paint the background either + * // red, yellow, blue, or green. + * function randomColor() { + * let c = random(['red', 'yellow', 'blue', 'green']); + * background(c); * } * *
*/ - /** - * @method attribute - * @param {String} attr attribute to set. - * @param {String} value value to assign to the attribute. - * @chainable - */ - 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 (let 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; - } - }; + mouseMoved(fxn) { + Element._adjustListener('mousemove', fxn, this); + return this; + } /** - * Removes an attribute from the element. + * Calls a function when the mouse moves onto the element. * - * The parameter `attr` is the attribute's name as a string. For example, - * calling `myElement.removeAttribute('align')` removes its `align` - * attribute if it's been set. + * Calling `myElement.mouseOver(false)` disables the function. * - * @method removeAttribute - * @param {String} attr attribute to remove. + * @param {Function|Boolean} fxn function to call when the mouse + * moves onto the element. + * `false` disables the function. * @chainable * * @example *
* - * let p; - * * function setup() { - * createCanvas(100, 100); + * // Create a canvas element and + * // assign it to cnv. + * let cnv = createCanvas(100, 100); * * background(200); * - * // Create a paragraph element and place it in the center of the canvas. - * // Set its "align" attribute to "center". - * p = createP('hi'); - * p.position(0, 20); - * p.attribute('align', 'center'); + * // Call randomColor() when the + * // mouse moves onto the canvas. + * cnv.mouseOver(randomColor); * - * describe('The text "hi" written in black at the center of a gray square. The text moves to the left edge when double-clicked.'); + * describe('A gray square changes color when the mouse moves onto the canvas.'); * } * - * // Remove the 'align' attribute when the user double-clicks the paragraph. - * function doubleClicked() { - * p.removeAttribute('align'); + * // Paint the background either + * // red, yellow, blue, or green. + * function randomColor() { + * let c = random(['red', 'yellow', 'blue', 'green']); + * background(c); * } * *
*/ - p5.Element.prototype.removeAttribute = function (attr) { - if ( - this.elt.firstChild != null && - (this.elt.firstChild.type === 'checkbox' || - this.elt.firstChild.type === 'radio') - ) { - for (let i = 0; i < this.elt.childNodes.length; i++) { - this.elt.childNodes[i].removeAttribute(attr); - } - } - this.elt.removeAttribute(attr); + mouseOver(fxn) { + Element._adjustListener('mouseover', fxn, this); return this; - }; + } /** - * Returns or sets the element's value. + * Calls a function when the mouse moves off the element. * - * Calling `myElement.value()` returns the element's current value. - * - * The parameter, `value`, is an optional number or string. If provided, - * as in `myElement.value(123)`, it's used to set the element's value. + * Calling `myElement.mouseOut(false)` disables the function. * - * @method value - * @return {String|Number} value of the element. + * @param {Function|Boolean} fxn function to call when the mouse + * moves off the element. + * `false` disables the function. + * @chainable * * @example *
* - * let input; - * * function setup() { - * createCanvas(100, 100); + * // Create a canvas element and + * // assign it to cnv. + * let cnv = createCanvas(100, 100); * - * // Create a text input and place it beneath the canvas. - * // Set its default value to "hello". - * input = createInput('hello'); - * input.position(0, 100); + * background(200); * - * describe('The text from an input box is displayed on a gray square.'); - * } + * // Call randomColor() when the + * // mouse moves off the canvas. + * cnv.mouseOut(randomColor); * - * function draw() { - * background(200); + * describe('A gray square changes color when the mouse moves off the canvas.'); + * } * - * // Use the input's value to display a message. - * let msg = input.value(); - * text(msg, 0, 55); + * // Paint the background either + * // red, yellow, blue, or green. + * function randomColor() { + * let c = random(['red', 'yellow', 'blue', 'green']); + * background(c); * } * *
+ */ + mouseOut(fxn) { + Element._adjustListener('mouseout', fxn, this); + return this; + } + + /** + * Calls a function when the element is touched. * - *
- * - * let input; + * Calling `myElement.touchStarted(false)` disables the function. * - * function setup() { - * createCanvas(100, 100); + * Note: Touch functions only work on mobile devices. * - * // Create a text input and place it beneath the canvas. - * // Set its default value to "hello". - * input = createInput('hello'); - * input.position(0, 100); + * @param {Function|Boolean} fxn function to call when the touch + * starts. + * `false` disables the function. + * @chainable * - * describe('The text from an input box is displayed on a gray square. The text resets to "hello" when the user double-clicks the square.'); - * } + * @example + *
+ * + * function setup() { + * // Create a canvas element and + * // assign it to cnv. + * let cnv = createCanvas(100, 100); * - * function draw() { * background(200); * - * // Use the input's value to display a message. - * let msg = input.value(); - * text(msg, 0, 55); + * // Call randomColor() when the + * // user touches the canvas. + * cnv.touchStarted(randomColor); + * + * describe('A gray square changes color when the user touches the canvas.'); * } * - * // Reset the input's value. - * function doubleClicked() { - * input.value('hello'); + * // Paint the background either + * // red, yellow, blue, or green. + * function randomColor() { + * let c = random(['red', 'yellow', 'blue', 'green']); + * background(c); * } * *
*/ - /** - * @method value - * @param {String|Number} value - * @chainable - */ - p5.Element.prototype.value = function (...args) { - if (args.length > 0) { - this.elt.value = args[0]; - return this; - } else { - if (this.elt.type === 'range') { - return parseFloat(this.elt.value); - } else return this.elt.value; - } - }; + touchStarted(fxn) { + Element._adjustListener('touchstart', fxn, this); + return this; + } /** - * Shows the current element. + * Calls a function when the user touches the element and moves. * - * @method show - * @chainable + * Calling `myElement.touchMoved(false)` disables the function. * + * Note: Touch functions only work on mobile devices. + * + * @param {Function|Boolean} fxn function to call when the touch + * moves over the element. + * `false` disables the function. + * @chainable * @example *
* - * let p; - * * function setup() { - * createCanvas(100, 100); + * // Create a canvas element and + * // assign it to cnv. + * let cnv = createCanvas(100, 100); * * background(200); * - * // Create a paragraph element and hide it. - * p = createP('p5*js'); - * p.position(10, 10); - * p.hide(); + * // Call randomColor() when the + * // user touches the canvas + * // and moves. + * cnv.touchMoved(randomColor); * - * describe('A gray square. The text "p5*js" appears when the user double-clicks the square.'); + * describe('A gray square changes color when the user touches the canvas and moves.'); * } * - * // Show the paragraph when the user double-clicks. - * function doubleClicked() { - * p.show(); + * // Paint the background either + * // red, yellow, blue, or green. + * function randomColor() { + * let c = random(['red', 'yellow', 'blue', 'green']); + * background(c); * } * *
*/ - p5.Element.prototype.show = function () { - this.elt.style.display = 'block'; + touchMoved(fxn) { + Element._adjustListener('touchmove', fxn, this); return this; - }; + } /** - * Hides the current element. + * Calls a function when the user stops touching the element. * - * @method hide - * @chainable + * Calling `myElement.touchMoved(false)` disables the function. * - * @example - * let p; + * Note: Touch functions only work on mobile devices. * + * @param {Function|Boolean} fxn function to call when the touch + * ends. + * `false` disables the function. + * @chainable + * @example + *
+ * * function setup() { - * createCanvas(100, 100); + * // Create a canvas element and + * // assign it to cnv. + * let cnv = createCanvas(100, 100); * * background(200); * - * // Create a paragraph element. - * p = createP('p5*js'); - * p.position(10, 10); + * // Call randomColor() when the + * // user touches the canvas, + * // then lifts their finger. + * cnv.touchEnded(randomColor); * - * describe('The text "p5*js" at the center of a gray square. The text disappears when the user double-clicks the square.'); + * describe('A gray square changes color when the user touches the canvas, then lifts their finger.'); * } * - * // Hide the paragraph when the user double-clicks. - * function doubleClicked() { - * p.hide(); + * // Paint the background either + * // red, yellow, blue, or green. + * function randomColor() { + * let c = random(['red', 'yellow', 'blue', 'green']); + * background(c); * } * *
*/ - p5.Element.prototype.hide = function () { - this.elt.style.display = 'none'; + touchEnded(fxn) { + Element._adjustListener('touchend', fxn, this); return this; - }; + } /** - * Sets the element's width and height. + * Calls a function when a file is dragged over the element. * - * Calling `myElement.size()` without an argument returns the element's size - * as an object with the properties `width` and `height`. For example, - * `{ width: 20, height: 10 }`. + * Calling `myElement.dragOver(false)` disables the function. * - * The first parameter, `width`, is optional. It's a number used to set the - * element's width. Calling `myElement.size(10)` + * @param {Function|Boolean} fxn function to call when the file is + * dragged over the element. + * `false` disables the function. + * @chainable * - * The second parameter, 'height`, is also optional. It's a - * number used to set the element's height. For example, calling - * `myElement.size(20, 10)` sets the element's width to 20 pixels and height - * to 10 pixels. + * @example + *
+ * + * // Drag a file over the canvas to test. * - * The constant `AUTO` can be used to adjust one dimension at a time while - * maintaining the aspect ratio, which is `width / height`. For example, - * consider an element that's 200 pixels wide and 100 pixels tall. Calling - * `myElement.size(20, AUTO)` sets the width to 20 pixels and height to 10 - * pixels. + * function setup() { + * // Create a canvas element and + * // assign it to cnv. + * let cnv = createCanvas(100, 100); * - * Note: In the case of elements that need to load data, such as images, wait - * to call `myElement.size()` until after the data loads. + * background(200); + * + * // Call helloFile() when a + * // file is dragged over + * // the canvas. + * cnv.dragOver(helloFile); + * + * describe('A gray square. The text "hello, file" appears when a file is dragged over the square.'); + * } + * + * function helloFile() { + * text('hello, file', 50, 50); + * } + * + *
+ */ + dragOver(fxn) { + Element._adjustListener('dragover', fxn, this); + return this; + } + + /** + * Calls a function when a file is dragged off the element. * - * @method size - * @return {Object} width and height of the element in an object. + * Calling `myElement.dragLeave(false)` disables the function. * + * @param {Function|Boolean} fxn function to call when the file is + * dragged off the element. + * `false` disables the function. + * @chainable * @example *
* + * // Drag a file over, then off + * // the canvas to test. + * * function setup() { - * createCanvas(100, 100); + * // Create a canvas element and + * // assign it to cnv. + * let cnv = createCanvas(100, 100); * * background(200); * - * // Create a pink div element and place it at the top-left corner. - * let div = createDiv(); - * div.position(10, 10); - * div.style('background-color', 'deeppink'); + * // Call byeFile() when a + * // file is dragged over, + * // then off the canvas. + * cnv.dragLeave(byeFile); * - * // Set the div's width to 80 pixels and height to 20 pixels. - * div.size(80, 20); + * describe('A gray square. The text "bye, file" appears when a file is dragged over, then off the square.'); + * } * - * describe('A gray square with a pink rectangle near its top.'); + * function byeFile() { + * text('bye, file', 50, 50); * } * *
+ */ + dragLeave(fxn) { + Element._adjustListener('dragleave', fxn, this); + return this; + } + + /** + * Calls a function when the element changes. + * + * Calling `myElement.changed(false)` disables the function. * + * @method changed + * @param {Function|Boolean} fxn function to call when the element changes. + * `false` disables the function. + * @chainable + * + * @example *
* + * let dropdown; + * * function setup() { * createCanvas(100, 100); * * background(200); * - * // Create a pink div element and place it at the top-left corner. - * let div = createDiv(); - * div.position(10, 10); - * div.style('background-color', 'deeppink'); - * - * // Set the div's width to 80 pixels and height to 40 pixels. - * div.size(80, 40); + * // Create a dropdown menu and add a few color options. + * dropdown = createSelect(); + * dropdown.position(0, 0); + * dropdown.option('red'); + * dropdown.option('green'); + * dropdown.option('blue'); * - * // Get the div's size as an object. - * let s = div.size(); + * // Call paintBackground() when the color option changes. + * dropdown.changed(paintBackground); * - * // Display the div's dimensions. - * div.html(`${s.width} x ${s.height}`); + * describe('A gray square with a dropdown menu at the top. The square changes color when an option is selected.'); + * } * - * describe('A gray square with a pink rectangle near its top. The text "80 x 40" is written within the rectangle.'); + * // Paint the background with the selected color. + * function paintBackground() { + * let c = dropdown.value(); + * background(c); * } * *
* *
* - * let img1; - * let img2; + * let checkbox; * * function setup() { * createCanvas(100, 100); * * background(200); * - * // Load an image of an astronaut on the moon - * // and place it at the top-left of the canvas. - * img1 = createImg( - * 'assets/moonwalk.jpg', - * 'An astronaut walking on the moon', - * '' - * ); - * img1.position(0, 0); + * // Create a checkbox and place it beneath the canvas. + * checkbox = createCheckbox(' circle'); + * checkbox.position(0, 100); * - * // Load an image of an astronaut on the moon - * // and place it at the top-left of the canvas. - * // Resize the image once it's loaded. - * img2 = createImg( - * 'assets/moonwalk.jpg', - * 'An astronaut walking on the moon', - * '', - * resizeImage - * ); - * img2.position(0, 0); + * // Call repaint() when the checkbox changes. + * checkbox.changed(repaint); * - * describe('A gray square two copies of a space image at the top-left. The copy in front is smaller.'); + * describe('A gray square with a checkbox underneath it that says "circle". A white circle appears when the box is checked and disappears otherwise.'); * } * - * // Resize img2 and keep its aspect ratio. - * function resizeImage() { - * img2.size(50, AUTO); + * // Paint the background gray and determine whether to draw a circle. + * function repaint() { + * background(200); + * if (checkbox.checked() === true) { + * circle(50, 50, 30); + * } * } * *
*/ - /** - * @method size - * @param {(Number|AUTO)} [w] width of the element, either AUTO, or a number. - * @param {(Number|AUTO)} [h] height of the element, either AUTO, or a number. - * @chainable - */ - p5.Element.prototype.size = function (w, h) { - if (arguments.length === 0) { - return { width: this.elt.offsetWidth, height: this.elt.offsetHeight }; - } else { - let aW = w; - let aH = h; - const AUTO = fn.AUTO; - if (aW !== AUTO || aH !== AUTO) { - if (aW === AUTO) { - aW = h * this.width / this.height; - } else if (aH === AUTO) { - aH = w * this.height / this.width; - } - // set diff for cnv vs normal div - if (this.elt instanceof HTMLCanvasElement) { - const j = {}; - const k = this.elt.getContext('2d'); - let prop; - for (prop in k) { - j[prop] = k[prop]; - } - this.elt.setAttribute('width', aW * this._pInst._pixelDensity); - this.elt.setAttribute('height', aH * this._pInst._pixelDensity); - this.elt.style.width = aW + 'px'; - this.elt.style.height = aH + 'px'; - this._pInst.scale(this._pInst._pixelDensity, this._pInst._pixelDensity); - for (prop in j) { - this.elt.getContext('2d')[prop] = j[prop]; - } - } else { - this.elt.style.width = aW + 'px'; - this.elt.style.height = aH + 'px'; - this.elt.width = aW; - this.elt.height = aH; - } - this.width = aW; - this.height = aH; - if (this._pInst && this._pInst._curElement) { - // main canvas associated with p5 instance - if (this._pInst._curElement.elt === this.elt) { - this._pInst.width = aW; - this._pInst.height = aH; - } - } - } - return this; - } - }; + changed(fxn) { + Element._adjustListener('change', fxn, this); + return this; + } /** - * Removes the element, stops all audio/video streams, and removes all - * callback functions. + * Calls a function when the element receives input. * - * @method remove + * `myElement.input()` is often used to with text inputs and sliders. Calling + * `myElement.input(false)` disables the function. + * + * @method input + * @param {Function|Boolean} fxn function to call when input is detected within + * the element. + * `false` disables the function. + * @chainable * * @example *
* - * let p; + * let slider; * * function setup() { * createCanvas(100, 100); * * background(200); * - * // Create a paragraph element. - * p = createP('p5*js'); - * p.position(10, 10); + * // Create a slider and place it beneath the canvas. + * slider = createSlider(0, 255, 200); + * slider.position(0, 100); * - * describe('The text "p5*js" written at the center of a gray square. '); + * // Call repaint() when the slider changes. + * slider.input(repaint); + * + * describe('A gray square with a range slider underneath it. The background changes shades of gray when the slider is moved.'); * } * - * // Remove the paragraph when the user double-clicks. - * function doubleClicked() { - * p.remove(); + * // Paint the background using slider's value. + * function repaint() { + * let g = slider.value(); + * background(g); + * } + * + *
+ * + *
+ * + * let input; + * + * function setup() { + * createCanvas(100, 100); + * + * background(200); + * + * // Create an input and place it beneath the canvas. + * input = createInput(''); + * input.position(0, 100); + * + * // Call repaint() when input is detected. + * input.input(repaint); + * + * describe('A gray square with a text input bar beneath it. Any text written in the input appears in the middle of the square.'); + * } + * + * // Paint the background gray and display the input's value. + * function repaint() { + * background(200); + * let msg = input.value(); + * text(msg, 5, 50); * } * *
*/ - p5.Element.prototype.remove = function () { - // stop all audios/videos and detach all devices like microphone/camera etc - // used as input/output for audios/videos. - if (this instanceof p5.MediaElement) { - this.stop(); - const sources = this.elt.srcObject; - if (sources !== null) { - const tracks = sources.getTracks(); - tracks.forEach(track => { - track.stop(); - }); - } - } - - // delete the reference in this._pInst._elements - const index = this._pInst._elements.indexOf(this); - if (index !== -1) { - this._pInst._elements.splice(index, 1); - } - - // deregister events - for (let ev in this._events) { - this.elt.removeEventListener(ev, this._events[ev]); - } - if (this.elt && this.elt.parentNode) { - this.elt.parentNode.removeChild(this.elt); - } - }; + input(fxn) { + Element._adjustListener('input', fxn, this); + return this; + } /** * Calls a function when the user drops a file on the element. @@ -1424,7 +2395,7 @@ function element(p5, fn){ *
*
*/ - p5.Element.prototype.drop = function (callback, fxn) { + drop(callback, fxn) { // Is the file stuff supported? if (window.File && window.FileReader && window.FileList && window.Blob) { if (!this._dragDisabled) { @@ -1444,7 +2415,7 @@ function element(p5, fn){ } // Deal with the files - p5.Element._attachListener( + Element._attachListener( 'drop', function (evt) { evt.preventDefault(); @@ -1457,7 +2428,7 @@ function element(p5, fn){ // Load each one and trigger the callback for (const f of files) { - p5.File._load(f, callback); + File._load(f, callback); } }, this @@ -1467,7 +2438,7 @@ function element(p5, fn){ } return this; - }; + } /** * Makes the element draggable. @@ -1537,7 +2508,7 @@ function element(p5, fn){ *
*
*/ - p5.Element.prototype.draggable = function (elmMove) { + draggable(elmMove) { let isTouch = 'ontouchstart' in window; let x = 0, @@ -1603,24 +2574,110 @@ function element(p5, fn){ } return this; - }; - - /*** SCHEDULE EVENTS ***/ - - // Cue inspired by JavaScript setTimeout, and the - // Tone.js Transport Timeline Event, MIT License Yotam Mann 2015 tonejs.org - // eslint-disable-next-line no-unused-vars - class Cue { - constructor(callback, time, id, val) { - this.callback = callback; - this.time = time; - this.id = id; - this.val = val; + } + + /** + * + * @private + * @static + * @param {String} ev + * @param {Boolean|Function} fxn + * @param {Element} ctx + * @chainable + * @alt + * General handler for event attaching and detaching + */ + static _adjustListener(ev, fxn, ctx) { + if (fxn === false) { + Element._detachListener(ev, ctx); + } else { + Element._attachListener(ev, fxn, ctx); + } + return this; + } + + /** + * + * @private + * @static + * @param {String} ev + * @param {Function} fxn + * @param {Element} ctx + */ + static _attachListener(ev, fxn, ctx) { + // detach the old listener if there was one + if (ctx._events[ev]) { + Element._detachListener(ev, ctx); } + const f = fxn.bind(ctx); + ctx.elt.addEventListener(ev, f, false); + ctx._events[ev] = f; + } + + /** + * + * @private + * @static + * @param {String} ev + * @param {Element} ctx + */ + static _detachListener(ev, ctx) { + const f = ctx._events[ev]; + ctx.elt.removeEventListener(ev, f, false); + ctx._events[ev] = null; } +}; + +function element(p5, fn){ + /** + * A class to describe an + * HTML element. + * + * Sketches can use many elements. Common elements include the drawing canvas, + * buttons, sliders, webcam feeds, and so on. + * + * All elements share the methods of the `p5.Element` class. They're created + * with functions such as createCanvas() and + * createButton(). + * + * @class p5.Element + * @param {HTMLElement} elt wrapped DOM element. + * @param {p5} [pInst] pointer to p5 instance. + * + * @example + *
+ * + * function setup() { + * createCanvas(100, 100); + * + * background(200); + * + * // Create a button element and + * // place it beneath the canvas. + * let btn = createButton('change'); + * btn.position(0, 100); + * + * // Call randomColor() when + * // the button is pressed. + * btn.mousePressed(randomColor); + * + * describe('A gray square with a button that says "change" beneath it. The square changes color when the user presses the button.'); + * } + * + * // Paint the background either + * // red, yellow, blue, or green. + * function randomColor() { + * let c = random(['red', 'yellow', 'blue', 'green']); + * background(c); + * } + * + *
+ */ + p5.Element = Element; } export default element; +export { Element }; if(typeof p5 !== 'undefined'){ element(p5, p5.prototype); diff --git a/src/dom/p5.File.js b/src/dom/p5.File.js index 001a575417..7852d4f435 100644 --- a/src/dom/p5.File.js +++ b/src/dom/p5.File.js @@ -1,3 +1,63 @@ +/** + * @module DOM + * @submodule DOM + * @for p5.Element + */ + +import { XML } from '../io/p5.XML'; + +class File { + constructor(file, pInst) { + this.file = file; + + this._pInst = pInst; + + // Splitting out the file type into two components + // This makes determining if image or text etc simpler + const typeList = file.type.split('/'); + this.type = typeList[0]; + this.subtype = typeList[1]; + this.name = file.name; + this.size = file.size; + this.data = undefined; + } + + + static _createLoader(theFile, callback) { + const reader = new FileReader(); + reader.onload = function (e) { + const p5file = new File(theFile); + if (p5file.file.type === 'application/json') { + // Parse JSON and store the result in data + p5file.data = JSON.parse(e.target.result); + } else if (p5file.file.type === 'text/xml') { + // Parse XML, wrap it in p5.XML and store the result in data + const parser = new DOMParser(); + const xml = parser.parseFromString(e.target.result, 'text/xml'); + p5file.data = new XML(xml.documentElement); + } else { + p5file.data = e.target.result; + } + callback(p5file); + }; + return reader; + } + + static _load(f, callback) { + // Text or data? + // This should likely be improved + if (/^text\//.test(f.type) || f.type === 'application/json') { + File._createLoader(f, callback).readAsText(f); + } else if (!/^(video|audio)\//.test(f.type)) { + File._createLoader(f, callback).readAsDataURL(f); + } else { + const file = new File(f); + file.data = URL.createObjectURL(f); + callback(file); + } + } +} + function file(p5, fn){ /** * A class to describe a file. @@ -87,57 +147,7 @@ function file(p5, fn){ * *
*/ - p5.File = class File { - constructor(file, pInst) { - this.file = file; - - this._pInst = pInst; - - // Splitting out the file type into two components - // This makes determining if image or text etc simpler - const typeList = file.type.split('/'); - this.type = typeList[0]; - this.subtype = typeList[1]; - this.name = file.name; - this.size = file.size; - this.data = undefined; - } - - - static _createLoader(theFile, callback) { - const reader = new FileReader(); - reader.onload = function (e) { - const p5file = new p5.File(theFile); - if (p5file.file.type === 'application/json') { - // Parse JSON and store the result in data - p5file.data = JSON.parse(e.target.result); - } else if (p5file.file.type === 'text/xml') { - // Parse XML, wrap it in p5.XML and store the result in data - const parser = new DOMParser(); - const xml = parser.parseFromString(e.target.result, 'text/xml'); - p5file.data = new p5.XML(xml.documentElement); - } else { - p5file.data = e.target.result; - } - callback(p5file); - }; - return reader; - } - - static _load(f, callback) { - // Text or data? - // This should likely be improved - if (/^text\//.test(f.type) || f.type === 'application/json') { - p5.File._createLoader(f, callback).readAsText(f); - } else if (!/^(video|audio)\//.test(f.type)) { - p5.File._createLoader(f, callback).readAsDataURL(f); - } else { - const file = new p5.File(f); - file.data = URL.createObjectURL(f); - callback(file); - } - } - }; + p5.File = File; /** * Underlying @@ -371,6 +381,7 @@ function file(p5, fn){ } export default file; +export { File }; if(typeof p5 !== 'undefined'){ file(p5, p5.prototype); diff --git a/src/dom/media_element.js b/src/dom/p5.MediaElement.js similarity index 99% rename from src/dom/media_element.js rename to src/dom/p5.MediaElement.js index 94e9654b0f..3d85047d39 100644 --- a/src/dom/media_element.js +++ b/src/dom/p5.MediaElement.js @@ -1,4 +1,10 @@ -import { Element } from '../core/p5.Element'; +/** + * @module DOM + * @submodule DOM + * @for p5.Element + */ + +import { Element } from './p5.Element'; class MediaElement extends Element { constructor(elt, pInst) { @@ -1286,10 +1292,21 @@ class MediaElement extends Element { this._prevTime = playbackTime; } -}; +} -function media(p5, fn){ +// Cue inspired by JavaScript setTimeout, and the +// Tone.js Transport Timeline Event, MIT License Yotam Mann 2015 tonejs.org +// eslint-disable-next-line no-unused-vars +class Cue { + constructor(callback, time, id, val) { + this.callback = callback; + this.time = time; + this.id = id; + this.val = val; + } +} +function media(p5, fn){ /** * Helpers for create methods. */ @@ -1297,8 +1314,8 @@ function media(p5, fn){ const node = pInst._userNode ? pInst._userNode : document.body; node.appendChild(elt); const c = media - ? new p5.MediaElement(elt, pInst) - : new p5.Element(elt, pInst); + ? new MediaElement(elt, pInst) + : new Element(elt, pInst); pInst._elements.push(c); return c; } diff --git a/src/io/files.js b/src/io/files.js index 7f00915914..ba7dd0b8ef 100644 --- a/src/io/files.js +++ b/src/io/files.js @@ -1905,7 +1905,7 @@ function files(p5, fn){ // otherwise, parse the arguments // if first param is a p5Graphics, then saveCanvas - fn.saveCanvas(args[0].elt, args[1], args[2]); + fn.saveCanvas(args[0].canvas, args[1], args[2]); return; } else if (args.length === 1 && typeof args[0] === 'string') { // if 1st param is String and only one arg, assume it is canvas filename diff --git a/src/io/p5.XML.js b/src/io/p5.XML.js index 2afd665bb3..fc2907c8b7 100644 --- a/src/io/p5.XML.js +++ b/src/io/p5.XML.js @@ -4,74 +4,17 @@ * @requires core */ -function xml(p5, fn){ - /** - * A class to describe an XML object. - * - * Each `p5.XML` object provides an easy way to interact with XML data. - * Extensible Markup Language - * (XML) - * is a standard format for sending data between applications. Like HTML, the - * XML format is based on tags and attributes, as in - * `<time units="s">1234</time>`. - * - * Note: Use loadXML() to load external XML files. - * - * @class p5.XML - * @example - *
- * - * let myXML; - * - * // Load the XML and create a p5.XML object. - * function preload() { - * myXML = loadXML('assets/animals.xml'); - * } - * - * function setup() { - * createCanvas(100, 100); - * - * background(200); - * - * // Get an array with all mammal tags. - * let mammals = myXML.getChildren('mammal'); - * - * // Style the text. - * textAlign(LEFT, CENTER); - * textFont('Courier New'); - * textSize(14); - * - * // Iterate over the mammals array. - * for (let i = 0; i < mammals.length; i += 1) { - * - * // Calculate the y-coordinate. - * let y = (i + 1) * 25; - * - * // Get the mammal's common name. - * let name = mammals[i].getContent(); - * - * // Display the mammal's name. - * text(name, 20, y); - * } - * - * describe( - * 'The words "Goat", "Leopard", and "Zebra" written on three separate lines. The text is black on a gray background.' - * ); - * } - * - *
- */ - p5.XML = class { - constructor(DOM){ - if (!DOM) { - const xmlDoc = document.implementation.createDocument(null, 'doc'); - this.DOM = xmlDoc.createElement('root'); - } else { - this.DOM = DOM; - } +class XML { + constructor(DOM){ + if (!DOM) { + const xmlDoc = document.implementation.createDocument(null, 'doc'); + this.DOM = xmlDoc.createElement('root'); + } else { + this.DOM = DOM; } + } - /** + /** * Returns the element's parent element as a new p5.XML * object. * @@ -117,11 +60,11 @@ function xml(p5, fn){ * *
*/ - getParent() { - return new p5.XML(this.DOM.parentElement); - } + getParent() { + return new XML(this.DOM.parentElement); + } - /** + /** * Returns the element's name as a `String`. * * An XML element's name is given by its tag. For example, the element @@ -166,11 +109,11 @@ function xml(p5, fn){ * *
*/ - getName() { - return this.DOM.tagName; - } + getName() { + return this.DOM.tagName; + } - /** + /** * Sets the element's tag name. * * An XML element's name is given by its tag. For example, the element @@ -221,19 +164,19 @@ function xml(p5, fn){ * } *
*/ - setName(name) { - const content = this.DOM.innerHTML; - const attributes = this.DOM.attributes; - const xmlDoc = document.implementation.createDocument(null, 'default'); - const newDOM = xmlDoc.createElement(name); - newDOM.innerHTML = content; - for (let i = 0; i < attributes.length; i++) { - newDOM.setAttribute(attributes[i].nodeName, attributes[i].nodeValue); - } - this.DOM = newDOM; + setName(name) { + const content = this.DOM.innerHTML; + const attributes = this.DOM.attributes; + const xmlDoc = document.implementation.createDocument(null, 'default'); + const newDOM = xmlDoc.createElement(name); + newDOM.innerHTML = content; + for (let i = 0; i < attributes.length; i++) { + newDOM.setAttribute(attributes[i].nodeName, attributes[i].nodeValue); } + this.DOM = newDOM; + } - /** + /** * Returns `true` if the element has child elements and `false` if not. * * @return {boolean} whether the element has children. @@ -273,11 +216,11 @@ function xml(p5, fn){ * *
*/ - hasChildren() { - return this.DOM.children.length > 0; - } + hasChildren() { + return this.DOM.children.length > 0; + } - /** + /** * Returns an array with the names of the element's child elements as * `String`s. * @@ -323,15 +266,15 @@ function xml(p5, fn){ * *
*/ - listChildren() { - const arr = []; - for (let i = 0; i < this.DOM.childNodes.length; i++) { - arr.push(this.DOM.childNodes[i].nodeName); - } - return arr; + listChildren() { + const arr = []; + for (let i = 0; i < this.DOM.childNodes.length; i++) { + arr.push(this.DOM.childNodes[i].nodeName); } + return arr; + } - /** + /** * Returns an array with the element's child elements as new * p5.XML objects. * @@ -428,15 +371,15 @@ function xml(p5, fn){ * *
*/ - getChildren(param) { - if (param) { - return elementsToP5XML(this.DOM.getElementsByTagName(param)); - } else { - return elementsToP5XML(this.DOM.children); - } + getChildren(param) { + if (param) { + return elementsToP5XML(this.DOM.getElementsByTagName(param)); + } else { + return elementsToP5XML(this.DOM.children); } + } - /** + /** * Returns the first matching child element as a new * p5.XML object. * @@ -515,17 +458,17 @@ function xml(p5, fn){ * *
*/ - getChild(param) { - if (typeof param === 'string') { - for (const child of this.DOM.children) { - if (child.tagName === param) return new p5.XML(child); - } - } else { - return new p5.XML(this.DOM.children[param]); + getChild(param) { + if (typeof param === 'string') { + for (const child of this.DOM.children) { + if (child.tagName === param) return new XML(child); } + } else { + return new XML(this.DOM.children[param]); } + } - /** + /** * Adds a new child element and returns a reference to it. * * The parameter, `child`, is the p5.XML object to add @@ -581,15 +524,15 @@ function xml(p5, fn){ * * */ - addChild(node) { - if (node instanceof p5.XML) { - this.DOM.appendChild(node.DOM); - } else { - // PEND - } + addChild(node) { + if (node instanceof XML) { + this.DOM.appendChild(node.DOM); + } else { + // PEND } + } - /** + /** * Removes the first matching child element. * * The parameter, `name`, is the child element to remove. If a string is @@ -691,24 +634,24 @@ function xml(p5, fn){ * * */ - removeChild(param) { - let ind = -1; - if (typeof param === 'string') { - for (let i = 0; i < this.DOM.children.length; i++) { - if (this.DOM.children[i].tagName === param) { - ind = i; - break; - } + removeChild(param) { + let ind = -1; + if (typeof param === 'string') { + for (let i = 0; i < this.DOM.children.length; i++) { + if (this.DOM.children[i].tagName === param) { + ind = i; + break; } - } else { - ind = param; - } - if (ind !== -1) { - this.DOM.removeChild(this.DOM.children[ind]); } + } else { + ind = param; } + if (ind !== -1) { + this.DOM.removeChild(this.DOM.children[ind]); + } + } - /** + /** * Returns the number of attributes the element has. * * @return {Integer} number of attributes. @@ -747,11 +690,11 @@ function xml(p5, fn){ * * */ - getAttributeCount() { - return this.DOM.attributes.length; - } + getAttributeCount() { + return this.DOM.attributes.length; + } - /** + /** * Returns an `Array` with the names of the element's attributes. * * Note: Use @@ -794,17 +737,17 @@ function xml(p5, fn){ * * */ - listAttributes() { - const arr = []; - - for (const attribute of this.DOM.attributes) { - arr.push(attribute.nodeName); - } + listAttributes() { + const arr = []; - return arr; + for (const attribute of this.DOM.attributes) { + arr.push(attribute.nodeName); } - /** + return arr; + } + + /** * Returns `true` if the element has a given attribute and `false` if not. * * The parameter, `name`, is a string with the name of the attribute being @@ -856,17 +799,17 @@ function xml(p5, fn){ * * */ - hasAttribute(name) { - const obj = {}; - - for (const attribute of this.DOM.attributes) { - obj[attribute.nodeName] = attribute.nodeValue; - } + hasAttribute(name) { + const obj = {}; - return obj[name] ? true : false; + for (const attribute of this.DOM.attributes) { + obj[attribute.nodeName] = attribute.nodeValue; } - /** + return obj[name] ? true : false; + } + + /** * Return an attribute's value as a `Number`. * * The first parameter, `name`, is a string with the name of the attribute @@ -960,17 +903,17 @@ function xml(p5, fn){ * * */ - getNum(name, defaultValue) { - const obj = {}; - - for (const attribute of this.DOM.attributes) { - obj[attribute.nodeName] = attribute.nodeValue; - } + getNum(name, defaultValue) { + const obj = {}; - return Number(obj[name]) || defaultValue || 0; + for (const attribute of this.DOM.attributes) { + obj[attribute.nodeName] = attribute.nodeValue; } - /** + return Number(obj[name]) || defaultValue || 0; + } + + /** * Return an attribute's value as a string. * * The first parameter, `name`, is a string with the name of the attribute @@ -1063,17 +1006,17 @@ function xml(p5, fn){ * * */ - getString(name, defaultValue) { - const obj = {}; + getString(name, defaultValue) { + const obj = {}; - for (const attribute of this.DOM.attributes) { - obj[attribute.nodeName] = attribute.nodeValue; - } - - return obj[name] ? String(obj[name]) : defaultValue || null; + for (const attribute of this.DOM.attributes) { + obj[attribute.nodeName] = attribute.nodeValue; } - /** + return obj[name] ? String(obj[name]) : defaultValue || null; + } + + /** * Sets an attribute to a given value. * * The first parameter, `name`, is a string with the name of the attribute @@ -1128,11 +1071,11 @@ function xml(p5, fn){ * * */ - setAttribute(name, value) { - this.DOM.setAttribute(name, value); - } + setAttribute(name, value) { + this.DOM.setAttribute(name, value); + } - /** + /** * Returns the element's content as a `String`. * * The parameter, `defaultValue`, is optional. If a string is passed, as in @@ -1203,14 +1146,14 @@ function xml(p5, fn){ * * */ - getContent(defaultValue) { - let str; - str = this.DOM.textContent; - str = str.replace(/\s\s+/g, ','); - return str || defaultValue || null; - } + getContent(defaultValue) { + let str; + str = this.DOM.textContent; + str = str.replace(/\s\s+/g, ','); + return str || defaultValue || null; + } - /** + /** * Sets the element's content. * * An element's content is the text between its tags. For example, the element @@ -1263,13 +1206,13 @@ function xml(p5, fn){ * * */ - setContent(content) { - if (!this.DOM.children.length) { - this.DOM.textContent = content; - } + setContent(content) { + if (!this.DOM.children.length) { + this.DOM.textContent = content; } + } - /** + /** * Returns the element as a `String`. * * `myXML.serialize()` is useful for sending the element over the network or @@ -1321,22 +1264,82 @@ function xml(p5, fn){ * * */ - serialize() { - const xmlSerializer = new XMLSerializer(); - return xmlSerializer.serializeToString(this.DOM); - } - }; + serialize() { + const xmlSerializer = new XMLSerializer(); + return xmlSerializer.serializeToString(this.DOM); + } +} - function elementsToP5XML(elements) { - const arr = []; - for (let i = 0; i < elements.length; i++) { - arr.push(new p5.XML(elements[i])); - } - return arr; +function elementsToP5XML(elements) { + const arr = []; + for (let i = 0; i < elements.length; i++) { + arr.push(new XML(elements[i])); } + return arr; +} + +function xml(p5, fn){ + /** + * A class to describe an XML object. + * + * Each `p5.XML` object provides an easy way to interact with XML data. + * Extensible Markup Language + * (XML) + * is a standard format for sending data between applications. Like HTML, the + * XML format is based on tags and attributes, as in + * `<time units="s">1234</time>`. + * + * Note: Use loadXML() to load external XML files. + * + * @class p5.XML + * @example + *
+ * + * let myXML; + * + * // Load the XML and create a p5.XML object. + * function preload() { + * myXML = loadXML('assets/animals.xml'); + * } + * + * function setup() { + * createCanvas(100, 100); + * + * background(200); + * + * // Get an array with all mammal tags. + * let mammals = myXML.getChildren('mammal'); + * + * // Style the text. + * textAlign(LEFT, CENTER); + * textFont('Courier New'); + * textSize(14); + * + * // Iterate over the mammals array. + * for (let i = 0; i < mammals.length; i += 1) { + * + * // Calculate the y-coordinate. + * let y = (i + 1) * 25; + * + * // Get the mammal's common name. + * let name = mammals[i].getContent(); + * + * // Display the mammal's name. + * text(name, 20, y); + * } + * + * describe( + * 'The words "Goat", "Leopard", and "Zebra" written on three separate lines. The text is black on a gray background.' + * ); + * } + * + *
+ */ + p5.XML = XML; } export default xml; +export { XML } if(typeof p5 !== 'undefined'){ xml(p5, p5.prototype); diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js index a11c9d6359..e806ef8b1f 100644 --- a/src/webgl/p5.RendererGL.js +++ b/src/webgl/p5.RendererGL.js @@ -5,14 +5,13 @@ import { Matrix } from './p5.Matrix'; import { Camera } from './p5.Camera'; import { Vector } from '../math/p5.Vector'; import { RenderBuffer } from './p5.RenderBuffer'; -import { Geometry } from './p5.Geometry'; import { DataArray } from './p5.DataArray'; import { Shader } from './p5.Shader'; import { Image } from '../image/p5.Image'; import { Texture, MipmapTexture } from './p5.Texture'; import { Framebuffer } from './p5.Framebuffer'; import { Graphics } from '../core/p5.Graphics'; -import { Element } from '../core/p5.Element'; +import { Element } from '../dom/p5.Element'; import { ShapeBuilder } from './ShapeBuilder'; import { GeometryBufferCache } from './GeometryBufferCache'; diff --git a/src/webgl/p5.Texture.js b/src/webgl/p5.Texture.js index ad9a7e7b33..f912f552bd 100644 --- a/src/webgl/p5.Texture.js +++ b/src/webgl/p5.Texture.js @@ -8,9 +8,9 @@ // import p5 from '../core/main'; import * as constants from '../core/constants'; -import { Element } from '../core/p5.Element'; +import { Element } from '../dom/p5.Element'; import { Renderer } from '../core/p5.Renderer'; -import { MediaElement } from '../dom/media_element'; +import { MediaElement } from '../dom/p5.MediaElement'; import { Image } from '../image/p5.Image'; import { Graphics } from '../core/p5.Graphics'; import { FramebufferTexture } from './p5.Framebuffer'; @@ -96,12 +96,11 @@ class Texture { textureData = this.src.canvas; } else if ( this.isSrcMediaElement || - this.isSrcP5Graphics || - this.isSrcHTMLElement + this.isSrcHTMLElement ) { // if param is a video HTML element textureData = this.src.elt; - } else if (this.isSrcP5Renderer) { + } else if (this.isSrcP5Graphics || this.isSrcP5Renderer) { textureData = this.src.canvas; } else if (this.isImageData) { textureData = this.src; diff --git a/test/unit/webgl/p5.Texture.js b/test/unit/webgl/p5.Texture.js index 4d05ec280c..80512f0e49 100644 --- a/test/unit/webgl/p5.Texture.js +++ b/test/unit/webgl/p5.Texture.js @@ -52,13 +52,15 @@ suite('p5.Texture', function() { var testTextureSet = function(src) { var lightShader = myp5._renderer._getLightShader(); var selectedShader = myp5._renderer._getFillShader(); + console.log('first'); assert( lightShader === selectedShader, "_renderer's retain mode shader was not light shader " + 'after call to texture()' ); - + console.log('second'); var tex = myp5._renderer.getTexture(src); + console.log('third'); assert(tex !== undefined, 'texture was undefined'); assert(tex instanceof p5.Texture, 'texture was not a p5.Texture object'); assert(tex.src === src, 'texture did not have expected image as source');