PageSourceSearch

https://wforadio.com/wp-content/plugins/simple-podcast-press/respo…/js/bar-ui-content.js?ver=5.5.22

js wforadio.com collected 2026-10-02 06:39:58 UTC 46,844 bytes, 1,831 lines download raw bytes

1/*jslint plusplus: true, white: true, nomen: true */
2/*global console, document, navigator, soundManager, window */
3
4(function(window) {
5
6  /**
7   * SoundManager 2: "Bar UI" player
8   * Copyright (c) 2014, Scott Schiller. All rights reserved.
9   * http://www.schillmania.com/projects/soundmanager2/
10   * Code provided under BSD license.
11   * http://schillmania.com/projects/soundmanager2/license.txt
12   */
13
14  "use strict";
15
16  var Player,
17      players = [],
18      // CSS selector that will get us the top-level DOM node for the player UI.
19      playerSelector = '.sm2-bar-ui',
20      playerOptions,
21      utils;
22
23  /**
24   * Slightly hackish: event callbacks.
25   * Override globally by setting window.sm2BarPlayers.on = {}, or individually by window.sm2BarPlayers[0].on = {} etc.
26   */
27  players.on = {
28    /*
29    play: function(player) {
30      console.log('playing', player);
31    },
32    pause: function(player) {
33      console.log('pause', player);
34    },
35    error: function(player) {
36      console.log('error', player);
37    },
38    stop: function(player) {
39      console.log('stop', player);
40    }
41    */
42  };
43  
44  playerOptions = {
45    // useful when multiple players are in use, or other SM2 sounds are active etc.
46    stopOtherSounds: true,
47    // CSS class to let the browser load the URL directly e.g., <a href="foo.mp3" class="sm2-exclude">download foo.mp3</a>
48    excludeClass: 'sm2-exclude'
49  };
50
51  soundManager.setup({
52    // trade-off: higher UI responsiveness (play/progress bar), but may use more CPU.
53    html5PollingInterval: 50,
54    flashVersion: 9
55  });
56
57  soundManager.onready(function() {
58
59    var nodes, i, j;
60
61    nodes = utils.dom.getAll(playerSelector);
62
63    if (nodes && nodes.length) {
64      for (i=0, j=nodes.length; i<j; i++) {
65        players.push(new Player(nodes[i]));
66      }
67    }
68  
69  });
70
71  /**
72   * player bits
73   */
74
75  Player = function(playerNode) {
76
77    var css, dom, extras, playlistController, soundObject, actions, actionData, defaultItem, defaultVolume, firstOpen, exports;
78
79    css = {
80      disabled: 'disabled',
81      selected: 'selected',
82      active: 'active',
83      legacy: 'legacy',
84      noVolume: 'no-volume',
85      playlistOpen: 'playlist-open'
86    };
87
88    dom = {
89      o: null,
90      playlist: null,
91      playlistTarget: null,
92      playlistContainer: null,
93      time: null,
94      player: null,
95      progress: null,
96      progressTrack: null,
97      progressBar: null,
98      duration: null,
99      volume: null
100    };
101
102    // prepended to tracks when a sound fails to load/play
103    extras = {
104      loadFailedCharacter: '<span title="Failed to load/play." class="load-error">✖</span>'
105    };
106
107    function stopOtherSounds() {
108
109      if (playerOptions.stopOtherSounds) {
110        soundManager.stopAll();
111      }
112
113    }
114
115    function callback(method) {
116      if (method) {
117        // fire callback, passing current turntable object
118        if (exports.on && exports.on[method]) {
119          exports.on[method](exports);
120        } else if (players.on[method]) {
121          players.on[method](exports);
122        }
123      }
124    }
125
126    function getTime(millisec, useString) {
127
128      // convert milliseconds to hh:mm:ss, return as object literal or string
129		var seconds = (millisec / 1000).toFixed(0);
130        var minutes = Math.floor(seconds / 60);
131        var hours = "";
132        if (minutes > 59) {
133            hours = Math.floor(minutes / 60);
134            hours = (hours >= 10) ? hours : "0" + hours;
135            minutes = minutes - (hours * 60);
136            minutes = (minutes >= 10) ? minutes : "0" + minutes;
137        }
138
139        seconds = Math.floor(seconds % 60);
140        seconds = (seconds >= 10) ? seconds : "0" + seconds;
141        if (hours != "") {
142            return hours + ":" + minutes + ":" + seconds;
143        }
144        return minutes + ":" + seconds;
145    }
146
147    function setTitle(item) {
148
149      // given a link, update the "now playing" UI.
150
151      // if this is an <li> with an inner link, grab and use the text from that.
152      var links = item.getElementsByTagName('a');
153
154      if (links.length) {
155        item = links[0];
156      }
157
158      // remove any failed character sequence, also
159      dom.playlistTarget.innerHTML = '<ul class="sm2-playlist-bd"><li>' + item.innerHTML.replace(extras.loadFailedCharacter, '') + '</li></ul>';
160
161      if (dom.playlistTarget.getElementsByTagName('li')[0].scrollWidth > dom.playlistTarget.offsetWidth) {
162        // this item can use <marquee>, in fact.
163        dom.playlistTarget.innerHTML = '<ul class="sm2-playlist-bd"><li><marquee>' + item.innerHTML + '</marquee></li></ul>';
164      }
165
166    }
167function md5 ( str ) {
168
169    var RotateLeft = function(lValue, iShiftBits) {
170            return (lValue<<iShiftBits) | (lValue>>>(32-iShiftBits));
171        };
172
173    var AddUnsigned = function(lX,lY) {
174            var lX4,lY4,lX8,lY8,lResult;
175            lX8 = (lX & 0x80000000);
176            lY8 = (lY & 0x80000000);
177            lX4 = (lX & 0x40000000);
178            lY4 = (lY & 0x40000000);
179            lResult = (lX & 0x3FFFFFFF)+(lY & 0x3FFFFFFF);
180            if (lX4 & lY4) {
181                return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
182            }
183            if (lX4 | lY4) {
184                if (lResult & 0x40000000) {
185                    return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
186                } else {
187                    return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
188                }
189            } else {
190                return (lResult ^ lX8 ^ lY8);
191            }
192        };
193
194    var F = function(x,y,z) { return (x & y) | ((~x) & z); };
195    var G = function(x,y,z) { return (x & z) | (y & (~z)); };
196    var H = function(x,y,z) { return (x ^ y ^ z); };
197    var I = function(x,y,z) { return (y ^ (x | (~z))); };
198
199    var FF = function(a,b,c,d,x,s,ac) {
200            a = AddUnsigned(a, AddUnsigned(AddUnsigned(F(b, c, d), x), ac));
201            return AddUnsigned(RotateLeft(a, s), b);
202        };
203
204    var GG = function(a,b,c,d,x,s,ac) {
205            a = AddUnsigned(a, AddUnsigned(AddUnsigned(G(b, c, d), x), ac));
206            return AddUnsigned(RotateLeft(a, s), b);
207        };
208
209    var HH = function(a,b,c,d,x,s,ac) {
210            a = AddUnsigned(a, AddUnsigned(AddUnsigned(H(b, c, d), x), ac));
211            return AddUnsigned(RotateLeft(a, s), b);
212        };
213
214    var II = function(a,b,c,d,x,s,ac) {
215            a = AddUnsigned(a, AddUnsigned(AddUnsigned(I(b, c, d), x), ac));
216            return AddUnsigned(RotateLeft(a, s), b);
217        };
218
219    var ConvertToWordArray = function(str) {
220            var lWordCount;
221            var lMessageLength = str.length;
222            var lNumberOfWords_temp1=lMessageLength + 8;
223            var lNumberOfWords_temp2=(lNumberOfWords_temp1-(lNumberOfWords_temp1 % 64))/64;
224            var lNumberOfWords = (lNumberOfWords_temp2+1)*16;
225            var lWordArray=Array(lNumberOfWords-1);
226            var lBytePosition = 0;
227            var lByteCount = 0;
228            while ( lByteCount < lMessageLength ) {
229                lWordCount = (lByteCount-(lByteCount % 4))/4;
230                lBytePosition = (lByteCount % 4)*8;
231                lWordArray[lWordCount] = (lWordArray[lWordCount] | (str.charCodeAt(lByteCount)<<lBytePosition));
232                lByteCount++;
233            }
234            lWordCount = (lByteCount-(lByteCount % 4))/4;
235            lBytePosition = (lByteCount % 4)*8;
236            lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80<<lBytePosition);
237            lWordArray[lNumberOfWords-2] = lMessageLength<<3;
238            lWordArray[lNumberOfWords-1] = lMessageLength>>>29;
239            return lWordArray;
240        };
241
242    var WordToHex = function(lValue) {
243            var WordToHexValue="",WordToHexValue_temp="",lByte,lCount;
244            for (lCount = 0;lCount<=3;lCount++) {
245                lByte = (lValue>>>(lCount*8)) & 255;
246                WordToHexValue_temp = "0" + lByte.toString(16);
247                WordToHexValue = WordToHexValue + WordToHexValue_temp.substr(WordToHexValue_temp.length-2,2);
248            }
249            return WordToHexValue;
250        };
251
252    var x=Array();
253    var k,AA,BB,CC,DD,a,b,c,d;
254    var S11=7, S12=12, S13=17, S14=22;
255    var S21=5, S22=9 , S23=14, S24=20;
256    var S31=4, S32=11, S33=16, S34=23;
257    var S41=6, S42=10, S43=15, S44=21;
258
259    x = ConvertToWordArray(str);
260    a = 0x67452301; b = 0xEFCDAB89; c = 0x98BADCFE; d = 0x10325476;
261
262    for (k=0;k<x.length;k+=16) {
263        AA=a; BB=b; CC=c; DD=d;
264        a=FF(a,b,c,d,x[k+0], S11,0xD76AA478);
265        d=FF(d,a,b,c,x[k+1], S12,0xE8C7B756);
266        c=FF(c,d,a,b,x[k+2], S13,0x242070DB);
267        b=FF(b,c,d,a,x[k+3], S14,0xC1BDCEEE);
268        a=FF(a,b,c,d,x[k+4], S11,0xF57C0FAF);
269        d=FF(d,a,b,c,x[k+5], S12,0x4787C62A);
270        c=FF(c,d,a,b,x[k+6], S13,0xA8304613);
271        b=FF(b,c,d,a,x[k+7], S14,0xFD469501);
272        a=FF(a,b,c,d,x[k+8], S11,0x698098D8);
273        d=FF(d,a,b,c,x[k+9], S12,0x8B44F7AF);
274        c=FF(c,d,a,b,x[k+10],S13,0xFFFF5BB1);
275        b=FF(b,c,d,a,x[k+11],S14,0x895CD7BE);
276        a=FF(a,b,c,d,x[k+12],S11,0x6B901122);
277        d=FF(d,a,b,c,x[k+13],S12,0xFD987193);
278        c=FF(c,d,a,b,x[k+14],S13,0xA679438E);
279        b=FF(b,c,d,a,x[k+15],S14,0x49B40821);
280        a=GG(a,b,c,d,x[k+1], S21,0xF61E2562);
281        d=GG(d,a,b,c,x[k+6], S22,0xC040B340);
282        c=GG(c,d,a,b,x[k+11],S23,0x265E5A51);
283        b=GG(b,c,d,a,x[k+0], S24,0xE9B6C7AA);
284        a=GG(a,b,c,d,x[k+5], S21,0xD62F105D);
285        d=GG(d,a,b,c,x[k+10],S22,0x2441453);
286        c=GG(c,d,a,b,x[k+15],S23,0xD8A1E681);
287        b=GG(b,c,d,a,x[k+4], S24,0xE7D3FBC8);
288        a=GG(a,b,c,d,x[k+9], S21,0x21E1CDE6);
289        d=GG(d,a,b,c,x[k+14],S22,0xC33707D6);
290        c=GG(c,d,a,b,x[k+3], S23,0xF4D50D87);
291        b=GG(b,c,d,a,x[k+8], S24,0x455A14ED);
292        a=GG(a,b,c,d,x[k+13],S21,0xA9E3E905);
293        d=GG(d,a,b,c,x[k+2], S22,0xFCEFA3F8);
294        c=GG(c,d,a,b,x[k+7], S23,0x676F02D9);
295        b=GG(b,c,d,a,x[k+12],S24,0x8D2A4C8A);
296        a=HH(a,b,c,d,x[k+5], S31,0xFFFA3942);
297        d=HH(d,a,b,c,x[k+8], S32,0x8771F681);
298        c=HH(c,d,a,b,x[k+11],S33,0x6D9D6122);
299        b=HH(b,c,d,a,x[k+14],S34,0xFDE5380C);
300        a=HH(a,b,c,d,x[k+1], S31,0xA4BEEA44);
301        d=HH(d,a,b,c,x[k+4], S32,0x4BDECFA9);
302        c=HH(c,d,a,b,x[k+7], S33,0xF6BB4B60);
303        b=HH(b,c,d,a,x[k+10],S34,0xBEBFBC70);
304        a=HH(a,b,c,d,x[k+13],S31,0x289B7EC6);
305        d=HH(d,a,b,c,x[k+0], S32,0xEAA127FA);
306        c=HH(c,d,a,b,x[k+3], S33,0xD4EF3085);
307        b=HH(b,c,d,a,x[k+6], S34,0x4881D05);
308        a=HH(a,b,c,d,x[k+9], S31,0xD9D4D039);
309        d=HH(d,a,b,c,x[k+12],S32,0xE6DB99E5);
310        c=HH(c,d,a,b,x[k+15],S33,0x1FA27CF8);
311        b=HH(b,c,d,a,x[k+2], S34,0xC4AC5665);
312        a=II(a,b,c,d,x[k+0], S41,0xF4292244);
313        d=II(d,a,b,c,x[k+7], S42,0x432AFF97);
314        c=II(c,d,a,b,x[k+14],S43,0xAB9423A7);
315        b=II(b,c,d,a,x[k+5], S44,0xFC93A039);
316        a=II(a,b,c,d,x[k+12],S41,0x655B59C3);
317        d=II(d,a,b,c,x[k+3], S42,0x8F0CCC92);
318        c=II(c,d,a,b,x[k+10],S43,0xFFEFF47D);
319        b=II(b,c,d,a,x[k+1], S44,0x85845DD1);
320        a=II(a,b,c,d,x[k+8], S41,0x6FA87E4F);
321        d=II(d,a,b,c,x[k+15],S42,0xFE2CE6E0);
322        c=II(c,d,a,b,x[k+6], S43,0xA3014314);
323        b=II(b,c,d,a,x[k+13],S44,0x4E0811A1);
324        a=II(a,b,c,d,x[k+4], S41,0xF7537E82);
325        d=II(d,a,b,c,x[k+11],S42,0xBD3AF235);
326        c=II(c,d,a,b,x[k+2], S43,0x2AD7D2BB);
327        b=II(b,c,d,a,x[k+9], S44,0xEB86D391);
328        a=AddUnsigned(a,AA);
329        b=AddUnsigned(b,BB);
330        c=AddUnsigned(c,CC);
331        d=AddUnsigned(d,DD);
332    }
333
334    var temp = WordToHex(a)+WordToHex(b)+WordToHex(c)+WordToHex(d);
335
336    return temp.toLowerCase();
337}
338    function makeSound(url) {
339
340	 var soundid = md5(url);
341	 var getsound = window.soundManager.getSoundById(soundid);
342
343	if (typeof getsound == 'undefined') {
344		 soundid = md5(url);	
345	}else{
346		 soundid = soundid + 'n';
347	}
348
349
350      var sound = soundManager.createSound({
351
352        url: url,
353
354		id: soundid,
355
356        volume: defaultVolume,
357
358        whileplaying: function() {
359          var progressMaxLeft = 100,
360              left,
361              width;
362  
363          left = Math.min(progressMaxLeft, Math.max(0, (progressMaxLeft * (this.position / this.durationEstimate)))) + '%';
364          width = Math.min(100, Math.max(0, (100 * this.position / this.durationEstimate))) + '%';
365  
366          if (this.duration) {
367
368            dom.progress.style.left = left;
369            dom.progressBar.style.width = width;
370              
371            // TODO: only write changes
372            dom.time.innerHTML = getTime(this.position, true);
373
374          }
375
376        },
377
378        onbufferchange: function(isBuffering) {
379          if (isBuffering) {
380            utils.css.add(dom.o, 'buffering');
381          } else {
382            utils.css.remove(dom.o, 'buffering');
383          }
384
385        },
386
387        onplay: function() {
388          utils.css.swap(dom.o, 'paused', 'playing');
389          callback('play');
390        },
391
392        onpause: function() {
393          utils.css.swap(dom.o, 'playing', 'paused');
394          callback('pause');
395        },
396
397        onresume: function() {
398          utils.css.swap(dom.o, 'paused', 'playing');
399        },
400
401        whileloading: function() {
402		  console.log(this.id);
403          if (!this.isHTML5) {
404            dom.duration.innerHTML = getTime(this.durationEstimate, true);
405          }
406
407        },
408
409        onload: function(ok) {
410
411          if (ok) {
412			jQuery(".playercontent").data('sound-id', this.id);
413            dom.duration.innerHTML = getTime(this.duration, true);
414
415          } else if (this._iO && this._iO.onerror) {
416
417            this._iO.onerror();
418
419          }
420
421        },
422
423        onerror: function() {
424
425          // sound failed to load.
426          var item, element, html;
427
428          item = playlistController.getItem();
429
430          if (item) {
431
432            // note error, delay 2 seconds and advance?
433            // playlistTarget.innerHTML = '<ul class="sm2-playlist-bd"><li>' + item.innerHTML + '</li></ul>';
434
435            if (extras.loadFailedCharacter) {
436              dom.playlistTarget.innerHTML = dom.playlistTarget.innerHTML.replace('<li>' ,'<li>' + extras.loadFailedCharacter + ' ');
437              if (playlistController.data.playlist && playlistController.data.playlist[playlistController.data.selectedIndex]) {
438                element = playlistController.data.playlist[playlistController.data.selectedIndex].getElementsByTagName('a')[0];
439                html = element.innerHTML;
440                if (html.indexOf(extras.loadFailedCharacter) === -1) {
441                  element.innerHTML = extras.loadFailedCharacter + ' ' + html;
442                }
443              }
444            }
445
446          }
447
448          callback('error');
449
450          // load next, possibly with delay.
451            
452          if (navigator.userAgent.match(/mobile/i)) {
453            // mobile will likely block the next play() call if there is a setTimeout() - so don't use one here.
454            actions.next();
455          } else {
456            if (playlistController.data.timer) {
457              window.clearTimeout(playlistController.data.timer);
458            }
459            playlistController.data.timer = window.setTimeout(actions.next, 2000);
460          }
461
462        },
463
464        onstop: function() {
465
466          utils.css.remove(dom.o, 'playing');
467          callback('stop');
468
469        },
470
471        onfinish: function() {
472
473          var lastIndex, item;
474
475          utils.css.remove(dom.o, 'playing');
476
477          dom.progress.style.left = '0%';
478
479          lastIndex = playlistController.data.selectedIndex;
480
481          // next track?
482          item = playlistController.getNext();
483
484          // don't play the same item over and over again, if at end of playlist etc.
485          if (item && playlistController.data.selectedIndex !== lastIndex) {
486
487            playlistController.select(item);
488
489            setTitle(item);
490
491            stopOtherSounds();
492
493            // play next
494            this.play({
495              url: playlistController.getURL()
496            });
497
498          }/* else {
499            // explicitly stop?
500            // this.stop();
501          }*/
502
503        }
504
505      });
506sound.load;
507      return sound;
508
509    }
510
511    function playLink(link) {
512
513      // if a link is OK, play it.
514
515      if (soundManager.canPlayURL(link.href)) {
516
517        // if there's a timer due to failure to play one track, cancel it.
518        // catches case when user may use previous/next after an error.
519        if (playlistController.data.timer) {
520          window.clearTimeout(playlistController.data.timer);
521          playlistController.data.timer = null;
522        }
523
524        if (!soundObject) {
525          soundObject = makeSound(link.href);
526        }
527
528        // required to reset pause/play state on iOS so whileplaying() works? odd.
529        soundObject.stop();
530
531        playlistController.select(link.parentNode);
532
533        // TODO: ancestor('li')
534        setTitle(link.parentNode);
535
536        // reset the UI
537        // TODO: function that also resets/hides timing info.
538        dom.progress.style.left = '0px';
539        dom.progressBar.style.width = '0px';
540
541        stopOtherSounds();
542
543        soundObject.play({
544          url: link.href,
545          position: 0
546        });
547
548      }
549
550    }
551
552    function PlaylistController() {
553
554      var data;
555
556      data = {
557
558        // list of nodes?
559        playlist: [],
560
561        // shuffledIndex: [],
562
563        // selection
564        selectedIndex: 0,
565
566        // shuffleMode: false,
567
568        loopMode: false,
569
570        timer: null
571
572      };
573
574      function getPlaylist() {
575
576        return data.playlist;
577
578      }
579
580      function getItem(offset) {
581
582        var list,
583            item;
584
585        // given the current selection (or an offset), return the current item.
586
587        // if currently null, may be end of list case. bail.
588        if (data.selectedIndex === null) {
589          return offset;
590        }
591
592        list = getPlaylist();
593
594        // use offset if provided, otherwise take default selected.
595        offset = (offset !== undefined ? offset : data.selectedIndex);
596
597        // safety check - limit to between 0 and list length
598        offset = Math.max(0, Math.min(offset, list.length));
599
600        item = list[offset];
601
602        return item;
603
604      }
605
606      function findOffsetFromItem(item) {
607
608        // given an <li> item, find it in the playlist array and return the index.
609        var list,
610            i,
611            j,
612            offset;
613
614        offset = -1;
615
616        list = getPlaylist();
617
618        if (list) {
619
620          for (i=0, j=list.length; i<j; i++) {
621            if (list[i] === item) {
622              offset = i;
623              break;
624            }
625          }
626
627        }
628
629        return offset;
630
631      }
632
633      function getNext() {
634
635        // don't increment if null.
636        if (data.selectedIndex !== null) {
637          data.selectedIndex++;
638        }
639
640        if (data.playlist.length > 1) {
641
642          if (data.selectedIndex >= data.playlist.length) {
643
644            if (data.loopMode) {
645
646              // loop to beginning
647              data.selectedIndex = 0;
648
649            } else {
650
651              // no change
652              data.selectedIndex--;
653
654              // end playback
655              // data.selectedIndex = null;
656
657            }
658
659          }
660
661        } else {
662
663          data.selectedIndex = null;
664
665        }
666
667        return getItem();
668
669      }
670
671      function getPrevious() {
672
673        data.selectedIndex--;
674
675        if (data.selectedIndex < 0) {
676          // wrapping around beginning of list? loop or exit.
677          if (data.loopMode) {
678            data.selectedIndex = data.playlist.length - 1;
679          } else {
680            // undo
681            data.selectedIndex++;
682          }
683        }
684
685        return getItem();
686
687      }
688
689      function resetLastSelected() {
690
691        // remove UI highlight(s) on selected items.
692        var items,
693            i, j;
694
695        items = utils.dom.getAll(dom.playlist, '.' + css.selected);
696
697        for (i=0, j=items.length; i<j; i++) {
698          utils.css.remove(items[i], css.selected);
699        }
700
701      }
702
703      function select(item) {
704
705        var offset,
706            itemTop,
707            itemBottom,
708            containerHeight,
709            scrollTop,
710            itemPadding;
711
712        // remove last selected, if any
713        resetLastSelected();
714
715        if (item) {
716
717          utils.css.add(item, css.selected);
718
719          itemTop = item.offsetTop;
720          itemBottom = itemTop + item.offsetHeight;
721          containerHeight = dom.playlistContainer.offsetHeight;
722          scrollTop = dom.playlist.scrollTop;
723          itemPadding = 8;
724
725          if (itemBottom > containerHeight + scrollTop) {
726            // bottom-align
727            dom.playlist.scrollTop = itemBottom - containerHeight + itemPadding;
728          } else if (itemTop < scrollTop) {
729            // top-align
730            dom.playlist.scrollTop = item.offsetTop - itemPadding;
731          }
732
733        }
734
735        // update selected offset, too.
736        offset = findOffsetFromItem(item);
737
738        data.selectedIndex = offset;
739
740      }
741
742      function playItemByOffset(offset) {
743
744        var item;
745
746        offset = (offset || 0);
747
748        item = getItem(offset);
749        
750        if (item) {
751          playLink(item.getElementsByTagName('a')[0]);
752        }
753
754      }
755
756      function getURL() {
757
758        // return URL of currently-selected item
759        var item, url;
760
761        item = getItem();
762      
763        if (item) {
764          url = item.getElementsByTagName('a')[0].href;
765        }
766
767        return url;
768
769      }
770
771      function refreshDOM() {
772
773        // get / update playlist from DOM
774
775        if (!dom.playlist) {
776          if (window.console && console.warn) {
777            console.warn('refreshDOM(): playlist node not found?');
778          }
779          return false;
780        }
781
782        data.playlist = dom.playlist.getElementsByTagName('li');
783
784      }
785
786      function initDOM() {
787
788        dom.playlistTarget = utils.dom.get(dom.o, '.sm2-playlist-target');
789        dom.playlistContainer = utils.dom.get(dom.o, '.sm2-playlist-drawer');
790        dom.playlist = utils.dom.get(dom.o, '.sm2-playlist-bd');
791
792      }
793
794      function init() {
795
796        // inherit the default SM2 volume
797        defaultVolume = soundManager.defaultOptions.volume;
798
799        initDOM();
800        refreshDOM();
801
802        // animate playlist open, if HTML classname indicates so.
803        if (utils.css.has(dom.o, css.playlistOpen)) {
804          // hackish: run this after API has returned
805          window.setTimeout(function() {
806            actions.menu(true);
807          }, 1);
808        }
809
810      }
811
812      init();
813
814      return {
815        data: data,
816        refresh: refreshDOM,
817        getNext: getNext,
818        getPrevious: getPrevious,
819        getItem: getItem,
820        getURL: getURL,
821        playItemByOffset: playItemByOffset,
822        select: select
823      };
824
825    }
826
827    function isRightClick(e) {
828
829      // only pay attention to left clicks. old IE differs where there's no e.which, but e.button is 1 on left click.
830      if (e && ((e.which && e.which === 2) || (e.which === undefined && e.button !== 1))) {
831        // http://www.quirksmode.org/js/events_properties.html#button
832        return true;
833      }
834
835    }
836
837    function getActionData(target) {
838
839      // DOM measurements for volume slider
840
841      if (!target) {
842        return false;
843      }
844
845      actionData.volume.x = utils.position.getOffX(target);
846      actionData.volume.y = utils.position.getOffY(target);
847
848      actionData.volume.width = target.offsetWidth;
849      actionData.volume.height = target.offsetHeight;
850
851      // potentially dangerous: this should, but may not be a percentage-based value.
852      actionData.volume.backgroundSize = parseInt(utils.style.get(target, 'background-size'), 10);
853
854      // IE gives pixels even if background-size specified as % in CSS. Boourns.
855      if (window.navigator.userAgent.match(/msie|trident/i)) {
856        actionData.volume.backgroundSize = (actionData.volume.backgroundSize / actionData.volume.width) * 100;
857      }
858
859    }
860
861    function handleMouseDown(e) {
862
863      var links,
864          target;
865
866      target = e.target || e.srcElement;
867
868      if (isRightClick(e)) {
869        return true;
870      }
871
872      // normalize to <a>, if applicable.
873      if (target.nodeName.toLowerCase() !== 'a') {
874
875        links = target.getElementsByTagName('a');
876        if (links && links.length) {
877          target = target.getElementsByTagName('a')[0];
878        }
879
880      }
881
882      if (utils.css.has(target, 'sm2-volume-control')) {
883
884        // drag case for volume
885
886        getActionData(target);
887
888        utils.events.add(document, 'mousemove', actions.adjustVolume);
889        utils.events.add(document, 'mouseup', actions.releaseVolume);
890
891        // and apply right away
892        return actions.adjustVolume(e);
893
894      }
895
896    }
897
898    function handleClick(e) {
899
900      var evt,
901          target,
902          offset,
903          targetNodeName,
904          methodName,
905          href,
906          handled;
907
908      evt = (e || window.event);
909
910      target = evt.target || evt.srcElement;
911
912      if (target && target.nodeName) {
913
914        targetNodeName = target.nodeName.toLowerCase();
915
916        if (targetNodeName !== 'a') {
917
918          // old IE (IE 8) might return nested elements inside the <a>, eg., <b> etc. Try to find the parent <a>.
919
920          if (target.parentNode) {
921
922            do {
923              target = target.parentNode;
924              targetNodeName = target.nodeName.toLowerCase();
925            } while (targetNodeName !== 'a' && target.parentNode);
926
927            if (!target) {
928              // something went wrong. bail.
929              return false;
930            }
931
932          }
933
934        }
935
936        if (targetNodeName === 'a') {
937
938          // yep, it's a link.
939
940          href = target.href;
941
942          if (soundManager.canPlayURL(href)) {
943
944            // not excluded
945            if (!utils.css.has(target, playerOptions.excludeClass)) {
946
947              // find this in the playlist
948
949              playLink(target);
950
951              handled = true;
952
953            }
954
955          } else {
956
957            // is this one of the action buttons, eg., play/pause, volume, etc.?
958            offset = target.href.lastIndexOf('#');
959
960            if (offset !== -1) {
961
962              methodName = target.href.substr(offset+1);
963
964              if (methodName && actions[methodName]) {
965                handled = true;
966                actions[methodName](e);
967              }
968
969            }
970
971          }
972
973          // fall-through case
974
975          if (handled) {
976            // prevent browser fall-through
977            return utils.events.preventDefault(evt);
978          }
979
980        }
981
982      }
983
984    }
985
986    function handleMouse(e) {
987
988      var target, barX, barWidth, x, newPosition, sound;
989
990      target = dom.progressTrack;
991
992      barX = utils.position.getOffX(target);
993      barWidth = target.offsetWidth;
994
995      x = (e.clientX - barX);
996
997      newPosition = (x / barWidth);
998
999      sound = soundObject;
1000
1001      if (sound && sound.duration) {
1002
1003        sound.setPosition(sound.duration * newPosition);
1004
1005        // a little hackish: ensure UI updates immediately with current position, even if audio is buffering and hasn't moved there yet.
1006        if (sound._iO && sound._iO.whileplaying) {
1007          sound._iO.whileplaying.apply(sound);
1008        }
1009
1010      }
1011
1012      if (e.preventDefault) {
1013        e.preventDefault();
1014      }
1015
1016      return false;
1017
1018    }
1019
1020    function releaseMouse(e) {
1021
1022      utils.events.remove(document, 'mousemove', handleMouse);
1023
1024      utils.css.remove(dom.o, 'grabbing');
1025
1026      utils.events.remove(document, 'mouseup', releaseMouse);
1027
1028      utils.events.preventDefault(e);
1029
1030      return false;
1031
1032    }
1033
1034    function init() {
1035
1036      // init DOM?
1037
1038      if (!playerNode) {
1039        console.warn('init(): No playerNode element?');
1040      }
1041
1042      dom.o = playerNode;
1043
1044      // are we dealing with a crap browser? apply legacy CSS if so.
1045      if (window.navigator.userAgent.match(/msie [678]/i)) {
1046        utils.css.add(dom.o, css.legacy);
1047      }
1048
1049      if (window.navigator.userAgent.match(/mobile/i)) {
1050        // majority of mobile devices don't let HTML5 audio set volume.
1051        utils.css.add(dom.o, css.noVolume);
1052      }
1053
1054      dom.progress = utils.dom.get(dom.o, '.sm2-progress-ball');
1055
1056      dom.progressTrack = utils.dom.get(dom.o, '.sm2-progress-track');
1057
1058      dom.progressBar = utils.dom.get(dom.o, '.sm2-progress-bar');
1059
1060      dom.volume = utils.dom.get(dom.o, 'a.sm2-volume-control');
1061
1062      // measure volume control dimensions
1063      if (dom.volume) {
1064        getActionData(dom.volume);
1065      }
1066
1067      dom.duration = utils.dom.get(dom.o, '.sm2-inline-duration');
1068
1069      dom.time = utils.dom.get(dom.o, '.sm2-inline-time');
1070
1071      playlistController = new PlaylistController();
1072
1073      defaultItem = playlistController.getItem(0);
1074
1075      playlistController.select(defaultItem);
1076
1077      setTitle(defaultItem);
1078
1079      utils.events.add(dom.o, 'mousedown', handleMouseDown);
1080
1081      utils.events.add(dom.o, 'click', handleClick);
1082
1083      utils.events.add(dom.progressTrack, 'mousedown', function(e) {
1084
1085        if (isRightClick(e)) {
1086          return true;
1087        }
1088
1089        utils.css.add(dom.o, 'grabbing');
1090        utils.events.add(document, 'mousemove', handleMouse);
1091        utils.events.add(document, 'mouseup', releaseMouse);
1092
1093        return handleMouse(e);
1094
1095      });
1096
1097    }
1098
1099    // ---
1100
1101    actionData = {
1102
1103      volume: {
1104        x: 0,
1105        y: 0,
1106        width: 0,
1107        height: 0,
1108        backgroundSize: 0
1109      }
1110
1111    };
1112
1113    actions = {
1114
1115      play: function(offsetOrEvent) {
1116
1117        /**
1118         * This is an overloaded function that takes mouse/touch events or offset-based item indices.
1119         * Remember, "auto-play" will not work on mobile devices unless this function is called immediately from a touch or click event.
1120         * If you have the link but not the offset, you can also pass a fake event object with a target of an <a> inside the playlist - e.g. { target: someMP3Link }         
1121         */
1122
1123        var target,
1124            href,
1125            e;
1126
1127        if (offsetOrEvent !== undefined && !isNaN(offsetOrEvent)) {
1128          // smells like a number.
1129          return playlistController.playItemByOffset(offsetOrEvent);
1130        }
1131
1132        // DRY things a bit
1133        e = offsetOrEvent;
1134
1135        if (e && e.target) {
1136
1137          target = e.target || e.srcElement;
1138
1139          href = target.href;
1140
1141        }
1142
1143        // haaaack - if null due to no event, OR '#' due to play/pause link, get first link from playlist
1144        if (!href || href.indexOf('#') !== -1) {
1145          href = dom.playlist.getElementsByTagName('a')[0].href;
1146        }
1147
1148        if (!soundObject) {
1149          soundObject = makeSound(href);
1150        }
1151
1152        // edge case: if the current sound is not playing, stop all others.
1153        if (!soundObject.playState) {
1154          stopOtherSounds();
1155        }
1156
1157        // TODO: if user pauses + unpauses a sound that had an error, try to play next?
1158        soundObject.togglePause();
1159
1160        // special case: clear "play next" timeout, if one exists.
1161        // edge case: user pauses after a song failed to load.
1162        if (soundObject.paused && playlistController.data.timer) {
1163          window.clearTimeout(playlistController.data.timer);
1164          playlistController.data.timer = null;
1165        }
1166
1167      },
1168
1169      pause: function() {
1170
1171        if (soundObject && soundObject.readyState) {
1172          soundObject.pause();
1173        }
1174
1175      },
1176
1177      resume: function() {
1178
1179        if (soundObject && soundObject.readyState) {
1180          soundObject.resume();
1181        }
1182
1183      },
1184
1185      stop: function() {
1186
1187        // just an alias for pause, really.
1188        // don't actually stop because that will mess up some UI state, i.e., dragging the slider.
1189        return actions.pause();
1190
1191      },
1192
1193      next: function(/* e */) {
1194
1195        var item, lastIndex;
1196
1197        // special case: clear "play next" timeout, if one exists.
1198        if (playlistController.data.timer) {
1199          window.clearTimeout(playlistController.data.timer);
1200          playlistController.data.timer = null;
1201        }
1202
1203        lastIndex = playlistController.data.selectedIndex;
1204
1205        item = playlistController.getNext(true);
1206
1207        // don't play the same item again
1208        if (item && playlistController.data.selectedIndex !== lastIndex) {
1209          playLink(item.getElementsByTagName('a')[0]);
1210        }
1211
1212      },
1213
1214      prev: function(/* e */) {
1215
1216        var item, lastIndex;
1217
1218        lastIndex = playlistController.data.selectedIndex;
1219
1220        item = playlistController.getPrevious();
1221
1222        // don't play the same item again
1223        if (item && playlistController.data.selectedIndex !== lastIndex) {
1224          playLink(item.getElementsByTagName('a')[0]);
1225        }
1226
1227      },
1228
1229      shuffle: function(e) {
1230
1231        // NOTE: not implemented yet.
1232
1233        var target = (e ? e.target || e.srcElement : utils.dom.get(dom.o, '.shuffle'));
1234
1235        if (target && !utils.css.has(target, css.disabled)) {
1236          utils.css.toggle(target.parentNode, css.active);
1237          playlistController.data.shuffleMode = !playlistController.data.shuffleMode;
1238        }
1239
1240      },
1241
1242      repeat: function(e) {
1243
1244        var target = (e ? e.target || e.srcElement : utils.dom.get(dom.o, '.repeat'));
1245
1246        if (target && !utils.css.has(target, css.disabled)) {
1247          utils.css.toggle(target.parentNode, css.active);
1248          playlistController.data.loopMode = !playlistController.data.loopMode;
1249        }
1250
1251      },
1252
1253      menu: function(ignoreToggle) {
1254
1255        var isOpen;
1256
1257        isOpen = utils.css.has(dom.o, css.playlistOpen);
1258
1259        // hackish: reset scrollTop in default first open case. odd, but some browsers have a non-zero scroll offset the first time the playlist opens.
1260        if (playlistController && !playlistController.data.selectedIndex && !firstOpen) {
1261          dom.playlist.scrollTop = 0;
1262          firstOpen = true;
1263        }
1264
1265        // sniff out booleans from mouse events, as this is referenced directly by event handlers.
1266        if (typeof ignoreToggle !== 'boolean' || !ignoreToggle) {
1267          
1268		if (!isOpen) {
1269			jQuery(".sm2-bar-ui .sm2-inline-element:first-of-type").css('border-bottom-left-radius', '0px');
1270		 }
1271		if (isOpen) {
1272			jQuery(".sm2-bar-ui .sm2-inline-element:first-of-type").css('border-bottom-left-radius', '6px');
1273		 }
1274          if (!isOpen) {
1275            // explicitly set height:0, so the first closed -> open animation runs properly
1276            dom.playlistContainer.style.height = '0px';
1277          }
1278
1279          isOpen = utils.css.toggle(dom.o, css.playlistOpen);
1280
1281        }
1282
1283        // playlist
1284        dom.playlistContainer.style.height = (isOpen ? dom.playlistContainer.scrollHeight : 0) + 'px';
1285
1286      },
1287
1288      adjustVolume: function(e) {
1289
1290        /**
1291         * NOTE: this is the mousemove() event handler version.
1292         * Use setVolume(50), etc., to assign volume directly.
1293         */
1294
1295        var backgroundMargin,
1296            pixelMargin,
1297            target,
1298            value,
1299            volume;
1300
1301        value = 0;
1302
1303        target = dom.volume;
1304
1305        // safety net
1306        if (e === undefined) {
1307          return false;
1308        }
1309
1310        if (!e || e.clientX === undefined) {
1311          // called directly or with a non-mouseEvent object, etc.
1312          // proxy to the proper method.
1313          if (arguments.length && window.console && window.console.warn) {
1314            console.warn('Bar UI: call setVolume(' + e + ') instead of adjustVolume(' + e + ').');
1315          }
1316          return actions.setVolume.apply(this, arguments);
1317        }
1318
1319        // based on getStyle() result
1320        // figure out spacing around background image based on background size, eg. 60% background size.
1321        // 60% wide means 20% margin on each side.
1322        backgroundMargin = (100 - actionData.volume.backgroundSize) / 2;
1323
1324        // relative position of mouse over element
1325        value = Math.max(0, Math.min(1, (e.clientX - actionData.volume.x) / actionData.volume.width));
1326
1327        target.style.clip = 'rect(0px, ' + (actionData.volume.width * value) + 'px, ' + actionData.volume.height + 'px, ' + (actionData.volume.width * (backgroundMargin/100)) + 'px)';
1328
1329        // determine logical volume, including background margin
1330        pixelMargin = ((backgroundMargin/100) * actionData.volume.width);
1331
1332        volume = Math.max(0, Math.min(1, ((e.clientX - actionData.volume.x) - pixelMargin) / (actionData.volume.width - (pixelMargin*2)))) * 100;
1333
1334        // set volume
1335        if (soundObject) {
1336          soundObject.setVolume(volume);
1337        }
1338
1339        defaultVolume = volume;
1340
1341        return utils.events.preventDefault(e);
1342
1343      },
1344
1345      releaseVolume: function(/* e */) {
1346
1347        utils.events.remove(document, 'mousemove', actions.adjustVolume);
1348        utils.events.remove(document, 'mouseup', actions.releaseVolume);
1349
1350      },
1351
1352      setVolume: function(volume) {
1353
1354        // set volume (0-100) and update volume slider UI.
1355
1356        var backgroundSize,
1357            backgroundMargin,
1358            backgroundOffset,
1359            target,
1360            from,
1361            to;
1362
1363        if (volume === undefined || isNaN(volume)) {
1364          return;
1365        }
1366
1367        if (dom.volume) {
1368
1369          target = dom.volume;
1370
1371          // based on getStyle() result
1372          backgroundSize = actionData.volume.backgroundSize;
1373
1374          // figure out spacing around background image based on background size, eg. 60% background size.
1375          // 60% wide means 20% margin on each side.
1376          backgroundMargin = (100 - backgroundSize) / 2;
1377
1378          // margin as pixel value relative to width
1379          backgroundOffset = actionData.volume.width * (backgroundMargin/100);
1380
1381          from = backgroundOffset;
1382          to = from + ((actionData.volume.width - (backgroundOffset*2)) * (volume/100));
1383
1384          target.style.clip = 'rect(0px, ' + to + 'px, ' + actionData.volume.height + 'px, ' + from + 'px)';
1385
1386        }
1387
1388        // apply volume to sound, as applicable
1389        if (soundObject) {
1390          soundObject.setVolume(volume);
1391        }
1392
1393        defaultVolume = volume;
1394
1395      }
1396
1397    };
1398
1399    init();
1400
1401    // TODO: mixin actions -> exports
1402
1403    exports = {
1404      // Per-instance events: window.sm2BarPlayers[0].on = { ... } etc. See global players.on example above for reference.
1405      on: null,
1406      actions: actions,
1407      dom: dom,
1408      playlistController: playlistController
1409    };
1410    soundObject = makeSound(playlistController.getURL());
1411
1412    return exports;
1413
1414  };
1415
1416  // barebones utilities for logic, CSS, DOM, events etc.
1417
1418  utils = {
1419
1420    array: (function() {
1421
1422      function compare(property) {
1423
1424        var result;
1425
1426        return function(a, b) {
1427
1428          if (a[property] < b[property]) {
1429            result = -1;
1430          } else if (a[property] > b[property]) {
1431            result = 1;
1432          } else {
1433            result = 0;
1434          }
1435          return result;
1436        };
1437
1438      }
1439
1440      function shuffle(array) {
1441
1442        // Fisher-Yates shuffle algo
1443
1444        var i, j, temp;
1445
1446        for (i = array.length - 1; i > 0; i--) {
1447          j = Math.floor(Math.random() * (i+1));
1448          temp = array[i];
1449          array[i] = array[j];
1450          array[j] = temp;
1451        }
1452
1453        return array;
1454
1455      }
1456
1457      return {
1458        compare: compare,
1459        shuffle: shuffle
1460      };
1461
1462    }()),
1463
1464    css: (function() {
1465
1466      function hasClass(o, cStr) {
1467
1468        return (o.className !== undefined ? new RegExp('(^|\\s)' + cStr + '(\\s|$)').test(o.className) : false);
1469
1470      }
1471
1472      function addClass(o, cStr) {
1473
1474        if (!o || !cStr || hasClass(o, cStr)) {
1475          return false; // safety net
1476        }
1477        o.className = (o.className ? o.className + ' ' : '') + cStr;
1478
1479      }
1480
1481      function removeClass(o, cStr) {
1482
1483        if (!o || !cStr || !hasClass(o, cStr)) {
1484          return false;
1485        }
1486        o.className = o.className.replace(new RegExp('( ' + cStr + ')|(' + cStr + ')', 'g'), '');
1487
1488      }
1489
1490      function swapClass(o, cStr1, cStr2) {
1491
1492        var tmpClass = {
1493          className: o.className
1494        };
1495
1496        removeClass(tmpClass, cStr1);
1497        addClass(tmpClass, cStr2);
1498
1499        o.className = tmpClass.className;
1500
1501      }
1502
1503      function toggleClass(o, cStr) {
1504
1505        var found,
1506            method;
1507
1508        found = hasClass(o, cStr);
1509
1510        method = (found ? removeClass : addClass);
1511
1512        method(o, cStr);
1513
1514        // indicate the new state...
1515        return !found;
1516
1517      }
1518
1519      return {
1520        has: hasClass,
1521        add: addClass,
1522        remove: removeClass,
1523        swap: swapClass,
1524        toggle: toggleClass
1525      };
1526
1527    }()),
1528
1529    dom: (function() {
1530
1531      function getAll(param1, param2) {
1532
1533        var node,
1534            selector,
1535            results;
1536
1537        if (arguments.length === 1) {
1538
1539          // .selector case
1540          node = document.documentElement;
1541          // first param is actually the selector
1542          selector = param1;
1543
1544        } else {
1545
1546          // node, .selector
1547          node = param1;
1548          selector = param2;
1549
1550        }
1551
1552        // sorry, IE 7 users; IE 8+ required.
1553        if (node && node.querySelectorAll) {
1554
1555          results = node.querySelectorAll(selector);
1556
1557        }
1558
1559        return results;
1560
1561      }
1562
1563      function get(/* parentNode, selector */) {
1564
1565        var results = getAll.apply(this, arguments);
1566
1567        // hackish: if an array, return the last item.
1568        if (results && results.length) {
1569          return results[results.length-1];
1570        }
1571
1572        // handle "not found" case
1573        return results && results.length === 0 ? null : results;
1574
1575      }
1576
1577      return {
1578        get: get,
1579        getAll: getAll
1580      };
1581
1582    }()),
1583
1584    position: (function() {
1585
1586      function getOffX(o) {
1587
1588        // http://www.xs4all.nl/~ppk/js/findpos.html
1589        var curleft = 0;
1590
1591        if (o.offsetParent) {
1592
1593          while (o.offsetParent) {
1594
1595            curleft += o.offsetLeft;
1596
1597            o = o.offsetParent;
1598
1599          }
1600
1601        } else if (o.x) {
1602
1603            curleft += o.x;
1604
1605        }
1606
1607        return curleft;
1608
1609      }
1610
1611      function getOffY(o) {
1612
1613        // http://www.xs4all.nl/~ppk/js/findpos.html
1614        var curtop = 0;
1615
1616        if (o.offsetParent) {
1617
1618          while (o.offsetParent) {
1619
1620            curtop += o.offsetTop;
1621
1622            o = o.offsetParent;
1623
1624          }
1625
1626        } else if (o.y) {
1627
1628            curtop += o.y;
1629
1630        }
1631
1632        return curtop;
1633
1634      }
1635
1636      return {
1637        getOffX: getOffX,
1638        getOffY: getOffY
1639      };
1640
1641    }()),
1642
1643    style: (function() {
1644
1645      function get(node, styleProp) {
1646
1647        // http://www.quirksmode.org/dom/getstyles.html
1648        var value;
1649
1650        if (node.currentStyle) {
1651
1652          value = node.currentStyle[styleProp];
1653
1654        } else if (window.getComputedStyle) {
1655
1656          value = document.defaultView.getComputedStyle(node, null).getPropertyValue(styleProp);
1657
1658        }
1659
1660        return value;
1661
1662      }
1663
1664      return {
1665        get: get
1666      };
1667
1668    }()),
1669
1670    events: (function() {
1671
1672      var add, remove, preventDefault;
1673
1674      add = function(o, evtName, evtHandler) {
1675        // return an object with a convenient detach method.
1676        var eventObject = {
1677          detach: function() {
1678            return remove(o, evtName, evtHandler);
1679          }
1680        };
1681        if (window.addEventListener) {
1682          o.addEventListener(evtName, evtHandler, false);
1683        } else {
1684          o.attachEvent('on' + evtName, evtHandler);
1685        }
1686        return eventObject;
1687      };
1688
1689      remove = (window.removeEventListener !== undefined ? function(o, evtName, evtHandler) {
1690        return o.removeEventListener(evtName, evtHandler, false);
1691      } : function(o, evtName, evtHandler) {
1692        return o.detachEvent('on' + evtName, evtHandler);
1693      });
1694
1695      preventDefault = function(e) {
1696        if (e.preventDefault) {
1697          e.preventDefault();
1698        } else {
1699          e.returnValue = false;
1700          e.cancelBubble = true;
1701        }
1702        return false;
1703      };
1704
1705      return {
1706        add: add,
1707        preventDefault: preventDefault,
1708        remove: remove
1709      };
1710
1711    }()),
1712
1713    features: (function() {
1714
1715      var getAnimationFrame,
1716          localAnimationFrame,
1717            localFeatures,
1718            prop,
1719            styles,
1720          testDiv,
1721          transform;
1722
1723        testDiv = document.createElement('div');
1724
1725      /**
1726       * hat tip: paul irish
1727       * http://paulirish.com/2011/requestanimationframe-for-smart-animating/
1728       * https://gist.github.com/838785
1729       */
1730
1731      localAnimationFrame = (window.requestAnimationFrame
1732        || window.webkitRequestAnimationFrame
1733        || window.mozRequestAnimationFrame
1734        || window.oRequestAnimationFrame
1735        || window.msRequestAnimationFrame
1736        || null);
1737
1738      // apply to window, avoid "illegal invocation" errors in Chrome
1739      getAnimationFrame = localAnimationFrame ? function() {
1740        return localAnimationFrame.apply(window, arguments);
1741      } : null;
1742
1743      function has(prop) {
1744
1745        // test for feature support
1746        var result = testDiv.style[prop];
1747
1748        return (result !== undefined ? prop : null);
1749
1750      }
1751
1752      // note local scope.
1753      localFeatures = {
1754
1755        transform: {
1756          ie: has('-ms-transform'),
1757          moz: has('MozTransform'),
1758          opera: has('OTransform'),
1759          webkit: has('webkitTransform'),
1760          w3: has('transform'),
1761          prop: null // the normalized property value
1762        },
1763
1764        rotate: {
1765          has3D: false,
1766          prop: null
1767        },
1768
1769        getAnimationFrame: getAnimationFrame
1770
1771      };
1772
1773      localFeatures.transform.prop = (
1774        localFeatures.transform.w3 ||
1775        localFeatures.transform.moz ||
1776        localFeatures.transform.webkit ||
1777        localFeatures.transform.ie ||
1778        localFeatures.transform.opera
1779      );
1780
1781      function attempt(style) {
1782
1783        try {
1784          testDiv.style[transform] = style;
1785        } catch(e) {
1786          // that *definitely* didn't work.
1787          return false;
1788        }
1789        // if we can read back the style, it should be cool.
1790        return !!testDiv.style[transform];
1791
1792      }
1793
1794      if (localFeatures.transform.prop) {
1795
1796        // try to derive the rotate/3D support.
1797        transform = localFeatures.transform.prop;
1798        styles = {
1799          css_2d: 'rotate(0deg)',
1800          css_3d: 'rotate3d(0,0,0,0deg)'
1801        };
1802
1803        if (attempt(styles.css_3d)) {
1804          localFeatures.rotate.has3D = true;
1805          prop = 'rotate3d';
1806        } else if (attempt(styles.css_2d)) {
1807          prop = 'rotate';
1808        }
1809
1810        localFeatures.rotate.prop = prop;
1811
1812      }
1813
1814      testDiv = null;
1815
1816      return localFeatures;
1817
1818    }())
1819
1820  };
1821
1822  // ---
1823
1824  // expose to global
1825  window.sm2BarPlayers = players;
1826  window.sm2BarPlayerOptions = playerOptions;
1827  window.SM2BarPlayer = Player;
1828//if (playervars.autoplay == 1){
1829//		
1830//}
1831}(window));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.