p5 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ # rspec failure tracking
12
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.1
5
+ before_install: gem install bundler -v 1.15.3
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in p5.gemspec
6
+ gemspec
@@ -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.
@@ -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).
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,3 @@
1
+ //= require p5/p5.min.js
2
+ //= require p5/p5.dom.js
3
+ //= require p5/p5.sound.js
@@ -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 &lt;div&gt;&lt;/div&gt; 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 &lt;p&gt;&lt;/p&gt; 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 &lt;span&gt;&lt;/span&gt; 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 &lt;img&gt; 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 &lt;a&gt;&lt;/a&gt; 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 &lt;input&gt;&lt;/input&gt; 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 &lt;button&gt;&lt;/button&gt; 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 &lt;input&gt;&lt;/input&gt; 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 &lt;select&gt;&lt;/select&gt; 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 &lt;input&gt;&lt;/input&gt; 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 &lt;input&gt;&lt;/input&gt; 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 &lt;input&gt;&lt;/input&gt; 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 &lt;video&gt; 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 &lt;audio&gt; 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 &lt;video&gt; 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
+ }));