PageSourceSearch

https://tina.ru/nassets/d89674cc/audio.min.js?v=1765633899

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

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.