2 * SoundManager 2: Javascript Sound for the Web
\r
3 * --------------------------------------------
\r
4 * http://schillmania.com/projects/soundmanager2/
\r
6 * Copyright (c) 2007, Scott Schiller. All rights reserved.
\r
7 * Code provided under the BSD License:
\r
8 * http://schillmania.com/projects/soundmanager2/license.txt
\r
13 /*jslint white: false, onevar: true, undef: true, nomen: false, eqeqeq: true, plusplus: false, bitwise: true, regexp: true, newcap: true, immed: true, regexp: false */
\r
14 /*global SM2_DEFER, sm2Debugger, alert, console, document, navigator, setTimeout, window, document, setInterval, clearInterval, Audio */
\r
18 var soundManager = null;
\r
20 function SoundManager(smURL, smID) {
\r
22 this.flashVersion = 8; // version of flash to require, either 8 or 9. Some API features require Flash 9.
\r
23 this.debugMode = true; // enable debugging output (div#soundmanager-debug, OR console if available+configured)
\r
24 this.debugFlash = false; // enable debugging output inside SWF, troubleshoot Flash/browser issues
\r
25 this.useConsole = true; // use firebug/safari console.log()-type debug console if available
\r
26 this.consoleOnly = false; // if console is being used, do not create/write to #soundmanager-debug
\r
27 this.waitForWindowLoad = false; // force SM2 to wait for window.onload() before trying to call soundManager.onload()
\r
28 this.nullURL = 'about:blank'; // path to "null" (empty) MP3 file, used to unload sounds (Flash 8 only)
\r
29 this.allowPolling = true; // allow flash to poll for status update (required for whileplaying() events, peak, sound spectrum functions to work.)
\r
30 this.useFastPolling = false; // uses lower flash timer interval for higher callback frequency, best combined with useHighPerformance
\r
31 this.useMovieStar = true; // enable support for Flash 9.0r115+ (codename "MovieStar") MPEG4 audio+video formats (AAC, M4V, FLV, MOV etc.)
\r
32 this.bgColor = '#ffffff'; // movie (.swf) background color, '#000000' useful if showing on-screen/full-screen video etc.
\r
33 this.useHighPerformance = false; // position:fixed flash movie can help increase js/flash speed, minimize lag
\r
34 this.flashLoadTimeout = 1000; // msec to wait for flash movie to load before failing (0 = infinity)
\r
35 this.wmode = null; // mode to render the flash movie in - null, transparent, opaque (last two allow layering of HTML on top)
\r
36 this.allowFullScreen = true; // enter full-screen (via double-click on movie) for flash 9+ video
\r
37 this.allowScriptAccess = 'always'; // for scripting the SWF (object/embed property), either 'always' or 'sameDomain'
\r
38 this.useFlashBlock = false; // *requires flashblock.css, see demos* - allow recovery from flash blockers. Wait indefinitely and apply timeout CSS to SWF, if applicable.
\r
39 this.useHTML5Audio = false; // Beta feature: Use HTML 5 Audio() where API is supported (most Safari, Chrome versions), Firefox (no MP3/MP4.) Ideally, transparent vs. Flash API where possible.
\r
40 this.html5Test = /^probably$/i; // HTML5 Audio().canPlayType() test. /^(probably|maybe)$/i if you want to be more liberal/risky.
\r
42 this.audioFormats = {
\r
43 // determines HTML5 support, flash requirements
\r
44 // eg. if MP3 or MP4 required, Flash fallback is used if HTML5 can't play it
\r
45 // shotgun approach to MIME testing due to browser variance
\r
47 type: ['audio/mpeg; codecs="mp3"','audio/mpeg','audio/mp3','audio/MPA','audio/mpa-robust'],
\r
51 related: ['aac','m4a'], // additional formats under the MP4 container.
\r
52 type: ['audio/mp4; codecs="mp4a.40.2"','audio/aac','audio/x-m4a','audio/MP4A-LATM','audio/mpeg4-generic'],
\r
56 type: ['audio/ogg; codecs=vorbis'],
\r
60 type: ['audio/wav; codecs="1"','audio/wav','audio/wave','audio/x-wav'],
\r
65 this.defaultOptions = {
\r
66 'autoLoad': false, // enable automatic loading (otherwise .load() will be called on demand with .play(), the latter being nicer on bandwidth - if you want to .load yourself, you also can)
\r
67 'stream': true, // allows playing before entire file has loaded (recommended)
\r
68 'autoPlay': false, // enable playing of file as soon as possible (much faster if "stream" is true)
\r
69 'loops': 1, // how many times to repeat the sound (position will wrap around to 0, setPosition() will break out of loop when >0)
\r
70 'onid3': null, // callback function for "ID3 data is added/available"
\r
71 'onload': null, // callback function for "load finished"
\r
72 'whileloading': null, // callback function for "download progress update" (X of Y bytes received)
\r
73 'onplay': null, // callback for "play" start
\r
74 'onpause': null, // callback for "pause"
\r
75 'onresume': null, // callback for "resume" (pause toggle)
\r
76 'whileplaying': null, // callback during play (position update)
\r
77 'onstop': null, // callback for "user stop"
\r
78 'onfinish': null, // callback function for "sound finished playing"
\r
79 'onbeforefinish': null, // callback for "before sound finished playing (at [time])"
\r
80 'onbeforefinishtime': 5000, // offset (milliseconds) before end of sound to trigger beforefinish (eg. 1000 msec = 1 second)
\r
81 'onbeforefinishcomplete': null,// function to call when said sound finishes playing
\r
82 'onjustbeforefinish': null, // callback for [n] msec before end of current sound
\r
83 'onjustbeforefinishtime': 200, // [n] - if not using, set to 0 (or null handler) and event will not fire.
\r
84 'multiShot': true, // let sounds "restart" or layer on top of each other when played multiple times, rather than one-shot/one at a time
\r
85 'multiShotEvents': false, // fire multiple sound events (currently onfinish() only) when multiShot is enabled
\r
86 'position': null, // offset (milliseconds) to seek to within loaded sound data.
\r
87 'pan': 0, // "pan" settings, left-to-right, -100 to 100
\r
88 'type': null, // MIME-like hint for file pattern / canPlay() tests, eg. audio/mp3
\r
89 'volume': 100 // self-explanatory. 0-100, the latter being the max.
\r
92 this.flash9Options = { // flash 9-only options, merged into defaultOptions if flash 9 is being used
\r
93 'isMovieStar': null, // "MovieStar" MPEG4 audio/video mode. Null (default) = auto detect MP4, AAC etc. based on URL. true = force on, ignore URL
\r
94 'usePeakData': false, // enable left/right channel peak (level) data
\r
95 'useWaveformData': false, // enable sound spectrum (raw waveform data) - WARNING: CPU-INTENSIVE: may set CPUs on fire.
\r
96 'useEQData': false, // enable sound EQ (frequency spectrum data) - WARNING: Also CPU-intensive.
\r
97 'onbufferchange': null, // callback for "isBuffering" property change
\r
98 'ondataerror': null // callback for waveform/eq data access error (flash playing audio in other tabs/domains)
\r
101 this.movieStarOptions = { // flash 9.0r115+ MPEG4 audio/video options, merged into defaultOptions if flash 9+movieStar mode is enabled
\r
102 'onmetadata': null, // callback for when video width/height etc. are received
\r
103 'useVideo': false, // if loading movieStar content, whether to show video
\r
104 'bufferTime': 3, // seconds of data to buffer before playback begins (null = flash default of 0.1 seconds - if AAC playback is gappy, try increasing.)
\r
105 'serverURL': null, // rtmp: FMS or FMIS server to connect to, required when requesting media via RTMP or one of its variants
\r
106 'onconnect': null // rtmp: callback for connection to flash media server
\r
108 'duration': null, // rtmp: song duration (msec)
\r
109 'totalbytes': null // rtmp: byte size of the song
\r
113 this.version = null;
\r
114 this.versionNumber = 'V2.96a.20100822';
\r
115 this.movieURL = null;
\r
116 this.url = (smURL || null);
\r
117 this.altURL = null;
\r
118 this.swfLoaded = false;
\r
119 this.enabled = false;
\r
121 this.movieID = 'sm2-container';
\r
122 this.id = (smID || 'sm2movie');
\r
124 swfDefault: 'movieContainer',
\r
125 swfError: 'swf_error', // SWF loaded, but SM2 couldn't start (other error)
\r
126 swfTimedout: 'swf_timedout',
\r
127 swfUnblocked: 'swf_unblocked', // or loaded OK
\r
128 sm2Debug: 'sm2_debug',
\r
129 highPerf: 'high_performance',
\r
130 flashDebug: 'flash_debug'
\r
134 this.soundIDs = [];
\r
135 this.muted = false;
\r
136 this.isFullScreen = false; // set later by flash 9+
\r
137 this.isIE = (navigator.userAgent.match(/MSIE/i));
\r
138 this.isSafari = (navigator.userAgent.match(/safari/i));
\r
139 this.debugID = 'soundmanager-debug';
\r
140 this.debugURLParam = /([#?&])debug=1/i;
\r
141 this.specialWmodeCase = false;
\r
142 this.didFlashBlock = false;
\r
144 this.filePattern = null;
\r
145 this.filePatterns = {
\r
146 flash8: /\.mp3(\?.*)?$/i,
\r
147 flash9: /\.mp3(\?.*)?$/i
\r
150 this.baseMimeTypes = /^\s*audio\/(?:x-)?(?:mp(?:eg|3))\s*(?:$|;)/i; // mp3
\r
151 this.netStreamMimeTypes = /^\s*audio\/(?:x-)?(?:mp(?:eg|3))\s*(?:$|;)/i; // mp3, mp4, aac etc.
\r
152 this.netStreamTypes = ['aac', 'flv', 'mov', 'mp4', 'm4v', 'f4v', 'm4a', 'mp4v', '3gp', '3g2']; // Flash v9.0r115+ "moviestar" formats
\r
153 this.netStreamPattern = new RegExp('\\.(' + this.netStreamTypes.join('|') + ')(\\?.*)?$', 'i');
\r
154 this.mimePattern = this.baseMimeTypes;
\r
159 waveformData: false,
\r
167 'remote': 'remote (domain-based) rules',
\r
168 'localWithFile': 'local with file access (no internet access)',
\r
169 'localWithNetwork': 'local with network (internet access only, no local access)',
\r
170 'localTrusted': 'local, trusted (local+internet access)'
\r
172 'description': null,
\r
177 this.hasHTML5 = null; // switch for handling logic
\r
178 this.html5 = { // stores canPlayType() results, etc. read-only.
\r
181 usingFlash: null // set if/when flash fallback is needed
\r
183 this.ignoreFlash = false; // used for special cases (eg. iPad/iPhone/palm OS?)
\r
185 // --- private SM2 internals ---
\r
188 _s = this, _sm = 'soundManager', _id, _ua = navigator.userAgent, _wl = window.location.href.toString(), _fV = this.flashVersion, _doNothing, _init, _onready = [], _debugOpen = true, _debugTS, _didAppend = false, _appendSuccess = false, _didInit = false, _disabled = false, _windowLoaded = false, _wDS, _wdCount, _initComplete, _mergeObjects, _addOnReady, _processOnReady, _initUserOnload, _go, _waitForEI, _setVersionInfo, _handleFocus, _beginInit, _strings, _initMovie, _dcLoaded, _didDCLoaded, _getDocument, _createMovie, _mobileFlash, _setPolling, _debugLevels = ['log', 'info', 'warn', 'error'], _defaultFlashVersion = 8, _disableObject, _failSafely, _normalizeMovieURL, _oRemoved = null, _oRemovedHTML = null, _str, _flashBlockHandler, _getSWFCSS, _toggleDebug, _loopFix, _complain, _idCheck, _waitingForEI = false, _initPending = false, _smTimer, _onTimer, _startTimer, _stopTimer, _needsFlash = null, _featureCheck, _html5OK, _html5Only = false, _html5CanPlay, _html5Ext, _dcIE, _testHTML5,
\r
189 _is_pre = _ua.match(/pre\//i),
\r
190 _iPadOrPhone = _ua.match(/(ipad|iphone)/i),
\r
191 _isMobile = (_ua.match(/mobile/i) || _is_pre || _iPadOrPhone),
\r
192 _hasConsole = (typeof console !== 'undefined' && typeof console.log !== 'undefined'),
\r
193 _isFocused = (typeof document.hasFocus !== 'undefined'?document.hasFocus():null),
\r
194 _tryInitOnFocus = (typeof document.hasFocus === 'undefined' && this.isSafari),
\r
195 _okToDisable = !_tryInitOnFocus;
\r
197 this._use_maybe = (_wl.match(/sm2\-useHTML5Maybe\=1/i)); // temporary feature: #sm2-useHTML5Maybe=1 forces loose canPlay() check
\r
198 this._overHTTP = (document.location?document.location.protocol.match(/http/i):null);
\r
199 this.useAltURL = !this._overHTTP; // use altURL if not "online"
\r
201 if (_iPadOrPhone || _is_pre) {
\r
202 // might as well force it on Apple + Palm, flash support unlikely
\r
203 _s.useHTML5Audio = true;
\r
204 _s.ignoreFlash = true;
\r
207 if (_is_pre || this._use_maybe) {
\r
208 // less-strict canPlayType() checking option
\r
209 _s.html5Test = /^(probably|maybe)$/i;
\r
212 // Temporary feature: allow force of HTML5 via URL: #sm2-usehtml5audio=0 or 1
\r
215 var a = '#sm2-usehtml5audio=', l = _wl, b = null;
\r
216 if (l.indexOf(a) !== -1) {
\r
217 b = (l.substr(l.indexOf(a)+a.length) === '1');
\r
218 if (typeof console !== 'undefined' && typeof console.log !== 'undefined') {
\r
219 console.log((b?'Enabling ':'Disabling ')+'useHTML5Audio via URL parameter');
\r
221 _s.useHTML5Audio = b;
\r
226 // --- public API methods ---
\r
228 this.supported = function() {
\r
229 return (_needsFlash?(_didInit && !_disabled):(_s.useHTML5Audio && _s.hasHTML5));
\r
232 this.getMovie = function(smID) {
\r
233 return _s.isIE?window[smID]:(_s.isSafari?_id(smID) || document[smID]:_id(smID));
\r
236 this.loadFromXML = function(sXmlUrl) {
\r
238 _s.o._loadFromXML(sXmlUrl);
\r
245 this.createSound = function(oOptions) {
\r
246 var _cs = 'soundManager.createSound(): ',
\r
247 thisOptions = null, oSound = null, _tO = null;
\r
249 throw _complain(_cs + _str('notReady'), arguments.callee.caller);
\r
251 if (arguments.length === 2) {
\r
252 // function overloading in JS! :) ..assume simple createSound(id,url) use case
\r
254 'id': arguments[0],
\r
255 'url': arguments[1]
\r
258 thisOptions = _mergeObjects(oOptions); // inherit SM2 defaults
\r
259 _tO = thisOptions; // alias
\r
261 if (_tO.id.toString().charAt(0).match(/^[0-9]$/)) {
\r
262 _s._wD(_cs + _str('badID', _tO.id), 2);
\r
264 _s._wD(_cs + _tO.id + ' (' + _tO.url + ')', 1);
\r
266 if (_idCheck(_tO.id, true)) {
\r
267 _s._wD(_cs + _tO.id + ' exists', 1);
\r
268 return _s.sounds[_tO.id];
\r
272 thisOptions = _loopFix(thisOptions);
\r
273 _s.sounds[_tO.id] = new SMSound(_tO);
\r
274 _s.soundIDs.push(_tO.id);
\r
275 return _s.sounds[_tO.id];
\r
278 if (_html5OK(_tO)) {
\r
280 _s._wD('Loading sound '+_tO.id+' from HTML5');
\r
281 oSound._setup_html5(_tO);
\r
283 if (_fV > 8 && _s.useMovieStar) {
\r
284 if (_tO.isMovieStar === null) {
\r
285 _tO.isMovieStar = ((_tO.serverURL || (_tO.type?_tO.type.match(_s.netStreamPattern):false)||_tO.url.match(_s.netStreamPattern))?true:false);
\r
287 if (_tO.isMovieStar) {
\r
288 _s._wD(_cs + 'using MovieStar handling');
\r
290 if (_tO.isMovieStar) {
\r
291 if (_tO.usePeakData) {
\r
293 _tO.usePeakData = false;
\r
295 if (_tO.loops > 1) {
\r
303 _s.o._createSound(_tO.id, _tO.onjustbeforefinishtime, _tO.loops||1);
\r
305 _s.o._createSound(_tO.id, _tO.url, _tO.onjustbeforefinishtime, _tO.usePeakData, _tO.useWaveformData, _tO.useEQData, _tO.isMovieStar, (_tO.isMovieStar?_tO.useVideo:false), (_tO.isMovieStar?_tO.bufferTime:false), _tO.loops||1, _tO.serverURL, _tO.duration||null, _tO.totalBytes||null, _tO.autoPlay, true);
\r
306 if (!_tO.serverURL) {
\r
307 // We are connected immediately
\r
308 oSound.connected = true;
\r
309 if (_tO.onconnect) {
\r
310 _tO.onconnect.apply(oSound);
\r
316 if (_tO.autoLoad || _tO.autoPlay) {
\r
319 oSound.autobuffer = 'auto'; // early HTML5 implementation (non-standard)
\r
320 oSound.preload = 'auto'; // standard
\r
326 if (_tO.autoPlay) {
\r
332 this.createVideo = function(oOptions) {
\r
333 var fN = 'soundManager.createVideo(): ';
\r
334 if (arguments.length === 2) {
\r
336 'id': arguments[0],
\r
337 'url': arguments[1]
\r
341 oOptions.isMovieStar = true;
\r
342 oOptions.useVideo = true;
\r
344 _s._wD(fN + _str('f9Vid'), 2);
\r
347 if (!_s.useMovieStar) {
\r
348 _s._wD(fN + _str('noMS'), 2);
\r
350 return _s.createSound(oOptions);
\r
353 this.destroySound = function(sID, bFromSound) {
\r
354 // explicitly destroy a sound before normal page unload, etc.
\r
355 if (!_idCheck(sID)) {
\r
358 for (var i = 0; i < _s.soundIDs.length; i++) {
\r
359 if (_s.soundIDs[i] === sID) {
\r
360 _s.soundIDs.splice(i, 1);
\r
364 _s.sounds[sID].unload();
\r
366 // ignore if being called from SMSound instance
\r
367 _s.sounds[sID].destruct();
\r
369 delete _s.sounds[sID];
\r
372 this.destroyVideo = this.destroySound;
\r
374 this.load = function(sID, oOptions) {
\r
375 if (!_idCheck(sID)) {
\r
378 return _s.sounds[sID].load(oOptions);
\r
381 this.unload = function(sID) {
\r
382 if (!_idCheck(sID)) {
\r
385 return _s.sounds[sID].unload();
\r
388 this.play = function(sID, oOptions) {
\r
389 var fN = 'soundManager.play(): ';
\r
391 throw _complain(fN + _str('notReady'), arguments.callee.caller);
\r
393 if (!_idCheck(sID)) {
\r
394 if (!(oOptions instanceof Object)) {
\r
397 }; // overloading use case: play('mySound','/path/to/some.mp3');
\r
399 if (oOptions && oOptions.url) {
\r
400 // overloading use case, creation+playing of sound: .play('someID',{url:'/path/to.mp3'});
\r
401 _s._wD(fN + 'attempting to create "' + sID + '"', 1);
\r
403 return _s.createSound(oOptions).play();
\r
408 return _s.sounds[sID].play(oOptions);
\r
411 this.start = this.play; // just for convenience
\r
413 this.setPosition = function(sID, nMsecOffset) {
\r
414 if (!_idCheck(sID)) {
\r
417 return _s.sounds[sID].setPosition(nMsecOffset);
\r
420 this.stop = function(sID) {
\r
421 if (!_idCheck(sID)) {
\r
424 _s._wD('soundManager.stop(' + sID + ')', 1);
\r
425 return _s.sounds[sID].stop();
\r
428 this.stopAll = function() {
\r
429 _s._wD('soundManager.stopAll()', 1);
\r
430 for (var oSound in _s.sounds) {
\r
431 if (_s.sounds[oSound] instanceof SMSound) {
\r
432 _s.sounds[oSound].stop(); // apply only to sound objects
\r
437 this.pause = function(sID) {
\r
438 if (!_idCheck(sID)) {
\r
441 return _s.sounds[sID].pause();
\r
444 this.pauseAll = function() {
\r
445 for (var i = _s.soundIDs.length; i--;) {
\r
446 _s.sounds[_s.soundIDs[i]].pause();
\r
450 this.resume = function(sID) {
\r
451 if (!_idCheck(sID)) {
\r
454 return _s.sounds[sID].resume();
\r
457 this.resumeAll = function() {
\r
458 for (var i = _s.soundIDs.length; i--;) {
\r
459 _s.sounds[_s.soundIDs[i]].resume();
\r
463 this.togglePause = function(sID) {
\r
464 if (!_idCheck(sID)) {
\r
467 return _s.sounds[sID].togglePause();
\r
470 this.setPan = function(sID, nPan) {
\r
471 if (!_idCheck(sID)) {
\r
474 return _s.sounds[sID].setPan(nPan);
\r
477 this.setVolume = function(sID, nVol) {
\r
478 if (!_idCheck(sID)) {
\r
481 return _s.sounds[sID].setVolume(nVol);
\r
484 this.mute = function(sID) {
\r
485 var fN = 'soundManager.mute(): ',
\r
487 if (typeof sID !== 'string') {
\r
491 _s._wD(fN + 'Muting all sounds');
\r
492 for (i = _s.soundIDs.length; i--;) {
\r
493 _s.sounds[_s.soundIDs[i]].mute();
\r
497 if (!_idCheck(sID)) {
\r
500 _s._wD(fN + 'Muting "' + sID + '"');
\r
501 return _s.sounds[sID].mute();
\r
505 this.muteAll = function() {
\r
509 this.unmute = function(sID) {
\r
510 var fN = 'soundManager.unmute(): ', i;
\r
511 if (typeof sID !== 'string') {
\r
515 _s._wD(fN + 'Unmuting all sounds');
\r
516 for (i = _s.soundIDs.length; i--;) {
\r
517 _s.sounds[_s.soundIDs[i]].unmute();
\r
521 if (!_idCheck(sID)) {
\r
524 _s._wD(fN + 'Unmuting "' + sID + '"');
\r
525 return _s.sounds[sID].unmute();
\r
529 this.unmuteAll = function() {
\r
533 this.toggleMute = function(sID) {
\r
534 if (!_idCheck(sID)) {
\r
537 return _s.sounds[sID].toggleMute();
\r
540 this.getMemoryUse = function() {
\r
542 // not supported in Flash 8
\r
546 return parseInt(_s.o._getMemoryUse(), 10);
\r
550 this.disable = function(bNoDisable) {
\r
551 // destroy all functions
\r
552 if (typeof bNoDisable === 'undefined') {
\r
553 bNoDisable = false;
\r
559 _wDS('shutdown', 1);
\r
560 for (var i = _s.soundIDs.length; i--;) {
\r
561 _disableObject(_s.sounds[_s.soundIDs[i]]);
\r
563 _initComplete(bNoDisable); // fire "complete", despite fail
\r
564 if (window.removeEventListener) {
\r
565 window.removeEventListener('load', _initUserOnload, false);
\r
567 // _disableObject(_s); // taken out to allow reboot()
\r
570 this.canPlayMIME = function(sMIME) {
\r
573 result = _html5CanPlay({type:sMIME});
\r
575 if (!_needsFlash || result) {
\r
579 return (sMIME?(sMIME.match(_s.mimePattern)?true:false):null);
\r
583 this.canPlayURL = function(sURL) {
\r
586 result = _html5CanPlay(sURL);
\r
588 if (!_needsFlash || result) {
\r
592 return (sURL?(sURL.match(_s.filePattern)?true:false):null);
\r
596 this.canPlayLink = function(oLink) {
\r
597 if (typeof oLink.type !== 'undefined' && oLink.type) {
\r
598 if (_s.canPlayMIME(oLink.type)) {
\r
602 return _s.canPlayURL(oLink.href);
\r
605 this.getSoundById = function(sID, suppressDebug) {
\r
607 throw new Error('SoundManager.getSoundById(): sID is null/undefined');
\r
609 var result = _s.sounds[sID];
\r
610 if (!result && !suppressDebug) {
\r
611 _s._wD('"' + sID + '" is an invalid sound ID.', 2);
\r
612 // soundManager._wD('trace: '+arguments.callee.caller);
\r
617 this.onready = function(oMethod, oScope) {
\r
619 soundManager.onready(function(oStatus) {
\r
620 console.log('SM2 init success: '+oStatus.success);
\r
623 if (oMethod && oMethod instanceof Function) {
\r
630 _addOnReady(oMethod, oScope);
\r
634 throw _str('needFunction');
\r
638 this.oninitmovie = function() {
\r
639 // called after SWF has been appended to the DOM via JS (or retrieved from HTML)
\r
640 // this is a stub for your own scripts.
\r
643 this.onload = function() {
\r
644 // window.onload() equivalent for SM2, ready to create sounds etc.
\r
645 // this is a stub for your own scripts.
\r
646 _s._wD('soundManager.onload()', 1);
\r
649 this.onerror = function() {
\r
650 // stub for user handler, called when SM2 fails to load/init
\r
653 this.getMoviePercent = function() {
\r
654 return (_s.o && typeof _s.o.PercentLoaded !== 'undefined'?_s.o.PercentLoaded():null);
\r
657 this._writeDebug = function(sText, sType, bTimestamp) {
\r
658 // pseudo-private console.log()-style output
\r
660 var sDID = 'soundmanager-debug', o, oItem, sMethod;
\r
661 if (!_s.debugMode) {
\r
664 if (typeof bTimestamp !== 'undefined' && bTimestamp) {
\r
665 sText = sText + ' | ' + new Date().getTime();
\r
667 if (_hasConsole && _s.useConsole) {
\r
668 sMethod = _debugLevels[sType];
\r
669 if (typeof console[sMethod] !== 'undefined') {
\r
670 console[sMethod](sText);
\r
672 console.log(sText);
\r
674 if (_s.useConsoleOnly) {
\r
683 oItem = document.createElement('div');
\r
684 if (++_wdCount % 2 === 0) {
\r
685 oItem.className = 'sm2-alt';
\r
687 // sText = sText.replace(/\n/g,'<br />');
\r
688 if (typeof sType === 'undefined') {
\r
691 sType = parseInt(sType, 10);
\r
693 oItem.appendChild(document.createTextNode(sText));
\r
696 oItem.style.fontWeight = 'bold';
\r
699 oItem.style.color = '#ff3333';
\r
702 // o.appendChild(oItem); // top-to-bottom
\r
703 o.insertBefore(oItem, o.firstChild); // bottom-to-top
\r
710 this._wD = this._writeDebug; // alias
\r
712 this._debug = function() {
\r
714 _wDS('currentObj', 1);
\r
715 for (var i = 0, j = _s.soundIDs.length; i < j; i++) {
\r
716 _s.sounds[_s.soundIDs[i]]._debug();
\r
721 this.reboot = function() {
\r
722 // attempt to reset and init SM2
\r
723 _s._wD('soundManager.reboot()');
\r
724 if (_s.soundIDs.length) {
\r
725 _s._wD('Destroying ' + _s.soundIDs.length + ' SMSound objects...');
\r
727 for (var i = _s.soundIDs.length; i--;) {
\r
728 _s.sounds[_s.soundIDs[i]].destruct();
\r
733 _oRemovedHTML = _s.o.innerHTML;
\r
735 _oRemoved = _s.o.parentNode.removeChild(_s.o);
\r
736 _s._wD('Flash movie removed.');
\r
739 _wDS('badRemove', 2);
\r
741 // actually, force recreate of movie.
\r
742 _oRemovedHTML = null;
\r
744 _s.enabled = false;
\r
746 _waitingForEI = false;
\r
747 _initPending = false;
\r
748 _didAppend = false;
\r
749 _appendSuccess = false;
\r
751 _s.swfLoaded = false;
\r
755 for (i = _onready.length; i--;) {
\r
756 _onready[i].fired = false;
\r
758 _s._wD(_sm + ': Rebooting...');
\r
759 window.setTimeout(function() {
\r
760 _s.beginDelayedInit();
\r
764 this.destruct = function() {
\r
765 _s._wD('soundManager.destruct()');
\r
769 this.beginDelayedInit = function() {
\r
770 // _s._wD('soundManager.beginDelayedInit()');
\r
771 _windowLoaded = true;
\r
773 setTimeout(_waitForEI, 500);
\r
774 setTimeout(_beginInit, 20);
\r
777 // --- private SM2 internals ---
\r
779 _html5OK = function(iO) {
\r
780 return ((iO.type?_html5CanPlay({type:iO.type}):false)||_html5CanPlay(iO.url));
\r
783 _html5CanPlay = function(sURL) {
\r
784 // try to find MIME, test and return truthiness
\r
785 if (!_s.useHTML5Audio || !_s.hasHTML5) {
\r
788 var result, mime, fileExt, item, aF = _s.audioFormats;
\r
792 if (aF.hasOwnProperty(item)) {
\r
793 _html5Ext.push(item);
\r
794 if (aF[item].related) {
\r
795 _html5Ext = _html5Ext.concat(aF[item].related);
\r
799 _html5Ext = new RegExp('\\.('+_html5Ext.join('|')+')','i');
\r
801 mime = (typeof sURL.type !== 'undefined'?sURL.type:null);
\r
802 fileExt = (typeof sURL === 'string'?sURL.toLowerCase().match(_html5Ext):null); // TODO: Strip URL queries, etc.
\r
803 if (!fileExt || !fileExt.length) {
\r
808 fileExt = fileExt[0].substr(1); // "mp3", for example
\r
810 if (fileExt && typeof _s.html5[fileExt] !== 'undefined') {
\r
812 return _s.html5[fileExt];
\r
815 if (fileExt && _s.html5[fileExt]) {
\r
816 return _s.html5[fileExt];
\r
818 // best-case guess, audio/whatever-dot-filename-format-you're-playing
\r
819 mime = 'audio/'+fileExt;
\r
822 result = _s.html5.canPlayType(mime);
\r
823 _s.html5[fileExt] = result;
\r
824 // _s._wD('canPlayType, found result: '+result);
\r
829 _testHTML5 = function() {
\r
830 if (!_s.useHTML5Audio || typeof Audio === 'undefined') {
\r
833 var a = (typeof Audio !== 'undefined' ? new Audio():null), item, support = {}, aF, i;
\r
836 var canPlay, i, j, isOK = false;
\r
837 if (!a || typeof a.canPlayType !== 'function') {
\r
840 if (m instanceof Array) {
\r
841 // iterate through all mime types, return any successes
\r
842 for (i=0, j=m.length; i<j && !isOK; i++) {
\r
843 if (_s.html5[m[i]] || a.canPlayType(m[i]).match(_s.html5Test)) {
\r
845 _s.html5[m[i]] = true;
\r
850 canPlay = (a && typeof a.canPlayType === 'function' ? a.canPlayType(m) : false);
\r
851 return (canPlay && (canPlay.match(_s.html5Test)?true:false));
\r
855 // test all registered formats + codecs
\r
856 aF = _s.audioFormats;
\r
858 if (aF.hasOwnProperty(item)) {
\r
859 support[item] = _cp(aF[item].type);
\r
860 // assign result to related formats, too
\r
861 if (aF[item] && aF[item].related) {
\r
862 for (i=0; i<aF[item].related.length; i++) {
\r
863 _s.html5[aF[item].related[i]] = support[item];
\r
868 support.canPlayType = (a?_cp:null);
\r
870 _s.html5 = _mergeObjects(_s.html5, support);
\r
875 notReady: 'Not loaded yet - wait for soundManager.onload() before calling sound-related methods',
\r
876 appXHTML: _sm + '::createMovie(): appendChild/innerHTML set failed. May be app/xhtml+xml DOM-related.',
\r
877 spcWmode: _sm + '::createMovie(): Removing wmode, preventing win32 below-the-fold SWF loading issue',
\r
878 swf404: _sm + ': Verify that %s is a valid path.',
\r
879 tryDebug: 'Try ' + _sm + '.debugFlash = true for more security details (output goes to SWF.)',
\r
880 checkSWF: 'See SWF output for more debug info.',
\r
881 localFail: _sm + ': Non-HTTP page (' + document.location.protocol + ' URL?) Review Flash player security settings for this special case:\nhttp://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html\nMay need to add/allow path, eg. c:/sm2/ or /users/me/sm2/',
\r
882 waitFocus: _sm + ': Special case: Waiting for focus-related event..',
\r
883 waitImpatient: _sm + ': Getting impatient, still waiting for Flash%s...',
\r
884 waitForever: _sm + ': Waiting indefinitely for Flash (will recover if unblocked)...',
\r
885 needFunction: _sm + '.onready(): Function object expected',
\r
886 badID: 'Warning: Sound ID "%s" should be a string, starting with a non-numeric character',
\r
887 fl9Vid: 'flash 9 required for video. Exiting.',
\r
888 noMS: 'MovieStar mode not enabled. Exiting.',
\r
889 currentObj: '--- ' + _sm + '._debug(): Current sound objects ---',
\r
890 waitEI: _sm + '::initMovie(): Waiting for ExternalInterface call from Flash..',
\r
891 waitOnload: _sm + ': Waiting for window.onload()',
\r
892 docLoaded: _sm + ': Document already loaded',
\r
893 onload: _sm + '::initComplete(): calling soundManager.onload()',
\r
894 onloadOK: _sm + '.onload() complete',
\r
895 init: '-- ' + _sm + '::init() --',
\r
896 didInit: _sm + '::init(): Already called?',
\r
897 flashJS: _sm + ': Attempting to call Flash from JS..',
\r
898 noPolling: _sm + ': Polling (whileloading()/whileplaying() support) is disabled.',
\r
899 secNote: 'Flash security note: Network/internet URLs will not load due to security restrictions. Access can be configured via Flash Player Global Security Settings Page: http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html',
\r
900 badRemove: 'Warning: Failed to remove flash movie.',
\r
901 noPeak: 'Warning: peakData features unsupported for movieStar formats',
\r
902 shutdown: _sm + '.disable(): Shutting down',
\r
903 queue: _sm + '.onready(): Queueing handler',
\r
904 smFail: _sm + ': Failed to initialise.',
\r
905 smError: 'SMSound.load(): Exception: JS-Flash communication failed, or JS error.',
\r
906 fbTimeout: 'No flash response, applying .'+_s.swfCSS.swfTimedout+' CSS..',
\r
907 fbLoaded: 'Flash loaded',
\r
908 manURL: 'SMSound.load(): Using manually-assigned URL',
\r
909 onURL: _sm + '.load(): current URL already assigned.',
\r
910 badFV: 'soundManager.flashVersion must be 8 or 9. "%s" is invalid. Reverting to %s.',
\r
911 as2loop: 'Note: Setting stream:false so looping can work (flash 8 limitation)',
\r
912 noNSLoop: 'Note: Looping not implemented for MovieStar formats',
\r
913 needfl9: 'Note: Switching to flash 9, required for MP4 formats.',
\r
914 mfTimeout: 'Setting flashLoadTimeout = 0 (infinite) for off-screen, mobile flash case',
\r
915 mfOn: 'mobileFlash::enabling on-screen flash repositioning'
\r
918 _id = function(sID) {
\r
919 return document.getElementById(sID);
\r
924 _str = function() { // o [,items to replace]
\r
925 var params = Array.prototype.slice.call(arguments), // real array, please
\r
926 o = params.shift(), // first arg
\r
927 str = (_strings && _strings[o]?_strings[o]:''), i, j;
\r
928 if (str && params && params.length) {
\r
929 for (i = 0, j = params.length; i < j; i++) {
\r
930 str = str.replace('%s', params[i]);
\r
936 _loopFix = function(sOpt) {
\r
937 // flash 8 requires stream = false for looping to work.
\r
938 if (_fV === 8 && sOpt.loops > 1 && sOpt.stream) {
\r
940 sOpt.stream = false;
\r
945 _complain = function(sMsg, oCaller) {
\r
946 // Try to create meaningful custom errors, w/stack trace to the "offending" line
\r
947 var sPre = 'Error: ', errorDesc;
\r
949 return new Error(sPre + sMsg);
\r
951 if (typeof console !== 'undefined' && typeof console.trace !== 'undefined') {
\r
954 errorDesc = sPre + sMsg + '. \nCaller: ' + oCaller.toString();
\r
955 // See JS error/debug/console output for real error source, stack trace / message detail where possible.
\r
956 return new Error(errorDesc);
\r
959 _doNothing = function() {
\r
963 _disableObject = function(o) {
\r
964 for (var oProp in o) {
\r
965 if (o.hasOwnProperty(oProp) && typeof o[oProp] === 'function') {
\r
966 o[oProp] = _doNothing;
\r
972 _failSafely = function(bNoDisable) {
\r
973 // general failure exception handler
\r
974 if (typeof bNoDisable === 'undefined') {
\r
975 bNoDisable = false;
\r
977 if (_disabled || bNoDisable) {
\r
979 _s.disable(bNoDisable);
\r
983 _normalizeMovieURL = function(smURL) {
\r
984 var urlParams = null;
\r
986 if (smURL.match(/\.swf(\?\.*)?$/i)) {
\r
987 urlParams = smURL.substr(smURL.toLowerCase().lastIndexOf('.swf?') + 4);
\r
989 return smURL; // assume user knows what they're doing
\r
991 } else if (smURL.lastIndexOf('/') !== smURL.length - 1) {
\r
992 smURL = smURL + '/';
\r
995 return (smURL && smURL.lastIndexOf('/') !== - 1?smURL.substr(0, smURL.lastIndexOf('/') + 1):'./') + _s.movieURL;
\r
998 _setVersionInfo = function() {
\r
999 if (_fV !== 8 && _fV !== 9) {
\r
1000 _s._wD(_str('badFV', _fV, _defaultFlashVersion));
\r
1001 _s.flashVersion = _defaultFlashVersion;
\r
1003 var isDebug = (_s.debugMode || _s.debugFlash?'_debug.swf':'.swf'); // debug flash movie, if applicable
\r
1004 if (_s.flashVersion < 9 && _s.useHTML5Audio && _s.audioFormats.mp4.required) {
\r
1005 _s._wD(_str('needfl9'));
\r
1006 _s.flashVersion = 9;
\r
1008 _fV = _s.flashVersion; // short-hand for internal use
\r
1009 _s.version = _s.versionNumber + (_html5Only?' (HTML5-only mode)':(_fV === 9?' (AS3/Flash 9)':' (AS2/Flash 8)'));
\r
1010 // set up default options
\r
1012 _s.defaultOptions = _mergeObjects(_s.defaultOptions, _s.flash9Options);
\r
1013 _s.features.buffering = true;
\r
1015 if (_fV > 8 && _s.useMovieStar) {
\r
1016 // flash 9+ support for movieStar formats as well as MP3
\r
1017 _s.defaultOptions = _mergeObjects(_s.defaultOptions, _s.movieStarOptions);
\r
1018 _s.filePatterns.flash9 = new RegExp('\\.(mp3|' + _s.netStreamTypes.join('|') + ')(\\?.*)?$', 'i');
\r
1019 _s.mimePattern = _s.netStreamMimeTypes;
\r
1020 _s.features.movieStar = true;
\r
1022 _s.features.movieStar = false;
\r
1024 _s.filePattern = _s.filePatterns[(_fV !== 8?'flash9':'flash8')];
\r
1025 _s.movieURL = (_fV === 8?'soundmanager2.swf':'soundmanager2_flash9.swf').replace('.swf',isDebug);
\r
1026 _s.features.peakData = _s.features.waveformData = _s.features.eqData = (_fV > 8);
\r
1029 _getDocument = function() {
\r
1030 return (document.body?document.body:(document.documentElement?document.documentElement:document.getElementsByTagName('div')[0]));
\r
1033 _setPolling = function(bPolling, bHighPerformance) {
\r
1034 if (!_s.o || !_s.allowPolling) {
\r
1037 _s.o._setPolling(bPolling, bHighPerformance);
\r
1040 function _initDebug() {
\r
1041 if (_s.debugURLParam.test(_wl)) {
\r
1042 _s.debugMode = true; // allow force of debug mode via URL
\r
1045 var oD, oDebug, oTarget, oToggle, tmp;
\r
1046 if (_s.debugMode) {
\r
1048 oD = document.createElement('div');
\r
1049 oD.id = _s.debugID + '-toggle';
\r
1051 position: 'fixed',
\r
1056 lineHeight: '1.2em',
\r
1058 textAlign: 'center',
\r
1059 border: '1px solid #999',
\r
1060 cursor: 'pointer',
\r
1061 background: '#fff',
\r
1066 oD.appendChild(document.createTextNode('-'));
\r
1067 oD.onclick = _toggleDebug;
\r
1068 oD.title = 'Toggle SM2 debug console';
\r
1070 if (_ua.match(/msie 6/i)) {
\r
1071 oD.style.position = 'absolute';
\r
1072 oD.style.cursor = 'hand';
\r
1075 for (tmp in oToggle) {
\r
1076 if (oToggle.hasOwnProperty(tmp)) {
\r
1077 oD.style[tmp] = oToggle[tmp];
\r
1082 if (_s.debugMode && !_id(_s.debugID) && ((!_hasConsole || !_s.useConsole) || (_s.useConsole && _hasConsole && !_s.consoleOnly))) {
\r
1083 oDebug = document.createElement('div');
\r
1084 oDebug.id = _s.debugID;
\r
1085 oDebug.style.display = (_s.debugMode?'block':'none');
\r
1086 if (_s.debugMode && !_id(oD.id)) {
\r
1088 oTarget = _getDocument();
\r
1089 oTarget.appendChild(oD);
\r
1091 throw new Error(_str('appXHTML'));
\r
1093 oTarget.appendChild(oDebug);
\r
1097 _initDebug = function(){}; // one-time function
\r
1101 _mobileFlash = (function(){
\r
1105 function resetPosition() {
\r
1107 oM.left = oM.top = '-9999px';
\r
1111 function reposition() {
\r
1112 oM.left = window.scrollX+'px';
\r
1113 oM.top = window.scrollY+'px';
\r
1116 function setReposition(bOn) {
\r
1117 _s._wD('mobileFlash::flash on-screen hack: '+(bOn?'ON':'OFF'));
\r
1118 var f = window[(bOn?'add':'remove')+'EventListener'];
\r
1119 f('resize', reposition, false);
\r
1120 f('scroll', reposition, false);
\r
1123 function check(inDoc) {
\r
1124 // mobile flash (Android for starters) check
\r
1125 oM = _s.oMC.style;
\r
1126 if (_ua.match(/android/i)) {
\r
1128 if (_s.flashLoadTimeout) {
\r
1129 _s._wDS('mfTimeout');
\r
1130 _s.flashLoadTimeout = 0;
\r
1135 oM.position = 'absolute';
\r
1136 oM.left = oM.top = '0px';
\r
1137 setReposition(true);
\r
1138 _s.onready(function(){
\r
1139 setReposition(false); // detach
\r
1140 resetPosition(); // restore when OK/timed out
\r
1152 _createMovie = function(smID, smURL) {
\r
1154 var specialCase = null,
\r
1155 remoteURL = (smURL?smURL:_s.url),
\r
1156 localURL = (_s.altURL?_s.altURL:remoteURL),
\r
1157 oEmbed, oMovie, oTarget, tmp, movieHTML, oEl, extraClass, s, x, sClass, side = '100%';
\r
1158 smID = (typeof smID === 'undefined'?_s.id:smID);
\r
1159 if (_didAppend && _appendSuccess) {
\r
1160 return false; // ignore if already succeeded
\r
1163 function _initMsg() {
\r
1164 _s._wD('-- SoundManager 2 ' + _s.version + (!_html5Only && _s.useHTML5Audio?(_s.hasHTML5?' + HTML5 audio':', no HTML5 audio support'):'') + (_s.useMovieStar?', MovieStar mode':'') + (_s.useHighPerformance?', high performance mode, ':', ') + ((_s.useFastPolling?'fast':'normal') + ' polling') + (_s.wmode?', wmode: ' + _s.wmode:'') + (_s.debugFlash?', flash debug mode':'') + (_s.useFlashBlock?', flashBlock mode':'') + ' --', 1);
\r
1167 _setVersionInfo();
\r
1169 _s.oMC = _id(_s.movieID);
\r
1171 // prevent multiple init attempts
\r
1172 _didAppend = true;
\r
1173 _appendSuccess = true;
\r
1177 _didAppend = true;
\r
1179 // safety check for legacy (change to Flash 9 URL)
\r
1180 _setVersionInfo();
\r
1181 _s.url = _normalizeMovieURL(this._overHTTP?remoteURL:localURL);
\r
1184 if (_s.useHighPerformance && _s.useMovieStar && _s.defaultOptions.useVideo === true) {
\r
1185 specialCase = 'soundManager note: disabling highPerformance, not applicable with movieStar mode+useVideo';
\r
1186 _s.useHighPerformance = false;
\r
1189 _s.wmode = (!_s.wmode && _s.useHighPerformance && !_s.useMovieStar?'transparent':_s.wmode);
\r
1191 if (_s.wmode !== null && !_s.isIE && !_s.useHighPerformance && navigator.platform.match(/win32/i)) {
\r
1192 _s.specialWmodeCase = true;
\r
1193 // extra-special case: movie doesn't load until scrolled into view when using wmode = anything but 'window' here
\r
1194 // does not apply when using high performance (position:fixed means on-screen), OR infinite flash load timeout
\r
1200 _s.allowFullScreen = false;
\r
1210 allowScriptAccess: _s.allowScriptAccess,
\r
1211 bgcolor: _s.bgColor,
\r
1212 pluginspage: 'http://www.macromedia.com/go/getflashplayer',
\r
1213 type: 'application/x-shockwave-flash',
\r
1215 allowFullScreen: (_s.allowFullScreen?'true':'false')
\r
1218 if (_s.debugFlash) {
\r
1219 oEmbed.FlashVars = 'debug=1';
\r
1223 delete oEmbed.wmode; // don't write empty attribute
\r
1227 // IE is "special".
\r
1228 oMovie = document.createElement('div');
\r
1229 movieHTML = '<object id="' + smID + '" data="' + smURL + '" type="' + oEmbed.type + '" width="' + oEmbed.width + '" height="' + oEmbed.height + '"><param name="movie" value="' + smURL + '" /><param name="AllowScriptAccess" value="' + _s.allowScriptAccess + '" /><param name="quality" value="' + oEmbed.quality + '" />' + (_s.wmode?'<param name="wmode" value="' + _s.wmode + '" /> ':'') + '<param name="bgcolor" value="' + _s.bgColor + '" /><param name="allowFullScreen" value="' + oEmbed.allowFullScreen + '" />' + (_s.debugFlash?'<param name="FlashVars" value="' + oEmbed.FlashVars + '" />':'') + '<!-- --></object>';
\r
1231 oMovie = document.createElement('embed');
\r
1232 for (tmp in oEmbed) {
\r
1233 if (oEmbed.hasOwnProperty(tmp)) {
\r
1234 oMovie.setAttribute(tmp, oEmbed[tmp]);
\r
1241 extraClass = _getSWFCSS();
\r
1242 oTarget = _getDocument();
\r
1245 _s.oMC = _id(_s.movieID)?_id(_s.movieID):document.createElement('div');
\r
1247 _s.oMC.id = _s.movieID;
\r
1248 _s.oMC.className = _s.swfCSS.swfDefault + ' ' + extraClass;
\r
1249 // "hide" flash movie
\r
1252 if (!_s.useFlashBlock) {
\r
1253 if (_s.useHighPerformance) {
\r
1255 position: 'fixed',
\r
1258 // >= 6px for flash to run fast, >= 8px to start up under Firefox/win32 in some cases. odd? yes.
\r
1261 overflow: 'hidden'
\r
1262 // zIndex:-1 // sit behind everything else - potentially dangerous/buggy?
\r
1266 position: 'absolute',
\r
1274 if (_ua.match(/webkit/i)) {
\r
1275 _s.oMC.style.zIndex = 10000; // soundcloud-reported render/crash fix, safari 5
\r
1279 if (!_s.debugFlash) {
\r
1281 if (s.hasOwnProperty(x)) {
\r
1282 _s.oMC.style[x] = s[x];
\r
1288 _s.oMC.appendChild(oMovie);
\r
1290 oTarget.appendChild(_s.oMC);
\r
1292 oEl = _s.oMC.appendChild(document.createElement('div'));
\r
1293 oEl.className = 'sm2-object-box';
\r
1294 oEl.innerHTML = movieHTML;
\r
1296 _appendSuccess = true;
\r
1298 throw new Error(_str('appXHTML'));
\r
1300 _mobileFlash.check();
\r
1303 // it's already in the document.
\r
1304 sClass = _s.oMC.className;
\r
1305 _s.oMC.className = (sClass?sClass+' ':_s.swfCSS.swfDefault) + (extraClass?' '+extraClass:'');
\r
1306 _s.oMC.appendChild(oMovie);
\r
1308 oEl = _s.oMC.appendChild(document.createElement('div'));
\r
1309 oEl.className = 'sm2-object-box';
\r
1310 oEl.innerHTML = movieHTML;
\r
1312 _appendSuccess = true;
\r
1313 _mobileFlash.check(true);
\r
1318 if (specialCase) {
\r
1319 _s._wD(specialCase);
\r
1323 _s._wD('soundManager::createMovie(): Trying to load ' + smURL + (!this._overHTTP && _s.altURL?' (alternate URL)':''), 1);
\r
1327 _idCheck = this.getSoundById;
\r
1330 _wDS = function(o, errorLevel) {
\r
1334 return _s._wD(_str(o), errorLevel);
\r
1338 if (_wl.indexOf('debug=alert') + 1 && _s.debugMode) {
\r
1339 _s._wD = function(sText) {alert(sText);};
\r
1342 _toggleDebug = function() {
\r
1343 var o = _id(_s.debugID),
\r
1344 oT = _id(_s.debugID + '-toggle');
\r
1350 oT.innerHTML = '+';
\r
1351 o.style.display = 'none';
\r
1353 oT.innerHTML = '-';
\r
1354 o.style.display = 'block';
\r
1356 _debugOpen = !_debugOpen;
\r
1359 _debugTS = function(sEventType, bSuccess, sMessage) {
\r
1360 // troubleshooter debug hooks
\r
1361 if (typeof sm2Debugger !== 'undefined') {
\r
1363 sm2Debugger.handleEvent(sEventType, bSuccess, sMessage);
\r
1371 _mergeObjects = function(oMain, oAdd) {
\r
1372 // non-destructive merge
\r
1373 var o1 = {}, // clone o1
\r
1375 for (i in oMain) {
\r
1376 if (oMain.hasOwnProperty(i)) {
\r
1380 o2 = (typeof oAdd === 'undefined'?_s.defaultOptions:oAdd);
\r
1382 if (o2.hasOwnProperty(o) && typeof o1[o] === 'undefined') {
\r
1389 _initMovie = function() {
\r
1394 // attempt to get, or create, movie
\r
1396 return false; // may already exist
\r
1398 _s.o = _s.getMovie(_s.id); // (inline markup)
\r
1402 _createMovie(_s.id, _s.url);
\r
1404 // try to re-append removed movie after reboot()
\r
1406 _s.oMC.appendChild(_oRemoved);
\r
1408 _s.oMC.innerHTML = _oRemovedHTML;
\r
1411 _didAppend = true;
\r
1413 _s.o = _s.getMovie(_s.id);
\r
1416 _s._wD('soundManager::initMovie(): Got '+_s.o.nodeName+' element ('+(_didAppend?'created via JS':'static HTML')+')');
\r
1419 if (typeof _s.oninitmovie === 'function') {
\r
1420 setTimeout(_s.oninitmovie, 1);
\r
1424 _go = function(sURL) {
\r
1425 // where it all begins.
\r
1432 _waitForEI = function() {
\r
1433 if (_waitingForEI) {
\r
1436 _waitingForEI = true;
\r
1437 if (_tryInitOnFocus && !_isFocused) {
\r
1438 _wDS('waitFocus');
\r
1443 p = _s.getMoviePercent();
\r
1444 _s._wD(_str('waitImpatient', (p === 100?' (SWF loaded)':(p > 0?' (SWF ' + p + '% loaded)':''))));
\r
1446 setTimeout(function() {
\r
1447 p = _s.getMoviePercent();
\r
1449 _s._wD(_sm + ': No Flash response within expected time.\nLikely causes: ' + (p === 0?'Loading ' + _s.movieURL + ' may have failed (and/or Flash ' + _fV + '+ not present?), ':'') + 'Flash blocked or JS-Flash security error.' + (_s.debugFlash?' ' + _str('checkSWF'):''), 2);
\r
1450 if (!this._overHTTP && p) {
\r
1451 _wDS('localFail', 2);
\r
1452 if (!_s.debugFlash) {
\r
1453 _wDS('tryDebug', 2);
\r
1457 // if 0 (not null), probably a 404.
\r
1458 _s._wD(_str('swf404', _s.url));
\r
1460 _debugTS('flashtojs', false, ': Timed out' + this._overHTTP?' (Check flash security or flash blockers)':' (No plugin/missing SWF?)');
\r
1462 // give up / time-out, depending
\r
1463 if (!_didInit && _okToDisable) {
\r
1465 // SWF failed. Maybe blocked.
\r
1466 if (_s.useFlashBlock || _s.flashLoadTimeout === 0) {
\r
1467 if (_s.useFlashBlock) {
\r
1468 _flashBlockHandler();
\r
1470 _wDS('waitForever');
\r
1472 // old SM2 behaviour, simply fail
\r
1473 _failSafely(true);
\r
1476 // flash loaded? Shouldn't be a blocking issue, then.
\r
1477 if (_s.flashLoadTimeout === 0) {
\r
1478 _wDS('waitForever');
\r
1480 _failSafely(true);
\r
1484 }, _s.flashLoadTimeout);
\r
1487 _getSWFCSS = function() {
\r
1489 if (_s.debugMode) {
\r
1490 css.push(_s.swfCSS.sm2Debug);
\r
1492 if (_s.debugFlash) {
\r
1493 css.push(_s.swfCSS.flashDebug);
\r
1495 if (_s.useHighPerformance) {
\r
1496 css.push(_s.swfCSS.highPerf);
\r
1498 return css.join(' ');
\r
1501 _flashBlockHandler = function() {
\r
1502 // *possible* flash block situation.
\r
1503 var name = 'soundManager::flashBlockHandler()', p = _s.getMoviePercent();
\r
1504 if (!_s.supported()) {
\r
1505 if (_needsFlash) {
\r
1506 // make the movie more visible, so user can fix
\r
1507 _s.oMC.className = _getSWFCSS() + ' ' + _s.swfCSS.swfDefault + ' ' + (p === null?_s.swfCSS.swfTimedout:_s.swfCSS.swfError);
\r
1508 _s._wD(name+': '+_str('fbTimeout')+(p?' ('+_str('fbLoaded')+')':''));
\r
1510 _s.didFlashBlock = true;
\r
1511 _processOnReady(true); // fire onready(), complain lightly
\r
1513 if (_s.onerror instanceof Function) {
\r
1514 _s.onerror.apply(window);
\r
1517 // SM2 loaded OK (or recovered)
\r
1518 if (_s.didFlashBlock) {
\r
1519 _s._wD(name+': Unblocked');
\r
1522 _s.oMC.className = _getSWFCSS() + ' ' + _s.swfCSS.swfDefault + (' '+_s.swfCSS.swfUnblocked);
\r
1527 _handleFocus = function() {
\r
1528 if (_isFocused || !_tryInitOnFocus) {
\r
1531 _okToDisable = true;
\r
1532 _isFocused = true;
\r
1533 _s._wD('soundManager::handleFocus()');
\r
1534 if (_tryInitOnFocus) {
\r
1535 // giant Safari 3.1 hack - assume window in focus if mouse is moving, since document.hasFocus() not currently implemented.
\r
1536 window.removeEventListener('mousemove', _handleFocus, false);
\r
1538 // allow init to restart
\r
1539 _waitingForEI = false;
\r
1540 setTimeout(_waitForEI, 500);
\r
1542 if (window.removeEventListener) {
\r
1543 window.removeEventListener('focus', _handleFocus, false);
\r
1544 } else if (window.detachEvent) {
\r
1545 window.detachEvent('onfocus', _handleFocus);
\r
1549 _initComplete = function(bNoDisable) {
\r
1555 _s._wD('-- SoundManager 2: loaded --');
\r
1557 _processOnReady();
\r
1558 _initUserOnload();
\r
1561 var sClass = _s.oMC.className,
\r
1562 wasTimeout = (_s.useFlashBlock && _s.flashLoadTimeout && !_s.getMoviePercent());
\r
1563 if (!wasTimeout) {
\r
1566 _s._wD('-- SoundManager 2 ' + (_disabled?'failed to load':'loaded') + ' (' + (_disabled?'security/load error':'OK') + ') --', 1);
\r
1567 if (_disabled || bNoDisable) {
\r
1568 if (_s.useFlashBlock) {
\r
1569 _s.oMC.className = _getSWFCSS() + ' ' + (_s.getMoviePercent() === null?_s.swfCSS.swfTimedout:_s.swfCSS.swfError);
\r
1571 _processOnReady();
\r
1572 _debugTS('onload', false);
\r
1573 if (_s.onerror instanceof Function) {
\r
1574 _s.onerror.apply(window);
\r
1578 _debugTS('onload', true);
\r
1580 if (_s.waitForWindowLoad && !_windowLoaded) {
\r
1581 _wDS('waitOnload');
\r
1582 if (window.addEventListener) {
\r
1583 window.addEventListener('load', _initUserOnload, false);
\r
1584 } else if (window.attachEvent) {
\r
1585 window.attachEvent('onload', _initUserOnload);
\r
1589 if (_s.waitForWindowLoad && _windowLoaded) {
\r
1590 _wDS('docLoaded');
\r
1592 _initUserOnload();
\r
1596 _addOnReady = function(oMethod, oScope) {
\r
1598 'method': oMethod,
\r
1599 'scope': (oScope || null),
\r
1604 _processOnReady = function(ignoreInit) {
\r
1605 if (!_didInit && !ignoreInit) {
\r
1610 success: (ignoreInit?_s.supported():!_disabled)
\r
1613 canRetry = (!_s.useFlashBlock || (_s.useFlashBlock && !_s.supported()));
\r
1614 for (i = 0, j = _onready.length; i < j; i++) {
\r
1615 if (_onready[i].fired !== true) {
\r
1616 queue.push(_onready[i]);
\r
1619 if (queue.length) {
\r
1620 _s._wD(_sm + ': Firing ' + queue.length + ' onready() item' + (queue.length > 1?'s':''));
\r
1621 for (i = 0, j = queue.length; i < j; i++) {
\r
1622 if (queue[i].scope) {
\r
1623 queue[i].method.apply(queue[i].scope, [status]);
\r
1625 queue[i].method(status);
\r
1627 if (!canRetry) { // flashblock case doesn't count here
\r
1628 queue[i].fired = true;
\r
1634 _initUserOnload = function() {
\r
1635 window.setTimeout(function() {
\r
1636 if (_s.useFlashBlock) {
\r
1637 _flashBlockHandler();
\r
1639 _processOnReady();
\r
1640 _wDS('onload', 1);
\r
1641 // call user-defined "onload", scoped to window
\r
1642 _s.onload.apply(window);
\r
1643 _wDS('onloadOK', 1);
\r
1647 _featureCheck = function() {
\r
1648 var needsFlash, item,
\r
1649 isBadSafari = (!_wl.match(/usehtml5audio/i) && !_wl.match(/sm2\-ignorebadua/i) && _s.isSafari && _ua.match(/OS X 10_6_(3|4)/i) && _ua.match(/(531\.22\.7|533\.16|533\.17\.8)/i)), // Safari 4.0.5 (531.22.7), 5.0 (533.16), and 5.1 (533.17.8) have buggy/broken HTML5 audio on Snow Leopard. :/ Known Apple "radar" bug. https://bugs.webkit.org/show_bug.cgi?id=32159
\r
1650 isSpecial = (_ua.match(/iphone os (1|2|3_0|3_1)/i)?true:false); // iPhone <= 3.1 has broken HTML5 audio(), but firmware 3.2 (iPad) + iOS4 works.
\r
1652 _s.hasHTML5 = false; // has Audio(), but is broken; let it load links directly.
\r
1653 _html5Only = true; // ignore flash case, however
\r
1655 _s.oMC.style.display = 'none';
\r
1659 if (_s.useHTML5Audio) {
\r
1660 if (!_s.html5 || !_s.html5.canPlayType) {
\r
1661 _s._wD('SoundManager: No HTML5 Audio() support detected.');
\r
1662 _s.hasHTML5 = false;
\r
1665 _s.hasHTML5 = true;
\r
1667 if (isBadSafari) {
\r
1668 _s._wD('SoundManager::Note: Buggy HTML5 Audio in this specific browser + OS, see https://bugs.webkit.org/show_bug.cgi?id=32159 - disabling HTML5',1);
\r
1669 _s.useHTML5Audio = false;
\r
1670 _s.hasHTML5 = false;
\r
1674 // flash required.
\r
1677 for (item in _s.audioFormats) {
\r
1678 if (_s.audioFormats.hasOwnProperty(item)) {
\r
1679 if (_s.audioFormats[item].required && !_s.html5.canPlayType(_s.audioFormats[item].type)) {
\r
1680 // may need flash for this format?
\r
1681 needsFlash = true;
\r
1686 if (_s.ignoreFlash) {
\r
1687 needsFlash = false;
\r
1689 _html5Only = (_s.useHTML5Audio && _s.hasHTML5 && !needsFlash);
\r
1690 return needsFlash;
\r
1693 _init = function() {
\r
1694 var item, tests = [];
\r
1696 // called after onload()
\r
1703 function _cleanup() {
\r
1704 if (window.removeEventListener) {
\r
1705 window.removeEventListener('load', _s.beginDelayedInit, false);
\r
1706 } else if (window.detachEvent) {
\r
1707 window.detachEvent('onload', _s.beginDelayedInit);
\r
1711 if (_s.hasHTML5) {
\r
1712 for (item in _s.audioFormats) {
\r
1713 if (_s.audioFormats.hasOwnProperty(item)) {
\r
1714 tests.push(item+': '+_s.html5[item]);
\r
1717 _s._wD('-- SoundManager 2: HTML5 support tests ('+_s.html5Test+'): '+tests.join(', ')+' --',1);
\r
1722 // we don't need no steenking flash!
\r
1724 _s.enabled = true;
\r
1735 _s.o._externalInterfaceTest(false); // attempt to talk to Flash
\r
1736 if (!_s.allowPolling) {
\r
1737 _wDS('noPolling', 1);
\r
1739 _setPolling(true, _s.useFastPolling?true:false);
\r
1741 if (!_s.debugMode) {
\r
1742 _s.o._disableDebug();
\r
1744 _s.enabled = true;
\r
1745 _debugTS('jstoflash', true);
\r
1747 _s._wD('js/flash exception: ' + e.toString());
\r
1748 _debugTS('jstoflash', false);
\r
1749 _failSafely(true); // don't disable, for reboot()
\r
1758 _beginInit = function() {
\r
1759 if (_initPending) {
\r
1764 _initPending = true;
\r
1768 _dcLoaded = function() {
\r
1769 if (_didDCLoaded) {
\r
1772 _didDCLoaded = true;
\r
1775 _s.html5.usingFlash = _featureCheck();
\r
1776 _needsFlash = _s.html5.usingFlash;
\r
1777 _didDCLoaded = true;
\r
1781 _startTimer = function(oSound) {
\r
1782 if (!oSound._hasTimer) {
\r
1783 oSound._hasTimer = true;
\r
1787 _stopTimer = function(oSound) {
\r
1788 if (oSound._hasTimer) {
\r
1789 oSound._hasTimer = false;
\r
1793 // "private" methods called by Flash
\r
1795 this._setSandboxType = function(sandboxType) {
\r
1796 var sb = _s.sandbox;
\r
1797 sb.type = sandboxType;
\r
1798 sb.description = sb.types[(typeof sb.types[sandboxType] !== 'undefined'?sandboxType:'unknown')];
\r
1799 _s._wD('Flash security sandbox type: ' + sb.type);
\r
1800 if (sb.type === 'localWithFile') {
\r
1801 sb.noRemote = true;
\r
1802 sb.noLocal = false;
\r
1803 _wDS('secNote', 2);
\r
1804 } else if (sb.type === 'localWithNetwork') {
\r
1805 sb.noRemote = false;
\r
1806 sb.noLocal = true;
\r
1807 } else if (sb.type === 'localTrusted') {
\r
1808 sb.noRemote = false;
\r
1809 sb.noLocal = false;
\r
1813 this._externalInterfaceOK = function(flashDate) {
\r
1814 // callback from flash for confirming that movie loaded, EI is working etc.
\r
1815 // flashDate = approx. timing/delay info for JS/flash bridge
\r
1816 if (_s.swfLoaded) {
\r
1819 var eiTime = new Date().getTime();
\r
1820 _s._wD('soundManager::externalInterfaceOK()' + (flashDate?' (~' + (eiTime - flashDate) + ' ms)':''));
\r
1821 _debugTS('swf', true);
\r
1822 _debugTS('flashtojs', true);
\r
1823 _s.swfLoaded = true;
\r
1824 _tryInitOnFocus = false;
\r
1826 // IE needs a timeout OR delay until window.onload - may need TODO: investigating
\r
1827 setTimeout(_init, 100);
\r
1833 this._onfullscreenchange = function(bFullScreen) {
\r
1834 _s._wD('onfullscreenchange(): ' + bFullScreen);
\r
1835 _s.isFullScreen = (bFullScreen === 1?true:false);
\r
1836 if (!_s.isFullScreen) {
\r
1837 // attempt to restore window focus after leaving full-screen
\r
1840 _s._wD('window.focus()');
\r
1847 // --- SMSound (sound object) instance ---
\r
1849 SMSound = function(oOptions) {
\r
1850 var _t = this, _resetProperties, _add_html5_events, _stop_html5_timer, _start_html5_timer, _get_html5_duration, _a;
\r
1851 this.sID = oOptions.id;
\r
1852 this.url = oOptions.url;
\r
1853 this.options = _mergeObjects(oOptions);
\r
1854 this.instanceOptions = this.options; // per-play-instance-specific options
\r
1855 this._iO = this.instanceOptions; // short alias
\r
1856 // assign property defaults (volume, pan etc.)
\r
1857 this.pan = this.options.pan;
\r
1858 this.volume = this.options.volume;
\r
1859 // this.autoPlay = oOptions.autoPlay ? oOptions.autoPlay : false;
\r
1860 this._lastURL = null;
\r
1861 this.isHTML5 = false;
\r
1863 // --- public methods ---
\r
1867 Name/value pairs eg. this.id3.songname set via Flash when available - download docs for reference
\r
1868 http://livedocs.macromedia.com/flash/8/
\r
1872 this._debug = function() {
\r
1874 // pseudo-private console.log()-style output
\r
1875 if (_s.debugMode) {
\r
1876 var stuff = null, msg = [], sF, sfBracket, maxLength = 64;
\r
1877 for (stuff in _t.options) {
\r
1878 if (_t.options[stuff] !== null) {
\r
1879 if (_t.options[stuff] instanceof Function) {
\r
1880 // handle functions specially
\r
1881 sF = _t.options[stuff].toString();
\r
1882 sF = sF.replace(/\s\s+/g, ' '); // normalize spaces
\r
1883 sfBracket = sF.indexOf('{');
\r
1884 msg.push(' ' + stuff + ': {' + sF.substr(sfBracket + 1, (Math.min(Math.max(sF.indexOf('\n') - 1, maxLength), maxLength))).replace(/\n/g, '') + '... }');
\r
1886 msg.push(' ' + stuff + ': ' + _t.options[stuff]);
\r
1890 _s._wD('SMSound() merged options: {\n' + msg.join(', \n') + '\n}');
\r
1897 this.load = function(oOptions) {
\r
1899 if (typeof oOptions !== 'undefined') {
\r
1900 _t._iO = _mergeObjects(oOptions);
\r
1901 _t.instanceOptions = _t._iO;
\r
1903 oOptions = _t.options;
\r
1904 _t._iO = oOptions;
\r
1905 _t.instanceOptions = _t._iO;
\r
1906 if (_t._lastURL && _t._lastURL !== _t.url) {
\r
1908 _t._iO.url = _t.url;
\r
1912 _s._wD('soundManager.load(): ' + _t._iO.url, 1);
\r
1913 if (_t._iO.url === _t.url && _t.readyState !== 0 && _t.readyState !== 2) {
\r
1917 _t._lastURL = _t.url;
\r
1918 _t.loaded = false;
\r
1919 _t.readyState = 1;
\r
1920 _t.playState = 0; // (oOptions.autoPlay?1:0); // if autoPlay, assume "playing" is true (no way to detect when it actually starts in Flash unless onPlay is watched?)
\r
1921 if (_html5OK(_t._iO)) {
\r
1922 _s._wD('HTML 5 load: '+_t._iO.url);
\r
1923 oS = _t._setup_html5(_t._iO);
\r
1925 if (_t._iO.autoPlay) {
\r
1930 _t.isHTML5 = false;
\r
1931 _t._iO = _loopFix(_t._iO);
\r
1933 _s.o._load(_t.sID, _t._iO.url, _t._iO.stream, _t._iO.autoPlay, (_t._iO.whileloading?1:0), _t._iO.loops||1);
\r
1935 _s.o._load(_t.sID, _t._iO.url, _t._iO.stream?true:false, _t._iO.autoPlay?true:false, _t._iO.loops||1); // ,(_tO.whileloading?true:false)
\r
1936 if (_t._iO.isMovieStar && _t._iO.autoLoad && !_t._iO.autoPlay) {
\r
1937 // special case: MPEG4 content must start playing to load, then pause to prevent playing.
\r
1942 _wDS('smError', 2);
\r
1943 _debugTS('onload', false);
\r
1951 this.unload = function() {
\r
1952 // Flash 8/AS2 can't "close" a stream - fake it by loading an empty MP3
\r
1953 // Flash 9/AS3: Close stream, preventing further load
\r
1954 if (_t.readyState !== 0) {
\r
1955 _s._wD('SMSound.unload(): "' + _t.sID + '"');
\r
1956 if (_t.readyState !== 2) { // reset if not error
\r
1957 _t.setPosition(0, true); // reset current sound positioning
\r
1959 if (!_t.isHTML5) {
\r
1961 _s.o._unload(_t.sID, _s.nullURL);
\r
1963 _t.setAutoPlay(false); // ?
\r
1964 _s.o._unload(_t.sID);
\r
1967 _stop_html5_timer();
\r
1969 // abort()-style method here, stop loading? (doesn't exist?)
\r
1971 _a.src = _s.nullURL; // needed? does nulling object work? any better way to cancel/unload/abort?
\r
1975 // delete _t._audio;
\r
1978 // reset load/status flags
\r
1979 _resetProperties();
\r
1984 this.destruct = function() {
\r
1985 _s._wD('SMSound.destruct(): "' + _t.sID + '"');
\r
1986 if (!_t.isHTML5) {
\r
1987 // kill sound within Flash
\r
1988 // Disable the onfailure handler
\r
1989 _t._iO.onfailure = null;
\r
1990 _s.o._destroySound(_t.sID);
\r
1992 _stop_html5_timer();
\r
1995 _a.src = 'about:blank';
\r
1999 // delete _t._audio;
\r
2002 _s.destroySound(_t.sID, true); // ensure deletion from controller
\r
2005 this.play = function(oOptions) {
\r
2006 var fN = 'SMSound.play(): ', allowMulti;
\r
2010 _t._iO = _mergeObjects(oOptions, _t._iO);
\r
2011 _t._iO = _mergeObjects(_t._iO, _t.options);
\r
2012 _t.instanceOptions = _t._iO;
\r
2013 if (_t._iO.serverURL) {
\r
2014 if (!_t.connected) {
\r
2015 _s._wD(fN+' Netstream not connected yet - setting autoPlay');
\r
2016 _t.setAutoPlay(true);
\r
2020 if (_html5OK(_t._iO)) {
\r
2021 _t._setup_html5(_t._iO);
\r
2022 _start_html5_timer();
\r
2024 if (_t.playState === 1) {
\r
2025 allowMulti = _t._iO.multiShot;
\r
2026 if (!allowMulti) {
\r
2027 _s._wD(fN + '"' + _t.sID + '" already playing (one-shot)', 1);
\r
2030 _s._wD(fN + '"' + _t.sID + '" already playing (multi-shot)', 1);
\r
2033 _t.setPosition(_t._iO.position);
\r
2038 if (_t.readyState === 0) {
\r
2039 _s._wD(fN + 'Attempting to load "' + _t.sID + '"', 1);
\r
2040 // try to get this sound playing ASAP
\r
2041 //_t._iO.stream = true; // breaks stream=false case?
\r
2042 if (!_t.isHTML5) {
\r
2043 // HTML5 double-play bug otherwise.
\r
2044 if (!_t._iO.serverURL) {
\r
2045 _t._iO.autoPlay = true;
\r
2050 _t.readyState = 1;
\r
2052 } else if (_t.readyState === 2) {
\r
2053 _s._wD(fN + 'Could not load "' + _t.sID + '" - exiting', 2);
\r
2056 _s._wD(fN + '"' + _t.sID + '" is loading - attempting to play..', 1);
\r
2059 _s._wD(fN + '"' + _t.sID + '"');
\r
2061 if (_t.paused && _t.position !== null) { // https://gist.github.com/37b17df75cc4d7a90bf6
\r
2062 _s._wD(fN + '"' + _t.sID + '" is resuming from paused state',1);
\r
2065 _s._wD(fN+'"'+ _t.sID+'" is starting to play');
\r
2067 _t.paused = false; // https://gist.github.com/859638f341b25669b587
\r
2068 if (!_t.instanceCount || (_fV > 8 && !_t.isHTML5)) {
\r
2069 _t.instanceCount++;
\r
2071 _t.position = (typeof _t._iO.position !== 'undefined' && !isNaN(_t._iO.position)?_t._iO.position:0);
\r
2072 _t._iO = _loopFix(_t._iO);
\r
2073 if (_t._iO.onplay) {
\r
2074 _t._iO.onplay.apply(_t);
\r
2076 _t.setVolume(_t._iO.volume, true); // restrict volume to instance options only
\r
2077 _t.setPan(_t._iO.pan, true);
\r
2078 if (!_t.isHTML5) {
\r
2079 if (_fV === 9 && _t._iO.serverURL) {
\r
2080 // autoPlay for RTMP case
\r
2081 _t.setAutoPlay(true);
\r
2083 _s.o._start(_t.sID, _t._iO.loops || 1, (_fV === 9?_t.position:_t.position / 1000));
\r
2085 _start_html5_timer();
\r
2086 _t._setup_html5().play();
\r
2092 this.start = this.play; // just for convenience
\r
2094 this.stop = function(bAll) {
\r
2095 if (_t.playState === 1) {
\r
2096 _t._onbufferchange(0);
\r
2097 _t.resetOnPosition(0);
\r
2098 if (!_t.isHTML5) {
\r
2101 _t.paused = false;
\r
2102 if (_t._iO.onstop) {
\r
2103 _t._iO.onstop.apply(_t);
\r
2105 if (!_t.isHTML5) {
\r
2106 _s.o._stop(_t.sID, bAll);
\r
2107 // hack for netStream: just unload
\r
2108 if (_t._iO.serverURL) {
\r
2113 _t.setPosition(0); // act like Flash, though
\r
2114 _a.pause(); // html5 has no stop()
\r
2116 _t._onTimer(); // and update UI
\r
2117 _stop_html5_timer();
\r
2121 _t.instanceCount = 0;
\r
2123 // _t.instanceOptions = _t._iO;
\r
2128 this.setAutoPlay = function(autoPlay) {
\r
2129 // _s._wD('setAutoPlay('+autoPlay+')');
\r
2130 _t._iO.autoPlay = autoPlay;
\r
2131 _s.o._setAutoPlay(_t.sID, autoPlay);
\r
2133 // _t.playState = 1; // ?
\r
2134 if (!_t.instanceCount) {
\r
2135 _t.instanceCount++;
\r
2140 this.setPosition = function(nMsecOffset, bNoDebug) {
\r
2141 if (typeof nMsecOffset === 'undefined') {
\r
2144 var offset = (_t.isHTML5 ? Math.max(nMsecOffset,0) : Math.min(_t.duration, Math.max(nMsecOffset, 0))); // position >= 0 and <= current available (loaded) duration
\r
2145 _t._iO.position = offset;
\r
2146 _t.resetOnPosition(_t._iO.position);
\r
2147 if (!_t.isHTML5) {
\r
2148 _s.o._setPosition(_t.sID, (_fV === 9?_t._iO.position:_t._iO.position / 1000), (_t.paused || !_t.playState)); // if paused or not playing, will not resume (by playing)
\r
2150 _s._wD('setPosition(): setting position to '+(_t._iO.position / 1000));
\r
2151 if (_t.playState) {
\r
2152 // DOM/JS errors/exceptions to watch out for:
\r
2153 // if seek is beyond (loaded?) position, "DOM exception 11"
\r
2154 // "INDEX_SIZE_ERR": DOM exception 1
\r
2156 _a.currentTime = _t._iO.position / 1000;
\r
2158 _s._wD('setPosition('+_t._iO.position+'): WARN: Caught exception: '+e.message, 2);
\r
2161 _s._wD('HTML 5 warning: cannot set position while playState == 0 (not playing)',2);
\r
2163 if (_t.paused) { // if paused, refresh UI right away
\r
2164 _t._onTimer(true); // force update
\r
2165 // TODO: resume for movieStar only?
\r
2166 if (_t._iO.useMovieStar) {
\r
2174 this.pause = function(bCallFlash) {
\r
2175 // if (_t.paused || _t.playState === 0) {
\r
2176 if (_t.paused || (_t.playState === 0 && _t.readyState !== 1)) { // TODO: Verify vs. old
\r
2179 _s._wD('SMSound.pause()');
\r
2181 if (!_t.isHTML5) {
\r
2182 if (bCallFlash || bCallFlash === undefined) {
\r
2183 _s.o._pause(_t.sID);
\r
2186 _t._setup_html5().pause();
\r
2187 _stop_html5_timer();
\r
2189 if (_t._iO.onpause) {
\r
2190 _t._iO.onpause.apply(_t);
\r
2195 this.resume = function() {
\r
2196 if (!_t.paused || _t.playState === 0) {
\r
2199 _s._wD('SMSound.resume()');
\r
2200 _t.paused = false;
\r
2201 _t.playState = 1; // TODO: verify that this is needed.
\r
2202 if (!_t.isHTML5) {
\r
2203 _s.o._pause(_t.sID); // flash method is toggle-based (pause/resume)
\r
2205 _t._setup_html5().play();
\r
2206 _start_html5_timer();
\r
2208 if (_t._iO.onresume) {
\r
2209 _t._iO.onresume.apply(_t);
\r
2214 this.togglePause = function() {
\r
2215 _s._wD('SMSound.togglePause()');
\r
2216 if (_t.playState === 0) {
\r
2218 position: (_fV === 9 && !_t.isHTML5 ? _t.position:_t.position / 1000)
\r
2230 this.setPan = function(nPan, bInstanceOnly) {
\r
2231 if (typeof nPan === 'undefined') {
\r
2234 if (typeof bInstanceOnly === 'undefined') {
\r
2235 bInstanceOnly = false;
\r
2237 if (!_t.isHTML5) {
\r
2238 _s.o._setPan(_t.sID, nPan);
\r
2242 _t._iO.pan = nPan;
\r
2243 if (!bInstanceOnly) {
\r
2249 this.setVolume = function(nVol, bInstanceOnly) {
\r
2250 if (typeof nVol === 'undefined') {
\r
2253 if (typeof bInstanceOnly === 'undefined') {
\r
2254 bInstanceOnly = false;
\r
2256 if (!_t.isHTML5) {
\r
2257 _s.o._setVolume(_t.sID, (_s.muted && !_t.muted) || _t.muted?0:nVol);
\r
2259 _a.volume = nVol/100;
\r
2261 _t._iO.volume = nVol;
\r
2262 if (!bInstanceOnly) {
\r
2268 this.mute = function() {
\r
2270 if (!_t.isHTML5) {
\r
2271 _s.o._setVolume(_t.sID, 0);
\r
2278 this.unmute = function() {
\r
2280 var hasIO = typeof _t._iO.volume !== 'undefined';
\r
2281 if (!_t.isHTML5) {
\r
2282 _s.o._setVolume(_t.sID, hasIO?_t._iO.volume:_t.options.volume);
\r
2289 this.toggleMute = function() {
\r
2290 return (_t.muted?_t.unmute():_t.mute());
\r
2293 this.onposition = function(nPosition, oMethod, oScope) {
\r
2294 // todo: allow for ranges, too? eg. (nPosition instanceof Array)
\r
2295 _t._onPositionItems.push({
\r
2296 position: nPosition,
\r
2298 scope: (typeof oScope !== 'undefined'?oScope:_t),
\r
2304 this.processOnPosition = function() {
\r
2305 // sound currently playing?
\r
2306 var i, item, j = _t._onPositionItems.length;
\r
2307 if (!j || !_t.playState || _t._onPositionFired >= j) {
\r
2311 item = _t._onPositionItems[i];
\r
2312 if (!item.fired && _t.position >= item.position) {
\r
2313 item.method.apply(item.scope,[item.position]);
\r
2314 item.fired = true;
\r
2315 _s._onPositionFired++;
\r
2320 this.resetOnPosition = function(nPosition) {
\r
2321 // reset "fired" for items interested in this position
\r
2322 var i, item, j = _t._onPositionItems.length;
\r
2327 item = _t._onPositionItems[i];
\r
2328 if (item.fired && nPosition <= item.position) {
\r
2329 item.fired = false;
\r
2330 _s._onPositionFired--;
\r
2335 // pseudo-private soundManager reference
\r
2337 this._onTimer = function(bForce) {
\r
2338 // HTML 5-only _whileplaying() etc.
\r
2339 if (_t._hasTimer || bForce) {
\r
2341 if (_a && (bForce || ((_t.playState > 0 || _t.readyState === 1) && !_t.paused))) { // TODO: May not need to track readyState (1 = loading)
\r
2342 _t.duration = _get_html5_duration();
\r
2343 _t.durationEstimate = _t.duration;
\r
2344 time = _a.currentTime?_a.currentTime*1000:0;
\r
2345 _t._whileplaying(time,{},{},{},{});
\r
2349 _s._wD('_onTimer: Warn for "'+_t.sID+'": '+(!_a?'Could not find element. ':'')+(_t.playState === 0?'playState bad, 0?':'playState = '+_t.playState+', OK'));
\r
2355 // --- private internals ---
\r
2357 _get_html5_duration = function() {
\r
2358 var d = (_a?_a.duration*1000:undefined);
\r
2360 return (!isNaN(d)?d:null);
\r
2364 _start_html5_timer = function() {
\r
2370 _stop_html5_timer = function() {
\r
2376 _resetProperties = function(bLoaded) {
\r
2377 _t._onPositionItems = [];
\r
2378 _t._onPositionFired = 0;
\r
2379 _t._hasTimer = null;
\r
2380 _t._added_events = null;
\r
2383 _t.bytesLoaded = null;
\r
2384 _t.bytesTotal = null;
\r
2385 _t.position = null;
\r
2386 _t.duration = null;
\r
2387 _t.durationEstimate = null;
\r
2389 _t.loaded = false;
\r
2391 _t.paused = false;
\r
2392 _t.readyState = 0; // 0 = uninitialised, 1 = loading, 2 = failed/error, 3 = loaded/success
\r
2394 _t.didBeforeFinish = false;
\r
2395 _t.didJustBeforeFinish = false;
\r
2396 _t.isBuffering = false;
\r
2397 _t.instanceOptions = {};
\r
2398 _t.instanceCount = 0;
\r
2403 _t.waveformData = {
\r
2408 // dirty hack for now: also have left/right arrays off this, maintain compatibility
\r
2409 _t.eqData.left = [];
\r
2410 _t.eqData.right = [];
\r
2413 _resetProperties();
\r
2415 // pseudo-private methods used by soundManager
\r
2417 this._setup_html5 = function(oOptions) {
\r
2418 var _iO = _mergeObjects(_t._iO, oOptions);
\r
2420 if (_t.url !== _iO.url) {
\r
2421 _s._wD('setting new URL on existing object: '+_iO.url);
\r
2425 _s._wD('creating HTML 5 audio element with URL: '+_iO.url);
\r
2426 _t._audio = new Audio(_iO.url);
\r
2428 _t.isHTML5 = true;
\r
2429 _add_html5_events();
\r
2431 _a.loop = (_iO.loops>1?'loop':'');
\r
2435 // related private methods
\r
2437 _add_html5_events = function() {
\r
2438 if (_t._added_events) {
\r
2441 _t._added_events = true;
\r
2443 function _add(oEvt, oFn, bBubble) {
\r
2444 return (_a ? _a.addEventListener(oEvt, oFn, bBubble||false) : null);
\r
2447 _add('load', function(e) {
\r
2448 _s._wD('HTML5::load: '+_t.sID);
\r
2450 _t._onbufferchange(0);
\r
2451 _t._whileloading(_t.bytesTotal, _t.bytesTotal, _get_html5_duration());
\r
2456 _add('canplay', function(e) {
\r
2457 _s._wD('HTML5::canplay: '+_t.sID);
\r
2458 // enough has loaded to play
\r
2459 _t._onbufferchange(0);
\r
2462 _add('waiting', function(e) {
\r
2463 _s._wD('HTML5::waiting: '+_t.sID);
\r
2464 // playback faster than download rate, etc.
\r
2465 _t._onbufferchange(1);
\r
2468 _add('progress', function(e) { // not supported everywhere yet..
\r
2469 _s._wD('HTML5::progress: '+_t.sID+': loaded/total: '+(e.loaded||0)+','+(e.total||1));
\r
2470 if (!_t.loaded && _a) {
\r
2471 _t._onbufferchange(0); // if progress, likely not buffering
\r
2472 _t._whileloading(e.loaded||0, e.total||1, _get_html5_duration());
\r
2476 _add('error', function(e) {
\r
2478 _s._wD('HTML5::error: '+_a.error.code);
\r
2479 // call load with error state?
\r
2484 _add('loadstart', function(e) {
\r
2485 _s._wD('HTML5::loadstart: '+_t.sID);
\r
2486 // assume buffering at first
\r
2487 _t._onbufferchange(1);
\r
2490 _add('play', function(e) {
\r
2491 _s._wD('HTML5::play: '+_t.sID);
\r
2492 // once play starts, no buffering
\r
2493 _t._onbufferchange(0);
\r
2496 // TODO: verify if this is actually implemented anywhere yet.
\r
2497 _add('playing', function(e) {
\r
2498 _s._wD('HTML5::playing: '+_t.sID);
\r
2499 // once play starts, no buffering
\r
2500 _t._onbufferchange(0);
\r
2503 _add('timeupdate', function(e) {
\r
2507 // avoid stupid premature event-firing bug in Safari(?)
\r
2508 setTimeout(function(){
\r
2510 _add('ended',function(e) {
\r
2511 _s._wD('HTML5::ended: '+_t.sID);
\r
2519 // --- "private" methods called by Flash ---
\r
2521 this._whileloading = function(nBytesLoaded, nBytesTotal, nDuration, nBufferLength) {
\r
2522 _t.bytesLoaded = nBytesLoaded;
\r
2523 _t.bytesTotal = nBytesTotal;
\r
2524 _t.duration = Math.floor(nDuration);
\r
2525 if (!_t._iO.isMovieStar) {
\r
2526 _t.durationEstimate = parseInt((_t.bytesTotal / _t.bytesLoaded) * _t.duration, 10);
\r
2527 if (_t.durationEstimate === undefined) {
\r
2529 _t.durationEstimate = _t.duration;
\r
2531 _t.bufferLength = nBufferLength;
\r
2532 if ((_t._iO.isMovieStar || _t.readyState !== 3) && _t._iO.whileloading) {
\r
2533 _t._iO.whileloading.apply(_t);
\r
2536 _t.durationEstimate = _t.duration;
\r
2537 if (_t.readyState !== 3 && _t._iO.whileloading) {
\r
2538 _t._iO.whileloading.apply(_t);
\r
2543 this._onid3 = function(oID3PropNames, oID3Data) {
\r
2544 // oID3PropNames: string array (names)
\r
2545 // ID3Data: string array (data)
\r
2546 _s._wD('SMSound._onid3(): "' + this.sID + '" ID3 data received.');
\r
2547 var oData = [], i, j;
\r
2548 for (i = 0, j = oID3PropNames.length; i < j; i++) {
\r
2549 oData[oID3PropNames[i]] = oID3Data[i];
\r
2551 _t.id3 = _mergeObjects(_t.id3, oData);
\r
2552 if (_t._iO.onid3) {
\r
2553 _t._iO.onid3.apply(_t);
\r
2557 this._whileplaying = function(nPosition, oPeakData, oWaveformDataLeft, oWaveformDataRight, oEQData) {
\r
2559 if (isNaN(nPosition) || nPosition === null) {
\r
2560 return false; // Flash may return NaN at times
\r
2562 if (_t.playState === 0 && nPosition > 0) {
\r
2563 // can happen at the end of a video where nPosition === 33 for some reason, after finishing.???
\r
2564 // can also happen with a normal stop operation. This resets the position to 0.
\r
2567 _t.position = nPosition;
\r
2568 _t.processOnPosition();
\r
2569 if (_fV > 8 && !_t.isHTML5) {
\r
2570 if (_t._iO.usePeakData && typeof oPeakData !== 'undefined' && oPeakData) {
\r
2572 left: oPeakData.leftPeak,
\r
2573 right: oPeakData.rightPeak
\r
2576 if (_t._iO.useWaveformData && typeof oWaveformDataLeft !== 'undefined' && oWaveformDataLeft) {
\r
2577 _t.waveformData = {
\r
2578 left: oWaveformDataLeft.split(','),
\r
2579 right: oWaveformDataRight.split(',')
\r
2582 if (_t._iO.useEQData) {
\r
2583 if (typeof oEQData !== 'undefined' && oEQData && oEQData.leftEQ) {
\r
2584 var eqLeft = oEQData.leftEQ.split(',');
\r
2585 _t.eqData = eqLeft;
\r
2586 _t.eqData.left = eqLeft;
\r
2587 if (typeof oEQData.rightEQ !== 'undefined' && oEQData.rightEQ) {
\r
2588 _t.eqData.right = oEQData.rightEQ.split(',');
\r
2593 if (_t.playState === 1) {
\r
2594 // special case/hack: ensure buffering is false (instant load from cache, thus buffering stuck at 1?)
\r
2595 if (!_t.isHTML5 && _t.isBuffering) {
\r
2596 _t._onbufferchange(0);
\r
2598 if (_t._iO.whileplaying) {
\r
2599 _t._iO.whileplaying.apply(_t); // flash may call after actual finish
\r
2602 if ((_t.loaded || (!_t.loaded && _t._iO.isMovieStar)) && _t._iO.onbeforefinish && _t._iO.onbeforefinishtime && !_t.didBeforeFinish && _t.duration - _t.position <= _t._iO.onbeforefinishtime) {
\r
2603 _s._wD('duration-position <= onbeforefinishtime: ' + _t.duration + ' - ' + _t.position + ' <= ' + _t._iO.onbeforefinishtime + ' (' + (_t.duration - _t.position) + ')');
\r
2604 _t._onbeforefinish();
\r
2609 this._onconnect = function(bSuccess) {
\r
2610 var fN = 'SMSound._onconnect(): ';
\r
2611 bSuccess = (bSuccess === 1);
\r
2612 _s._wD(fN+'"'+_t.sID+'"'+(bSuccess?' connected.':' failed to connect? - '+_t.url), (bSuccess?1:2));
\r
2613 _t.connected = bSuccess;
\r
2616 if (_t._iO.autoLoad || _t._iO.autoPlay) {
\r
2619 if (_t._iO.autoPlay) {
\r
2622 if (_t._iO.onconnect) {
\r
2623 _t._iO.onconnect.apply(_t,[bSuccess]);
\r
2628 this._onload = function(nSuccess) {
\r
2629 var fN = 'SMSound._onload(): ';
\r
2630 nSuccess = (nSuccess === 1?true:false);
\r
2631 _s._wD(fN + '"' + _t.sID + '"' + (nSuccess?' loaded.':' failed to load? - ' + _t.url), (nSuccess?1:2));
\r
2633 if (!nSuccess && !_t.isHTML5) {
\r
2634 if (_s.sandbox.noRemote === true) {
\r
2635 _s._wD(fN + _str('noNet'), 1);
\r
2637 if (_s.sandbox.noLocal === true) {
\r
2638 _s._wD(fN + _str('noLocal'), 1);
\r
2642 _t.loaded = nSuccess;
\r
2643 _t.readyState = nSuccess?3:2;
\r
2644 if (_t._iO.onload) {
\r
2645 _t._iO.onload.apply(_t);
\r
2649 // Only fire the onfailure callback once because after one failure we often get another.
\r
2650 // At this point we just recreate failed sounds rather than trying to reconnect.
\r
2651 this._onfailure = function(msg) {
\r
2653 _s._wD('SMSound._onfailure(): "'+_t.sID+'" count '+_t.failures);
\r
2654 if (_t._iO.onfailure && _t.failures === 1) {
\r
2655 _t._iO.onfailure(_t, msg);
\r
2657 _s._wD('SMSound._onfailure(): ignoring');
\r
2661 this._onbeforefinish = function() {
\r
2662 if (!_t.didBeforeFinish) {
\r
2663 _t.didBeforeFinish = true;
\r
2664 if (_t._iO.onbeforefinish) {
\r
2665 _s._wD('SMSound._onbeforefinish(): "' + _t.sID + '"');
\r
2666 _t._iO.onbeforefinish.apply(_t);
\r
2671 this._onjustbeforefinish = function(msOffset) {
\r
2672 // msOffset: "end of sound" delay actual value (eg. 200 msec, value at event fire time was 187)
\r
2673 if (!_t.didJustBeforeFinish) {
\r
2674 _t.didJustBeforeFinish = true;
\r
2675 if (_t._iO.onjustbeforefinish) {
\r
2676 _s._wD('SMSound._onjustbeforefinish(): "' + _t.sID + '"');
\r
2677 _t._iO.onjustbeforefinish.apply(_t);
\r
2682 this._onfinish = function() {
\r
2683 // sound has finished playing
\r
2684 // TODO: calling user-defined onfinish() should happen after setPosition(0)
\r
2685 // OR: onfinish() and then setPosition(0) is bad.
\r
2686 _t._onbufferchange(0); // ensure buffer has ended
\r
2687 _t.resetOnPosition(0);
\r
2688 if (_t._iO.onbeforefinishcomplete) {
\r
2689 _t._iO.onbeforefinishcomplete.apply(_t);
\r
2691 // reset some state items
\r
2692 _t.didBeforeFinish = false;
\r
2693 _t.didJustBeforeFinish = false;
\r
2694 if (_t.instanceCount) {
\r
2695 _t.instanceCount--;
\r
2696 if (!_t.instanceCount) {
\r
2697 // reset instance options
\r
2698 // _t.setPosition(0);
\r
2700 _t.paused = false;
\r
2701 _t.instanceCount = 0;
\r
2702 _t.instanceOptions = {};
\r
2703 _stop_html5_timer();
\r
2705 // KJV May interfere with multi-shot events, but either way, instanceCount is sometimes 0 when it should not be.
\r
2706 if (!_t.instanceCount || _t._iO.multiShotEvents) {
\r
2707 // fire onfinish for last, or every instance
\r
2708 if (_t._iO.onfinish) {
\r
2709 _s._wD('SMSound._onfinish(): "' + _t.sID + '"');
\r
2710 _t._iO.onfinish.apply(_t);
\r
2711 } else if (_t.isHTML5) {
\r
2712 // assume safe to unload, etc.
\r
2719 this._onmetadata = function(oMetaData) {
\r
2720 // movieStar mode only
\r
2721 var fN = 'SMSound.onmetadata()';
\r
2723 // Contains a subset of metadata. Note that files may have their own unique metadata.
\r
2724 // http://livedocs.adobe.com/flash/9.0/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00000267.html
\r
2725 if (!oMetaData.width && !oMetaData.height) {
\r
2727 oMetaData.width = 320;
\r
2728 oMetaData.height = 240;
\r
2730 _t.metadata = oMetaData; // potentially-large object from flash
\r
2731 _t.width = oMetaData.width;
\r
2732 _t.height = oMetaData.height;
\r
2733 if (_t._iO.onmetadata) {
\r
2734 _s._wD(fN + ': "' + _t.sID + '"');
\r
2735 _t._iO.onmetadata.apply(_t);
\r
2737 _s._wD(fN + ' complete');
\r
2740 this._onbufferchange = function(nIsBuffering) {
\r
2741 var fN = 'SMSound._onbufferchange()';
\r
2742 if (_t.playState === 0) {
\r
2743 // ignore if not playing
\r
2746 if ((nIsBuffering && _t.isBuffering) || (!nIsBuffering && !_t.isBuffering)) {
\r
2747 // _s._wD(fN + ': Note: buffering already = '+nIsBuffering);
\r
2750 _t.isBuffering = (nIsBuffering === 1?true:false);
\r
2751 if (_t._iO.onbufferchange) {
\r
2752 _s._wD(fN + ': ' + nIsBuffering);
\r
2753 _t._iO.onbufferchange.apply(_t);
\r
2757 this._ondataerror = function(sError) {
\r
2758 // flash 9 wave/eq data handler
\r
2759 if (_t.playState > 0) { // hack: called at start, and end from flash at/after onfinish().
\r
2760 _s._wD('SMSound._ondataerror(): ' + sError);
\r
2761 if (_t._iO.ondataerror) {
\r
2762 _t._iO.ondataerror.apply(_t);
\r
2770 // register a few event handlers
\r
2772 if (!_s.hasHTML5 || _needsFlash) {
\r
2773 // only applies to Flash mode.
\r
2774 if (window.addEventListener) {
\r
2775 window.addEventListener('focus', _handleFocus, false);
\r
2776 window.addEventListener('load', _s.beginDelayedInit, false);
\r
2777 window.addEventListener('unload', _s.destruct, false);
\r
2778 if (_tryInitOnFocus) {
\r
2779 window.addEventListener('mousemove', _handleFocus, false); // massive Safari focus hack
\r
2781 } else if (window.attachEvent) {
\r
2782 window.attachEvent('onfocus', _handleFocus);
\r
2783 window.attachEvent('onload', _s.beginDelayedInit);
\r
2784 window.attachEvent('unload', _s.destruct);
\r
2786 // no add/attachevent support - safe to assume no JS -> Flash either.
\r
2787 _debugTS('onload', false);
\r
2788 soundManager.onerror();
\r
2789 soundManager.disable();
\r
2793 _dcIE = function() {
\r
2794 if (document.readyState === 'complete') {
\r
2796 document.detachEvent('onreadystatechange', _dcIE);
\r
2800 if (document.addEventListener) {
\r
2801 document.addEventListener('DOMContentLoaded', _dcLoaded, false);
\r
2802 } else if (document.attachEvent) {
\r
2803 document.attachEvent('onreadystatechange', _dcIE);
\r
2806 if (document.readyState === 'complete') {
\r
2807 setTimeout(_dcLoaded,100);
\r
2810 } // SoundManager()
\r
2812 // var SM2_DEFER = true;
\r
2813 // un-comment here or define in your own script to prevent immediate SoundManager() constructor call+start-up.
\r
2815 // if deferring, construct later with window.soundManager = new SoundManager(); followed by soundManager.beginDelayedInit();
\r
2817 if (typeof SM2_DEFER === 'undefined' || !SM2_DEFER) {
\r
2818 soundManager = new SoundManager();
\r
2821 // expose public interfaces
\r
2822 window.SoundManager = SoundManager; // SoundManager constructor
\r
2823 window.soundManager = soundManager; // public instance: API, Flash callbacks etc.
\r
2825 }(window)); // invocation closure
\r