PageSourceSearch

https://h5.anhuinews.com/app/post/js/audio.js?_m=mt9hzgf3

js anhuinews.com collected 2026-10-01 10:40:38 UTC 37,069 bytes, 739 lines download raw bytes

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

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.