p5 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +41 -0
- data/Rakefile +6 -0
- data/app/assets/javascripts/p5.js +3 -0
- data/app/assets/javascripts/p5/p5.dom.js +2250 -0
- data/app/assets/javascripts/p5/p5.min.js +36 -0
- data/app/assets/javascripts/p5/p5.sound.js +10520 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/generators/p5/p5_generator.rb +15 -0
- data/lib/generators/p5/templates/index.html.erb +1 -0
- data/lib/generators/p5/templates/sketch.js +7 -0
- data/lib/p5.rb +5 -0
- data/lib/p5/engine.rb +4 -0
- data/lib/p5/version.rb +3 -0
- data/p5.gemspec +35 -0
- metadata +107 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 186f0127d1ce0234b98ca6e06aaac2b1a7f477ac
|
|
4
|
+
data.tar.gz: b4c50d8a674e95173fcab3235e84b52305381c40
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 575942bc436e73349e015c27a1101a1d9ecad9669660517f7c3f669fcf638c9e726b750699b43057a26e2de873e0036d2692fe4754fbea3c263dccc5095ff25f
|
|
7
|
+
data.tar.gz: b4726de9a1a1e210f31b85032f9dbbc8013e28b6d231a9e2d691fcc2566582389b341d01da9193b417a142a7a655335a262667912df61cc57584d1f14899732b
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2017 Josh Humphrey
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# P5
|
|
2
|
+
|
|
3
|
+
This gem is used to add necessary Javascript dependencies for using p5.js. You can learn all about p5.js here https://p5js.org/.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add this line to your application's Gemfile:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
gem 'p5'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
And then execute:
|
|
14
|
+
|
|
15
|
+
$ bundle
|
|
16
|
+
|
|
17
|
+
Or install it yourself as:
|
|
18
|
+
|
|
19
|
+
$ gem install p5
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
After installing the gem, simply generate a sketch.js, and index.html with:
|
|
24
|
+
|
|
25
|
+
$rails generate p5
|
|
26
|
+
or
|
|
27
|
+
|
|
28
|
+
$rails g p5
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
Lastly, in your application.js add:
|
|
32
|
+
//= require p5
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
## Contributing
|
|
36
|
+
|
|
37
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/humphrey91/p5.
|
|
38
|
+
|
|
39
|
+
## License
|
|
40
|
+
|
|
41
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,2250 @@
|
|
|
1
|
+
/*! p5.dom.js v0.3.4 Aug 11, 2017 */
|
|
2
|
+
/**
|
|
3
|
+
* <p>The web is much more than just canvas and p5.dom makes it easy to interact
|
|
4
|
+
* with other HTML5 objects, including text, hyperlink, image, input, video,
|
|
5
|
+
* audio, and webcam.</p>
|
|
6
|
+
* <p>There is a set of creation methods, DOM manipulation methods, and
|
|
7
|
+
* an extended p5.Element that supports a range of HTML elements. See the
|
|
8
|
+
* <a href="https://github.com/processing/p5.js/wiki/Beyond-the-canvas">
|
|
9
|
+
* beyond the canvas tutorial</a> for a full overview of how this addon works.
|
|
10
|
+
*
|
|
11
|
+
* <p>Methods and properties shown in black are part of the p5.js core, items in
|
|
12
|
+
* blue are part of the p5.dom library. You will need to include an extra file
|
|
13
|
+
* in order to access the blue functions. See the
|
|
14
|
+
* <a href="http://p5js.org/libraries/#using-a-library">using a library</a>
|
|
15
|
+
* section for information on how to include this library. p5.dom comes with
|
|
16
|
+
* <a href="http://p5js.org/download">p5 complete</a> or you can download the single file
|
|
17
|
+
* <a href="https://raw.githubusercontent.com/lmccart/p5.js/master/lib/addons/p5.dom.js">
|
|
18
|
+
* here</a>.</p>
|
|
19
|
+
* <p>See <a href="https://github.com/processing/p5.js/wiki/Beyond-the-canvas">tutorial: beyond the canvas</a>
|
|
20
|
+
* for more info on how to use this libary.</a>
|
|
21
|
+
*
|
|
22
|
+
* @module p5.dom
|
|
23
|
+
* @submodule p5.dom
|
|
24
|
+
* @for p5.dom
|
|
25
|
+
* @main
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
(function (root, factory) {
|
|
29
|
+
if (typeof define === 'function' && define.amd)
|
|
30
|
+
define('p5.dom', ['p5'], function (p5) { (factory(p5));});
|
|
31
|
+
else if (typeof exports === 'object')
|
|
32
|
+
factory(require('../p5'));
|
|
33
|
+
else
|
|
34
|
+
factory(root['p5']);
|
|
35
|
+
}(this, function (p5) {
|
|
36
|
+
|
|
37
|
+
// =============================================================================
|
|
38
|
+
// p5 additions
|
|
39
|
+
// =============================================================================
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Searches the page for an element with the given ID, class, or tag name (using the '#' or '.'
|
|
43
|
+
* prefixes to specify an ID or class respectively, and none for a tag) and returns it as
|
|
44
|
+
* a p5.Element. If a class or tag name is given with more than 1 element,
|
|
45
|
+
* only the first element will be returned.
|
|
46
|
+
* The DOM node itself can be accessed with .elt.
|
|
47
|
+
* Returns null if none found. You can also specify a container to search within.
|
|
48
|
+
*
|
|
49
|
+
* @method select
|
|
50
|
+
* @param {String} name id, class, or tag name of element to search for
|
|
51
|
+
* @param {String} [container] id, p5.Element, or HTML element to search within
|
|
52
|
+
* @return {Object|p5.Element|Null} p5.Element containing node found
|
|
53
|
+
* @example
|
|
54
|
+
* <div ><code class='norender'>
|
|
55
|
+
* function setup() {
|
|
56
|
+
* createCanvas(100,100);
|
|
57
|
+
* //translates canvas 50px down
|
|
58
|
+
* select('canvas').position(100, 100);
|
|
59
|
+
* }
|
|
60
|
+
* </code></div>
|
|
61
|
+
* <div ><code class='norender'>
|
|
62
|
+
* // these are all valid calls to select()
|
|
63
|
+
* var a = select('#moo');
|
|
64
|
+
* var b = select('#blah', '#myContainer');
|
|
65
|
+
* var c = select('#foo', b);
|
|
66
|
+
* var d = document.getElementById('beep');
|
|
67
|
+
* var e = select('p', d);
|
|
68
|
+
* </code></div>
|
|
69
|
+
*
|
|
70
|
+
*/
|
|
71
|
+
p5.prototype.select = function (e, p) {
|
|
72
|
+
var res = null;
|
|
73
|
+
var container = getContainer(p);
|
|
74
|
+
if (e[0] === '.'){
|
|
75
|
+
e = e.slice(1);
|
|
76
|
+
res = container.getElementsByClassName(e);
|
|
77
|
+
if (res.length) {
|
|
78
|
+
res = res[0];
|
|
79
|
+
} else {
|
|
80
|
+
res = null;
|
|
81
|
+
}
|
|
82
|
+
}else if (e[0] === '#'){
|
|
83
|
+
e = e.slice(1);
|
|
84
|
+
res = container.getElementById(e);
|
|
85
|
+
}else {
|
|
86
|
+
res = container.getElementsByTagName(e);
|
|
87
|
+
if (res.length) {
|
|
88
|
+
res = res[0];
|
|
89
|
+
} else {
|
|
90
|
+
res = null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (res) {
|
|
94
|
+
return wrapElement(res);
|
|
95
|
+
} else {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Searches the page for elements with the given class or tag name (using the '.' prefix
|
|
102
|
+
* to specify a class and no prefix for a tag) and returns them as p5.Elements
|
|
103
|
+
* in an array.
|
|
104
|
+
* The DOM node itself can be accessed with .elt.
|
|
105
|
+
* Returns an empty array if none found.
|
|
106
|
+
* You can also specify a container to search within.
|
|
107
|
+
*
|
|
108
|
+
* @method selectAll
|
|
109
|
+
* @param {String} name class or tag name of elements to search for
|
|
110
|
+
* @param {String} [container] id, p5.Element, or HTML element to search within
|
|
111
|
+
* @return {Array} Array of p5.Elements containing nodes found
|
|
112
|
+
* @example
|
|
113
|
+
* <div class='norender'><code>
|
|
114
|
+
* function setup() {
|
|
115
|
+
* createButton('btn');
|
|
116
|
+
* createButton('2nd btn');
|
|
117
|
+
* createButton('3rd btn');
|
|
118
|
+
* var buttons = selectAll('button');
|
|
119
|
+
*
|
|
120
|
+
* for (var i = 0; i < buttons.length; i++){
|
|
121
|
+
* buttons[i].size(100,100);
|
|
122
|
+
* }
|
|
123
|
+
* }
|
|
124
|
+
* </code></div>
|
|
125
|
+
* <div class='norender'><code>
|
|
126
|
+
* // these are all valid calls to selectAll()
|
|
127
|
+
* var a = selectAll('.moo');
|
|
128
|
+
* var b = selectAll('div');
|
|
129
|
+
* var c = selectAll('button', '#myContainer');
|
|
130
|
+
* var d = select('#container');
|
|
131
|
+
* var e = selectAll('p', d);
|
|
132
|
+
* var f = document.getElementById('beep');
|
|
133
|
+
* var g = select('.blah', f);
|
|
134
|
+
* </code></div>
|
|
135
|
+
*
|
|
136
|
+
*/
|
|
137
|
+
p5.prototype.selectAll = function (e, p) {
|
|
138
|
+
var arr = [];
|
|
139
|
+
var res;
|
|
140
|
+
var container = getContainer(p);
|
|
141
|
+
if (e[0] === '.'){
|
|
142
|
+
e = e.slice(1);
|
|
143
|
+
res = container.getElementsByClassName(e);
|
|
144
|
+
} else {
|
|
145
|
+
res = container.getElementsByTagName(e);
|
|
146
|
+
}
|
|
147
|
+
if (res) {
|
|
148
|
+
for (var j = 0; j < res.length; j++) {
|
|
149
|
+
var obj = wrapElement(res[j]);
|
|
150
|
+
arr.push(obj);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return arr;
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Helper function for select and selectAll
|
|
158
|
+
*/
|
|
159
|
+
function getContainer(p) {
|
|
160
|
+
var container = document;
|
|
161
|
+
if (typeof p === 'string' && p[0] === '#'){
|
|
162
|
+
p = p.slice(1);
|
|
163
|
+
container = document.getElementById(p) || document;
|
|
164
|
+
} else if (p instanceof p5.Element){
|
|
165
|
+
container = p.elt;
|
|
166
|
+
} else if (p instanceof HTMLElement){
|
|
167
|
+
container = p;
|
|
168
|
+
}
|
|
169
|
+
return container;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Helper function for getElement and getElements.
|
|
174
|
+
*/
|
|
175
|
+
function wrapElement(elt) {
|
|
176
|
+
if(elt.tagName === "INPUT" && elt.type === "checkbox") {
|
|
177
|
+
var converted = new p5.Element(elt);
|
|
178
|
+
converted.checked = function(){
|
|
179
|
+
if (arguments.length === 0){
|
|
180
|
+
return this.elt.checked;
|
|
181
|
+
} else if(arguments[0]) {
|
|
182
|
+
this.elt.checked = true;
|
|
183
|
+
} else {
|
|
184
|
+
this.elt.checked = false;
|
|
185
|
+
}
|
|
186
|
+
return this;
|
|
187
|
+
};
|
|
188
|
+
return converted;
|
|
189
|
+
} else if (elt.tagName === "VIDEO" || elt.tagName === "AUDIO") {
|
|
190
|
+
return new p5.MediaElement(elt);
|
|
191
|
+
} else if ( elt.tagName === "SELECT" ){
|
|
192
|
+
return createSelect( new p5.Element(elt) );
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
return new p5.Element(elt);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Removes all elements created by p5, except any canvas / graphics
|
|
201
|
+
* elements created by createCanvas or createGraphics.
|
|
202
|
+
* Event handlers are removed, and element is removed from the DOM.
|
|
203
|
+
* @method removeElements
|
|
204
|
+
* @example
|
|
205
|
+
* <div class='norender'><code>
|
|
206
|
+
* function setup() {
|
|
207
|
+
* createCanvas(100, 100);
|
|
208
|
+
* createDiv('this is some text');
|
|
209
|
+
* createP('this is a paragraph');
|
|
210
|
+
* }
|
|
211
|
+
* function mousePressed() {
|
|
212
|
+
* removeElements(); // this will remove the div and p, not canvas
|
|
213
|
+
* }
|
|
214
|
+
* </code></div>
|
|
215
|
+
*
|
|
216
|
+
*/
|
|
217
|
+
p5.prototype.removeElements = function (e) {
|
|
218
|
+
for (var i=0; i<this._elements.length; i++) {
|
|
219
|
+
if (!(this._elements[i].elt instanceof HTMLCanvasElement)) {
|
|
220
|
+
this._elements[i].remove();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Helpers for create methods.
|
|
227
|
+
*/
|
|
228
|
+
function addElement(elt, pInst, media) {
|
|
229
|
+
var node = pInst._userNode ? pInst._userNode : document.body;
|
|
230
|
+
node.appendChild(elt);
|
|
231
|
+
var c = media ? new p5.MediaElement(elt) : new p5.Element(elt);
|
|
232
|
+
pInst._elements.push(c);
|
|
233
|
+
return c;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Creates a <div></div> element in the DOM with given inner HTML.
|
|
238
|
+
* Appends to the container node if one is specified, otherwise
|
|
239
|
+
* appends to body.
|
|
240
|
+
*
|
|
241
|
+
* @method createDiv
|
|
242
|
+
* @param {String} [html] inner HTML for element created
|
|
243
|
+
* @return {Object|p5.Element} pointer to p5.Element holding created node
|
|
244
|
+
* @example
|
|
245
|
+
* <div class='norender'><code>
|
|
246
|
+
* var myDiv;
|
|
247
|
+
* function setup() {
|
|
248
|
+
* myDiv = createDiv('this is some text');
|
|
249
|
+
* }
|
|
250
|
+
* </code></div>
|
|
251
|
+
*/
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Creates a <p></p> element in the DOM with given inner HTML. Used
|
|
255
|
+
* for paragraph length text.
|
|
256
|
+
* Appends to the container node if one is specified, otherwise
|
|
257
|
+
* appends to body.
|
|
258
|
+
*
|
|
259
|
+
* @method createP
|
|
260
|
+
* @param {String} [html] inner HTML for element created
|
|
261
|
+
* @return {Object|p5.Element} pointer to p5.Element holding created node
|
|
262
|
+
* @example
|
|
263
|
+
* <div class='norender'><code>
|
|
264
|
+
* var myP;
|
|
265
|
+
* function setup() {
|
|
266
|
+
* myP = createP('this is some text');
|
|
267
|
+
* }
|
|
268
|
+
* </code></div>
|
|
269
|
+
*/
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Creates a <span></span> element in the DOM with given inner HTML.
|
|
273
|
+
* Appends to the container node if one is specified, otherwise
|
|
274
|
+
* appends to body.
|
|
275
|
+
*
|
|
276
|
+
* @method createSpan
|
|
277
|
+
* @param {String} [html] inner HTML for element created
|
|
278
|
+
* @return {Object|p5.Element} pointer to p5.Element holding created node
|
|
279
|
+
* @example
|
|
280
|
+
* <div class='norender'><code>
|
|
281
|
+
* var mySpan;
|
|
282
|
+
* function setup() {
|
|
283
|
+
* mySpan = createSpan('this is some text');
|
|
284
|
+
* }
|
|
285
|
+
* </code></div>
|
|
286
|
+
*/
|
|
287
|
+
var tags = ['div', 'p', 'span'];
|
|
288
|
+
tags.forEach(function(tag) {
|
|
289
|
+
var method = 'create' + tag.charAt(0).toUpperCase() + tag.slice(1);
|
|
290
|
+
p5.prototype[method] = function(html) {
|
|
291
|
+
var elt = document.createElement(tag);
|
|
292
|
+
elt.innerHTML = typeof html === undefined ? "" : html;
|
|
293
|
+
return addElement(elt, this);
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Creates an <img> element in the DOM with given src and
|
|
299
|
+
* alternate text.
|
|
300
|
+
* Appends to the container node if one is specified, otherwise
|
|
301
|
+
* appends to body.
|
|
302
|
+
*
|
|
303
|
+
* @method createImg
|
|
304
|
+
* @param {String} src src path or url for image
|
|
305
|
+
* @param {String} [alt] alternate text to be used if image does not load
|
|
306
|
+
* @param {Function} [successCallback] callback to be called once image data is loaded
|
|
307
|
+
* @return {Object|p5.Element} pointer to p5.Element holding created node
|
|
308
|
+
* @example
|
|
309
|
+
* <div class='norender'><code>
|
|
310
|
+
* var img;
|
|
311
|
+
* function setup() {
|
|
312
|
+
* img = createImg('http://p5js.org/img/asterisk-01.png');
|
|
313
|
+
* }
|
|
314
|
+
* </code></div>
|
|
315
|
+
*/
|
|
316
|
+
p5.prototype.createImg = function() {
|
|
317
|
+
var elt = document.createElement('img');
|
|
318
|
+
var args = arguments;
|
|
319
|
+
var self;
|
|
320
|
+
var setAttrs = function(){
|
|
321
|
+
self.width = elt.offsetWidth || elt.width;
|
|
322
|
+
self.height = elt.offsetHeight || elt.height;
|
|
323
|
+
if (args.length > 1 && typeof args[1] === 'function'){
|
|
324
|
+
self.fn = args[1];
|
|
325
|
+
self.fn();
|
|
326
|
+
}else if (args.length > 1 && typeof args[2] === 'function'){
|
|
327
|
+
self.fn = args[2];
|
|
328
|
+
self.fn();
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
elt.src = args[0];
|
|
332
|
+
if (args.length > 1 && typeof args[1] === 'string'){
|
|
333
|
+
elt.alt = args[1];
|
|
334
|
+
}
|
|
335
|
+
elt.onload = function(){
|
|
336
|
+
setAttrs();
|
|
337
|
+
}
|
|
338
|
+
self = addElement(elt, this);
|
|
339
|
+
return self;
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Creates an <a></a> element in the DOM for including a hyperlink.
|
|
344
|
+
* Appends to the container node if one is specified, otherwise
|
|
345
|
+
* appends to body.
|
|
346
|
+
*
|
|
347
|
+
* @method createA
|
|
348
|
+
* @param {String} href url of page to link to
|
|
349
|
+
* @param {String} html inner html of link element to display
|
|
350
|
+
* @param {String} [target] target where new link should open,
|
|
351
|
+
* could be _blank, _self, _parent, _top.
|
|
352
|
+
* @return {Object|p5.Element} pointer to p5.Element holding created node
|
|
353
|
+
* @example
|
|
354
|
+
* <div class='norender'><code>
|
|
355
|
+
* var myLink;
|
|
356
|
+
* function setup() {
|
|
357
|
+
* myLink = createA('http://p5js.org/', 'this is a link');
|
|
358
|
+
* }
|
|
359
|
+
* </code></div>
|
|
360
|
+
*/
|
|
361
|
+
p5.prototype.createA = function(href, html, target) {
|
|
362
|
+
var elt = document.createElement('a');
|
|
363
|
+
elt.href = href;
|
|
364
|
+
elt.innerHTML = html;
|
|
365
|
+
if (target) elt.target = target;
|
|
366
|
+
return addElement(elt, this);
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
/** INPUT **/
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Creates a slider <input></input> element in the DOM.
|
|
374
|
+
* Use .size() to set the display length of the slider.
|
|
375
|
+
* Appends to the container node if one is specified, otherwise
|
|
376
|
+
* appends to body.
|
|
377
|
+
*
|
|
378
|
+
* @method createSlider
|
|
379
|
+
* @param {Number} min minimum value of the slider
|
|
380
|
+
* @param {Number} max maximum value of the slider
|
|
381
|
+
* @param {Number} [value] default value of the slider
|
|
382
|
+
* @param {Number} [step] step size for each tick of the slider (if step is set to 0, the slider will move continuously from the minimum to the maximum value)
|
|
383
|
+
* @return {Object|p5.Element} pointer to p5.Element holding created node
|
|
384
|
+
* @example
|
|
385
|
+
* <div><code>
|
|
386
|
+
* var slider;
|
|
387
|
+
* function setup() {
|
|
388
|
+
* slider = createSlider(0, 255, 100);
|
|
389
|
+
* slider.position(10, 10);
|
|
390
|
+
* slider.style('width', '80px');
|
|
391
|
+
* }
|
|
392
|
+
*
|
|
393
|
+
* function draw() {
|
|
394
|
+
* var val = slider.value();
|
|
395
|
+
* background(val);
|
|
396
|
+
* }
|
|
397
|
+
* </code></div>
|
|
398
|
+
*
|
|
399
|
+
* <div><code>
|
|
400
|
+
* var slider;
|
|
401
|
+
* function setup() {
|
|
402
|
+
* colorMode(HSB);
|
|
403
|
+
* slider = createSlider(0, 360, 60, 40);
|
|
404
|
+
* slider.position(10, 10);
|
|
405
|
+
* slider.style('width', '80px');
|
|
406
|
+
* }
|
|
407
|
+
*
|
|
408
|
+
* function draw() {
|
|
409
|
+
* var val = slider.value();
|
|
410
|
+
* background(val, 100, 100, 1);
|
|
411
|
+
* }
|
|
412
|
+
* </code></div>
|
|
413
|
+
*/
|
|
414
|
+
p5.prototype.createSlider = function(min, max, value, step) {
|
|
415
|
+
var elt = document.createElement('input');
|
|
416
|
+
elt.type = 'range';
|
|
417
|
+
elt.min = min;
|
|
418
|
+
elt.max = max;
|
|
419
|
+
if (step === 0) {
|
|
420
|
+
elt.step = .000000000000000001; // smallest valid step
|
|
421
|
+
} else if (step) {
|
|
422
|
+
elt.step = step;
|
|
423
|
+
}
|
|
424
|
+
if (typeof(value) === "number") elt.value = value;
|
|
425
|
+
return addElement(elt, this);
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Creates a <button></button> element in the DOM.
|
|
430
|
+
* Use .size() to set the display size of the button.
|
|
431
|
+
* Use .mousePressed() to specify behavior on press.
|
|
432
|
+
* Appends to the container node if one is specified, otherwise
|
|
433
|
+
* appends to body.
|
|
434
|
+
*
|
|
435
|
+
* @method createButton
|
|
436
|
+
* @param {String} label label displayed on the button
|
|
437
|
+
* @param {String} [value] value of the button
|
|
438
|
+
* @return {Object|p5.Element} pointer to p5.Element holding created node
|
|
439
|
+
* @example
|
|
440
|
+
* <div class='norender'><code>
|
|
441
|
+
* var button;
|
|
442
|
+
* function setup() {
|
|
443
|
+
* createCanvas(100, 100);
|
|
444
|
+
* background(0);
|
|
445
|
+
* button = createButton('click me');
|
|
446
|
+
* button.position(19, 19);
|
|
447
|
+
* button.mousePressed(changeBG);
|
|
448
|
+
* }
|
|
449
|
+
*
|
|
450
|
+
* function changeBG() {
|
|
451
|
+
* var val = random(255);
|
|
452
|
+
* background(val);
|
|
453
|
+
* }
|
|
454
|
+
* </code></div>
|
|
455
|
+
*/
|
|
456
|
+
p5.prototype.createButton = function(label, value) {
|
|
457
|
+
var elt = document.createElement('button');
|
|
458
|
+
elt.innerHTML = label;
|
|
459
|
+
if (value) elt.value = value;
|
|
460
|
+
return addElement(elt, this);
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Creates a checkbox <input></input> element in the DOM.
|
|
465
|
+
* Calling .checked() on a checkbox returns if it is checked or not
|
|
466
|
+
*
|
|
467
|
+
* @method createCheckbox
|
|
468
|
+
* @param {String} [label] label displayed after checkbox
|
|
469
|
+
* @param {boolean} [value] value of the checkbox; checked is true, unchecked is false.Unchecked if no value given
|
|
470
|
+
* @return {Object|p5.Element} pointer to p5.Element holding created node
|
|
471
|
+
* @example
|
|
472
|
+
* <div class='norender'><code>
|
|
473
|
+
* var checkbox;
|
|
474
|
+
*
|
|
475
|
+
* function setup() {
|
|
476
|
+
* checkbox = createCheckbox('label', false);
|
|
477
|
+
* checkbox.changed(myCheckedEvent);
|
|
478
|
+
* }
|
|
479
|
+
*
|
|
480
|
+
* function myCheckedEvent() {
|
|
481
|
+
* if (this.checked()) {
|
|
482
|
+
* console.log("Checking!");
|
|
483
|
+
* } else {
|
|
484
|
+
* console.log("Unchecking!");
|
|
485
|
+
* }
|
|
486
|
+
* }
|
|
487
|
+
* </code></div>
|
|
488
|
+
*/
|
|
489
|
+
p5.prototype.createCheckbox = function() {
|
|
490
|
+
var elt = document.createElement('div');
|
|
491
|
+
var checkbox = document.createElement('input');
|
|
492
|
+
checkbox.type = 'checkbox';
|
|
493
|
+
elt.appendChild(checkbox);
|
|
494
|
+
//checkbox must be wrapped in p5.Element before label so that label appears after
|
|
495
|
+
var self = addElement(elt, this);
|
|
496
|
+
self.checked = function(){
|
|
497
|
+
var cb = self.elt.getElementsByTagName('input')[0];
|
|
498
|
+
if (cb) {
|
|
499
|
+
if (arguments.length === 0){
|
|
500
|
+
return cb.checked;
|
|
501
|
+
}else if(arguments[0]){
|
|
502
|
+
cb.checked = true;
|
|
503
|
+
}else{
|
|
504
|
+
cb.checked = false;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
return self;
|
|
508
|
+
};
|
|
509
|
+
this.value = function(val){
|
|
510
|
+
self.value = val;
|
|
511
|
+
return this;
|
|
512
|
+
};
|
|
513
|
+
if (arguments[0]){
|
|
514
|
+
var ran = Math.random().toString(36).slice(2);
|
|
515
|
+
var label = document.createElement('label');
|
|
516
|
+
checkbox.setAttribute('id', ran);
|
|
517
|
+
label.htmlFor = ran;
|
|
518
|
+
self.value(arguments[0]);
|
|
519
|
+
label.appendChild(document.createTextNode(arguments[0]));
|
|
520
|
+
elt.appendChild(label);
|
|
521
|
+
}
|
|
522
|
+
if (arguments[1]){
|
|
523
|
+
checkbox.checked = true;
|
|
524
|
+
}
|
|
525
|
+
return self;
|
|
526
|
+
};
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Creates a dropdown menu <select></select> element in the DOM.
|
|
530
|
+
* It also helps to assign select-box methods to p5.Element when selecting existing select box
|
|
531
|
+
* @method createSelect
|
|
532
|
+
* @param {boolean} [multiple] true if dropdown should support multiple selections
|
|
533
|
+
* @return {p5.Element}
|
|
534
|
+
* @example
|
|
535
|
+
* <div><code>
|
|
536
|
+
* var sel;
|
|
537
|
+
*
|
|
538
|
+
* function setup() {
|
|
539
|
+
* textAlign(CENTER);
|
|
540
|
+
* background(200);
|
|
541
|
+
* sel = createSelect();
|
|
542
|
+
* sel.position(10, 10);
|
|
543
|
+
* sel.option('pear');
|
|
544
|
+
* sel.option('kiwi');
|
|
545
|
+
* sel.option('grape');
|
|
546
|
+
* sel.changed(mySelectEvent);
|
|
547
|
+
* }
|
|
548
|
+
*
|
|
549
|
+
* function mySelectEvent() {
|
|
550
|
+
* var item = sel.value();
|
|
551
|
+
* background(200);
|
|
552
|
+
* text("it's a "+item+"!", 50, 50);
|
|
553
|
+
* }
|
|
554
|
+
* </code></div>
|
|
555
|
+
*/
|
|
556
|
+
/**
|
|
557
|
+
* @method createSelect
|
|
558
|
+
* @param {Object} existing DOM select element
|
|
559
|
+
* @return {p5.Element}
|
|
560
|
+
*/
|
|
561
|
+
|
|
562
|
+
p5.prototype.createSelect = function() {
|
|
563
|
+
var elt, self;
|
|
564
|
+
var arg = arguments[0];
|
|
565
|
+
if( typeof arg === 'object' && arg.elt.nodeName === 'SELECT' ) {
|
|
566
|
+
self = arg;
|
|
567
|
+
elt = this.elt = arg.elt;
|
|
568
|
+
} else {
|
|
569
|
+
elt = document.createElement('select');
|
|
570
|
+
if( arg && typeof arg === 'boolean' ) {
|
|
571
|
+
elt.setAttribute('multiple', 'true');
|
|
572
|
+
}
|
|
573
|
+
self = addElement(elt, this);
|
|
574
|
+
}
|
|
575
|
+
self.option = function(name, value) {
|
|
576
|
+
var index;
|
|
577
|
+
//see if there is already an option with this name
|
|
578
|
+
for (var i = 0; i < this.elt.length; i++) {
|
|
579
|
+
if(this.elt[i].innerHTML == name) {
|
|
580
|
+
index = i;
|
|
581
|
+
break;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
//if there is an option with this name we will modify it
|
|
585
|
+
if(index !== undefined) {
|
|
586
|
+
//if the user passed in false then delete that option
|
|
587
|
+
if(value === false) {
|
|
588
|
+
this.elt.remove(index);
|
|
589
|
+
} else {
|
|
590
|
+
//otherwise if the name and value are the same then change both
|
|
591
|
+
if(this.elt[index].innerHTML == this.elt[index].value) {
|
|
592
|
+
this.elt[index].innerHTML = this.elt[index].value = value;
|
|
593
|
+
//otherwise just change the value
|
|
594
|
+
} else {
|
|
595
|
+
this.elt[index].value = value;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
//if it doesn't exist make it
|
|
600
|
+
else {
|
|
601
|
+
var opt = document.createElement('option');
|
|
602
|
+
opt.innerHTML = name;
|
|
603
|
+
if (arguments.length > 1)
|
|
604
|
+
opt.value = value;
|
|
605
|
+
else
|
|
606
|
+
opt.value = name;
|
|
607
|
+
elt.appendChild(opt);
|
|
608
|
+
}
|
|
609
|
+
};
|
|
610
|
+
self.selected = function(value) {
|
|
611
|
+
var arr = [];
|
|
612
|
+
if (arguments.length > 0) {
|
|
613
|
+
for (var i = 0; i < this.elt.length; i++) {
|
|
614
|
+
if (value.toString() === this.elt[i].value) {
|
|
615
|
+
this.elt.selectedIndex = i;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
return this;
|
|
619
|
+
} else {
|
|
620
|
+
if (arg) {
|
|
621
|
+
for (var i = 0; i < this.elt.selectedOptions.length; i++) {
|
|
622
|
+
arr.push(this.elt.selectedOptions[i].value);
|
|
623
|
+
}
|
|
624
|
+
return arr;
|
|
625
|
+
} else {
|
|
626
|
+
return this.elt.value;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
};
|
|
630
|
+
return self;
|
|
631
|
+
};
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Creates a radio button <input></input> element in the DOM.
|
|
635
|
+
* The .option() method can be used to set options for the radio after it is
|
|
636
|
+
* created. The .value() method will return the currently selected option.
|
|
637
|
+
*
|
|
638
|
+
* @method createRadio
|
|
639
|
+
* @param {String} [divId] the id and name of the created div and input field respectively
|
|
640
|
+
* @return {Object|p5.Element} pointer to p5.Element holding created node
|
|
641
|
+
* @example
|
|
642
|
+
* <div><code>
|
|
643
|
+
* var radio;
|
|
644
|
+
*
|
|
645
|
+
* function setup() {
|
|
646
|
+
* radio = createRadio();
|
|
647
|
+
* radio.option("black");
|
|
648
|
+
* radio.option("white");
|
|
649
|
+
* radio.option("gray");
|
|
650
|
+
* radio.style('width', '60px');
|
|
651
|
+
* textAlign(CENTER);
|
|
652
|
+
* fill(255, 0, 0);
|
|
653
|
+
* }
|
|
654
|
+
*
|
|
655
|
+
* function draw() {
|
|
656
|
+
* var val = radio.value();
|
|
657
|
+
* background(val);
|
|
658
|
+
* text(val, width/2, height/2);
|
|
659
|
+
* }
|
|
660
|
+
* </code></div>
|
|
661
|
+
* <div><code>
|
|
662
|
+
* var radio;
|
|
663
|
+
*
|
|
664
|
+
* function setup() {
|
|
665
|
+
* radio = createRadio();
|
|
666
|
+
* radio.option('apple', 1);
|
|
667
|
+
* radio.option('bread', 2);
|
|
668
|
+
* radio.option('juice', 3);
|
|
669
|
+
* radio.style('width', '60px');
|
|
670
|
+
* textAlign(CENTER);
|
|
671
|
+
* }
|
|
672
|
+
*
|
|
673
|
+
* function draw() {
|
|
674
|
+
* background(200);
|
|
675
|
+
* var val = radio.value();
|
|
676
|
+
* if (val) {
|
|
677
|
+
* text('item cost is $'+val, width/2, height/2);
|
|
678
|
+
* }
|
|
679
|
+
* }
|
|
680
|
+
* </code></div>
|
|
681
|
+
*/
|
|
682
|
+
p5.prototype.createRadio = function() {
|
|
683
|
+
var radios = document.querySelectorAll("input[type=radio]");
|
|
684
|
+
var count = 0;
|
|
685
|
+
if(radios.length > 1){
|
|
686
|
+
var length = radios.length;
|
|
687
|
+
var prev=radios[0].name;
|
|
688
|
+
var current = radios[1].name;
|
|
689
|
+
count = 1;
|
|
690
|
+
for(var i = 1; i < length; i++) {
|
|
691
|
+
current = radios[i].name;
|
|
692
|
+
if(prev != current){
|
|
693
|
+
count++;
|
|
694
|
+
}
|
|
695
|
+
prev = current;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
else if (radios.length == 1){
|
|
699
|
+
count = 1;
|
|
700
|
+
}
|
|
701
|
+
var elt = document.createElement('div');
|
|
702
|
+
var self = addElement(elt, this);
|
|
703
|
+
var times = -1;
|
|
704
|
+
self.option = function(name, value){
|
|
705
|
+
var opt = document.createElement('input');
|
|
706
|
+
opt.type = 'radio';
|
|
707
|
+
opt.innerHTML = name;
|
|
708
|
+
if (arguments.length > 1)
|
|
709
|
+
opt.value = value;
|
|
710
|
+
else
|
|
711
|
+
opt.value = name;
|
|
712
|
+
opt.setAttribute('name',"defaultradio"+count);
|
|
713
|
+
elt.appendChild(opt);
|
|
714
|
+
if (name){
|
|
715
|
+
times++;
|
|
716
|
+
var ran = Math.random().toString(36).slice(2);
|
|
717
|
+
var label = document.createElement('label');
|
|
718
|
+
opt.setAttribute('id', "defaultradio"+count+"-"+times);
|
|
719
|
+
label.htmlFor = "defaultradio"+count+"-"+times;
|
|
720
|
+
label.appendChild(document.createTextNode(name));
|
|
721
|
+
elt.appendChild(label);
|
|
722
|
+
}
|
|
723
|
+
return opt;
|
|
724
|
+
};
|
|
725
|
+
self.selected = function(){
|
|
726
|
+
var length = this.elt.childNodes.length;
|
|
727
|
+
if(arguments.length == 1) {
|
|
728
|
+
for (var i = 0; i < length; i+=2){
|
|
729
|
+
if(this.elt.childNodes[i].value == arguments[0])
|
|
730
|
+
this.elt.childNodes[i].checked = true;
|
|
731
|
+
}
|
|
732
|
+
return this;
|
|
733
|
+
} else {
|
|
734
|
+
for (var i = 0; i < length; i+=2){
|
|
735
|
+
if(this.elt.childNodes[i].checked == true)
|
|
736
|
+
return this.elt.childNodes[i].value;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
};
|
|
740
|
+
self.value = function(){
|
|
741
|
+
var length = this.elt.childNodes.length;
|
|
742
|
+
if(arguments.length == 1) {
|
|
743
|
+
for (var i = 0; i < length; i+=2){
|
|
744
|
+
if(this.elt.childNodes[i].value == arguments[0])
|
|
745
|
+
this.elt.childNodes[i].checked = true;
|
|
746
|
+
}
|
|
747
|
+
return this;
|
|
748
|
+
} else {
|
|
749
|
+
for (var i = 0; i < length; i+=2){
|
|
750
|
+
if(this.elt.childNodes[i].checked == true)
|
|
751
|
+
return this.elt.childNodes[i].value;
|
|
752
|
+
}
|
|
753
|
+
return "";
|
|
754
|
+
}
|
|
755
|
+
};
|
|
756
|
+
return self
|
|
757
|
+
};
|
|
758
|
+
|
|
759
|
+
/**
|
|
760
|
+
* Creates an <input></input> element in the DOM for text input.
|
|
761
|
+
* Use .size() to set the display length of the box.
|
|
762
|
+
* Appends to the container node if one is specified, otherwise
|
|
763
|
+
* appends to body.
|
|
764
|
+
*
|
|
765
|
+
* @method createInput
|
|
766
|
+
* @param {Number} [value] default value of the input box
|
|
767
|
+
* @param {String} [type] type of text, ie text, password etc. Defaults to text
|
|
768
|
+
* @return {Object|p5.Element} pointer to p5.Element holding created node
|
|
769
|
+
* @example
|
|
770
|
+
* <div class='norender'><code>
|
|
771
|
+
* function setup(){
|
|
772
|
+
* var inp = createInput('');
|
|
773
|
+
* inp.input(myInputEvent);
|
|
774
|
+
* }
|
|
775
|
+
*
|
|
776
|
+
* function myInputEvent(){
|
|
777
|
+
* console.log('you are typing: ', this.value());
|
|
778
|
+
* }
|
|
779
|
+
*
|
|
780
|
+
* </code></div>
|
|
781
|
+
*/
|
|
782
|
+
p5.prototype.createInput = function(value, type) {
|
|
783
|
+
var elt = document.createElement('input');
|
|
784
|
+
elt.type = type ? type : 'text';
|
|
785
|
+
if (value) elt.value = value;
|
|
786
|
+
return addElement(elt, this);
|
|
787
|
+
};
|
|
788
|
+
|
|
789
|
+
/**
|
|
790
|
+
* Creates an <input></input> element in the DOM of type 'file'.
|
|
791
|
+
* This allows users to select local files for use in a sketch.
|
|
792
|
+
*
|
|
793
|
+
* @method createFileInput
|
|
794
|
+
* @param {Function} [callback] callback function for when a file loaded
|
|
795
|
+
* @param {String} [multiple] optional to allow multiple files selected
|
|
796
|
+
* @return {Object|p5.Element} pointer to p5.Element holding created DOM element
|
|
797
|
+
* @example
|
|
798
|
+
* var input;
|
|
799
|
+
* var img;
|
|
800
|
+
*
|
|
801
|
+
* function setup() {
|
|
802
|
+
* input = createFileInput(handleFile);
|
|
803
|
+
* input.position(0, 0);
|
|
804
|
+
* }
|
|
805
|
+
*
|
|
806
|
+
* function draw() {
|
|
807
|
+
* if (img) {
|
|
808
|
+
* image(img, 0, 0, width, height);
|
|
809
|
+
* }
|
|
810
|
+
* }
|
|
811
|
+
*
|
|
812
|
+
* function handleFile(file) {
|
|
813
|
+
* print(file);
|
|
814
|
+
* if (file.type === 'image') {
|
|
815
|
+
* img = createImg(file.data);
|
|
816
|
+
* img.hide();
|
|
817
|
+
* }
|
|
818
|
+
* }
|
|
819
|
+
*/
|
|
820
|
+
p5.prototype.createFileInput = function(callback, multiple) {
|
|
821
|
+
|
|
822
|
+
// Is the file stuff supported?
|
|
823
|
+
if (window.File && window.FileReader && window.FileList && window.Blob) {
|
|
824
|
+
// Yup, we're ok and make an input file selector
|
|
825
|
+
var elt = document.createElement('input');
|
|
826
|
+
elt.type = 'file';
|
|
827
|
+
|
|
828
|
+
// If we get a second argument that evaluates to true
|
|
829
|
+
// then we are looking for multiple files
|
|
830
|
+
if (multiple) {
|
|
831
|
+
// Anything gets the job done
|
|
832
|
+
elt.multiple = 'multiple';
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
// Function to handle when a file is selected
|
|
836
|
+
// We're simplifying life and assuming that we always
|
|
837
|
+
// want to load every selected file
|
|
838
|
+
function handleFileSelect(evt) {
|
|
839
|
+
// These are the files
|
|
840
|
+
var files = evt.target.files;
|
|
841
|
+
// Load each one and trigger a callback
|
|
842
|
+
for (var i = 0; i < files.length; i++) {
|
|
843
|
+
var f = files[i];
|
|
844
|
+
var reader = new FileReader();
|
|
845
|
+
function makeLoader(theFile) {
|
|
846
|
+
// Making a p5.File object
|
|
847
|
+
var p5file = new p5.File(theFile);
|
|
848
|
+
return function(e) {
|
|
849
|
+
p5file.data = e.target.result;
|
|
850
|
+
callback(p5file);
|
|
851
|
+
};
|
|
852
|
+
};
|
|
853
|
+
reader.onload = makeLoader(f);
|
|
854
|
+
|
|
855
|
+
// Text or data?
|
|
856
|
+
// This should likely be improved
|
|
857
|
+
if (f.type.indexOf('text') > -1) {
|
|
858
|
+
reader.readAsText(f);
|
|
859
|
+
} else {
|
|
860
|
+
reader.readAsDataURL(f);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
// Now let's handle when a file was selected
|
|
866
|
+
elt.addEventListener('change', handleFileSelect, false);
|
|
867
|
+
return addElement(elt, this);
|
|
868
|
+
} else {
|
|
869
|
+
console.log('The File APIs are not fully supported in this browser. Cannot create element.');
|
|
870
|
+
}
|
|
871
|
+
};
|
|
872
|
+
|
|
873
|
+
|
|
874
|
+
/** VIDEO STUFF **/
|
|
875
|
+
|
|
876
|
+
function createMedia(pInst, type, src, callback) {
|
|
877
|
+
var elt = document.createElement(type);
|
|
878
|
+
|
|
879
|
+
// allow src to be empty
|
|
880
|
+
var src = src || '';
|
|
881
|
+
if (typeof src === 'string') {
|
|
882
|
+
src = [src];
|
|
883
|
+
}
|
|
884
|
+
for (var i=0; i<src.length; i++) {
|
|
885
|
+
var source = document.createElement('source');
|
|
886
|
+
source.src = src[i];
|
|
887
|
+
elt.appendChild(source);
|
|
888
|
+
}
|
|
889
|
+
if (typeof callback !== 'undefined') {
|
|
890
|
+
var callbackHandler = function() {
|
|
891
|
+
callback();
|
|
892
|
+
elt.removeEventListener('canplaythrough', callbackHandler);
|
|
893
|
+
}
|
|
894
|
+
elt.addEventListener('canplaythrough', callbackHandler);
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
var c = addElement(elt, pInst, true);
|
|
898
|
+
c.loadedmetadata = false;
|
|
899
|
+
// set width and height onload metadata
|
|
900
|
+
elt.addEventListener('loadedmetadata', function() {
|
|
901
|
+
c.width = elt.videoWidth;
|
|
902
|
+
c.height = elt.videoHeight;
|
|
903
|
+
// set elt width and height if not set
|
|
904
|
+
if (c.elt.width === 0) c.elt.width = elt.videoWidth;
|
|
905
|
+
if (c.elt.height === 0) c.elt.height = elt.videoHeight;
|
|
906
|
+
c.loadedmetadata = true;
|
|
907
|
+
});
|
|
908
|
+
|
|
909
|
+
return c;
|
|
910
|
+
}
|
|
911
|
+
/**
|
|
912
|
+
* Creates an HTML5 <video> element in the DOM for simple playback
|
|
913
|
+
* of audio/video. Shown by default, can be hidden with .hide()
|
|
914
|
+
* and drawn into canvas using video(). Appends to the container
|
|
915
|
+
* node if one is specified, otherwise appends to body. The first parameter
|
|
916
|
+
* can be either a single string path to a video file, or an array of string
|
|
917
|
+
* paths to different formats of the same video. This is useful for ensuring
|
|
918
|
+
* that your video can play across different browsers, as each supports
|
|
919
|
+
* different formats. See <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Supported_media_formats">this
|
|
920
|
+
* page</a> for further information about supported formats.
|
|
921
|
+
*
|
|
922
|
+
* @method createVideo
|
|
923
|
+
* @param {String|Array} src path to a video file, or array of paths for
|
|
924
|
+
* supporting different browsers
|
|
925
|
+
* @param {Object} [callback] callback function to be called upon
|
|
926
|
+
* 'canplaythrough' event fire, that is, when the
|
|
927
|
+
* browser can play the media, and estimates that
|
|
928
|
+
* enough data has been loaded to play the media
|
|
929
|
+
* up to its end without having to stop for
|
|
930
|
+
* further buffering of content
|
|
931
|
+
* @return {p5.MediaElement|p5.Element} pointer to video p5.Element
|
|
932
|
+
*/
|
|
933
|
+
p5.prototype.createVideo = function(src, callback) {
|
|
934
|
+
return createMedia(this, 'video', src, callback);
|
|
935
|
+
};
|
|
936
|
+
|
|
937
|
+
/** AUDIO STUFF **/
|
|
938
|
+
|
|
939
|
+
/**
|
|
940
|
+
* Creates a hidden HTML5 <audio> element in the DOM for simple audio
|
|
941
|
+
* playback. Appends to the container node if one is specified,
|
|
942
|
+
* otherwise appends to body. The first parameter
|
|
943
|
+
* can be either a single string path to a audio file, or an array of string
|
|
944
|
+
* paths to different formats of the same audio. This is useful for ensuring
|
|
945
|
+
* that your audio can play across different browsers, as each supports
|
|
946
|
+
* different formats. See <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Supported_media_formats">this
|
|
947
|
+
* page for further information about supported formats</a>.
|
|
948
|
+
*
|
|
949
|
+
* @method createAudio
|
|
950
|
+
* @param {String|Array} src path to an audio file, or array of paths for
|
|
951
|
+
* supporting different browsers
|
|
952
|
+
* @param {Object} [callback] callback function to be called upon
|
|
953
|
+
* 'canplaythrough' event fire, that is, when the
|
|
954
|
+
* browser can play the media, and estimates that
|
|
955
|
+
* enough data has been loaded to play the media
|
|
956
|
+
* up to its end without having to stop for
|
|
957
|
+
* further buffering of content
|
|
958
|
+
* @return {p5.MediaElement|p5.Element} pointer to audio p5.Element
|
|
959
|
+
*/
|
|
960
|
+
p5.prototype.createAudio = function(src, callback) {
|
|
961
|
+
return createMedia(this, 'audio', src, callback);
|
|
962
|
+
};
|
|
963
|
+
|
|
964
|
+
|
|
965
|
+
/** CAMERA STUFF **/
|
|
966
|
+
|
|
967
|
+
p5.prototype.VIDEO = 'video';
|
|
968
|
+
p5.prototype.AUDIO = 'audio';
|
|
969
|
+
|
|
970
|
+
navigator.getUserMedia = navigator.getUserMedia ||
|
|
971
|
+
navigator.webkitGetUserMedia ||
|
|
972
|
+
navigator.mozGetUserMedia ||
|
|
973
|
+
navigator.msGetUserMedia;
|
|
974
|
+
|
|
975
|
+
/**
|
|
976
|
+
* <p>Creates a new <video> element that contains the audio/video feed
|
|
977
|
+
* from a webcam. This can be drawn onto the canvas using video().</p>
|
|
978
|
+
* <p>More specific properties of the feed can be passing in a Constraints object.
|
|
979
|
+
* See the
|
|
980
|
+
* <a href="http://w3c.github.io/mediacapture-main/getusermedia.html#media-track-constraints"> W3C
|
|
981
|
+
* spec</a> for possible properties. Note that not all of these are supported
|
|
982
|
+
* by all browsers.</p>
|
|
983
|
+
* <p>Security note: A new browser security specification requires that getUserMedia,
|
|
984
|
+
* which is behind createCapture(), only works when you're running the code locally,
|
|
985
|
+
* or on HTTPS. Learn more <a href="http://stackoverflow.com/questions/34197653/getusermedia-in-chrome-47-without-using-https">here</a>
|
|
986
|
+
* and <a href="https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia">here</a>.</p>
|
|
987
|
+
*
|
|
988
|
+
* @method createCapture
|
|
989
|
+
* @param {String|Constant|Object} type type of capture, either VIDEO or
|
|
990
|
+
* AUDIO if none specified, default both,
|
|
991
|
+
* or a Constraints object
|
|
992
|
+
* @param {Function} callback function to be called once
|
|
993
|
+
* stream has loaded
|
|
994
|
+
* @return {Object|p5.Element} capture video p5.Element
|
|
995
|
+
* @example
|
|
996
|
+
* <div class='norender'><code>
|
|
997
|
+
* var capture;
|
|
998
|
+
*
|
|
999
|
+
* function setup() {
|
|
1000
|
+
* createCanvas(480, 120);
|
|
1001
|
+
* capture = createCapture(VIDEO);
|
|
1002
|
+
* }
|
|
1003
|
+
*
|
|
1004
|
+
* function draw() {
|
|
1005
|
+
* image(capture, 0, 0, width, width*capture.height/capture.width);
|
|
1006
|
+
* filter(INVERT);
|
|
1007
|
+
* }
|
|
1008
|
+
* </code></div>
|
|
1009
|
+
* <div class='norender'><code>
|
|
1010
|
+
* function setup() {
|
|
1011
|
+
* createCanvas(480, 120);
|
|
1012
|
+
* var constraints = {
|
|
1013
|
+
* video: {
|
|
1014
|
+
* mandatory: {
|
|
1015
|
+
* minWidth: 1280,
|
|
1016
|
+
* minHeight: 720
|
|
1017
|
+
* },
|
|
1018
|
+
* optional: [
|
|
1019
|
+
* { maxFrameRate: 10 }
|
|
1020
|
+
* ]
|
|
1021
|
+
* },
|
|
1022
|
+
* audio: true
|
|
1023
|
+
* };
|
|
1024
|
+
* createCapture(constraints, function(stream) {
|
|
1025
|
+
* console.log(stream);
|
|
1026
|
+
* });
|
|
1027
|
+
* }
|
|
1028
|
+
* </code></div>
|
|
1029
|
+
*/
|
|
1030
|
+
p5.prototype.createCapture = function() {
|
|
1031
|
+
var useVideo = true;
|
|
1032
|
+
var useAudio = true;
|
|
1033
|
+
var constraints;
|
|
1034
|
+
var cb;
|
|
1035
|
+
for (var i=0; i<arguments.length; i++) {
|
|
1036
|
+
if (arguments[i] === p5.prototype.VIDEO) {
|
|
1037
|
+
useAudio = false;
|
|
1038
|
+
} else if (arguments[i] === p5.prototype.AUDIO) {
|
|
1039
|
+
useVideo = false;
|
|
1040
|
+
} else if (typeof arguments[i] === 'object') {
|
|
1041
|
+
constraints = arguments[i];
|
|
1042
|
+
} else if (typeof arguments[i] === 'function') {
|
|
1043
|
+
cb = arguments[i];
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
if (navigator.getUserMedia) {
|
|
1048
|
+
var elt = document.createElement('video');
|
|
1049
|
+
|
|
1050
|
+
if (!constraints) {
|
|
1051
|
+
constraints = {video: useVideo, audio: useAudio};
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
navigator.getUserMedia(constraints, function(stream) {
|
|
1055
|
+
elt.src = window.URL.createObjectURL(stream);
|
|
1056
|
+
if (cb) {
|
|
1057
|
+
cb(stream);
|
|
1058
|
+
}
|
|
1059
|
+
}, function(e) { console.log(e); });
|
|
1060
|
+
} else {
|
|
1061
|
+
throw 'getUserMedia not supported in this browser';
|
|
1062
|
+
}
|
|
1063
|
+
var c = addElement(elt, this, true);
|
|
1064
|
+
c.loadedmetadata = false;
|
|
1065
|
+
// set width and height onload metadata
|
|
1066
|
+
elt.addEventListener('loadedmetadata', function() {
|
|
1067
|
+
elt.play();
|
|
1068
|
+
if (elt.width) {
|
|
1069
|
+
c.width = elt.videoWidth = elt.width;
|
|
1070
|
+
c.height = elt.videoHeight = elt.height;
|
|
1071
|
+
} else {
|
|
1072
|
+
c.width = c.elt.width = elt.videoWidth;
|
|
1073
|
+
c.height = c.elt.height = elt.videoHeight;
|
|
1074
|
+
}
|
|
1075
|
+
c.loadedmetadata = true;
|
|
1076
|
+
});
|
|
1077
|
+
return c;
|
|
1078
|
+
};
|
|
1079
|
+
|
|
1080
|
+
/**
|
|
1081
|
+
* Creates element with given tag in the DOM with given content.
|
|
1082
|
+
* Appends to the container node if one is specified, otherwise
|
|
1083
|
+
* appends to body.
|
|
1084
|
+
*
|
|
1085
|
+
* @method createElement
|
|
1086
|
+
* @param {String} tag tag for the new element
|
|
1087
|
+
* @param {String} [content] html content to be inserted into the element
|
|
1088
|
+
* @return {Object|p5.Element} pointer to p5.Element holding created node
|
|
1089
|
+
* @example
|
|
1090
|
+
* <div class='norender'><code>
|
|
1091
|
+
* var h2 = createElement('h2','im an h2 p5.element!');
|
|
1092
|
+
* </code></div>
|
|
1093
|
+
*/
|
|
1094
|
+
p5.prototype.createElement = function(tag, content) {
|
|
1095
|
+
var elt = document.createElement(tag);
|
|
1096
|
+
if (typeof content !== 'undefined') {
|
|
1097
|
+
elt.innerHTML = content;
|
|
1098
|
+
}
|
|
1099
|
+
return addElement(elt, this);
|
|
1100
|
+
};
|
|
1101
|
+
|
|
1102
|
+
|
|
1103
|
+
// =============================================================================
|
|
1104
|
+
// p5.Element additions
|
|
1105
|
+
// =============================================================================
|
|
1106
|
+
/**
|
|
1107
|
+
*
|
|
1108
|
+
* Adds specified class to the element.
|
|
1109
|
+
*
|
|
1110
|
+
* @for p5.Element
|
|
1111
|
+
* @method addClass
|
|
1112
|
+
* @param {String} class name of class to add
|
|
1113
|
+
* @return {Object|p5.Element}
|
|
1114
|
+
* @example
|
|
1115
|
+
* <div class='norender'><code>
|
|
1116
|
+
* var div = createDiv('div');
|
|
1117
|
+
* div.addClass('myClass');
|
|
1118
|
+
* </code></div>
|
|
1119
|
+
*/
|
|
1120
|
+
p5.Element.prototype.addClass = function(c) {
|
|
1121
|
+
if (this.elt.className) {
|
|
1122
|
+
// PEND don't add class more than once
|
|
1123
|
+
//var regex = new RegExp('[^a-zA-Z\d:]?'+c+'[^a-zA-Z\d:]?');
|
|
1124
|
+
//if (this.elt.className.search(/[^a-zA-Z\d:]?hi[^a-zA-Z\d:]?/) === -1) {
|
|
1125
|
+
this.elt.className = this.elt.className+' '+c;
|
|
1126
|
+
//}
|
|
1127
|
+
} else {
|
|
1128
|
+
this.elt.className = c;
|
|
1129
|
+
}
|
|
1130
|
+
return this;
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
/**
|
|
1134
|
+
*
|
|
1135
|
+
* Removes specified class from the element.
|
|
1136
|
+
*
|
|
1137
|
+
* @method removeClass
|
|
1138
|
+
* @param {String} class name of class to remove
|
|
1139
|
+
* @return {Object|p5.Element}
|
|
1140
|
+
*/
|
|
1141
|
+
p5.Element.prototype.removeClass = function(c) {
|
|
1142
|
+
var regex = new RegExp('(?:^|\\s)'+c+'(?!\\S)');
|
|
1143
|
+
this.elt.className = this.elt.className.replace(regex, '');
|
|
1144
|
+
this.elt.className = this.elt.className.replace(/^\s+|\s+$/g, ""); //prettify (optional)
|
|
1145
|
+
return this;
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
/**
|
|
1149
|
+
*
|
|
1150
|
+
* Attaches the element as a child to the parent specified.
|
|
1151
|
+
* Accepts either a string ID, DOM node, or p5.Element.
|
|
1152
|
+
* If no argument is specified, an array of children DOM nodes is returned.
|
|
1153
|
+
*
|
|
1154
|
+
* @method child
|
|
1155
|
+
* @param {String|Object|p5.Element} [child] the ID, DOM node, or p5.Element
|
|
1156
|
+
* to add to the current element
|
|
1157
|
+
* @return {p5.Element}
|
|
1158
|
+
* @example
|
|
1159
|
+
* <div class='norender'><code>
|
|
1160
|
+
* var div0 = createDiv('this is the parent');
|
|
1161
|
+
* var div1 = createDiv('this is the child');
|
|
1162
|
+
* div0.child(div1); // use p5.Element
|
|
1163
|
+
* </code></div>
|
|
1164
|
+
* <div class='norender'><code>
|
|
1165
|
+
* var div0 = createDiv('this is the parent');
|
|
1166
|
+
* var div1 = createDiv('this is the child');
|
|
1167
|
+
* div1.id('apples');
|
|
1168
|
+
* div0.child('apples'); // use id
|
|
1169
|
+
* </code></div>
|
|
1170
|
+
* <div class='norender'><code>
|
|
1171
|
+
* var div0 = createDiv('this is the parent');
|
|
1172
|
+
* var elt = document.getElementById('myChildDiv');
|
|
1173
|
+
* div0.child(elt); // use element from page
|
|
1174
|
+
* </code></div>
|
|
1175
|
+
*/
|
|
1176
|
+
p5.Element.prototype.child = function(c) {
|
|
1177
|
+
if (typeof c === 'undefined'){
|
|
1178
|
+
return this.elt.childNodes
|
|
1179
|
+
}
|
|
1180
|
+
if (typeof c === 'string') {
|
|
1181
|
+
if (c[0] === '#') {
|
|
1182
|
+
c = c.substring(1);
|
|
1183
|
+
}
|
|
1184
|
+
c = document.getElementById(c);
|
|
1185
|
+
} else if (c instanceof p5.Element) {
|
|
1186
|
+
c = c.elt;
|
|
1187
|
+
}
|
|
1188
|
+
this.elt.appendChild(c);
|
|
1189
|
+
return this;
|
|
1190
|
+
};
|
|
1191
|
+
|
|
1192
|
+
/**
|
|
1193
|
+
* Centers a p5 Element either vertically, horizontally,
|
|
1194
|
+
* or both, relative to its parent or according to
|
|
1195
|
+
* the body if the Element has no parent. If no argument is passed
|
|
1196
|
+
* the Element is aligned both vertically and horizontally.
|
|
1197
|
+
*
|
|
1198
|
+
* @param {String} align passing 'vertical', 'horizontal' aligns element accordingly
|
|
1199
|
+
* @return {Object|p5.Element} pointer to p5.Element
|
|
1200
|
+
* @example
|
|
1201
|
+
* <div><code>
|
|
1202
|
+
* function setup() {
|
|
1203
|
+
* var div = createDiv('').size(10,10);
|
|
1204
|
+
* div.style('background-color','orange');
|
|
1205
|
+
* div.center();
|
|
1206
|
+
*
|
|
1207
|
+
* }
|
|
1208
|
+
* </code></div>
|
|
1209
|
+
*/
|
|
1210
|
+
p5.Element.prototype.center = function(align) {
|
|
1211
|
+
var style = this.elt.style.display;
|
|
1212
|
+
var hidden = this.elt.style.display === 'none';
|
|
1213
|
+
var parentHidden = this.parent().style.display === 'none';
|
|
1214
|
+
var pos = { x : this.elt.offsetLeft, y : this.elt.offsetTop };
|
|
1215
|
+
|
|
1216
|
+
if (hidden) this.show();
|
|
1217
|
+
|
|
1218
|
+
this.elt.style.display = 'block';
|
|
1219
|
+
this.position(0,0);
|
|
1220
|
+
|
|
1221
|
+
if (parentHidden) this.parent().style.display = 'block';
|
|
1222
|
+
|
|
1223
|
+
var wOffset = Math.abs(this.parent().offsetWidth - this.elt.offsetWidth);
|
|
1224
|
+
var hOffset = Math.abs(this.parent().offsetHeight - this.elt.offsetHeight);
|
|
1225
|
+
var y = pos.y;
|
|
1226
|
+
var x = pos.x;
|
|
1227
|
+
|
|
1228
|
+
if (align === 'both' || align === undefined){
|
|
1229
|
+
this.position(wOffset/2, hOffset/2);
|
|
1230
|
+
}else if (align === 'horizontal'){
|
|
1231
|
+
this.position(wOffset/2, y);
|
|
1232
|
+
}else if (align === 'vertical'){
|
|
1233
|
+
this.position(x, hOffset/2);
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
this.style('display', style);
|
|
1237
|
+
|
|
1238
|
+
if (hidden) this.hide();
|
|
1239
|
+
|
|
1240
|
+
if (parentHidden) this.parent().style.display = 'none';
|
|
1241
|
+
|
|
1242
|
+
return this;
|
|
1243
|
+
};
|
|
1244
|
+
|
|
1245
|
+
/**
|
|
1246
|
+
*
|
|
1247
|
+
* If an argument is given, sets the inner HTML of the element,
|
|
1248
|
+
* replacing any existing html. If true is included as a second
|
|
1249
|
+
* argument, html is appended instead of replacing existing html.
|
|
1250
|
+
* If no arguments are given, returns
|
|
1251
|
+
* the inner HTML of the element.
|
|
1252
|
+
*
|
|
1253
|
+
* @for p5.Element
|
|
1254
|
+
* @method html
|
|
1255
|
+
* @param {String} [html] the HTML to be placed inside the element
|
|
1256
|
+
* @param {boolean} [append] whether to append HTML to existing
|
|
1257
|
+
* @return {Object|p5.Element|String}
|
|
1258
|
+
* @example
|
|
1259
|
+
* <div class='norender'><code>
|
|
1260
|
+
* var div = createDiv('').size(100,100);
|
|
1261
|
+
* div.html('hi');
|
|
1262
|
+
* </code></div>
|
|
1263
|
+
* <div class='norender'><code>
|
|
1264
|
+
* var div = createDiv('Hello ').size(100,100);
|
|
1265
|
+
* div.html('World', true);
|
|
1266
|
+
* </code></div>
|
|
1267
|
+
*/
|
|
1268
|
+
p5.Element.prototype.html = function() {
|
|
1269
|
+
if (arguments.length === 0) {
|
|
1270
|
+
return this.elt.innerHTML;
|
|
1271
|
+
} else if (arguments[1]) {
|
|
1272
|
+
this.elt.innerHTML += arguments[0];
|
|
1273
|
+
return this;
|
|
1274
|
+
} else {
|
|
1275
|
+
this.elt.innerHTML = arguments[0];
|
|
1276
|
+
return this;
|
|
1277
|
+
}
|
|
1278
|
+
};
|
|
1279
|
+
|
|
1280
|
+
/**
|
|
1281
|
+
*
|
|
1282
|
+
* Sets the position of the element relative to (0, 0) of the
|
|
1283
|
+
* window. Essentially, sets position:absolute and left and top
|
|
1284
|
+
* properties of style. If no arguments given returns the x and y position
|
|
1285
|
+
* of the element in an object.
|
|
1286
|
+
*
|
|
1287
|
+
* @method position
|
|
1288
|
+
* @param {Number} [x] x-position relative to upper left of window
|
|
1289
|
+
* @param {Number} [y] y-position relative to upper left of window
|
|
1290
|
+
* @return {Object|p5.Element}
|
|
1291
|
+
* @example
|
|
1292
|
+
* <div><code class='norender'>
|
|
1293
|
+
* function setup() {
|
|
1294
|
+
* var cnv = createCanvas(100, 100);
|
|
1295
|
+
* // positions canvas 50px to the right and 100px
|
|
1296
|
+
* // below upper left corner of the window
|
|
1297
|
+
* cnv.position(50, 100);
|
|
1298
|
+
* }
|
|
1299
|
+
* </code></div>
|
|
1300
|
+
*/
|
|
1301
|
+
p5.Element.prototype.position = function() {
|
|
1302
|
+
if (arguments.length === 0){
|
|
1303
|
+
return { 'x' : this.elt.offsetLeft , 'y' : this.elt.offsetTop };
|
|
1304
|
+
}else{
|
|
1305
|
+
this.elt.style.position = 'absolute';
|
|
1306
|
+
this.elt.style.left = arguments[0]+'px';
|
|
1307
|
+
this.elt.style.top = arguments[1]+'px';
|
|
1308
|
+
this.x = arguments[0];
|
|
1309
|
+
this.y = arguments[1];
|
|
1310
|
+
return this;
|
|
1311
|
+
}
|
|
1312
|
+
};
|
|
1313
|
+
|
|
1314
|
+
/* Helper method called by p5.Element.style() */
|
|
1315
|
+
p5.Element.prototype._translate = function(){
|
|
1316
|
+
this.elt.style.position = 'absolute';
|
|
1317
|
+
// save out initial non-translate transform styling
|
|
1318
|
+
var transform = '';
|
|
1319
|
+
if (this.elt.style.transform) {
|
|
1320
|
+
transform = this.elt.style.transform.replace(/translate3d\(.*\)/g, '');
|
|
1321
|
+
transform = transform.replace(/translate[X-Z]?\(.*\)/g, '');
|
|
1322
|
+
}
|
|
1323
|
+
if (arguments.length === 2) {
|
|
1324
|
+
this.elt.style.transform = 'translate('+arguments[0]+'px, '+arguments[1]+'px)';
|
|
1325
|
+
} else if (arguments.length > 2) {
|
|
1326
|
+
this.elt.style.transform = 'translate3d('+arguments[0]+'px,'+arguments[1]+'px,'+arguments[2]+'px)';
|
|
1327
|
+
if (arguments.length === 3) {
|
|
1328
|
+
this.elt.parentElement.style.perspective = '1000px';
|
|
1329
|
+
} else {
|
|
1330
|
+
this.elt.parentElement.style.perspective = arguments[3]+'px';
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
// add any extra transform styling back on end
|
|
1334
|
+
this.elt.style.transform += transform;
|
|
1335
|
+
return this;
|
|
1336
|
+
};
|
|
1337
|
+
|
|
1338
|
+
/* Helper method called by p5.Element.style() */
|
|
1339
|
+
p5.Element.prototype._rotate = function(){
|
|
1340
|
+
// save out initial non-rotate transform styling
|
|
1341
|
+
var transform = '';
|
|
1342
|
+
if (this.elt.style.transform) {
|
|
1343
|
+
var transform = this.elt.style.transform.replace(/rotate3d\(.*\)/g, '');
|
|
1344
|
+
transform = transform.replace(/rotate[X-Z]?\(.*\)/g, '');
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
if (arguments.length === 1){
|
|
1348
|
+
this.elt.style.transform = 'rotate('+arguments[0]+'deg)';
|
|
1349
|
+
}else if (arguments.length === 2){
|
|
1350
|
+
this.elt.style.transform = 'rotate('+arguments[0]+'deg, '+arguments[1]+'deg)';
|
|
1351
|
+
}else if (arguments.length === 3){
|
|
1352
|
+
this.elt.style.transform = 'rotateX('+arguments[0]+'deg)';
|
|
1353
|
+
this.elt.style.transform += 'rotateY('+arguments[1]+'deg)';
|
|
1354
|
+
this.elt.style.transform += 'rotateZ('+arguments[2]+'deg)';
|
|
1355
|
+
}
|
|
1356
|
+
// add remaining transform back on
|
|
1357
|
+
this.elt.style.transform += transform;
|
|
1358
|
+
return this;
|
|
1359
|
+
};
|
|
1360
|
+
|
|
1361
|
+
/**
|
|
1362
|
+
* Sets the given style (css) property (1st arg) of the element with the
|
|
1363
|
+
* given value (2nd arg). If a single argument is given, .style()
|
|
1364
|
+
* returns the value of the given property; however, if the single argument
|
|
1365
|
+
* is given in css syntax ('text-align:center'), .style() sets the css
|
|
1366
|
+
* appropriatly. .style() also handles 2d and 3d css transforms. If
|
|
1367
|
+
* the 1st arg is 'rotate', 'translate', or 'position', the following arguments
|
|
1368
|
+
* accept Numbers as values. ('translate', 10, 100, 50);
|
|
1369
|
+
*
|
|
1370
|
+
* @method style
|
|
1371
|
+
* @param {String} property property to be set
|
|
1372
|
+
* @param {String|Number|p5.Color} [value] value to assign to property (only String|Number for rotate/translate)
|
|
1373
|
+
* @return {String|Object|p5.Element} value of property, if no value is specified
|
|
1374
|
+
* or p5.Element
|
|
1375
|
+
* @example
|
|
1376
|
+
* <div><code class="norender">
|
|
1377
|
+
* var myDiv = createDiv("I like pandas.");
|
|
1378
|
+
* myDiv.style("font-size", "18px");
|
|
1379
|
+
* myDiv.style("color", "#ff0000");
|
|
1380
|
+
* </code></div>
|
|
1381
|
+
* <div><code class="norender">
|
|
1382
|
+
* var col = color(25,23,200,50);
|
|
1383
|
+
* var button = createButton("button");
|
|
1384
|
+
* button.style("background-color", col);
|
|
1385
|
+
* button.position(10, 10);
|
|
1386
|
+
* </code></div>
|
|
1387
|
+
* <div><code class="norender">
|
|
1388
|
+
* var myDiv = createDiv("I like lizards.");
|
|
1389
|
+
* myDiv.style("position", 20, 20);
|
|
1390
|
+
* myDiv.style("rotate", 45);
|
|
1391
|
+
* </code></div>
|
|
1392
|
+
* <div><code class="norender">
|
|
1393
|
+
* var myDiv;
|
|
1394
|
+
* function setup() {
|
|
1395
|
+
* background(200);
|
|
1396
|
+
* myDiv = createDiv("I like gray.");
|
|
1397
|
+
* myDiv.position(20, 20);
|
|
1398
|
+
* }
|
|
1399
|
+
*
|
|
1400
|
+
* function draw() {
|
|
1401
|
+
* myDiv.style("font-size", mouseX+"px");
|
|
1402
|
+
* }
|
|
1403
|
+
* </code></div>
|
|
1404
|
+
*/
|
|
1405
|
+
p5.Element.prototype.style = function(prop, val) {
|
|
1406
|
+
var self = this;
|
|
1407
|
+
|
|
1408
|
+
if (val instanceof p5.Color) {
|
|
1409
|
+
val = 'rgba(' + val.levels[0] + ',' + val.levels[1] + ',' + val.levels[2] + ',' + val.levels[3]/255 + ')'
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
if (typeof val === 'undefined') {
|
|
1413
|
+
if (prop.indexOf(':') === -1) {
|
|
1414
|
+
var styles = window.getComputedStyle(self.elt);
|
|
1415
|
+
var style = styles.getPropertyValue(prop);
|
|
1416
|
+
return style;
|
|
1417
|
+
} else {
|
|
1418
|
+
var attrs = prop.split(';');
|
|
1419
|
+
for (var i = 0; i < attrs.length; i++) {
|
|
1420
|
+
var parts = attrs[i].split(':');
|
|
1421
|
+
if (parts[0] && parts[1]) {
|
|
1422
|
+
this.elt.style[parts[0].trim()] = parts[1].trim();
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
} else {
|
|
1427
|
+
if (prop === 'rotate' || prop === 'translate' || prop === 'position'){
|
|
1428
|
+
var trans = Array.prototype.shift.apply(arguments);
|
|
1429
|
+
var f = this[trans] || this['_'+trans];
|
|
1430
|
+
f.apply(this, arguments);
|
|
1431
|
+
} else {
|
|
1432
|
+
this.elt.style[prop] = val;
|
|
1433
|
+
if (prop === 'width' || prop === 'height' || prop === 'left' || prop === 'top') {
|
|
1434
|
+
var numVal = val.replace(/\D+/g, '');
|
|
1435
|
+
this[prop] = parseInt(numVal, 10); // pend: is this necessary?
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
return this;
|
|
1440
|
+
};
|
|
1441
|
+
|
|
1442
|
+
|
|
1443
|
+
/**
|
|
1444
|
+
*
|
|
1445
|
+
* Adds a new attribute or changes the value of an existing attribute
|
|
1446
|
+
* on the specified element. If no value is specified, returns the
|
|
1447
|
+
* value of the given attribute, or null if attribute is not set.
|
|
1448
|
+
*
|
|
1449
|
+
* @method attribute
|
|
1450
|
+
* @param {String} attr attribute to set
|
|
1451
|
+
* @param {String} [value] value to assign to attribute
|
|
1452
|
+
* @return {String|Object|p5.Element} value of attribute, if no value is
|
|
1453
|
+
* specified or p5.Element
|
|
1454
|
+
* @example
|
|
1455
|
+
* <div class="norender"><code>
|
|
1456
|
+
* var myDiv = createDiv("I like pandas.");
|
|
1457
|
+
* myDiv.attribute("align", "center");
|
|
1458
|
+
* </code></div>
|
|
1459
|
+
*/
|
|
1460
|
+
p5.Element.prototype.attribute = function(attr, value) {
|
|
1461
|
+
//handling for checkboxes and radios to ensure options get
|
|
1462
|
+
//attributes not divs
|
|
1463
|
+
if(this.elt.firstChild != null &&
|
|
1464
|
+
(this.elt.firstChild.type === 'checkbox' ||
|
|
1465
|
+
this.elt.firstChild.type === 'radio')) {
|
|
1466
|
+
if(typeof value === 'undefined') {
|
|
1467
|
+
return this.elt.firstChild.getAttribute(attr);
|
|
1468
|
+
} else {
|
|
1469
|
+
for(var i=0; i<this.elt.childNodes.length; i++) {
|
|
1470
|
+
this.elt.childNodes[i].setAttribute(attr, value);
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
else if (typeof value === 'undefined') {
|
|
1475
|
+
return this.elt.getAttribute(attr);
|
|
1476
|
+
} else {
|
|
1477
|
+
this.elt.setAttribute(attr, value);
|
|
1478
|
+
return this;
|
|
1479
|
+
}
|
|
1480
|
+
};
|
|
1481
|
+
|
|
1482
|
+
|
|
1483
|
+
/**
|
|
1484
|
+
*
|
|
1485
|
+
* Removes an attribute on the specified element.
|
|
1486
|
+
*
|
|
1487
|
+
* @method removeAttribute
|
|
1488
|
+
* @param {String} attr attribute to remove
|
|
1489
|
+
* @return {Object|p5.Element}
|
|
1490
|
+
*
|
|
1491
|
+
* @example
|
|
1492
|
+
* <div><code>
|
|
1493
|
+
* var button;
|
|
1494
|
+
* var checkbox;
|
|
1495
|
+
*
|
|
1496
|
+
* function setup() {
|
|
1497
|
+
* checkbox = createCheckbox('enable', true);
|
|
1498
|
+
* checkbox.changed(enableButton);
|
|
1499
|
+
* button = createButton('button');
|
|
1500
|
+
* button.position(10, 10);
|
|
1501
|
+
* }
|
|
1502
|
+
*
|
|
1503
|
+
* function enableButton() {
|
|
1504
|
+
* if( this.checked() ) {
|
|
1505
|
+
* // Re-enable the button
|
|
1506
|
+
* button.removeAttribute('disabled');
|
|
1507
|
+
* } else {
|
|
1508
|
+
* // Disable the button
|
|
1509
|
+
* button.attribute('disabled','');
|
|
1510
|
+
* }
|
|
1511
|
+
* }
|
|
1512
|
+
* </code></div>
|
|
1513
|
+
*/
|
|
1514
|
+
p5.Element.prototype.removeAttribute = function(attr) {
|
|
1515
|
+
if(this.elt.firstChild != null &&
|
|
1516
|
+
(this.elt.firstChild.type === 'checkbox' ||
|
|
1517
|
+
this.elt.firstChild.type === 'radio')) {
|
|
1518
|
+
for(var i=0; i<this.elt.childNodes.length; i++) {
|
|
1519
|
+
this.elt.childNodes[i].removeAttribute(attr);
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
this.elt.removeAttribute(attr);
|
|
1523
|
+
return this;
|
|
1524
|
+
};
|
|
1525
|
+
|
|
1526
|
+
|
|
1527
|
+
/**
|
|
1528
|
+
* Either returns the value of the element if no arguments
|
|
1529
|
+
* given, or sets the value of the element.
|
|
1530
|
+
*
|
|
1531
|
+
* @method value
|
|
1532
|
+
* @param {String|Number} [value]
|
|
1533
|
+
* @return {String|Object|p5.Element} value of element if no value is specified or p5.Element
|
|
1534
|
+
* @example
|
|
1535
|
+
* <div class='norender'><code>
|
|
1536
|
+
* // gets the value
|
|
1537
|
+
* var inp;
|
|
1538
|
+
* function setup() {
|
|
1539
|
+
* inp = createInput('');
|
|
1540
|
+
* }
|
|
1541
|
+
*
|
|
1542
|
+
* function mousePressed() {
|
|
1543
|
+
* print(inp.value());
|
|
1544
|
+
* }
|
|
1545
|
+
* </code></div>
|
|
1546
|
+
* <div class='norender'><code>
|
|
1547
|
+
* // sets the value
|
|
1548
|
+
* var inp;
|
|
1549
|
+
* function setup() {
|
|
1550
|
+
* inp = createInput('myValue');
|
|
1551
|
+
* }
|
|
1552
|
+
*
|
|
1553
|
+
* function mousePressed() {
|
|
1554
|
+
* inp.value("myValue");
|
|
1555
|
+
* }
|
|
1556
|
+
* </code></div>
|
|
1557
|
+
*/
|
|
1558
|
+
p5.Element.prototype.value = function() {
|
|
1559
|
+
if (arguments.length > 0) {
|
|
1560
|
+
this.elt.value = arguments[0];
|
|
1561
|
+
return this;
|
|
1562
|
+
} else {
|
|
1563
|
+
if (this.elt.type === 'range') {
|
|
1564
|
+
return parseFloat(this.elt.value);
|
|
1565
|
+
}
|
|
1566
|
+
else return this.elt.value;
|
|
1567
|
+
}
|
|
1568
|
+
};
|
|
1569
|
+
|
|
1570
|
+
/**
|
|
1571
|
+
*
|
|
1572
|
+
* Shows the current element. Essentially, setting display:block for the style.
|
|
1573
|
+
*
|
|
1574
|
+
* @method show
|
|
1575
|
+
* @return {Object|p5.Element}
|
|
1576
|
+
* @example
|
|
1577
|
+
* <div class='norender'><code>
|
|
1578
|
+
* var div = createDiv('div');
|
|
1579
|
+
* div.style("display", "none");
|
|
1580
|
+
* div.show(); // turns display to block
|
|
1581
|
+
* </code></div>
|
|
1582
|
+
*/
|
|
1583
|
+
p5.Element.prototype.show = function() {
|
|
1584
|
+
this.elt.style.display = 'block';
|
|
1585
|
+
return this;
|
|
1586
|
+
};
|
|
1587
|
+
|
|
1588
|
+
/**
|
|
1589
|
+
* Hides the current element. Essentially, setting display:none for the style.
|
|
1590
|
+
*
|
|
1591
|
+
* @method hide
|
|
1592
|
+
* @return {Object|p5.Element}
|
|
1593
|
+
* @example
|
|
1594
|
+
* <div class='norender'><code>
|
|
1595
|
+
* var div = createDiv('this is a div');
|
|
1596
|
+
* div.hide();
|
|
1597
|
+
* </code></div>
|
|
1598
|
+
*/
|
|
1599
|
+
p5.Element.prototype.hide = function() {
|
|
1600
|
+
this.elt.style.display = 'none';
|
|
1601
|
+
return this;
|
|
1602
|
+
};
|
|
1603
|
+
|
|
1604
|
+
/**
|
|
1605
|
+
*
|
|
1606
|
+
* Sets the width and height of the element. AUTO can be used to
|
|
1607
|
+
* only adjust one dimension. If no arguments given returns the width and height
|
|
1608
|
+
* of the element in an object.
|
|
1609
|
+
*
|
|
1610
|
+
* @method size
|
|
1611
|
+
* @param {Number} [w] width of the element
|
|
1612
|
+
* @param {Number} [h] height of the element
|
|
1613
|
+
* @return {Object|p5.Element}
|
|
1614
|
+
* @example
|
|
1615
|
+
* <div class='norender'><code>
|
|
1616
|
+
* var div = createDiv('this is a div');
|
|
1617
|
+
* div.size(100, 100);
|
|
1618
|
+
* </code></div>
|
|
1619
|
+
*/
|
|
1620
|
+
p5.Element.prototype.size = function(w, h) {
|
|
1621
|
+
if (arguments.length === 0){
|
|
1622
|
+
return { 'width' : this.elt.offsetWidth , 'height' : this.elt.offsetHeight };
|
|
1623
|
+
}else{
|
|
1624
|
+
var aW = w;
|
|
1625
|
+
var aH = h;
|
|
1626
|
+
var AUTO = p5.prototype.AUTO;
|
|
1627
|
+
if (aW !== AUTO || aH !== AUTO) {
|
|
1628
|
+
if (aW === AUTO) {
|
|
1629
|
+
aW = h * this.width / this.height;
|
|
1630
|
+
} else if (aH === AUTO) {
|
|
1631
|
+
aH = w * this.height / this.width;
|
|
1632
|
+
}
|
|
1633
|
+
// set diff for cnv vs normal div
|
|
1634
|
+
if (this.elt instanceof HTMLCanvasElement) {
|
|
1635
|
+
var j = {};
|
|
1636
|
+
var k = this.elt.getContext('2d');
|
|
1637
|
+
for (var prop in k) {
|
|
1638
|
+
j[prop] = k[prop];
|
|
1639
|
+
}
|
|
1640
|
+
this.elt.setAttribute('width', aW * this._pInst._pixelDensity);
|
|
1641
|
+
this.elt.setAttribute('height', aH * this._pInst._pixelDensity);
|
|
1642
|
+
this.elt.setAttribute('style', 'width:' + aW + 'px; height:' + aH + 'px');
|
|
1643
|
+
this._pInst.scale(this._pInst._pixelDensity, this._pInst._pixelDensity);
|
|
1644
|
+
for (var prop in j) {
|
|
1645
|
+
this.elt.getContext('2d')[prop] = j[prop];
|
|
1646
|
+
}
|
|
1647
|
+
} else {
|
|
1648
|
+
this.elt.style.width = aW+'px';
|
|
1649
|
+
this.elt.style.height = aH+'px';
|
|
1650
|
+
this.elt.width = aW;
|
|
1651
|
+
this.elt.height = aH;
|
|
1652
|
+
this.width = aW;
|
|
1653
|
+
this.height = aH;
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
this.width = this.elt.offsetWidth;
|
|
1657
|
+
this.height = this.elt.offsetHeight;
|
|
1658
|
+
|
|
1659
|
+
if (this._pInst) { // main canvas associated with p5 instance
|
|
1660
|
+
if (this._pInst._curElement.elt === this.elt) {
|
|
1661
|
+
this._pInst._setProperty('width', this.elt.offsetWidth);
|
|
1662
|
+
this._pInst._setProperty('height', this.elt.offsetHeight);
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
return this;
|
|
1667
|
+
}
|
|
1668
|
+
};
|
|
1669
|
+
|
|
1670
|
+
/**
|
|
1671
|
+
* Removes the element and deregisters all listeners.
|
|
1672
|
+
* @method remove
|
|
1673
|
+
* @example
|
|
1674
|
+
* <div class='norender'><code>
|
|
1675
|
+
* var myDiv = createDiv('this is some text');
|
|
1676
|
+
* myDiv.remove();
|
|
1677
|
+
* </code></div>
|
|
1678
|
+
*/
|
|
1679
|
+
p5.Element.prototype.remove = function() {
|
|
1680
|
+
// deregister events
|
|
1681
|
+
for (var ev in this._events) {
|
|
1682
|
+
this.elt.removeEventListener(ev, this._events[ev]);
|
|
1683
|
+
}
|
|
1684
|
+
if (this.elt.parentNode) {
|
|
1685
|
+
this.elt.parentNode.removeChild(this.elt);
|
|
1686
|
+
}
|
|
1687
|
+
delete(this);
|
|
1688
|
+
};
|
|
1689
|
+
|
|
1690
|
+
|
|
1691
|
+
|
|
1692
|
+
// =============================================================================
|
|
1693
|
+
// p5.MediaElement additions
|
|
1694
|
+
// =============================================================================
|
|
1695
|
+
|
|
1696
|
+
|
|
1697
|
+
/**
|
|
1698
|
+
* Extends p5.Element to handle audio and video. In addition to the methods
|
|
1699
|
+
* of p5.Element, it also contains methods for controlling media. It is not
|
|
1700
|
+
* called directly, but p5.MediaElements are created by calling createVideo,
|
|
1701
|
+
* createAudio, and createCapture.
|
|
1702
|
+
*
|
|
1703
|
+
* @class p5.MediaElement
|
|
1704
|
+
* @constructor
|
|
1705
|
+
* @param {String} elt DOM node that is wrapped
|
|
1706
|
+
*/
|
|
1707
|
+
p5.MediaElement = function(elt, pInst) {
|
|
1708
|
+
p5.Element.call(this, elt, pInst);
|
|
1709
|
+
|
|
1710
|
+
var self = this;
|
|
1711
|
+
this.elt.crossOrigin = 'anonymous';
|
|
1712
|
+
|
|
1713
|
+
this._prevTime = 0;
|
|
1714
|
+
this._cueIDCounter = 0;
|
|
1715
|
+
this._cues = [];
|
|
1716
|
+
this._pixelDensity = 1;
|
|
1717
|
+
|
|
1718
|
+
/**
|
|
1719
|
+
* Path to the media element source.
|
|
1720
|
+
*
|
|
1721
|
+
* @property src
|
|
1722
|
+
* @return {String} src
|
|
1723
|
+
*/
|
|
1724
|
+
Object.defineProperty(self, 'src', {
|
|
1725
|
+
get: function() {
|
|
1726
|
+
var firstChildSrc = self.elt.children[0].src;
|
|
1727
|
+
var srcVal = self.elt.src === window.location.href ? '' : self.elt.src;
|
|
1728
|
+
var ret = firstChildSrc === window.location.href ? srcVal : firstChildSrc;
|
|
1729
|
+
return ret;
|
|
1730
|
+
},
|
|
1731
|
+
set: function(newValue) {
|
|
1732
|
+
for (var i = 0; i < self.elt.children.length; i++) {
|
|
1733
|
+
self.elt.removeChild(self.elt.children[i]);
|
|
1734
|
+
}
|
|
1735
|
+
var source = document.createElement('source');
|
|
1736
|
+
source.src = newValue;
|
|
1737
|
+
elt.appendChild(source);
|
|
1738
|
+
self.elt.src = newValue;
|
|
1739
|
+
},
|
|
1740
|
+
});
|
|
1741
|
+
|
|
1742
|
+
// private _onended callback, set by the method: onended(callback)
|
|
1743
|
+
self._onended = function() {};
|
|
1744
|
+
self.elt.onended = function() {
|
|
1745
|
+
self._onended(self);
|
|
1746
|
+
}
|
|
1747
|
+
};
|
|
1748
|
+
p5.MediaElement.prototype = Object.create(p5.Element.prototype);
|
|
1749
|
+
|
|
1750
|
+
|
|
1751
|
+
|
|
1752
|
+
|
|
1753
|
+
/**
|
|
1754
|
+
* Play an HTML5 media element.
|
|
1755
|
+
*
|
|
1756
|
+
* @method play
|
|
1757
|
+
* @return {Object|p5.Element}
|
|
1758
|
+
*/
|
|
1759
|
+
p5.MediaElement.prototype.play = function() {
|
|
1760
|
+
if (this.elt.currentTime === this.elt.duration) {
|
|
1761
|
+
this.elt.currentTime = 0;
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1764
|
+
if (this.elt.readyState > 1) {
|
|
1765
|
+
this.elt.play();
|
|
1766
|
+
} else {
|
|
1767
|
+
// in Chrome, playback cannot resume after being stopped and must reload
|
|
1768
|
+
this.elt.load();
|
|
1769
|
+
this.elt.play();
|
|
1770
|
+
}
|
|
1771
|
+
return this;
|
|
1772
|
+
};
|
|
1773
|
+
|
|
1774
|
+
/**
|
|
1775
|
+
* Stops an HTML5 media element (sets current time to zero).
|
|
1776
|
+
*
|
|
1777
|
+
* @method stop
|
|
1778
|
+
* @return {Object|p5.Element}
|
|
1779
|
+
*/
|
|
1780
|
+
p5.MediaElement.prototype.stop = function() {
|
|
1781
|
+
this.elt.pause();
|
|
1782
|
+
this.elt.currentTime = 0;
|
|
1783
|
+
return this;
|
|
1784
|
+
};
|
|
1785
|
+
|
|
1786
|
+
/**
|
|
1787
|
+
* Pauses an HTML5 media element.
|
|
1788
|
+
*
|
|
1789
|
+
* @method pause
|
|
1790
|
+
* @return {Object|p5.Element}
|
|
1791
|
+
*/
|
|
1792
|
+
p5.MediaElement.prototype.pause = function() {
|
|
1793
|
+
this.elt.pause();
|
|
1794
|
+
return this;
|
|
1795
|
+
};
|
|
1796
|
+
|
|
1797
|
+
/**
|
|
1798
|
+
* Set 'loop' to true for an HTML5 media element, and starts playing.
|
|
1799
|
+
*
|
|
1800
|
+
* @method loop
|
|
1801
|
+
* @return {Object|p5.Element}
|
|
1802
|
+
*/
|
|
1803
|
+
p5.MediaElement.prototype.loop = function() {
|
|
1804
|
+
this.elt.setAttribute('loop', true);
|
|
1805
|
+
this.play();
|
|
1806
|
+
return this;
|
|
1807
|
+
};
|
|
1808
|
+
/**
|
|
1809
|
+
* Set 'loop' to false for an HTML5 media element. Element will stop
|
|
1810
|
+
* when it reaches the end.
|
|
1811
|
+
*
|
|
1812
|
+
* @method noLoop
|
|
1813
|
+
* @return {Object|p5.Element}
|
|
1814
|
+
*/
|
|
1815
|
+
p5.MediaElement.prototype.noLoop = function() {
|
|
1816
|
+
this.elt.setAttribute('loop', false);
|
|
1817
|
+
return this;
|
|
1818
|
+
};
|
|
1819
|
+
|
|
1820
|
+
|
|
1821
|
+
/**
|
|
1822
|
+
* Set HTML5 media element to autoplay or not.
|
|
1823
|
+
*
|
|
1824
|
+
* @method autoplay
|
|
1825
|
+
* @param {Boolean} autoplay whether the element should autoplay
|
|
1826
|
+
* @return {Object|p5.Element}
|
|
1827
|
+
*/
|
|
1828
|
+
p5.MediaElement.prototype.autoplay = function(val) {
|
|
1829
|
+
this.elt.setAttribute('autoplay', val);
|
|
1830
|
+
return this;
|
|
1831
|
+
};
|
|
1832
|
+
|
|
1833
|
+
/**
|
|
1834
|
+
* Sets volume for this HTML5 media element. If no argument is given,
|
|
1835
|
+
* returns the current volume.
|
|
1836
|
+
*
|
|
1837
|
+
* @param {Number} [val] volume between 0.0 and 1.0
|
|
1838
|
+
* @return {Number|p5.MediaElement} current volume or p5.MediaElement
|
|
1839
|
+
* @method volume
|
|
1840
|
+
*/
|
|
1841
|
+
p5.MediaElement.prototype.volume = function(val) {
|
|
1842
|
+
if (typeof val === 'undefined') {
|
|
1843
|
+
return this.elt.volume;
|
|
1844
|
+
} else {
|
|
1845
|
+
this.elt.volume = val;
|
|
1846
|
+
}
|
|
1847
|
+
};
|
|
1848
|
+
|
|
1849
|
+
/**
|
|
1850
|
+
* If no arguments are given, returns the current playback speed of the
|
|
1851
|
+
* element. The speed parameter sets the speed where 2.0 will play the
|
|
1852
|
+
* element twice as fast, 0.5 will play at half the speed, and -1 will play
|
|
1853
|
+
* the element in normal speed in reverse.(Note that not all browsers support
|
|
1854
|
+
* backward playback and even if they do, playback might not be smooth.)
|
|
1855
|
+
*
|
|
1856
|
+
* @method speed
|
|
1857
|
+
* @param {Number} [speed] speed multiplier for element playback
|
|
1858
|
+
* @return {Number|Object|p5.MediaElement} current playback speed or p5.MediaElement
|
|
1859
|
+
*/
|
|
1860
|
+
p5.MediaElement.prototype.speed = function(val) {
|
|
1861
|
+
if (typeof val === 'undefined') {
|
|
1862
|
+
return this.elt.playbackRate;
|
|
1863
|
+
} else {
|
|
1864
|
+
this.elt.playbackRate = val;
|
|
1865
|
+
}
|
|
1866
|
+
};
|
|
1867
|
+
|
|
1868
|
+
/**
|
|
1869
|
+
* If no arguments are given, returns the current time of the element.
|
|
1870
|
+
* If an argument is given the current time of the element is set to it.
|
|
1871
|
+
*
|
|
1872
|
+
* @method time
|
|
1873
|
+
* @param {Number} [time] time to jump to (in seconds)
|
|
1874
|
+
* @return {Number|Object|p5.MediaElement} current time (in seconds)
|
|
1875
|
+
* or p5.MediaElement
|
|
1876
|
+
*/
|
|
1877
|
+
p5.MediaElement.prototype.time = function(val) {
|
|
1878
|
+
if (typeof val === 'undefined') {
|
|
1879
|
+
return this.elt.currentTime;
|
|
1880
|
+
} else {
|
|
1881
|
+
this.elt.currentTime = val;
|
|
1882
|
+
}
|
|
1883
|
+
};
|
|
1884
|
+
|
|
1885
|
+
/**
|
|
1886
|
+
* Returns the duration of the HTML5 media element.
|
|
1887
|
+
*
|
|
1888
|
+
* @method duration
|
|
1889
|
+
* @return {Number} duration
|
|
1890
|
+
*/
|
|
1891
|
+
p5.MediaElement.prototype.duration = function() {
|
|
1892
|
+
return this.elt.duration;
|
|
1893
|
+
};
|
|
1894
|
+
p5.MediaElement.prototype.pixels = [];
|
|
1895
|
+
p5.MediaElement.prototype.loadPixels = function() {
|
|
1896
|
+
if (!this.canvas) {
|
|
1897
|
+
this.canvas = document.createElement('canvas');
|
|
1898
|
+
this.drawingContext = this.canvas.getContext('2d');
|
|
1899
|
+
}
|
|
1900
|
+
if (this.loadedmetadata) { // wait for metadata for w/h
|
|
1901
|
+
if (this.canvas.width !== this.elt.width) {
|
|
1902
|
+
this.canvas.width = this.elt.width;
|
|
1903
|
+
this.canvas.height = this.elt.height;
|
|
1904
|
+
this.width = this.canvas.width;
|
|
1905
|
+
this.height = this.canvas.height;
|
|
1906
|
+
}
|
|
1907
|
+
this.drawingContext.drawImage(this.elt, 0, 0, this.canvas.width, this.canvas.height);
|
|
1908
|
+
p5.Renderer2D.prototype.loadPixels.call(this);
|
|
1909
|
+
}
|
|
1910
|
+
return this;
|
|
1911
|
+
}
|
|
1912
|
+
p5.MediaElement.prototype.updatePixels = function(x, y, w, h){
|
|
1913
|
+
if (this.loadedmetadata) { // wait for metadata
|
|
1914
|
+
p5.Renderer2D.prototype.updatePixels.call(this, x, y, w, h);
|
|
1915
|
+
}
|
|
1916
|
+
return this;
|
|
1917
|
+
}
|
|
1918
|
+
p5.MediaElement.prototype.get = function(x, y, w, h){
|
|
1919
|
+
if (this.loadedmetadata) { // wait for metadata
|
|
1920
|
+
return p5.Renderer2D.prototype.get.call(this, x, y, w, h);
|
|
1921
|
+
} else if (typeof x === 'undefined') {
|
|
1922
|
+
return new p5.Image(1, 1);
|
|
1923
|
+
} else if (w > 1) {
|
|
1924
|
+
return new p5.Image(x, y, w, h);
|
|
1925
|
+
} else {
|
|
1926
|
+
return [0, 0, 0, 255];
|
|
1927
|
+
}
|
|
1928
|
+
};
|
|
1929
|
+
p5.MediaElement.prototype.set = function(x, y, imgOrCol){
|
|
1930
|
+
if (this.loadedmetadata) { // wait for metadata
|
|
1931
|
+
p5.Renderer2D.prototype.set.call(this, x, y, imgOrCol);
|
|
1932
|
+
}
|
|
1933
|
+
};
|
|
1934
|
+
p5.MediaElement.prototype.copy = function(){
|
|
1935
|
+
p5.Renderer2D.prototype.copy.apply(this, arguments);
|
|
1936
|
+
};
|
|
1937
|
+
p5.MediaElement.prototype.mask = function(){
|
|
1938
|
+
this.loadPixels();
|
|
1939
|
+
p5.Image.prototype.mask.apply(this, arguments);
|
|
1940
|
+
};
|
|
1941
|
+
/**
|
|
1942
|
+
* Schedule an event to be called when the audio or video
|
|
1943
|
+
* element reaches the end. If the element is looping,
|
|
1944
|
+
* this will not be called. The element is passed in
|
|
1945
|
+
* as the argument to the onended callback.
|
|
1946
|
+
*
|
|
1947
|
+
* @method onended
|
|
1948
|
+
* @param {Function} callback function to call when the
|
|
1949
|
+
* soundfile has ended. The
|
|
1950
|
+
* media element will be passed
|
|
1951
|
+
* in as the argument to the
|
|
1952
|
+
* callback.
|
|
1953
|
+
* @return {Object|p5.MediaElement}
|
|
1954
|
+
* @example
|
|
1955
|
+
* <div><code>
|
|
1956
|
+
* function setup() {
|
|
1957
|
+
* audioEl = createAudio('assets/beat.mp3');
|
|
1958
|
+
* audioEl.showControls(true);
|
|
1959
|
+
* audioEl.onended(sayDone);
|
|
1960
|
+
* }
|
|
1961
|
+
*
|
|
1962
|
+
* function sayDone(elt) {
|
|
1963
|
+
* alert('done playing ' + elt.src );
|
|
1964
|
+
* }
|
|
1965
|
+
* </code></div>
|
|
1966
|
+
*/
|
|
1967
|
+
p5.MediaElement.prototype.onended = function(callback) {
|
|
1968
|
+
this._onended = callback;
|
|
1969
|
+
return this;
|
|
1970
|
+
};
|
|
1971
|
+
|
|
1972
|
+
|
|
1973
|
+
/*** CONNECT TO WEB AUDIO API / p5.sound.js ***/
|
|
1974
|
+
|
|
1975
|
+
/**
|
|
1976
|
+
* Send the audio output of this element to a specified audioNode or
|
|
1977
|
+
* p5.sound object. If no element is provided, connects to p5's master
|
|
1978
|
+
* output. That connection is established when this method is first called.
|
|
1979
|
+
* All connections are removed by the .disconnect() method.
|
|
1980
|
+
*
|
|
1981
|
+
* This method is meant to be used with the p5.sound.js addon library.
|
|
1982
|
+
*
|
|
1983
|
+
* @method connect
|
|
1984
|
+
* @param {AudioNode|Object} audioNode AudioNode from the Web Audio API,
|
|
1985
|
+
* or an object from the p5.sound library
|
|
1986
|
+
*/
|
|
1987
|
+
p5.MediaElement.prototype.connect = function(obj) {
|
|
1988
|
+
var audioContext, masterOutput;
|
|
1989
|
+
|
|
1990
|
+
// if p5.sound exists, same audio context
|
|
1991
|
+
if (typeof p5.prototype.getAudioContext === 'function') {
|
|
1992
|
+
audioContext = p5.prototype.getAudioContext();
|
|
1993
|
+
masterOutput = p5.soundOut.input;
|
|
1994
|
+
} else {
|
|
1995
|
+
try {
|
|
1996
|
+
audioContext = obj.context;
|
|
1997
|
+
masterOutput = audioContext.destination
|
|
1998
|
+
} catch(e) {
|
|
1999
|
+
throw 'connect() is meant to be used with Web Audio API or p5.sound.js'
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
// create a Web Audio MediaElementAudioSourceNode if none already exists
|
|
2004
|
+
if (!this.audioSourceNode) {
|
|
2005
|
+
this.audioSourceNode = audioContext.createMediaElementSource(this.elt);
|
|
2006
|
+
|
|
2007
|
+
// connect to master output when this method is first called
|
|
2008
|
+
this.audioSourceNode.connect(masterOutput);
|
|
2009
|
+
}
|
|
2010
|
+
|
|
2011
|
+
// connect to object if provided
|
|
2012
|
+
if (obj) {
|
|
2013
|
+
if (obj.input) {
|
|
2014
|
+
this.audioSourceNode.connect(obj.input);
|
|
2015
|
+
} else {
|
|
2016
|
+
this.audioSourceNode.connect(obj);
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
// otherwise connect to master output of p5.sound / AudioContext
|
|
2021
|
+
else {
|
|
2022
|
+
this.audioSourceNode.connect(masterOutput);
|
|
2023
|
+
}
|
|
2024
|
+
|
|
2025
|
+
};
|
|
2026
|
+
|
|
2027
|
+
/**
|
|
2028
|
+
* Disconnect all Web Audio routing, including to master output.
|
|
2029
|
+
* This is useful if you want to re-route the output through
|
|
2030
|
+
* audio effects, for example.
|
|
2031
|
+
*
|
|
2032
|
+
* @method disconnect
|
|
2033
|
+
*/
|
|
2034
|
+
p5.MediaElement.prototype.disconnect = function() {
|
|
2035
|
+
if (this.audioSourceNode) {
|
|
2036
|
+
this.audioSourceNode.disconnect();
|
|
2037
|
+
} else {
|
|
2038
|
+
throw 'nothing to disconnect';
|
|
2039
|
+
}
|
|
2040
|
+
};
|
|
2041
|
+
|
|
2042
|
+
|
|
2043
|
+
/*** SHOW / HIDE CONTROLS ***/
|
|
2044
|
+
|
|
2045
|
+
/**
|
|
2046
|
+
* Show the default MediaElement controls, as determined by the web browser.
|
|
2047
|
+
*
|
|
2048
|
+
* @method showControls
|
|
2049
|
+
*/
|
|
2050
|
+
p5.MediaElement.prototype.showControls = function() {
|
|
2051
|
+
// must set style for the element to show on the page
|
|
2052
|
+
this.elt.style['text-align'] = 'inherit';
|
|
2053
|
+
this.elt.controls = true;
|
|
2054
|
+
};
|
|
2055
|
+
|
|
2056
|
+
/**
|
|
2057
|
+
* Hide the default mediaElement controls.
|
|
2058
|
+
*
|
|
2059
|
+
* @method hideControls
|
|
2060
|
+
*/
|
|
2061
|
+
p5.MediaElement.prototype.hideControls = function() {
|
|
2062
|
+
this.elt.controls = false;
|
|
2063
|
+
};
|
|
2064
|
+
|
|
2065
|
+
/*** SCHEDULE EVENTS ***/
|
|
2066
|
+
|
|
2067
|
+
/**
|
|
2068
|
+
* Schedule events to trigger every time a MediaElement
|
|
2069
|
+
* (audio/video) reaches a playback cue point.
|
|
2070
|
+
*
|
|
2071
|
+
* Accepts a callback function, a time (in seconds) at which to trigger
|
|
2072
|
+
* the callback, and an optional parameter for the callback.
|
|
2073
|
+
*
|
|
2074
|
+
* Time will be passed as the first parameter to the callback function,
|
|
2075
|
+
* and param will be the second parameter.
|
|
2076
|
+
*
|
|
2077
|
+
*
|
|
2078
|
+
* @method addCue
|
|
2079
|
+
* @param {Number} time Time in seconds, relative to this media
|
|
2080
|
+
* element's playback. For example, to trigger
|
|
2081
|
+
* an event every time playback reaches two
|
|
2082
|
+
* seconds, pass in the number 2. This will be
|
|
2083
|
+
* passed as the first parameter to
|
|
2084
|
+
* the callback function.
|
|
2085
|
+
* @param {Function} callback Name of a function that will be
|
|
2086
|
+
* called at the given time. The callback will
|
|
2087
|
+
* receive time and (optionally) param as its
|
|
2088
|
+
* two parameters.
|
|
2089
|
+
* @param {Object} [value] An object to be passed as the
|
|
2090
|
+
* second parameter to the
|
|
2091
|
+
* callback function.
|
|
2092
|
+
* @return {Number} id ID of this cue,
|
|
2093
|
+
* useful for removeCue(id)
|
|
2094
|
+
* @example
|
|
2095
|
+
* <div><code>
|
|
2096
|
+
* function setup() {
|
|
2097
|
+
* background(255,255,255);
|
|
2098
|
+
*
|
|
2099
|
+
* audioEl = createAudio('assets/beat.mp3');
|
|
2100
|
+
* audioEl.showControls();
|
|
2101
|
+
*
|
|
2102
|
+
* // schedule three calls to changeBackground
|
|
2103
|
+
* audioEl.addCue(0.5, changeBackground, color(255,0,0) );
|
|
2104
|
+
* audioEl.addCue(1.0, changeBackground, color(0,255,0) );
|
|
2105
|
+
* audioEl.addCue(2.5, changeBackground, color(0,0,255) );
|
|
2106
|
+
* audioEl.addCue(3.0, changeBackground, color(0,255,255) );
|
|
2107
|
+
* audioEl.addCue(4.2, changeBackground, color(255,255,0) );
|
|
2108
|
+
* audioEl.addCue(5.0, changeBackground, color(255,255,0) );
|
|
2109
|
+
* }
|
|
2110
|
+
*
|
|
2111
|
+
* function changeBackground(val) {
|
|
2112
|
+
* background(val);
|
|
2113
|
+
* }
|
|
2114
|
+
* </code></div>
|
|
2115
|
+
*/
|
|
2116
|
+
p5.MediaElement.prototype.addCue = function(time, callback, val) {
|
|
2117
|
+
var id = this._cueIDCounter++;
|
|
2118
|
+
|
|
2119
|
+
var cue = new Cue(callback, time, id, val);
|
|
2120
|
+
this._cues.push(cue);
|
|
2121
|
+
|
|
2122
|
+
if (!this.elt.ontimeupdate) {
|
|
2123
|
+
this.elt.ontimeupdate = this._onTimeUpdate.bind(this);
|
|
2124
|
+
}
|
|
2125
|
+
|
|
2126
|
+
return id;
|
|
2127
|
+
};
|
|
2128
|
+
|
|
2129
|
+
/**
|
|
2130
|
+
* Remove a callback based on its ID. The ID is returned by the
|
|
2131
|
+
* addCue method.
|
|
2132
|
+
*
|
|
2133
|
+
* @method removeCue
|
|
2134
|
+
* @param {Number} id ID of the cue, as returned by addCue
|
|
2135
|
+
*/
|
|
2136
|
+
p5.MediaElement.prototype.removeCue = function(id) {
|
|
2137
|
+
for (var i = 0; i < this._cues.length; i++) {
|
|
2138
|
+
if (this._cues[i] === id) {
|
|
2139
|
+
console.log(id)
|
|
2140
|
+
this._cues.splice(i, 1);
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
|
|
2144
|
+
if (this._cues.length === 0) {
|
|
2145
|
+
this.elt.ontimeupdate = null
|
|
2146
|
+
}
|
|
2147
|
+
};
|
|
2148
|
+
|
|
2149
|
+
/**
|
|
2150
|
+
* Remove all of the callbacks that had originally been scheduled
|
|
2151
|
+
* via the addCue method.
|
|
2152
|
+
*
|
|
2153
|
+
* @method clearCues
|
|
2154
|
+
*/
|
|
2155
|
+
p5.MediaElement.prototype.clearCues = function() {
|
|
2156
|
+
this._cues = [];
|
|
2157
|
+
this.elt.ontimeupdate = null;
|
|
2158
|
+
};
|
|
2159
|
+
|
|
2160
|
+
// private method that checks for cues to be fired if events
|
|
2161
|
+
// have been scheduled using addCue(callback, time).
|
|
2162
|
+
p5.MediaElement.prototype._onTimeUpdate = function() {
|
|
2163
|
+
var playbackTime = this.time();
|
|
2164
|
+
|
|
2165
|
+
for (var i = 0 ; i < this._cues.length; i++) {
|
|
2166
|
+
var callbackTime = this._cues[i].time;
|
|
2167
|
+
var val = this._cues[i].val;
|
|
2168
|
+
|
|
2169
|
+
|
|
2170
|
+
if (this._prevTime < callbackTime && callbackTime <= playbackTime) {
|
|
2171
|
+
|
|
2172
|
+
// pass the scheduled callbackTime as parameter to the callback
|
|
2173
|
+
this._cues[i].callback(val);
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2176
|
+
}
|
|
2177
|
+
|
|
2178
|
+
this._prevTime = playbackTime;
|
|
2179
|
+
};
|
|
2180
|
+
|
|
2181
|
+
|
|
2182
|
+
// Cue inspired by JavaScript setTimeout, and the
|
|
2183
|
+
// Tone.js Transport Timeline Event, MIT License Yotam Mann 2015 tonejs.org
|
|
2184
|
+
var Cue = function(callback, time, id, val) {
|
|
2185
|
+
this.callback = callback;
|
|
2186
|
+
this.time = time;
|
|
2187
|
+
this.id = id;
|
|
2188
|
+
this.val = val;
|
|
2189
|
+
};
|
|
2190
|
+
|
|
2191
|
+
// =============================================================================
|
|
2192
|
+
// p5.File
|
|
2193
|
+
// =============================================================================
|
|
2194
|
+
|
|
2195
|
+
|
|
2196
|
+
/**
|
|
2197
|
+
* Base class for a file
|
|
2198
|
+
* Using this for createFileInput
|
|
2199
|
+
*
|
|
2200
|
+
* @class p5.File
|
|
2201
|
+
* @constructor
|
|
2202
|
+
* @param {File} file File that is wrapped
|
|
2203
|
+
*/
|
|
2204
|
+
p5.File = function(file, pInst) {
|
|
2205
|
+
/**
|
|
2206
|
+
* Underlying File object. All normal File methods can be called on this.
|
|
2207
|
+
*
|
|
2208
|
+
* @property file
|
|
2209
|
+
*/
|
|
2210
|
+
this.file = file;
|
|
2211
|
+
|
|
2212
|
+
this._pInst = pInst;
|
|
2213
|
+
|
|
2214
|
+
// Splitting out the file type into two components
|
|
2215
|
+
// This makes determining if image or text etc simpler
|
|
2216
|
+
var typeList = file.type.split('/');
|
|
2217
|
+
/**
|
|
2218
|
+
* File type (image, text, etc.)
|
|
2219
|
+
*
|
|
2220
|
+
* @property type
|
|
2221
|
+
*/
|
|
2222
|
+
this.type = typeList[0];
|
|
2223
|
+
/**
|
|
2224
|
+
* File subtype (usually the file extension jpg, png, xml, etc.)
|
|
2225
|
+
*
|
|
2226
|
+
* @property subtype
|
|
2227
|
+
*/
|
|
2228
|
+
this.subtype = typeList[1];
|
|
2229
|
+
/**
|
|
2230
|
+
* File name
|
|
2231
|
+
*
|
|
2232
|
+
* @property name
|
|
2233
|
+
*/
|
|
2234
|
+
this.name = file.name;
|
|
2235
|
+
/**
|
|
2236
|
+
* File size
|
|
2237
|
+
*
|
|
2238
|
+
* @property size
|
|
2239
|
+
*/
|
|
2240
|
+
this.size = file.size;
|
|
2241
|
+
|
|
2242
|
+
/**
|
|
2243
|
+
* URL string containing image data.
|
|
2244
|
+
*
|
|
2245
|
+
* @property data
|
|
2246
|
+
*/
|
|
2247
|
+
this.data = undefined;
|
|
2248
|
+
};
|
|
2249
|
+
|
|
2250
|
+
}));
|