PageSourceSearch

https://www.amionline.org/Scripts/audiojs/audio.min.js

js amionline.org collected 2026-10-05 02:51:56 UTC 37,307 bytes, 741 lines download raw bytes

1// A cross-browser javascript shim for html5 audio
2(function (audiojs, audiojsInstance, container) {
3    // Use the path to the audio.js file to create relative paths to the swf and player graphics
4    // Remember that some systems (e.g. ruby on rails) append strings like '?1301478336' to asset paths
5    var path = (function () {
6        var re = new RegExp('audio(\.min)?\.js.*'),
7            scripts = document.getElementsByTagName('script');
8        for (var i = 0, ii = scripts.length; i < ii; i++) {
9            var path = scripts[i].getAttribute('src');
10            if (re.test(path)) return path.replace(re, '');
11        }
12    })();
13
14    // ##The audiojs interface
15    // This is the global object which provides an interface for creating new `audiojs` instances.
16    // It also stores all of the construction helper methods and variables.
17    container[audiojs] = {
18        instanceCount: 0,
19        instances: {},
20        // The markup for the swf. It is injected into the page if there is not support for the `<audio>` element. The `$n`s are placeholders.  
21        // `$1` The name of the flash movie  
22        // `$2` The path to the swf  
23        // `$3` Cache invalidation  
24        flashSource: '\
25      <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" id="$1" width="1" height="1" name="$1" style="position: absolute; left: -1px;"> \
26        <param name="movie" value="$2?playerInstance='+ audiojs + '.instances[\'$1\']&datetime=$3"> \
27        <param name="allowscriptaccess" value="always"> \
28        <embed name="$1" src="$2?playerInstance='+ audiojs + '.instances[\'$1\']&datetime=$3" width="1" height="1" allowscriptaccess="always"> \
29      </object>',
30
31        // ### The main settings object
32        // Where all the default settings are stored. Each of these variables and methods can be overwritten by the user-provided `options` object.
33        settings: {
34            autoplay: false,
35            loop: false,
36            preload: true,
37            imageLocation: path + 'player-graphics.gif',
38            swfLocation: path + 'audiojs.swf',
39            useFlash: (function () {
40                var a = document.createElement('audio');
41                return !(a.canPlayType && a.canPlayType('audio/mpeg;').replace(/no/, ''));
42            })(),
43            hasFlash: (function () {
44                if (navigator.plugins && navigator.plugins.length && navigator.plugins['Shockwave Flash']) {
45                    return true;
46                } else if (navigator.mimeTypes && navigator.mimeTypes.length) {
47                    var mimeType = navigator.mimeTypes['application/x-shockwave-flash'];
48                    return mimeType && mimeType.enabledPlugin;
49                } else {
50                    try {
51                        var ax = new ActiveXObject('ShockwaveFlash.ShockwaveFlash');
52                        return true;
53                    } catch (e) { }
54                }
55                return false;
56            })(),
57            // The default markup and classes for creating the player:
58            createPlayer: {
59                markup: '\
60          <div class="play-pause"> \
61            <p class="play"></p> \
62            <p class="pause"></p> \
63            <p class="loading"></p> \
64            <p class="error"></p> \
65          </div> \
66          <div class="scrubber"> \
67            <div class="progress"></div> \
68            <div class="loaded"></div> \
69          </div> \
70          <div class="time"> \
71            <em class="played">00:00</em>/<strong class="duration">00:00</strong> \
72          </div> \
73          <div class="error-message"></div>',
74                playPauseClass: 'play-pause',
75                scrubberClass: 'scrubber',
76                progressClass: 'progress',
77                loaderClass: 'loaded',
78                timeClass: 'time',
79                durationClass: 'duration',
80                playedClass: 'played',
81                errorMessageClass: 'error-message',
82                playingClass: 'playing',
83                loadingClass: 'loading',
84                errorClass: 'error'
85            },
86            // The css used by the default player. This is is dynamically injected into a `<style>` tag in the top of the head.
87            css: '\
88        .audiojs audio { position: absolute; left: -1px; } \
89        .audiojs { width: 475px; height: 80px; background: #7e776d; overflow: hidden; font-family: monospace; font-size: 12px; } \
90        .audiojs .play-pause {  padding: 22.5px 35px; margin: 0px; float: left; overflow: hidden; background: #534d45; cursor: pointer; } \
91        .audiojs p { display: none; width: 25px; height: 35px; margin: 0px; cursor: pointer; } \
92        .audiojs .play { display: block; } \
93        .audiojs .scrubber { position: relative; float: left; width: 75%; background: #b5b1ac; height: 14px; margin: 34px 30px; border-left: 0px; border-bottom: 0px; overflow: hidden; } \
94        .audiojs .progress { position: absolute; top: 0px; left: 0px; height: 14px; width: 0px; background: #fff; z-index: 1; } \
95        .audiojs .loaded { position: absolute; top: 0px; left: 0px; height: 14px; width: 0px; background: #a59e96;} \
96        .audiojs .time { float: left; height: 36px; line-height: 36px; margin: 33px 0; padding: 0px 6px 0px 0; color: #ddd; font: 400 12px/1.4em "Roboto Regular", Verdana, Sans-serif; } \
97        .audiojs .time em { padding: 0px 2px 0px 0px; color: #e2d9cc; font-style: normal; } \
98        .audiojs .time strong { padding: 0px 0px 0px 2px; font-weight: normal;
98 color:#fff; } \
99        .audiojs .error-message { float: left; display: none; margin: 0px 10px; height: 36px; width: 400px; overflow: hidden; line-height: 36px; white-space: nowrap; color: #fff; \
100        text-overflow: ellipsis; -o-text-overflow: ellipsis; -icab-text-overflow: ellipsis; -khtml-text-overflow: ellipsis; -moz-text-overflow: ellipsis; -webkit-text-overflow: ellipsis; } \
101        .audiojs .error-message a { color: #eee; text-decoration: none; padding-bottom: 1px; border-bottom: 1px solid #999; white-space: wrap; } \
102        \
103        .audiojs .play { background: url("$1") -2px 3px no-repeat; } \
104        .audiojs .loading { background: url("$1") -2px -31px no-repeat; } \
105        .audiojs .error { background: url("$1") -2px -61px no-repeat; } \
106        .audiojs .pause { background: url("$1") -2px -87px no-repeat; } \
107        \
108        .playing .play, .playing .loading, .playing .error { display: none; } \
109        .playing .pause { display: block; } \
110        \
111        .loading .play, .loading .pause, .loading .error { display: none; } \
112        .loading .loading { display: block; } \
113        \
114        .error .time, .error .play, .error .pause, .error .scrubber, .error .loading { display: none; } \
115        .error .error { display: block; } \
116        .error .play-pause p { cursor: auto; } \
117        .error .error-message { display: block; }',
118            // The default event callbacks:
119            trackEnded: function (e) { },
120            flashError: function () {
121                var player = this.settings.createPlayer,
122                    errorMessage = getByClass(player.errorMessageClass, this.wrapper),
123                    html = 'Missing <a href="http://get.adobe.com/flashplayer/">flash player</a> plugin.';
124                if (this.mp3) html += ' <a href="' + this.mp3 + '">Download audio file</a>.';
125                container[audiojs].helpers.removeClass(this.wrapper, player.loadingClass);
126                container[audiojs].helpers.addClass(this.wrapper, player.errorClass);
127                errorMessage.innerHTML = html;
128            },
129            loadError: function (e) {
130                var player = this.settings.createPlayer,
131                    errorMessage = getByClass(player.errorMessageClass, this.wrapper);
132                container[audiojs].helpers.removeClass(this.wrapper, player.loadingClass);
133                container[audiojs].helpers.addClass(this.wrapper, player.errorClass);
134                errorMessage.innerHTML = 'Error loading: "' + this.mp3 + '"';
135            },
136            init: function () {
137                var player = this.settings.createPlayer;
138                container[audiojs].helpers.addClass(this.wrapper, player.loadingClass);
139            },
140            loadStarted: function () {
141                var player = this.settings.createPlayer,
142                    duration = getByClass(player.durationClass, this.wrapper),
143                    m = Math.floor(this.duration / 60),
144                    s = Math.floor(this.duration % 60);
145                container[audiojs].helpers.removeClass(this.wrapper, player.loadingClass);
146                duration.innerHTML = ((m < 10 ? '0' : '') + m + ':' + (s < 10 ? '0' : '') + s);
147            },
148            loadProgress: function (percent) {
149                var player = this.settings.createPlayer,
150                    scrubber = getByClass(player.scrubberClass, this.wrapper),
151                    loaded = getByClass(player.loaderClass, this.wrapper);
152                loaded.style.width = (scrubber.offsetWidth * percent) + 'px';
153            },
154            playPause: function () {
155                if (this.playing) this.settings.play();
156                else this.settings.pause();
157            },
158            play: function () {
159                var player = this.settings.createPlayer;
160                container[audiojs].helpers.addClass(this.wrapper, player.playingClass);
161            },
162            pause: function () {
163                var player = this.settings.createPlayer;
164                container[audiojs].helpers.removeClass(this.wrapper, player.playingClass);
165            },
166            updatePlayhead: function (percent) {
167                var player = this.settings.createPlayer,
168                    scrubber = getByClass(player.scrubberClass, this.wrapper),
169                    progress = getByClass(player.progressClass, this.wrapper);
170                progress.style.width = (scrubber.offsetWidth * percent) + 'px';
171
172                var played = getByClass(player.playedClass, this.wrapper),
173                    p = this.duration * percent,
174                    m = Math.floor(p / 60),
175                    s = Math.floor(p % 60);
176                played.innerHTML = ((m < 10 ? '0' : '') + m + ':' + (s < 10 ? '0' : '') + s);
177            }
178        },
179
180        // ### Contructor functions
181
182        // `create()`  
183        // Used to create a single `audiojs` instance.  
184        // If an array is passed then it calls back to `createAll()`.  
185        // Otherwise, it creates a single instance and returns it.  
186        create: function (element, options) {
187            var options = options || {}
188            if (element.length) {
189                return this.createAll(options, element);
190            } else {
191                return this.newInstance(element, options);
192            }
193        },
194
195        // `createAll()`  
196        // Creates multiple `audiojs` instances.  
197        // If `elements` is `null`, then automatically find any `<audio>` tags on the page and create `audiojs` instances for them.
198        createAll: function (options, elements) {
199            var audioElements = elements || document.getElementsByTagName('audio'),
200                instances = []
201            options = options || {};
202            for (var i = 0, ii = audioElements.length; i < ii; i++) {
203                instances.push(this.newInstance(audioElements[i], options));
204            }
205            return instances;
206        },
207
208        // ### Creating and returning a new instance
209        // This goes through all the steps required to build out a usable `audiojs` instance.
210        newInstance: function (element, options) {
211            var element = element,
212                s = this.helpers.clone(this.settings),
213                id = 'audiojs' + this.instanceCount,
214                wrapperId = 'audiojs_wrapper' + this.instanceCount,
215                instanceCount = this.instanceCount++;
216
217            // Check for `autoplay`, `loop` and `preload` attributes and write them into the settings.
218            if (element.getAttribute('autoplay') != null) s.autoplay = true;
219            if (element.getAttribute('loop') != null) s.loop = true;
220            if (element.getAttribute('preload') == 'none') s.preload = false;
221            // Merge the default settings with the user-defined `options`.
222            if (options) this.helpers.merge(s, options);
223
224            // Inject the player html if required.
225            if (s.createPlayer.markup) element = this.createPlayer(element, s.createPlayer, wrapperId);
226            else element.parentNode.setAttribute('id', wrapperId);
227
228            // Return a new `audiojs` instance.
229            var audio = new container[audiojsInstance](element, s);
230
231            // If css has been passed in, dynamically inject it into the `<head>`.
232            if (s.css) this.helpers.injectCss(audio, s.css);
233
234            // If `<audio>` or mp3 playback isn't supported, insert the swf & attach the required events for it.
235            if (s.useFlash && s.hasFlash) {
236                this.injectFlash(audio, id);
237                this.attachFlashEvents(audio.wrapper, audio);
238            } else if (s.useFlash && !s.hasFlash) {
239                this.settings.flashError.apply(audio);
240            }
241
242            // Attach event callbacks to the new audiojs instance.
243            if (!s.useFlash || (s.useFlash && s.hasFlash)) this.attachEvents(audio.wrapper, audio);
244
245            // Store the newly-created `audiojs` instance.
246            this.instances[id] = audio;
247            return audio;
248        },
249
250        // ### Helper methods for constructing a working player
251        // Inject a wrapping div and the markup for the html player.
252        createPlayer: function (element, player, id) {
253            var wrapper = document.createElement('div'),
254                newElement = element.cloneNode(true);
255            wrapper.setAttribute('class', 'audiojs');
256            wrapper.setAttribute('className', 'audiojs');
257            wrapper.setAttribute('id', id);
258
259            // Fix IE's broken implementation of `innerHTML` & `cloneNode` for HTML5 elements.
260            if (newElement.outerHTML && !document.createElement('audio').canPlayType) {
261                newElement = this.helpers.cloneHtml5Node(element);
262                wrapper.innerHTML = player.markup;
263                wrapper.appendChild(newElement);
264                element.outerHTML = wrapper.outerHTML;
265                wrapper = document.getElementById(id);
266            } else {
267                wrapper.appendChild(newElement);
268                wrapper.innerHTML = wrapper.innerHTML + player.markup;
269                element.parentNode.replaceChild(wrapper, element);
270            }
271            return wrapper.getElementsByTagName('audio')[0];
272        },
273
274        // Attaches useful event callbacks to an `audiojs` instance.
275        attachEvents: function (wrapper, audio) {
276            if (!audio.settings.createPlayer) return;
277            var player = audio.settings.createPlayer,
278                playPause = getByClass(player.playPauseClass, wrapper),
279                scrubber = getByClass(player.scrubberClass, wrapper),
280                leftPos = function (elem) {
281                    var curleft = 0;
282                    if (elem.offsetParent) {
283                        do { curleft += elem.offsetLeft; } while (elem = elem.offsetParent);
284                    }
285                    return curleft;
286                };
287
288            container[audiojs].events.addListener(playPause, 'click', function (e) {
289                audio.playPause.apply(audio);
290            });
291
292            container[audiojs].events.addListener(scrubber, 'click', function (e) {
293                var relativeLeft = e.clientX - leftPos(this);
294                audio.skipTo(relativeLeft / scrubber.offsetWidth);
295            });
296
297            // _If flash is being used, then the following handlers don't need to be registered._
298            if (audio.settings.useFlash) return;
299
300            // Start tracking the load progress of the track.
301            container[audiojs].events.trackLoadProgress(audio);
302
303            container[audiojs].events.addListener(audio.element, 'timeupdate', function (e) {
304                audio.updatePlayhead.apply(audio);
305            });
306
307            container[audiojs].events.addListener(audio.element, 'ended', function (e) {
308                audio.trackEnded.apply(audio);
309            });
310
311            container[audiojs].events.addListener(audio.source, 'error', function (e) {
312                // on error, cancel any load timers that are running.
313                clearInterval(audio.readyTimer);
314                clearInterval(audio.loadTimer);
315                audio.settings.loadError.apply(audio);
316            });
317
318        },
319
320        // Flash requires a slightly different API to the `<audio>` element, so this method is used to overwrite the standard event handlers.
321        attachFlashEvents: function (element, audio) {
322            audio['swfReady'] = false;
323            audio['load'] = function (mp3) {
324                // If the swf isn't ready yet then just set `audio.mp3`. `init()` will load it in once the swf is ready.
325                audio.mp3 = mp3;
326                if (audio.swfReady) audio.element.load(mp3);
327            }
328            audio['loadProgress'] = function (percent, duration) {
329                audio.loadedPercent = percent;
330                audio.duration = duration;
331                audio.settings.loadStarted.apply(audio);
332                audio.settings.loadProgress.apply(audio, [percent]);
333            }
334            audio['skipTo'] = function (percent) {
335                if (percent > audio.loadedPercent) return;
336                audio.updatePlayhead.call(audio, [percent])
337                audio.element.skipTo(percent);
338            }
339            audio['updatePlayhead'] = function (percent) {
340                audio.settings.updatePlayhead.apply(audio, [percent]);
341            }
342            audio['play'] = function () {
343                // If the audio hasn't started preloading, then start it now.  
344                // Then set `preload` to `true`, so that any tracks loaded in subsequently are loaded straight away.
345                if (!audio.settings.preload) {
346                    audio.settings.preload = true;
347                    audio.element.init(audio.mp3);
348                }
349                audio.playing = true;
350                // IE doesn't allow a method named `play()` to be exposed through `ExternalInterface`, so lets go with `pplay()`.  
351                // <http://dev.nuclearrooster.com/2008/07/27/externalinterfaceaddcallback-can-cause-ie-js-errors-with-certain-keyworkds/>
352                audio.element.pplay();
353                audio.settings.play.apply(audio);
354            }
355            audio['pause'] = function () {
356                audio.playing = false;
357                // Use `ppause()` for consistency with `pplay()`, even though it isn't really required.
358                audio.element.ppause();
359                audio.settings.pause.apply(audio);
360            }
361            audio['setVolume'] = function (v) {
362                audio.element.setVolume(v);
363            }
364            audio['loadStarted'] = function () {
365                // Load the mp3 specified by the audio element into the swf.
366                audio.swfReady = true;
367                if (audio.settings.preload) audio.element.init(audio.mp3);
368                if (audio.settings.autoplay) audio.play.apply(audio);
369            }
370        },
371
372        // ### Injecting an swf from a string
373        // Build up the swf source by replacing the `$keys` and then inject the markup into the page.
374        injectFlash: function (audio, id) {
375            var flashSource = this.flashSource.replace(/\$1/g, id);
376            flashSource = flashSource.replace(/\$2/g, audio.settings.swfLocation);
377            // `(+new Date)` ensures the swf is not pulled out of cache. The fixes an issue with Firefox running multiple players on the same page.
378            flashSource = flashSource.replace(/\$3/g, (+new Date + Math.random()));
379            // Inject the player markup using a more verbose `innerHTML` insertion technique that works with IE.
380            var html = audio.wrapper.innerHTML,
381                div = document.createElement('div');
382            div.innerHTML = flashSource + html;
383            audio.wrapper.innerHTML = div.innerHTML;
384            audio.element = this.helpers.getSwf(id);
385        },
386
387        // ## Helper functions
388        helpers: {
389            // **Merge two objects, with `obj2` overwriting `obj1`**  
390            // The merge is shallow, but that's all that is required for our purposes.
391            merge: function (obj1, obj2) {
392                for (attr in obj2) {
393                    if (obj1.hasOwnProperty(attr) || obj2.hasOwnProperty(attr)) {
394                        obj1[attr] = obj2[attr];
395                    }
396                }
397            },
398            // **Clone a javascript object (recursively)**
399            clone: function (obj) {
400                if (obj == null || typeof (obj) !== 'object') return obj;
401                var temp = new obj.constructor();
402                for (var key in obj) temp[key] = arguments.callee(obj[key]);
403                return temp;
404            },
405            // **Adding/removing classnames from elements**
406            addClass: function (element, className) {
407                var re = new RegExp('(\\s|^)' + className + '(\\s|$)');
408                if (re.test(element.className)) return;
409                element.className += ' ' + className;
410            },
411            removeClass: function (element, className) {
412                var re = new RegExp('(\\s|^)' + className + '(\\s|$)');
413                element.className = element.className.replace(re, ' ');
414            },
415            // **Dynamic CSS injection**  
416            // Takes a string of css, inserts it into a `<style>`, then injects it in at the very top of the `<head>`. This ensures any user-defined styles will take precedence.
417            injectCss: function (audio, string) {
418
419                // If an `audiojs` `<style>` tag already exists, then append to it rather than creating a whole new `<style>`.
420                var prepend = '',
421                    styles = document.getElementsByTagName('style'),
422                    css = string.replace(/\$1/g, audio.settings.imageLocation);
423
424                for (var i = 0, ii = styles.length; i < ii; i++) {
425                    var title = styles[i].getAttribute('title');
426                    if (title && ~title.indexOf('audiojs')) {
427                        style = styles[i];
428                        if (style.innerHTML === css) return;
429                        prepend = style.innerHTML;
430                        break;
431                    }
432                };
433
434                var head = document.getElementsByTagName('head')[0],
435                    firstchild = head.firstChild,
436                    style = document.createElement('style');
437
438                if (!head) return;
439
440                style.setAttribute('type', 'text/css');
441                style.setAttribute('title', 'audiojs');
442
443                if (style.styleSheet) style.styleSheet.cssText = prepend + css;
444                else style.appendChild(document.createTextNode(prepend + css));
445
446                if (firstchild) head.insertBefore(style, firstchild);
447                else head.appendChild(styleElement);
448            },
449            // **Handle all the IE6+7 requirements for cloning `<audio>` nodes**  
450            // Create a html5-safe document fragment by injecting an `<audio>` element into the document fragment.
451            cloneHtml5Node: function (audioTag) {
452                var fragment = document.createDocumentFragment(),
453                    doc = fragment.createElement ? fragment : document;
454                doc.createElement('audio');
455                var div = doc.createElement('div');
456                fragment.appendChild(div);
457                div.innerHTML = audioTag.outerHTML;
458                return div.firstChild;
459            },
460            // **Cross-browser `<object>` / `<embed>` element selection**
461            getSwf: function (name) {
462                var swf = document[name] || window[name];
463                return swf.length > 1 ? swf[swf.length - 1] : swf;
464            }
465        },
466        // ## Event-handling
467        events: {
468            memoryLeaking: false,
469            listeners: [],
470            // **A simple cross-browser event handler abstraction**
471            addListener: function (element, eventName, func) {
472                // For modern browsers use the standard DOM-compliant `addEventListener`.
473                if (element.addEventListener) {
474                    element.addEventListener(eventName, func, false);
475                    // For older versions of Internet Explorer, use `attachEvent`.  
476                    // Also provide a fix for scoping `this` to the calling element and register each listener so the containing elements can be purged on page unload.
477                } else if (element.attachEvent) {
478                    this.listeners.push(element);
479                    if (!this.memoryLeaking) {
480                        window.attachEvent('onunload', function () {
481                            if (this.listeners) {
482                                for (var i = 0, ii = this.listeners.length; i < ii; i++) {
483                                    container[audiojs].events.purge(this.listeners[i]);
484                                }
485                            }
486                        });
487                        this.memoryLeaking = true;
488                    }
489                    element.attachEvent('on' + eventName, function () {
490                        func.call(element, window.event);
491                    });
492                }
493            },
494
495            trackLoadProgress: function (audio) {
496                // If `preload` has been set to `none`, then we don't want to start loading the track yet.
497                if (!audio.settings.preload) return;
498
499                var readyTimer,
500                    loadTimer,
501                    audio = audio,
502                    ios = (/(ipod|iphone|ipad)/i).test(navigator.userAgent);
503
504                // Use timers here rather than the official `progress` event, as Chrome has issues calling `progress` when loading mp3 files from cache.
505                readyTimer = setInterval(function () {
506                    if (audio.element.readyState > -1) {
507                        // iOS doesn't start preloading the mp3 until the user interacts manually, so this stops the loader being displayed prematurely.
508                        if (!ios) audio.init.apply(audio);
509                    }
510                    if (audio.element.readyState > 1) {
511                        if (audio.settings.autoplay) audio.play.apply(audio);
512                        clearInterval(readyTimer);
513                        // Once we have data, start tracking the load progress.
514                        loadTimer = setInterval(function () {
515                            audio.loadProgress.apply(audio);
516                            if (audio.loadedPercent >= 1) clearInterval(loadTimer);
517                        });
518                    }
519                }, 10);
520                audio.readyTimer = readyTimer;
521                audio.loadTimer = loadTimer;
522            },
523
524            // **Douglas Crockford's IE6 memory leak fix**  
525            // <http://javascript.crockford.com/memory/leak.html>  
526            // This is used to release the memory leak created by the circular references created when fixing `this` scoping for IE. It is called on page unload.
527            purge: function (d) {
528                var a = d.attributes, i;
529                if (a) {
530                    for (i = 0; i < a.length; i += 1) {
531                        if (typeof d[a[i].name] === 'function') d[a[i].name] = null;
532                    }
533                }
534                a = d.childNodes;
535                if (a) {
536                    for (i = 0; i < a.length; i += 1) purge(d.childNodes[i]);
537                }
538            },
539
540            // **DOMready function**  
541            // As seen here: <https://github.com/dperini/ContentLoaded/>.
542            ready: (function () {
543                return function (fn) {
544                    var win = window, done = false, top = true,
545                    doc = win.document, root = doc.documentElement,
546                    add = doc.addEventListener ? 'addEventListener' : 'attachEvent',
547                    rem = doc.addEventListener ? 'removeEventListener' : 'detachEvent',
548                    pre = doc.addEventListener ? '' : 'on',
549                    init = function (e) {
550                        if (e.type == 'readystatechange' && doc.readyState != 'complete') return;
551                        (e.type == 'load' ? win : doc)[rem](pre + e.type, init, false);
552                        if (!done && (done = true)) fn.call(win, e.type || e);
553                    },
554                    poll = function () {
555                        try { root.doScroll('left'); } catch (e) { setTimeout(poll, 50); return; }
556                        init('poll');
557                    };
558                    if (doc.readyState == 'complete') fn.call(win, 'lazy');
559                    else {
560                        if (doc.createEventObject && root.doScroll) {
561                            try { top = !win.frameElement; } catch (e) { }
562                            if (top) poll();
563                        }
564                        doc[add](pre + 'DOMContentLoaded', init, false);
565                        doc[add](pre + 'readystatechange', init, false);
566                        win[add](pre + 'load', init, false);
567                    }
568                }
569            })()
570
571        }
572    }
573
574    // ## The audiojs class
575    // We create one of these per `<audio>` and then push them into `audiojs['instances']`.
576    container[audiojsInstance] = function (element, settings) {
577        // Each audio instance returns an object which contains an API back into the `<audio>` element.
578        this.element = element;
579        this.wrapper = element.parentNode;
580        this.source = element.getElementsByTagName('source')[0] || element;
581        // First check the `<audio>` element directly for a src and if one is not found, look for a `<source>` element.
582        this.mp3 = (function (element) {
583            var source = element.getElementsByTagName('source')[0];
584            return element.getAttribute('src') || (source ? source.getAttribute('src') : null);
585        })(element);
586        this.settings = settings;
587        this.loadStartedCalled = false;
588        this.loadedPercent = 0;
589        this.duration = 1;
590        this.playing = false;
591    }
592
593    container[audiojsInstance].prototype = {
594        // API access events:
595        // Each of these do what they need do and then call the matching methods defined in the settings object.
596        updatePlayhead: function () {
597            var percent = this.element.currentTime / this.duration;
598            this.settings.updatePlayhead.apply(this, [percent]);
599        },
600        skipTo: function (percent) {
601            if (percent > this.loadedPercent) return;
602            this.element.currentTime = this.duration * percent;
603            this.updatePlayhead();
604        },
605        load: function (mp3) {
606            this.loadStartedCalled = false;
607            this.source.setAttribute('src', mp3);
608            // The now outdated `load()` method is required for Safari 4
609            this.element.load();
610            this.mp3 = mp3;
611            container[audiojs].events.trackLoadProgress(this);
612        },
613        loadError: function () {
614            this.settings.loadError.apply(this);
615        },
616        init: function () {
617            this.settings.init.apply(this);
618        },
619        loadStarted: function () {
620            // Wait until `element.duration` exists before setting up the audio player.
621            if (!this.element.duration) return false;
622
623            this.duration = this.element.duration;
624            this.updatePlayhead();
625            this.settings.loadStarted.apply(this);
626        },
627        loadProgress: function () {
628            if (this.element.buffered != null && this.element.buffered.length) {
629                // Ensure `loadStarted()` is only called once.
630                if (!this.loadStartedCalled) {
631                    this.loadStartedCalled = this.loadStarted();
632                }
633                var durationLoaded = this.element.buffered.end(this.element.buffered.length - 1);
634                this.loadedPercent = durationLoaded / this.duration;
635
636                this.settings.loadProgress.apply(this, [this.loadedPercent]);
637            }
638        },
639        playPause: function () {
640            if (this.playing) this.pause();
641            else this.play();
642        },
643        play: function () {
644            var ios = (/(ipod|iphone|ipad)/i).test(navigator.userAgent);
645            // On iOS this interaction will trigger loading the mp3, so run `init()`.
646            if (ios && this.element.readyState == 0) this.init.apply(this);
647            // If the audio hasn't started preloading, then start it now.  
648            // Then set `preload` to `true`, so that any tracks loaded in subsequently are loaded straight away.
649            if (!this.settings.preload) {
650                this.settings.preload = true;
651                this.element.setAttribute('preload', 'auto');
652                container[audiojs].events.trackLoadProgress(this);
653            }
654            this.playing = true;
655            this.element.play();
656            this.settings.play.apply(this);
657        },
658        pause: function () {
659            this.playing = false;
660            this.element.pause();
661            this.settings.pause.apply(this);
662        },
663        setVolume: function (v) {
664            this.element.volume = v;
665        },
666        trackEnded: function (e) {
667            this.skipTo.apply(this, [0]);
668            if (!this.settings.loop) this.pause.apply(this);
669            this.settings.trackEnded.apply(this);
670        }
671    }
672
673    // **getElementsByClassName**  
674    // Having to rely on `getElementsByTagName` is pretty inflexible internally, so a modified version of Dustin Diaz's `getElementsByClassName` has been included.
675    // This version cleans things up and prefers the native DOM method if it's available.
676    var getByClass = function (searchClass, node) {
677        var matches = [];
678        node = node || document;
679
680        if (node.getElementsByClassName) {
681            matches = node.getElementsByClassName(searchClass);
682        } else {
683            var i, l,
684                els = node.getElementsByTagName("*"),
685                pattern = new RegExp("(^|\\s)" + searchClass + "(\\s|$)");
686
687            for (i = 0, l = els.length; i < l; i++) {
688                if (pattern.test(els[i].className)) {
689                    matches.push(els[i]);
690                }
691            }
692        }
693        return matches.length > 1 ? matches : matches[0];
694    };
695    // The global variable names are passed in here and can be changed if they conflict with anything else.
696})('audiojs', 'audiojsInstance', this);
697
698$(function () {
699    // Setup the player to autoplay the next track
700    var a = audiojs.createAll({
701        trackEnded: function () {
702            var next = $('ol li.playing').next();
703            if (!next.length) next = $('ol li').first();
704            next.addClass('playing').siblings().removeClass('playing');
705            audio.load($('a', next).attr('data-src'));
706            audio.play();
707        }
708    });
709
710    // Load in the first track
711    var audio = a[0];
712    first = $('ol a').attr('data-src');
713    $('ol li').first().addClass('playing');
714    audio.load(first);
715
716    // Load in a track on click
717    $('ol li').click(function (e) {
718        e.preventDefault();
719        $(this).addClass('playing').siblings().removeClass('playing');
720        audio.load($('a', this).attr('data-src'));
721        audio.play();
722    });
723    // Keyboard shortcuts
724    $(document).keydown(function (e) {
725        var unicode = e.charCode ? e.charCode : e.keyCode;
726        // right arrow
727        if (unicode == 39) {
728            var next = $('li.playing').next();
729            if (!next.length) next = $('ol li').first();
730            next.click();
731            // back arrow
732        } else if (unicode == 37) {
733            var prev = $('li.playing').prev();
734            if (!prev.length) prev = $('ol li').last();
735            prev.click();
736            // spacebar
737        } else if (unicode == 32) {
738            audio.playPause();
739        }
740    })
741}); 

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.