gluttonberg-core 2.5.7 → 2.5.8

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.
Files changed (29) hide show
  1. data/app/assets/images/gb_360-button-pause-light.gif +0 -0
  2. data/app/assets/images/gb_360-button-pause-light.png +0 -0
  3. data/app/assets/images/gb_360-button-pause.gif +0 -0
  4. data/app/assets/images/gb_360-button-pause.png +0 -0
  5. data/app/assets/images/gb_360-button-play-light.gif +0 -0
  6. data/app/assets/images/gb_360-button-play-light.png +0 -0
  7. data/app/assets/images/gb_360-button-play.gif +0 -0
  8. data/app/assets/images/gb_360-button-play.png +0 -0
  9. data/app/assets/images/gb_360-button-vis-pause-light.gif +0 -0
  10. data/app/assets/images/gb_360-button-vis-pause-light.png +0 -0
  11. data/app/assets/images/gb_360-button-vis-pause.gif +0 -0
  12. data/app/assets/images/gb_360-button-vis-pause.png +0 -0
  13. data/app/assets/images/gb_360-button-vis-play-light.gif +0 -0
  14. data/app/assets/images/gb_360-button-vis-play-light.png +0 -0
  15. data/app/assets/images/gb_360-button-vis-play.gif +0 -0
  16. data/app/assets/images/gb_360-button-vis-play.png +0 -0
  17. data/app/assets/javascripts/gb_360player.js +1396 -0
  18. data/app/assets/javascripts/gb_application.js +49 -4
  19. data/app/assets/javascripts/gb_berniecode-animator.js +674 -0
  20. data/app/assets/javascripts/gb_soundmanager2-jsmin.js +106 -0
  21. data/app/assets/stylesheets/gb_360player.css +271 -0
  22. data/app/assets/stylesheets/gb_admin-override.sass +6 -0
  23. data/app/helpers/gluttonberg/asset_library.rb +5 -1
  24. data/app/views/gluttonberg/admin/asset_library/assets/show.html.haml +7 -1
  25. data/app/views/gluttonberg/admin/asset_library/shared/_asset_panels.html.haml +9 -3
  26. data/app/views/gluttonberg/admin/shared/_asset_panel.html.haml +12 -6
  27. data/app/views/layouts/gluttonberg.html.haml +4 -1
  28. data/lib/gluttonberg/version.rb +1 -1
  29. metadata +38 -18
