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.
@@ -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
+ }));