@@ -0,0 +1,1396 @@
1
+ /**
2
+ *
3
+ * SoundManager 2 Demo: 360-degree / "donut player"
4
+ * ------------------------------------------------
5
+ * http://schillmania.com/projects/soundmanager2/
6
+ *
7
+ * An inline player with a circular UI.
8
+ * Based on the original SM2 inline player.
9
+ * Inspired by Apple's preview feature in the
10
+ * iTunes music store (iPhone), among others.
11
+ *
12
+ * Requires SoundManager 2 Javascript API.
13
+ * Also uses Bernie's Better Animation Class (BSD):
14
+ * http://www.berniecode.com/writing/animator.html
15
+ *
16
+ */
17
+
18
+ /*jslint white: false, onevar: true, undef: true, nomen: false, eqeqeq: true, plusplus: false, bitwise: true, regexp: false, newcap: true, immed: true */
19
+ /*global document, window, soundManager, navigator */
20
+
21
+ var threeSixtyPlayer, // instance
22
+ ThreeSixtyPlayer; // constructor
23
+
24
+ (function(window) {
25
+
26
+ function ThreeSixtyPlayer() {
27
+
28
+ var self = this,
29
+ pl = this,
30
+ sm = soundManager, // soundManager instance
31
+ uA = navigator.userAgent,
32
+ isIE = (uA.match(/msie/i)),
33
+ isOpera = (uA.match(/opera/i)),
34
+ isSafari = (uA.match(/safari/i)),
35
+ isChrome = (uA.match(/chrome/i)),
36
+ isFirefox = (uA.match(/firefox/i)),
37
+ isTouchDevice = (uA.match(/ipad|iphone/i)),
38
+ hasRealCanvas = (typeof window.G_vmlCanvasManager === 'undefined' && typeof document.createElement('canvas').getContext('2d') !== 'undefined'),
39
+ // I dunno what Opera doesn't like about this. I'm probably doing it wrong.
40
+ fullCircle = (isOpera||isChrome?359.9:360);
41
+
42
+ // CSS class for ignoring MP3 links
43
+ this.excludeClass = 'threesixty-exclude';
44
+ this.links = [];
45
+ this.sounds = [];
46
+ this.soundsByURL = [];
47
+ this.indexByURL = [];
48
+ this.lastSound = null;
49
+ this.lastTouchedSound = null;
50
+ this.soundCount = 0;
51
+ this.oUITemplate = null;
52
+ this.oUIImageMap = null;
53
+ this.vuMeter = null;
54
+ this.callbackCount = 0;
55
+ this.peakDataHistory = [];
56
+
57
+ // 360player configuration options
58
+ this.config = {
59
+
60
+ playNext: false, // stop after one sound, or play through list until end
61
+ autoPlay: false, // start playing the first sound right away
62
+ allowMultiple: false, // let many sounds play at once (false = only one sound playing at a time)
63
+ loadRingColor: '#ccc', // how much has loaded
64
+ playRingColor: '#000', // how much has played
65
+ backgroundRingColor: '#eee', // color shown underneath load + play ("not yet loaded" color)
66
+
67
+ // optional segment/annotation (metadata) stuff..
68
+ segmentRingColor: 'rgba(255,255,255,0.33)', // metadata/annotation (segment) colors
69
+ segmentRingColorAlt: 'rgba(0,0,0,0.1)',
70
+ loadRingColorMetadata: '#ddd', // "annotations" load color
71
+ playRingColorMetadata: 'rgba(128,192,256,0.9)', // how much has played when metadata is present
72
+
73
+ circleDiameter: null, // set dynamically according to values from CSS
74
+ circleRadius: null,
75
+ animDuration: 500,
76
+ animTransition: window.Animator.tx.bouncy, // http://www.berniecode.com/writing/animator.html
77
+ showHMSTime: false, // hours:minutes:seconds vs. seconds-only
78
+ scaleFont: true, // also set the font size (if possible) while animating the circle
79
+
80
+ // optional: spectrum or EQ graph in canvas (not supported in IE <9, too slow via ExCanvas)
81
+ useWaveformData: false,
82
+ waveformDataColor: '#0099ff',
83
+ waveformDataDownsample: 3, // use only one in X (of a set of 256 values) - 1 means all 256
84
+ waveformDataOutside: false,
85
+ waveformDataConstrain: false, // if true, +ve values only - keep within inside circle
86
+ waveformDataLineRatio: 0.64,
87
+
88
+ // "spectrum frequency" option
89
+ useEQData: false,
90
+ eqDataColor: '#339933',
91
+ eqDataDownsample: 4, // use only one in X (of 256 values)
92
+ eqDataOutside: true,
93
+ eqDataLineRatio: 0.54,
94
+
95
+ // enable "amplifier" (canvas pulses like a speaker) effect
96
+ usePeakData: true,
97
+ peakDataColor: '#ff33ff',
98
+ peakDataOutside: true,
99
+ peakDataLineRatio: 0.5,
100
+
101
+ useAmplifier: true, // "pulse" like a speaker
102
+
103
+ fontSizeMax: null, // set according to CSS
104
+
105
+ scaleArcWidth: 1, // thickness factor of playback progress ring
106
+
107
+ useFavIcon: false // Experimental (also requires usePeakData: true).. Try to draw a "VU Meter" in the favicon area, if browser supports it (Firefox + Opera as of 2009)
108
+
109
+ };
110
+
111
+ this.css = {
112
+
113
+ // CSS class names appended to link during various states
114
+ sDefault: 'sm2_link', // default state
115
+ sBuffering: 'sm2_buffering',
116
+ sPlaying: 'sm2_playing',
117
+ sPaused: 'sm2_paused'
118
+
119
+ };
120
+
121
+ this.addEventHandler = (typeof window.addEventListener !== 'undefined' ? function(o, evtName, evtHandler) {
122
+ return o.addEventListener(evtName,evtHandler,false);
123
+ } : function(o, evtName, evtHandler) {
124
+ o.attachEvent('on'+evtName,evtHandler);
125
+ });
126
+
127
+ this.removeEventHandler = (typeof window.removeEventListener !== 'undefined' ? function(o, evtName, evtHandler) {
128
+ return o.removeEventListener(evtName,evtHandler,false);
129
+ } : function(o, evtName, evtHandler) {
130
+ return o.detachEvent('on'+evtName,evtHandler);
131
+ });
132
+
133
+ this.hasClass = function(o,cStr) {
134
+ return typeof(o.className)!=='undefined'?o.className.match(new RegExp('(\\s|^)'+cStr+'(\\s|$)')):false;
135
+ };
136
+
137
+ this.addClass = function(o,cStr) {
138
+
139
+ if (!o || !cStr || self.hasClass(o,cStr)) {
140
+ return false;
141
+ }
142
+ o.className = (o.className?o.className+' ':'')+cStr;
143
+
144
+ };
145
+
146
+ this.removeClass = function(o,cStr) {
147
+
148
+ if (!o || !cStr || !self.hasClass(o,cStr)) {
149
+ return false;
150
+ }
151
+ o.className = o.className.replace(new RegExp('( '+cStr+')|('+cStr+')','g'),'');
152
+
153
+ };
154
+
155
+ this.getElementsByClassName = function(className,tagNames,oParent) {
156
+
157
+ var doc = (oParent||document),
158
+ matches = [], i,j, nodes = [];
159
+ if (typeof tagNames !== 'undefined' && typeof tagNames !== 'string') {
160
+ for (i=tagNames.length; i--;) {
161
+ if (!nodes || !nodes[tagNames[i]]) {
162
+ nodes[tagNames[i]] = doc.getElementsByTagName(tagNames[i]);
163
+ }
164
+ }
165
+ } else if (tagNames) {
166
+ nodes = doc.getElementsByTagName(tagNames);
167
+ } else {
168
+ nodes = doc.all||doc.getElementsByTagName('*');
169
+ }
170
+ if (typeof(tagNames)!=='string') {
171
+ for (i=tagNames.length; i--;) {
172
+ for (j=nodes[tagNames[i]].length; j--;) {
173
+ if (self.hasClass(nodes[tagNames[i]][j],className)) {
174
+ matches.push(nodes[tagNames[i]][j]);
175
+ }
176
+ }
177
+ }
178
+ } else {
179
+ for (i=0; i<nodes.length; i++) {
180
+ if (self.hasClass(nodes[i],className)) {
181
+ matches.push(nodes[i]);
182
+ }
183
+ }
184
+ }
185
+ return matches;
186
+
187
+ };
188
+
189
+ this.getParentByNodeName = function(oChild,sParentNodeName) {
190
+
191
+ if (!oChild || !sParentNodeName) {
192
+ return false;
193
+ }
194
+ sParentNodeName = sParentNodeName.toLowerCase();
195
+ while (oChild.parentNode && sParentNodeName !== oChild.parentNode.nodeName.toLowerCase()) {
196
+ oChild = oChild.parentNode;
197
+ }
198
+ return (oChild.parentNode && sParentNodeName === oChild.parentNode.nodeName.toLowerCase()?oChild.parentNode:null);
199
+
200
+ };
201
+
202
+ this.getParentByClassName = function(oChild,sParentClassName) {
203
+
204
+ if (!oChild || !sParentClassName) {
205
+ return false;
206
+ }
207
+ while (oChild.parentNode && !self.hasClass(oChild.parentNode,sParentClassName)) {
208
+ oChild = oChild.parentNode;
209
+ }
210
+ return (oChild.parentNode && self.hasClass(oChild.parentNode,sParentClassName)?oChild.parentNode:null);
211
+
212
+ };
213
+
214
+ this.getSoundByURL = function(sURL) {
215
+ return (typeof self.soundsByURL[sURL] !== 'undefined'?self.soundsByURL[sURL]:null);
216
+ };
217
+
218
+ this.isChildOfNode = function(o,sNodeName) {
219
+
220
+ if (!o || !o.parentNode) {
221
+ return false;
222
+ }
223
+ sNodeName = sNodeName.toLowerCase();
224
+ do {
225
+ o = o.parentNode;
226
+ } while (o && o.parentNode && o.nodeName.toLowerCase() !== sNodeName);
227
+ return (o && o.nodeName.toLowerCase() === sNodeName?o:null);
228
+
229
+ };
230
+
231
+ this.isChildOfClass = function(oChild,oClass) {
232
+
233
+ if (!oChild || !oClass) {
234
+ return false;
235
+ }
236
+ while (oChild.parentNode && !self.hasClass(oChild,oClass)) {
237
+ oChild = self.findParent(oChild);
238
+ }
239
+ return (self.hasClass(oChild,oClass));
240
+
241
+ };
242
+
243
+ this.findParent = function(o) {
244
+
245
+ if (!o || !o.parentNode) {
246
+ return false;
247
+ }
248
+ o = o.parentNode;
249
+ if (o.nodeType === 2) {
250
+ while (o && o.parentNode && o.parentNode.nodeType === 2) {
251
+ o = o.parentNode;
252
+ }
253
+ }
254
+ return o;
255
+
256
+ };
257
+
258
+ this.getStyle = function(o,sProp) {
259
+
260
+ // http://www.quirksmode.org/dom/getstyles.html
261
+ try {
262
+ if (o.currentStyle) {
263
+ return o.currentStyle[sProp];
264
+ } else if (window.getComputedStyle) {
265
+ return document.defaultView.getComputedStyle(o,null).getPropertyValue(sProp);
266
+ }
267
+ } catch(e) {
268
+ // oh well
269
+ }
270
+ return null;
271
+
272
+ };
273
+
274
+ this.findXY = function(obj) {
275
+
276
+ var curleft = 0, curtop = 0;
277
+ do {
278
+ curleft += obj.offsetLeft;
279
+ curtop += obj.offsetTop;
280
+ } while (!!(obj = obj.offsetParent));
281
+ return [curleft,curtop];
282
+
283
+ };
284
+
285
+ this.getMouseXY = function(e) {
286
+
287
+ // http://www.quirksmode.org/js/events_properties.html
288
+ e = e?e:window.event;
289
+ if (isTouchDevice && e.touches) {
290
+ e = e.touches[0];
291
+ }
292
+ if (e.pageX || e.pageY) {
293
+ return [e.pageX,e.pageY];
294
+ } else if (e.clientX || e.clientY) {
295
+ return [e.clientX+self.getScrollLeft(),e.clientY+self.getScrollTop()];
296
+ }
297
+
298
+ };
299
+
300
+ this.getScrollLeft = function() {
301
+ return (document.body.scrollLeft+document.documentElement.scrollLeft);
302
+ };
303
+
304
+ this.getScrollTop = function() {
305
+ return (document.body.scrollTop+document.documentElement.scrollTop);
306
+ };
307
+
308
+ this.events = {
309
+
310
+ // handlers for sound events as they're started/stopped/played
311
+
312
+ play: function() {
313
+ pl.removeClass(this._360data.oUIBox,this._360data.className);
314
+ this._360data.className = pl.css.sPlaying;
315
+ pl.addClass(this._360data.oUIBox,this._360data.className);
316
+ self.fanOut(this);
317
+ },
318
+
319
+ stop: function() {
320
+ pl.removeClass(this._360data.oUIBox,this._360data.className);
321
+ this._360data.className = '';
322
+ self.fanIn(this);
323
+ },
324
+
325
+ pause: function() {
326
+ pl.removeClass(this._360data.oUIBox,this._360data.className);
327
+ this._360data.className = pl.css.sPaused;
328
+ pl.addClass(this._360data.oUIBox,this._360data.className);
329
+ },
330
+
331
+ resume: function() {
332
+ pl.removeClass(this._360data.oUIBox,this._360data.className);
333
+ this._360data.className = pl.css.sPlaying;
334
+ pl.addClass(this._360data.oUIBox,this._360data.className);
335
+ },
336
+
337
+ finish: function() {
338
+ var nextLink;
339
+ pl.removeClass(this._360data.oUIBox,this._360data.className);
340
+ this._360data.className = '';
341
+ // self.clearCanvas(this._360data.oCanvas);
342
+ this._360data.didFinish = true; // so fan draws full circle
343
+ self.fanIn(this);
344
+ if (pl.config.playNext) {
345
+ nextLink = (pl.indexByURL[this._360data.oLink.href]+1);
346
+ if (nextLink<pl.links.length) {
347
+ pl.handleClick({'target':pl.links[nextLink]});
348
+ }
349
+ }
350
+ },
351
+
352
+ whileloading: function() {
353
+ if (this.paused) {
354
+ self.updatePlaying.apply(this);
355
+ }
356
+ },
357
+
358
+ whileplaying: function() {
359
+ self.updatePlaying.apply(this);
360
+ this._360data.fps++;
361
+ },
362
+
363
+ bufferchange: function() {
364
+ if (this.isBuffering) {
365
+ pl.addClass(this._360data.oUIBox,pl.css.sBuffering);
366
+ } else {
367
+ pl.removeClass(this._360data.oUIBox,pl.css.sBuffering);
368
+ }
369
+ }
370
+
371
+ };
372
+
373
+ this.stopEvent = function(e) {
374
+
375
+ if (typeof e !== 'undefined' && typeof e.preventDefault !== 'undefined') {
376
+ e.preventDefault();
377
+ } else if (typeof window.event !== 'undefined' && typeof window.event.returnValue !== 'undefined') {
378
+ window.event.returnValue = false;
379
+ }
380
+ return false;
381
+
382
+ };
383
+
384
+ this.getTheDamnLink = (isIE)?function(e) {
385
+ // I really didn't want to have to do this.
386
+ return (e && e.target?e.target:window.event.srcElement);
387
+ }:function(e) {
388
+ return e.target;
389
+ };
390
+
391
+ this.handleClick = function(e) {
392
+
393
+ // a sound link was clicked
394
+ if (e.button > 1) {
395
+ // only catch left-clicks
396
+ return true;
397
+ }
398
+
399
+ var o = self.getTheDamnLink(e),
400
+ sURL, soundURL, thisSound, oContainer, has_vis, diameter;
401
+
402
+ if (o.nodeName.toLowerCase() !== 'a') {
403
+ o = self.isChildOfNode(o,'a');
404
+ if (!o) {
405
+ return true;
406
+ }
407
+ }
408
+
409
+ if (!self.isChildOfClass(o,'ui360')) {
410
+ // not a link we're interested in
411
+ return true;
412
+ }
413
+
414
+ sURL = o.getAttribute('href');
415
+
416
+ if (!o.href || !sm.canPlayLink(o) || self.hasClass(o,self.excludeClass)) {
417
+ return true; // pass-thru for non-MP3/non-links
418
+ }
419
+
420
+ sm._writeDebug('handleClick()');
421
+ soundURL = (o.href);
422
+ thisSound = self.getSoundByURL(soundURL);
423
+
424
+ if (thisSound) {
425
+
426
+ // already exists
427
+ if (thisSound === self.lastSound) {
428
+ // and was playing (or paused)
429
+ thisSound.togglePause();
430
+ } else {
431
+ // different sound
432
+ thisSound.togglePause(); // start playing current
433
+ sm._writeDebug('sound different than last sound: '+self.lastSound.id);
434
+ if (!self.config.allowMultiple && self.lastSound) {
435
+ self.stopSound(self.lastSound);
436
+ }
437
+ }
438
+
439
+ } else {
440
+
441
+ // append some dom shiz, make noise
442
+
443
+ oContainer = o.parentNode;
444
+ has_vis = (self.getElementsByClassName('ui360-vis','div',oContainer.parentNode).length);
445
+
446
+ // create sound
447
+ thisSound = sm.createSound({
448
+ id:'ui360Sound'+(self.soundCount++),
449
+ url:soundURL,
450
+ onplay:self.events.play,
451
+ onstop:self.events.stop,
452
+ onpause:self.events.pause,
453
+ onresume:self.events.resume,
454
+ onfinish:self.events.finish,
455
+ onbufferchange:self.events.bufferchange,
456
+ type:(o.type||null),
457
+ whileloading:self.events.whileloading,
458
+ whileplaying:self.events.whileplaying,
459
+ useWaveformData:(has_vis && self.config.useWaveformData),
460
+ useEQData:(has_vis && self.config.useEQData),
461
+ usePeakData:(has_vis && self.config.usePeakData)
462
+ });
463
+
464
+ // tack on some custom data
465
+
466
+ diameter = parseInt(self.getElementsByClassName('sm2-360ui','div',oContainer)[0].offsetWidth, 10);
467
+
468
+ thisSound._360data = {
469
+ oUI360: self.getParentByClassName(o,'ui360'), // the (whole) entire container
470
+ oLink: o, // DOM node for reference within SM2 object event handlers
471
+ className: self.css.sPlaying,
472
+ oUIBox: self.getElementsByClassName('sm2-360ui','div',oContainer)[0],
473
+ oCanvas: self.getElementsByClassName('sm2-canvas','canvas',oContainer)[0],
474
+ oButton: self.getElementsByClassName('sm2-360btn','span',oContainer)[0],
475
+ oTiming: self.getElementsByClassName('sm2-timing','div',oContainer)[0],
476
+ oCover: self.getElementsByClassName('sm2-cover','div',oContainer)[0],
477
+ circleDiameter: diameter,
478
+ circleRadius: diameter/2,
479
+ lastTime: null,
480
+ didFinish: null,
481
+ pauseCount:0,
482
+ radius:0,
483
+ fontSize: 1,
484
+ fontSizeMax: self.config.fontSizeMax,
485
+ scaleFont: (has_vis && self.config.scaleFont),
486
+ showHMSTime: has_vis,
487
+ amplifier: (has_vis && self.config.usePeakData?0.9:1), // TODO: x1 if not being used, else use dynamic "how much to amplify by" value
488
+ radiusMax: diameter*0.175, // circle radius
489
+ width:0,
490
+ widthMax: diameter*0.4, // width of the outer ring
491
+ lastValues: {
492
+ bytesLoaded: 0,
493
+ bytesTotal: 0,
494
+ position: 0,
495
+ durationEstimate: 0
496
+ }, // used to track "last good known" values before sound finish/reset for anim
497
+ animating: false,
498
+ oAnim: new window.Animator({
499
+ duration: self.config.animDuration,
500
+ transition:self.config.animTransition,
501
+ onComplete: function() {
502
+ // var thisSound = this;
503
+ // thisSound._360data.didFinish = false; // reset full circle
504
+ }
505
+ }),
506
+ oAnimProgress: function(nProgress) {
507
+ var thisSound = this;
508
+ thisSound._360data.radius = parseInt(thisSound._360data.radiusMax*thisSound._360data.amplifier*nProgress, 10);
509
+ thisSound._360data.width = parseInt(thisSound._360data.widthMax*thisSound._360data.amplifier*nProgress, 10);
510
+ if (thisSound._360data.scaleFont && thisSound._360data.fontSizeMax !== null) {
511
+ thisSound._360data.oTiming.style.fontSize = parseInt(Math.max(1,thisSound._360data.fontSizeMax*nProgress), 10)+'px';
512
+ thisSound._360data.oTiming.style.opacity = nProgress;
513
+ }
514
+ if (thisSound.paused || thisSound.playState === 0 || thisSound._360data.lastValues.bytesLoaded === 0 || thisSound._360data.lastValues.position === 0) {
515
+ self.updatePlaying.apply(thisSound);
516
+ }
517
+ },
518
+ fps: 0
519
+ };
520
+
521
+ // "Metadata" (annotations)
522
+ if (typeof self.Metadata !== 'undefined' && self.getElementsByClassName('metadata','div',thisSound._360data.oUI360).length) {
523
+ thisSound._360data.metadata = new self.Metadata(thisSound,self);
524
+ }
525
+
526
+ // minimize ze font
527
+ if (thisSound._360data.scaleFont && thisSound._360data.fontSizeMax !== null) {
528
+ thisSound._360data.oTiming.style.fontSize = '1px';
529
+ }
530
+
531
+ // set up ze animation
532
+ thisSound._360data.oAnim.addSubject(thisSound._360data.oAnimProgress,thisSound);
533
+
534
+ // animate the radius out nice
535
+ self.refreshCoords(thisSound);
536
+
537
+ self.updatePlaying.apply(thisSound);
538
+
539
+ self.soundsByURL[soundURL] = thisSound;
540
+ self.sounds.push(thisSound);
541
+ if (!self.config.allowMultiple && self.lastSound) {
542
+ self.stopSound(self.lastSound);
543
+ }
544
+ thisSound.play();
545
+
546
+ }
547
+
548
+ self.lastSound = thisSound; // reference for next call
549
+
550
+ if (typeof e !== 'undefined' && typeof e.preventDefault !== 'undefined') {
551
+ e.preventDefault();
552
+ } else if (typeof window.event !== 'undefined') {
553
+ window.event.returnValue = false;
554
+ }
555
+ return false;
556
+
557
+ };
558
+
559
+ this.fanOut = function(oSound) {
560
+
561
+ var thisSound = oSound;
562
+ if (thisSound._360data.animating === 1) {
563
+ return false;
564
+ }
565
+ thisSound._360data.animating = 0;
566
+ soundManager._writeDebug('fanOut: '+thisSound.id+': '+thisSound._360data.oLink.href);
567
+ thisSound._360data.oAnim.seekTo(1); // play to end
568
+ window.setTimeout(function() {
569
+ // oncomplete hack
570
+ thisSound._360data.animating = 0;
571
+ },self.config.animDuration+20);
572
+
573
+ };
574
+
575
+ this.fanIn = function(oSound) {
576
+
577
+ var thisSound = oSound;
578
+ if (thisSound._360data.animating === -1) {
579
+ return false;
580
+ }
581
+ thisSound._360data.animating = -1;
582
+ soundManager._writeDebug('fanIn: '+thisSound.id+': '+thisSound._360data.oLink.href);
583
+ // massive hack
584
+ thisSound._360data.oAnim.seekTo(0); // play to end
585
+ window.setTimeout(function() {
586
+ // reset full 360 fill after animation has completed (oncomplete hack)
587
+ thisSound._360data.didFinish = false;
588
+ thisSound._360data.animating = 0;
589
+ self.resetLastValues(thisSound);
590
+ }, self.config.animDuration+20);
591
+
592
+ };
593
+
594
+ this.resetLastValues = function(oSound) {
595
+ oSound._360data.lastValues.position = 0;
596
+ };
597
+
598
+ this.refreshCoords = function(thisSound) {
599
+
600
+ thisSound._360data.canvasXY = self.findXY(thisSound._360data.oCanvas);
601
+ thisSound._360data.canvasMid = [thisSound._360data.circleRadius,thisSound._360data.circleRadius];
602
+ thisSound._360data.canvasMidXY = [thisSound._360data.canvasXY[0]+thisSound._360data.canvasMid[0], thisSound._360data.canvasXY[1]+thisSound._360data.canvasMid[1]];
603
+
604
+ };
605
+
606
+ this.stopSound = function(oSound) {
607
+
608
+ soundManager._writeDebug('stopSound: '+oSound.id);
609
+ soundManager.stop(oSound.id);
610
+ if (!isTouchDevice) { // iOS 4.2+ security blocks onfinish() -> playNext() if we set a .src in-between(?)
611
+ soundManager.unload(oSound.id);
612
+ }
613
+
614
+ };
615
+
616
+ this.buttonClick = function(e) {
617
+
618
+ var o = e?(e.target?e.target:e.srcElement):window.event.srcElement;
619
+ self.handleClick({target:self.getParentByClassName(o,'sm2-360ui').nextSibling}); // link next to the nodes we inserted
620
+ return false;
621
+
622
+ };
623
+
624
+ this.buttonMouseDown = function(e) {
625
+
626
+ // user might decide to drag from here
627
+ // watch for mouse move
628
+ if (!isTouchDevice) {
629
+ document.onmousemove = function(e) {
630
+ // should be boundary-checked, really (eg. move 3px first?)
631
+ self.mouseDown(e);
632
+ };
633
+ } else {
634
+ self.addEventHandler(document,'touchmove',self.mouseDown);
635
+ }
636
+ self.stopEvent(e);
637
+ return false;
638
+
639
+ };
640
+
641
+ this.mouseDown = function(e) {
642
+
643
+ if (!isTouchDevice && e.button > 1) {
644
+ return true; // ignore non-left-click
645
+ }
646
+
647
+ if (!self.lastSound) {
648
+ self.stopEvent(e);
649
+ return false;
650
+ }
651
+
652
+ var evt = e?e:window.event,
653
+ target, thisSound, oData;
654
+
655
+ if (isTouchDevice && evt.touches) {
656
+ evt = evt.touches[0];
657
+ }
658
+ target = (evt.target||evt.srcElement);
659
+
660
+ thisSound = self.getSoundByURL(self.getElementsByClassName('sm2_link','a',self.getParentByClassName(target,'ui360'))[0].href); // self.lastSound; // TODO: In multiple sound case, figure out which sound is involved etc.
661
+ // just in case, update coordinates (maybe the element moved since last time.)
662
+ self.lastTouchedSound = thisSound;
663
+ self.refreshCoords(thisSound);
664
+ oData = thisSound._360data;
665
+ self.addClass(oData.oUIBox,'sm2_dragging');
666
+ oData.pauseCount = (self.lastTouchedSound.paused?1:0);
667
+ // self.lastSound.pause();
668
+ self.mmh(e?e:window.event);
669
+
670
+ if (isTouchDevice) {
671
+ self.removeEventHandler(document,'touchmove',self.mouseDown);
672
+ self.addEventHandler(document,'touchmove',self.mmh);
673
+ self.addEventHandler(document,'touchend',self.mouseUp);
674
+ } else {
675
+ // incredibly old-skool. TODO: Modernize.
676
+ document.onmousemove = self.mmh;
677
+ document.onmouseup = self.mouseUp;
678
+ }
679
+
680
+ self.stopEvent(e);
681
+ return false;
682
+
683
+ };
684
+
685
+ this.mouseUp = function(e) {
686
+
687
+ var oData = self.lastTouchedSound._360data;
688
+ self.removeClass(oData.oUIBox,'sm2_dragging');
689
+ if (oData.pauseCount === 0) {
690
+ self.lastTouchedSound.resume();
691
+ }
692
+ if (!isTouchDevice) {
693
+ document.onmousemove = null;
694
+ document.onmouseup = null;
695
+ } else {
696
+ self.removeEventHandler(document,'touchmove',self.mmh);
697
+ self.removeEventHandler(document,'touchend',self.mouseUP);
698
+ }
699
+
700
+ };
701
+
702
+ this.mmh = function(e) {
703
+
704
+ if (typeof e === 'undefined') {
705
+ e = window.event;
706
+ }
707
+ var oSound = self.lastTouchedSound,
708
+ coords = self.getMouseXY(e),
709
+ x = coords[0],
710
+ y = coords[1],
711
+ deltaX = x-oSound._360data.canvasMidXY[0],
712
+ deltaY = y-oSound._360data.canvasMidXY[1],
713
+ angle = Math.floor(fullCircle-(self.rad2deg(Math.atan2(deltaX,deltaY))+180));
714
+
715
+ oSound.setPosition(oSound.durationEstimate*(angle/fullCircle));
716
+ self.stopEvent(e);
717
+ return false;
718
+
719
+ };
720
+
721
+ // assignMouseDown();
722
+
723
+ this.drawSolidArc = function(oCanvas, color, radius, width, radians, startAngle, noClear) {
724
+
725
+ // thank you, http://www.snipersystems.co.nz/community/polarclock/tutorial.html
726
+
727
+ var x = radius,
728
+ y = radius,
729
+ canvas = oCanvas,
730
+ ctx, innerRadius, doesntLikeZero, endPoint;
731
+
732
+ if (canvas.getContext){
733
+ // use getContext to use the canvas for drawing
734
+ ctx = canvas.getContext('2d');
735
+ }
736
+
737
+ // re-assign canvas as the actual context
738
+ oCanvas = ctx;
739
+
740
+ if (!noClear) {
741
+ self.clearCanvas(canvas);
742
+ }
743
+ // ctx.restore();
744
+
745
+ if (color) {
746
+ ctx.fillStyle = color;
747
+ }
748
+
749
+ oCanvas.beginPath();
750
+
751
+ if (isNaN(radians)) {
752
+ radians = 0;
753
+ }
754
+
755
+ innerRadius = radius-width;
756
+ doesntLikeZero = (isOpera || isSafari); // safari 4 doesn't actually seem to mind.
757
+
758
+ if (!doesntLikeZero || (doesntLikeZero && radius > 0)) {
759
+ oCanvas.arc(0, 0, radius, startAngle, radians, false);
760
+ endPoint = self.getArcEndpointCoords(innerRadius, radians);
761
+ oCanvas.lineTo(endPoint.x, endPoint.y);
762
+ oCanvas.arc(0, 0, innerRadius, radians, startAngle, true);
763
+ oCanvas.closePath();
764
+ oCanvas.fill();
765
+ }
766
+
767
+ };
768
+
769
+ this.getArcEndpointCoords = function(radius, radians) {
770
+
771
+ return {
772
+ x: radius * Math.cos(radians),
773
+ y: radius * Math.sin(radians)
774
+ };
775
+
776
+ };
777
+
778
+ this.deg2rad = function(nDeg) {
779
+ return (nDeg * Math.PI/180);
780
+ };
781
+
782
+ this.rad2deg = function(nRad) {
783
+ return (nRad * 180/Math.PI);
784
+ };
785
+
786
+ this.getTime = function(nMSec,bAsString) {
787
+
788
+ // convert milliseconds to mm:ss, return as object literal or string
789
+ var nSec = Math.floor(nMSec/1000),
790
+ min = Math.floor(nSec/60),
791
+ sec = nSec-(min*60);
792
+ // if (min === 0 && sec === 0) return null; // return 0:00 as null
793
+ return (bAsString?(min+':'+(sec<10?'0'+sec:sec)):{'min':min,'sec':sec});
794
+
795
+ };
796
+
797
+ this.clearCanvas = function(oCanvas) {
798
+
799
+ var canvas = oCanvas,
800
+ ctx = null,
801
+ width, height;
802
+ if (canvas.getContext){
803
+ // use getContext to use the canvas for drawing
804
+ ctx = canvas.getContext('2d');
805
+ }
806
+ width = canvas.offsetWidth;
807
+ height = canvas.offsetHeight;
808
+ ctx.clearRect(-(width/2), -(height/2), width, height);
809
+
810
+ };
811
+
812
+ this.updatePlaying = function() {
813
+
814
+ var timeNow = (this._360data.showHMSTime?self.getTime(this.position,true):parseInt(this.position/1000, 10));
815
+ var ringScaleFactor = self.config.scaleArcWidth;
816
+
817
+ if (this.bytesLoaded) {
818
+ this._360data.lastValues.bytesLoaded = this.bytesLoaded;
819
+ this._360data.lastValues.bytesTotal = this.bytesTotal;
820
+ }
821
+
822
+ if (this.position) {
823
+ this._360data.lastValues.position = this.position;
824
+ }
825
+
826
+ if (this.durationEstimate) {
827
+ this._360data.lastValues.durationEstimate = this.durationEstimate;
828
+ }
829
+
830
+ // background ring
831
+ self.drawSolidArc(this._360data.oCanvas,self.config.backgroundRingColor,this._360data.width,this._360data.radius * ringScaleFactor,self.deg2rad(fullCircle),false);
832
+
833
+ // loaded ring
834
+ self.drawSolidArc(this._360data.oCanvas,(this._360data.metadata?self.config.loadRingColorMetadata:self.config.loadRingColor),this._360data.width,this._360data.radius * ringScaleFactor,self.deg2rad(fullCircle*(this._360data.lastValues.bytesLoaded/this._360data.lastValues.bytesTotal)),0,true);
835
+
836
+ // don't draw if 0 (full black circle in Opera)
837
+ if (this._360data.lastValues.position !== 0) {
838
+ self.drawSolidArc(this._360data.oCanvas,(this._360data.metadata?self.config.playRingColorMetadata:self.config.playRingColor),this._360data.width,this._360data.radius * ringScaleFactor,self.deg2rad((this._360data.didFinish===1?fullCircle:fullCircle*(this._360data.lastValues.position/this._360data.lastValues.durationEstimate))),0,true);
839
+ }
840
+
841
+ // metadata goes here
842
+ if (this._360data.metadata) {
843
+ this._360data.metadata.events.whileplaying();
844
+ }
845
+
846
+ if (timeNow !== this._360data.lastTime) {
847
+ this._360data.lastTime = timeNow;
848
+ this._360data.oTiming.innerHTML = timeNow;
849
+ }
850
+
851
+ // draw spectrum, if applicable
852
+ if ((this.instanceOptions.useWaveformData || this.instanceOptions.useEQData) && hasRealCanvas) { // IE <9 can render maybe 3 or 4 FPS when including the wave/EQ, so don't bother.
853
+ self.updateWaveform(this);
854
+ }
855
+
856
+ if (self.config.useFavIcon && self.vuMeter) {
857
+ self.vuMeter.updateVU(this);
858
+ }
859
+
860
+ };
861
+
862
+ this.updateWaveform = function(oSound) {
863
+
864
+ if ((!self.config.useWaveformData && !self.config.useEQData) || (!sm.features.waveformData && !sm.features.eqData)) {
865
+ // feature not enabled..
866
+ return false;
867
+ }
868
+
869
+ if (!oSound.waveformData.left.length && !oSound.eqData.length && !oSound.peakData.left) {
870
+ // no data (or errored out/paused/unavailable?)
871
+ return false;
872
+ }
873
+
874
+ /* use for testing the data */
875
+ /*
876
+ for (i=0; i<256; i++) {
877
+ oSound.eqData[i] = 1-(i/256);
878
+ }
879
+ */
880
+
881
+ var oCanvas = oSound._360data.oCanvas.getContext('2d'),
882
+ offX = 0,
883
+ offY = parseInt(oSound._360data.circleDiameter/2, 10),
884
+ scale = offY/2, // Y axis (+/- this distance from 0)
885
+ // lineWidth = Math.floor(oSound._360data.circleDiameter-(oSound._360data.circleDiameter*0.175)/(oSound._360data.circleDiameter/255)); // width for each line
886
+ lineWidth = 1,
887
+ lineHeight = 1,
888
+ thisY = 0,
889
+ offset = offY,
890
+ i, j, direction, downSample, dataLength, sampleCount, startAngle, endAngle, waveData, innerRadius, perItemAngle, yDiff, eqSamples, playedAngle, iAvg, nPeak;
891
+
892
+ if (self.config.useWaveformData) {
893
+ // raw waveform
894
+ downSample = self.config.waveformDataDownsample; // only sample X in 256 (greater number = less sample points)
895
+ downSample = Math.max(1,downSample); // make sure it's at least 1
896
+ dataLength = 256;
897
+ sampleCount = (dataLength/downSample);
898
+ startAngle = 0;
899
+ endAngle = 0;
900
+ waveData = null;
901
+ innerRadius = (self.config.waveformDataOutside?1:(self.config.waveformDataConstrain?0.5:0.565));
902
+ scale = (self.config.waveformDataOutside?0.7:0.75);
903
+ perItemAngle = self.deg2rad((360/sampleCount)*self.config.waveformDataLineRatio); // 0.85 = clean pixel lines at 150? // self.deg2rad(360*(Math.max(1,downSample-1))/sampleCount);
904
+ for (i=0; i<dataLength; i+=downSample) {
905
+ startAngle = self.deg2rad(360*(i/(sampleCount)*1/downSample)); // +0.67 - counter for spacing
906
+ endAngle = startAngle+perItemAngle;
907
+ waveData = oSound.waveformData.left[i];
908
+ if (waveData<0 && self.config.waveformDataConstrain) {
909
+ waveData = Math.abs(waveData);
910
+ }
911
+ self.drawSolidArc(oSound._360data.oCanvas,self.config.waveformDataColor,oSound._360data.width*innerRadius*(2-self.config.scaleArcWidth),oSound._360data.radius*scale*1.25*waveData,endAngle,startAngle,true);
912
+ }
913
+ }
914
+
915
+ if (self.config.useEQData) {
916
+ // EQ spectrum
917
+ downSample = self.config.eqDataDownsample; // only sample N in 256
918
+ yDiff = 0;
919
+ downSample = Math.max(1,downSample); // make sure it's at least 1
920
+ eqSamples = 192; // drop the last 25% of the spectrum (>16500 Hz), most stuff won't actually use it.
921
+ sampleCount = (eqSamples/downSample);
922
+ innerRadius = (self.config.eqDataOutside?1:0.565);
923
+ direction = (self.config.eqDataOutside?-1:1);
924
+ scale = (self.config.eqDataOutside?0.5:0.75);
925
+ startAngle = 0;
926
+ endAngle = 0;
927
+ perItemAngle = self.deg2rad((360/sampleCount)*self.config.eqDataLineRatio); // self.deg2rad(360/(sampleCount+1));
928
+ playedAngle = self.deg2rad((oSound._360data.didFinish===1?360:360*(oSound._360data.lastValues.position/oSound._360data.lastValues.durationEstimate)));
929
+ j=0;
930
+ iAvg = 0;
931
+ for (i=0; i<eqSamples; i+=downSample) {
932
+ startAngle = self.deg2rad(360*(i/eqSamples));
933
+ endAngle = startAngle+perItemAngle;
934
+ self.drawSolidArc(oSound._360data.oCanvas,(endAngle>playedAngle?self.config.eqDataColor:self.config.playRingColor),oSound._360data.width*innerRadius,oSound._360data.radius*scale*(oSound.eqData.left[i]*direction),endAngle,startAngle,true);
935
+ }
936
+ }
937
+
938
+ if (self.config.usePeakData) {
939
+ if (!oSound._360data.animating) {
940
+ nPeak = (oSound.peakData.left||oSound.peakData.right);
941
+ // GIANT HACK: use EQ spectrum data for bass frequencies
942
+ eqSamples = 3;
943
+ for (i=0; i<eqSamples; i++) {
944
+ nPeak = (nPeak||oSound.eqData[i]);
945
+ }
946
+ oSound._360data.amplifier = (self.config.useAmplifier?(0.9+(nPeak*0.1)):1);
947
+ oSound._360data.radiusMax = oSound._360data.circleDiameter*0.175*oSound._360data.amplifier;
948
+ oSound._360data.widthMax = oSound._360data.circleDiameter*0.4*oSound._360data.amplifier;
949
+ oSound._360data.radius = parseInt(oSound._360data.radiusMax*oSound._360data.amplifier, 10);
950
+ oSound._360data.width = parseInt(oSound._360data.widthMax*oSound._360data.amplifier, 10);
951
+ }
952
+ }
953
+
954
+ };
955
+
956
+ this.getUIHTML = function(diameter) {
957
+
958
+ return [
959
+ '<canvas class="sm2-canvas" width="'+diameter+'" height="'+diameter+'"></canvas>',
960
+ ' <span class="sm2-360btn sm2-360btn-default"></span>', // note use of imageMap, edit or remove if you use a different-size image.
961
+ ' <div class="sm2-timing'+(navigator.userAgent.match(/safari/i)?' alignTweak':'')+'"></div>', // + Ever-so-slight Safari horizontal alignment tweak
962
+ ' <div class="sm2-cover"></div>'
963
+ ];
964
+
965
+ };
966
+
967
+ this.uiTest = function(sClass) {
968
+
969
+ // fake a 360 UI so we can get some numbers from CSS, etc.
970
+
971
+ var oTemplate = document.createElement('div'),
972
+ oFakeUI, oFakeUIBox, oTemp, fakeDiameter, uiHTML, circleDiameter, circleRadius, fontSizeMax, oTiming;
973
+
974
+ oTemplate.className = 'sm2-360ui';
975
+
976
+ oFakeUI = document.createElement('div');
977
+ oFakeUI.className = 'ui360'+(sClass?' '+sClass:''); // ui360 ui360-vis
978
+
979
+ oFakeUIBox = oFakeUI.appendChild(oTemplate.cloneNode(true));
980
+
981
+ oFakeUI.style.position = 'absolute';
982
+ oFakeUI.style.left = '-9999px';
983
+
984
+ oTemp = document.body.appendChild(oFakeUI);
985
+
986
+ fakeDiameter = oFakeUIBox.offsetWidth;
987
+
988
+ uiHTML = self.getUIHTML(fakeDiameter);
989
+
990
+ oFakeUIBox.innerHTML = uiHTML[1]+uiHTML[2]+uiHTML[3];
991
+
992
+ circleDiameter = parseInt(oFakeUIBox.offsetWidth, 10);
993
+ circleRadius = parseInt(circleDiameter/2, 10);
994
+
995
+ oTiming = self.getElementsByClassName('sm2-timing','div',oTemp)[0];
996
+ fontSizeMax = parseInt(self.getStyle(oTiming,'font-size'), 10);
997
+ if (isNaN(fontSizeMax)) {
998
+ // getStyle() etc. didn't work.
999
+ fontSizeMax = null;
1000
+ }
1001
+
1002
+ // soundManager._writeDebug('diameter, font size: '+circleDiameter+','+fontSizeMax);
1003
+
1004
+ oFakeUI.parentNode.removeChild(oFakeUI);
1005
+
1006
+ uiHTML = oFakeUI = oFakeUIBox = oTemp = null;
1007
+
1008
+ return {
1009
+ circleDiameter: circleDiameter,
1010
+ circleRadius: circleRadius,
1011
+ fontSizeMax: fontSizeMax
1012
+ };
1013
+
1014
+ };
1015
+
1016
+ this.init = function() {
1017
+ sm._writeDebug('threeSixtyPlayer.init()');
1018
+ var oItems = self.getElementsByClassName('ui360','div'),
1019
+ i, j, oLinks = [], is_vis = false, foundItems = 0, oCanvas, oCanvasCTX, oCover, diameter, radius, uiData, uiDataVis, oUI, oBtn, o, o2, oID;
1020
+
1021
+
1022
+
1023
+ for (i=0,j=oItems.length; i<j; i++) {
1024
+ oLinks.push(oItems[i].getElementsByTagName('a')[0]);
1025
+ // remove "fake" play button (unsupported case)
1026
+ oItems[i].style.backgroundImage = 'none';
1027
+ }
1028
+ // grab all links, look for .mp3
1029
+
1030
+ self.oUITemplate = document.createElement('div');
1031
+ self.oUITemplate.className = 'sm2-360ui';
1032
+
1033
+ self.oUITemplateVis = document.createElement('div');
1034
+ self.oUITemplateVis.className = 'sm2-360ui';
1035
+
1036
+ uiData = self.uiTest();
1037
+
1038
+ self.config.circleDiameter = uiData.circleDiameter;
1039
+ self.config.circleRadius = uiData.circleRadius;
1040
+ // self.config.fontSizeMax = uiData.fontSizeMax;
1041
+
1042
+ uiDataVis = self.uiTest('ui360-vis');
1043
+
1044
+ self.config.fontSizeMax = uiDataVis.fontSizeMax;
1045
+
1046
+ // canvas needs inline width and height, doesn't quite work otherwise
1047
+ self.oUITemplate.innerHTML = self.getUIHTML(self.config.circleDiameter).join('');
1048
+
1049
+ self.oUITemplateVis.innerHTML = self.getUIHTML(uiDataVis.circleDiameter).join('');
1050
+
1051
+ for (i=0,j=oLinks.length; i<j; i++) {
1052
+ if (sm.canPlayLink(oLinks[i]) && !self.hasClass(oLinks[i],self.excludeClass) && !self.hasClass(oLinks[i],self.css.sDefault)) {
1053
+ self.addClass(oLinks[i],self.css.sDefault); // add default CSS decoration
1054
+ self.links[foundItems] = (oLinks[i]);
1055
+ self.indexByURL[oLinks[i].href] = foundItems; // hack for indexing
1056
+ foundItems++;
1057
+
1058
+ is_vis = self.hasClass(oLinks[i].parentNode, 'ui360-vis');
1059
+
1060
+ diameter = (is_vis ? uiDataVis : uiData).circleDiameter;
1061
+ radius = (is_vis ? uiDataVis : uiData).circleRadius;
1062
+
1063
+ // add canvas shiz
1064
+ oUI = oLinks[i].parentNode.insertBefore((is_vis?self.oUITemplateVis:self.oUITemplate).cloneNode(true),oLinks[i]);
1065
+
1066
+ if (isIE && typeof window.G_vmlCanvasManager !== 'undefined') { // IE only
1067
+ o = oLinks[i].parentNode;
1068
+ o2 = document.createElement('canvas');
1069
+ o2.className = 'sm2-canvas';
1070
+ oID = 'sm2_canvas_'+parseInt(Math.random()*1048576, 10);
1071
+ o2.id = oID;
1072
+ o2.width = diameter;
1073
+ o2.height = diameter;
1074
+ oUI.appendChild(o2);
1075
+ window.G_vmlCanvasManager.initElement(o2); // Apply ExCanvas compatibility magic
1076
+ oCanvas = document.getElementById(oID);
1077
+ } else {
1078
+ // add a handler for the button
1079
+ oCanvas = oLinks[i].parentNode.getElementsByTagName('canvas')[0];
1080
+ }
1081
+ oCover = self.getElementsByClassName('sm2-cover','div',oLinks[i].parentNode)[0];
1082
+ oBtn = oLinks[i].parentNode.getElementsByTagName('span')[0];
1083
+ self.addEventHandler(oBtn,'click',self.buttonClick);
1084
+ if (!isTouchDevice) {
1085
+ self.addEventHandler(oCover,'mousedown',self.mouseDown);
1086
+ } else {
1087
+ self.addEventHandler(oCover,'touchstart',self.mouseDown);
1088
+ }
1089
+ oCanvasCTX = oCanvas.getContext('2d');
1090
+ oCanvasCTX.translate(radius, radius);
1091
+ oCanvasCTX.rotate(self.deg2rad(-90)); // compensate for arc starting at EAST // http://stackoverflow.com/questions/319267/tutorial-for-html-canvass-arc-function
1092
+ }
1093
+ }
1094
+ if (foundItems>0) {
1095
+ self.addEventHandler(document,'click',self.handleClick);
1096
+ if (self.config.autoPlay) {
1097
+ self.handleClick({target:self.links[0],preventDefault:function(){}});
1098
+ }
1099
+ }
1100
+ sm._writeDebug('threeSixtyPlayer.init(): Found '+foundItems+' relevant items.');
1101
+
1102
+ if (self.config.useFavIcon && typeof this.VUMeter !== 'undefined') {
1103
+ this.vuMeter = new this.VUMeter(this);
1104
+ }
1105
+
1106
+ };
1107
+
1108
+ }
1109
+
1110
+ // Optional: VU Meter component
1111
+
1112
+ ThreeSixtyPlayer.prototype.VUMeter = function(oParent) {
1113
+
1114
+ var self = oParent,
1115
+ me = this,
1116
+ _head = document.getElementsByTagName('head')[0],
1117
+ isOpera = (navigator.userAgent.match(/opera/i)),
1118
+ isFirefox = (navigator.userAgent.match(/firefox/i));
1119
+
1120
+ this.vuMeterData = [];
1121
+ this.vuDataCanvas = null;
1122
+
1123
+ this.setPageIcon = function(sDataURL) {
1124
+
1125
+ if (!self.config.useFavIcon || !self.config.usePeakData || !sDataURL) {
1126
+ return false;
1127
+ }
1128
+
1129
+ var link = document.getElementById('sm2-favicon');
1130
+ if (link) {
1131
+ _head.removeChild(link);
1132
+ link = null;
1133
+ }
1134
+ if (!link) {
1135
+ link = document.createElement('link');
1136
+ link.id = 'sm2-favicon';
1137
+ link.rel = 'shortcut icon';
1138
+ link.type = 'image/png';
1139
+ link.href = sDataURL;
1140
+ document.getElementsByTagName('head')[0].appendChild(link);
1141
+ }
1142
+
1143
+ };
1144
+
1145
+ this.resetPageIcon = function() {
1146
+
1147
+ if (!self.config.useFavIcon) {
1148
+ return false;
1149
+ }
1150
+ var link = document.getElementById('favicon');
1151
+ if (link) {
1152
+ link.href = '/favicon.ico';
1153
+ }
1154
+
1155
+ };
1156
+
1157
+ this.updateVU = function(oSound) {
1158
+
1159
+ if (soundManager.flashVersion >= 9 && self.config.useFavIcon && self.config.usePeakData) {
1160
+ me.setPageIcon(me.vuMeterData[parseInt(16*oSound.peakData.left, 10)][parseInt(16*oSound.peakData.right, 10)]);
1161
+ }
1162
+
1163
+ };
1164
+
1165
+ this.createVUData = function() {
1166
+
1167
+ var i=0, j=0,
1168
+ canvas = me.vuDataCanvas.getContext('2d'),
1169
+ vuGrad = canvas.createLinearGradient(0, 16, 0, 0),
1170
+ bgGrad = canvas.createLinearGradient(0, 16, 0, 0),
1171
+ outline = 'rgba(0,0,0,0.2)';
1172
+
1173
+ vuGrad.addColorStop(0,'rgb(0,192,0)');
1174
+ vuGrad.addColorStop(0.30,'rgb(0,255,0)');
1175
+ vuGrad.addColorStop(0.625,'rgb(255,255,0)');
1176
+ vuGrad.addColorStop(0.85,'rgb(255,0,0)');
1177
+ bgGrad.addColorStop(0,outline);
1178
+ bgGrad.addColorStop(1,'rgba(0,0,0,0.5)');
1179
+ for (i=0; i<16; i++) {
1180
+ me.vuMeterData[i] = [];
1181
+ }
1182
+ for (i=0; i<16; i++) {
1183
+ for (j=0; j<16; j++) {
1184
+ // reset/erase canvas
1185
+ me.vuDataCanvas.setAttribute('width',16);
1186
+ me.vuDataCanvas.setAttribute('height',16);
1187
+ // draw new stuffs
1188
+ canvas.fillStyle = bgGrad;
1189
+ canvas.fillRect(0,0,7,15);
1190
+ canvas.fillRect(8,0,7,15);
1191
+ /*
1192
+ // shadow
1193
+ canvas.fillStyle = 'rgba(0,0,0,0.1)';
1194
+ canvas.fillRect(1,15-i,7,17-(17-i));
1195
+ canvas.fillRect(9,15-j,7,17-(17-j));
1196
+ */
1197
+ canvas.fillStyle = vuGrad;
1198
+ canvas.fillRect(0,15-i,7,16-(16-i));
1199
+ canvas.fillRect(8,15-j,7,16-(16-j));
1200
+ // and now, clear out some bits.
1201
+ canvas.clearRect(0,3,16,1);
1202
+ canvas.clearRect(0,7,16,1);
1203
+ canvas.clearRect(0,11,16,1);
1204
+ me.vuMeterData[i][j] = me.vuDataCanvas.toDataURL('image/png');
1205
+ // for debugging VU images
1206
+ /*
1207
+ var o = document.createElement('img');
1208
+ o.style.marginRight = '5px';
1209
+ o.src = vuMeterData[i][j];
1210
+ document.documentElement.appendChild(o);
1211
+ */
1212
+ }
1213
+ }
1214
+
1215
+ };
1216
+
1217
+ this.testCanvas = function() {
1218
+
1219
+ // canvas + toDataURL();
1220
+ var c = document.createElement('canvas'),
1221
+ ctx = null, ok;
1222
+ if (!c || typeof c.getContext === 'undefined') {
1223
+ return null;
1224
+ }
1225
+ ctx = c.getContext('2d');
1226
+ if (!ctx || typeof c.toDataURL !== 'function') {
1227
+ return null;
1228
+ }
1229
+ // just in case..
1230
+ try {
1231
+ ok = c.toDataURL('image/png');
1232
+ } catch(e) {
1233
+ // no canvas or no toDataURL()
1234
+ return null;
1235
+ }
1236
+ // assume we're all good.
1237
+ return c;
1238
+
1239
+ };
1240
+
1241
+ this.init = function() {
1242
+
1243
+ if (self.config.useFavIcon) {
1244
+ me.vuDataCanvas = me.testCanvas();
1245
+ if (me.vuDataCanvas && (isFirefox || isOpera)) {
1246
+ // these browsers support dynamically-updating the favicon
1247
+ me.createVUData();
1248
+ } else {
1249
+ // browser doesn't support doing this
1250
+ self.config.useFavIcon = false;
1251
+ }
1252
+ }
1253
+
1254
+ };
1255
+
1256
+ this.init();
1257
+
1258
+ };
1259
+
1260
+ // completely optional: Metadata/annotations/segments code
1261
+
1262
+ ThreeSixtyPlayer.prototype.Metadata = function(oSound, oParent) {
1263
+
1264
+ soundManager._wD('Metadata()');
1265
+
1266
+ var me = this,
1267
+ oBox = oSound._360data.oUI360,
1268
+ o = oBox.getElementsByTagName('ul')[0],
1269
+ oItems = o.getElementsByTagName('li'),
1270
+ isFirefox = (navigator.userAgent.match(/firefox/i)),
1271
+ isAlt = false, i, oDuration;
1272
+
1273
+ this.lastWPExec = 0;
1274
+ this.refreshInterval = 250;
1275
+ this.totalTime = 0;
1276
+
1277
+ this.events = {
1278
+
1279
+ whileplaying: function() {
1280
+
1281
+ var width = oSound._360data.width,
1282
+ radius = oSound._360data.radius,
1283
+ fullDuration = (oSound.durationEstimate||(me.totalTime*1000)),
1284
+ isAlt = null, i, j, d;
1285
+
1286
+ for (i=0,j=me.data.length; i<j; i++) {
1287
+ isAlt = (i%2===0);
1288
+ oParent.drawSolidArc(oSound._360data.oCanvas,(isAlt?oParent.config.segmentRingColorAlt:oParent.config.segmentRingColor),isAlt?width:width, isAlt?radius/2:radius/2, oParent.deg2rad(360*(me.data[i].endTimeMS/fullDuration)), oParent.deg2rad(360*((me.data[i].startTimeMS||1)/fullDuration)), true);
1289
+ }
1290
+ d = new Date();
1291
+ if (d-me.lastWPExec>me.refreshInterval) {
1292
+ me.refresh();
1293
+ me.lastWPExec = d;
1294
+ }
1295
+
1296
+ }
1297
+
1298
+ };
1299
+
1300
+ this.refresh = function() {
1301
+
1302
+ // Display info as appropriate
1303
+ var i, j, index = null,
1304
+ now = oSound.position,
1305
+ metadata = oSound._360data.metadata.data;
1306
+
1307
+ for (i=0, j=metadata.length; i<j; i++) {
1308
+ if (now >= metadata[i].startTimeMS && now <= metadata[i].endTimeMS) {
1309
+ index = i;
1310
+ break;
1311
+ }
1312
+ }
1313
+ if (index !== metadata.currentItem && index < metadata.length) {
1314
+ // update
1315
+ oSound._360data.oLink.innerHTML = metadata.mainTitle+' <span class="metadata"><span class="sm2_divider"> | </span><span class="sm2_metadata">'+metadata[index].title+'</span></span>';
1316
+ // self.setPageTitle(metadata[index].title+' | '+metadata.mainTitle);
1317
+ metadata.currentItem = index;
1318
+ }
1319
+
1320
+ };
1321
+
1322
+ this.strToTime = function(sTime) {
1323
+ var segments = sTime.split(':'),
1324
+ seconds = 0, i;
1325
+ for (i=segments.length; i--;) {
1326
+ seconds += parseInt(segments[i], 10)*Math.pow(60,segments.length-1-i); // hours, minutes
1327
+ }
1328
+ return seconds;
1329
+ };
1330
+
1331
+ this.data = [];
1332
+ this.data.givenDuration = null;
1333
+ this.data.currentItem = null;
1334
+ this.data.mainTitle = oSound._360data.oLink.innerHTML;
1335
+
1336
+ for (i=0; i<oItems.length; i++) {
1337
+ this.data[i] = {
1338
+ o: null,
1339
+ title: oItems[i].getElementsByTagName('p')[0].innerHTML,
1340
+ startTime: oItems[i].getElementsByTagName('span')[0].innerHTML,
1341
+ startSeconds: me.strToTime(oItems[i].getElementsByTagName('span')[0].innerHTML.replace(/[()]/g,'')),
1342
+ duration: 0,
1343
+ durationMS: null,
1344
+ startTimeMS: null,
1345
+ endTimeMS: null,
1346
+ oNote: null
1347
+ };
1348
+ }
1349
+ oDuration = oParent.getElementsByClassName('duration','div',oBox);
1350
+ this.data.givenDuration = (oDuration.length?me.strToTime(oDuration[0].innerHTML)*1000:0);
1351
+ for (i=0; i<this.data.length; i++) {
1352
+ this.data[i].duration = parseInt(this.data[i+1]?this.data[i+1].startSeconds:(me.data.givenDuration?me.data.givenDuration:oSound.durationEstimate)/1000, 10)-this.data[i].startSeconds;
1353
+ this.data[i].startTimeMS = this.data[i].startSeconds*1000;
1354
+ this.data[i].durationMS = this.data[i].duration*1000;
1355
+ this.data[i].endTimeMS = this.data[i].startTimeMS+this.data[i].durationMS;
1356
+ this.totalTime += this.data[i].duration;
1357
+ }
1358
+
1359
+ };
1360
+
1361
+ if (navigator.userAgent.match(/webkit/i) && navigator.userAgent.match(/mobile/i)) {
1362
+ // iPad, iPhone etc.
1363
+ soundManager.setup({
1364
+ useHTML5Audio: true
1365
+ });
1366
+ }
1367
+
1368
+ soundManager.setup({
1369
+ html5PollingInterval: 50, // increased framerate for whileplaying() etc.
1370
+ debugMode: (window.location.href.match(/debug=1/i)), // disable or enable debug output
1371
+ consoleOnly: true,
1372
+ flashVersion: 9,
1373
+ useHighPerformance: true,
1374
+ useFlashBlock: true
1375
+ });
1376
+
1377
+ // FPS data, testing/debug only
1378
+ if (soundManager.debugMode) {
1379
+ window.setInterval(function() {
1380
+ var p = window.threeSixtyPlayer;
1381
+ if (p && p.lastSound && p.lastSound._360data.fps && typeof window.isHome === 'undefined') {
1382
+ soundManager._writeDebug('fps: ~'+p.lastSound._360data.fps);
1383
+ p.lastSound._360data.fps = 0;
1384
+ }
1385
+ },1000);
1386
+ }
1387
+
1388
+ window.ThreeSixtyPlayer = ThreeSixtyPlayer; // constructor
1389
+
1390
+ }(window));
1391
+
1392
+ threeSixtyPlayer = new ThreeSixtyPlayer();
1393
+
1394
+ // hook into SM2 init
1395
+ soundManager.onready(threeSixtyPlayer.init);
1396
+