PageSourceSearch

https://yyc-git.github.io/lianliankan/SoundManager/soundmanager2.js

js yyc-git.github.io collected 2026-10-03 10:15:25 UTC 60,816 bytes, 1,820 lines download raw bytes

1/*!
2   SoundManager 2: Javascript Sound for the Web
3   --------------------------------------------
4   http://schillmania.com/projects/soundmanager2/
5
6   Copyright (c) 2008, Scott Schiller. All rights reserved.
7   Code licensed under the BSD License:
8   http://schillmania.com/projects/soundmanager2/license.txt
9
10   V2.95a.20090717
11*/
12
13var soundManager = null;
14
15function SoundManager(smURL,smID) {
16 
17  this.flashVersion = 8;           // version of flash to require, either 8 or 9. Some API features require Flash 9.
18  this.debugMode = true;           // enable debugging output (div#soundmanager-debug, OR console if available + configured)
19  this.useConsole = true;          // use firebug/safari console.log()-type debug console if available
20  this.consoleOnly = false;        // if console is being used, do not create/write to #soundmanager-debug
21  this.waitForWindowLoad = false;  // force SM2 to wait for window.onload() before trying to call soundManager.onload()
22  this.nullURL = 'null.mp3';       // path to "null" (empty) MP3 file, used to unload sounds (Flash 8 only)
23  this.allowPolling = true;        // allow flash to poll for status update (required for whileplaying() events, peak, sound spectrum functions to work.)
24  this.useFastPolling = false;     // uses 1 msec flash timer interval (vs. default of 20) for higher callback frequency, best combined with useHighPerformance
25  this.useMovieStar = false;	   // enable support for Flash 9.0r115+ (codename "MovieStar") MPEG4 audio + video formats (AAC, M4V, FLV, MOV etc.)
26  this.bgColor = '#ffffff';	       // movie (.swf) background color, '#000000' useful if showing on-screen/full-screen video etc.
27  this.useHighPerformance = false; // position:fixed flash movie can help increase js/flash speed, minimize lag
28  this.flashLoadTimeout = 1000;    // msec to wait for flash movie to load before failing (0 = infinity)
29  this.wmode = null;	   		   // mode to render the flash movie in - null, transparent, opaque (last two allow layering of HTML on top)
30  this.allowFullScreen = true;     // enter full-screen (via double-click on movie) for flash 9+ video
31
32  this.defaultOptions = {
33    '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)
34    'stream': true,                // allows playing before entire file has loaded (recommended)
35    'autoPlay': false,             // enable playing of file as soon as possible (much faster if "stream" is true)
36    'onid3': null,                 // callback function for "ID3 data is added/available"
37    'onload': null,                // callback function for "load finished"
38    'whileloading': null,          // callback function for "download progress update" (X of Y bytes received)
39    'onplay': null,                // callback for "play" start
40    'onpause': null,               // callback for "pause"
41    'onresume': null,              // callback for "resume" (pause toggle)
42    'whileplaying': null,          // callback during play (position update)
43    'onstop': null,                // callback for "user stop"
44    'onfinish': null,              // callback function for "sound finished playing"
45    'onbeforefinish': null,        // callback for "before sound finished playing (at [time])"
46    'onbeforefinishtime': 5000,    // offset (milliseconds) before end of sound to trigger beforefinish (eg. 1000 msec = 1 second)
47    'onbeforefinishcomplete':null, // function to call when said sound finishes playing
48    'onjustbeforefinish':null,     // callback for [n] msec before end of current sound
49    'onjustbeforefinishtime':200,  // [n] - if not using, set to 0 (or null handler) and event will not fire.
50    'multiShot': true,             // let sounds "restart" or layer on top of each other when played multiple times, rather than one-shot/one at a time
51    'multiShotEvents': false,      // fire multiple sound events (currently onfinish() only) when multiShot is enabled
52    'position': null,              // offset (milliseconds) to seek to within loaded sound data.
53    'pan': 0,                      // "pan" settings, left-to-right, -100 to 100
54    'volume': 100                  // self-explanatory. 0-100, the latter being the max.
55  };
56
57  this.flash9Options = {           // flash 9-only options, merged into defaultOptions if flash 9 is being used
58    'isMovieStar': null,	  	   // "MovieStar" MPEG4 audio/video mode. Null (default) = auto detect MP4, AAC etc. based on URL. true = force on, ignore URL
59    'usePeakData': false,          // enable left/right channel peak (level) data
60    'useWaveformData': false,      // enable sound spectrum (raw waveform data) - WARNING: CPU-INTENSIVE: may set CPUs on fire.
61    'useEQData': false,            // enable sound EQ (frequency spectrum data) - WARNING: Also CPU-intensive.
62    'onbufferchange': null,	   	   // callback for "isBuffering" property change
63    'ondataerror': null			   // callback for waveform/eq data access error (flash playing audio in other tabs/domains)
64  };
65
66  this.movieStarOptions = {        // flash 9.0r115+ MPEG4 audio/video options, merged into defaultOptions if flash 9 + movieStar mode is enabled
67    'onmetadata': null,		   	   // callback for when video width/height etc. are received
68    'useVideo': false,		   	   // if loading movieStar content, whether to show video
69    'bufferTime': null		   	   // seconds of data to buffer before playback begins (null = flash default of 0.1 seconds - if AAC playback is gappy, try up to 3 seconds)
70  };
71
72  // jslint global declarations
73  /*global SM2_DEFER, sm2Debugger, alert, console, document, navigator, setTimeout, window */
74
75  var SMSound = null; // defined later
76
77  var _s = this;
78  this.version = null;
79  this.versionNumber = 'V2.95a.20090717';
80  this.movieURL = null;
81  this.url = null;
82  this.altURL = null;
83  this.swfLoaded = false;
84  this.enabled = false;
85  this.o = null;
86  this.id = (smID||'sm2movie');
87  this.oMC = null;
88  this.sounds = {};
89  this.soundIDs = [];
90  this.muted = false;
91  this.isFullScreen = false; // set later by flash 9+
92  this.isIE = (navigator.userAgent.match(/MSIE/i));
93  this.isSafari = (navigator.userAgent.match(/safari/i));
94  this.debugID = 'soundmanager-debug';
95  this.debugURLParam = /([#?&])debug=1/i;
96  this.specialWmodeCase = false;
97  this._onready = [];
98  this._debugOpen = true;
99  this._didAppend = false;
100  this._appendSuccess = false;
101  this._didInit = false;
102  this._disabled = false;
103  this._windowLoaded = false;
104  this._hasConsole = (typeof console != 'undefined' && typeof console.log != 'undefined');
105  this._debugLevels = ['log','info','warn','error'];
106  this._defaultFlashVersion = 8;
107  this._oRemoved = null;
108  this._oRemovedHTML = null;
109
110  var _$ = function(sID) {
111    return document.getElementById(sID);
112  };
113
114  this.filePatterns = {
115	flash8: /\.mp3(\?.*)?$/i,
116	flash9: /\.mp3(\?.*)?$/i
117  };
118
119  this.netStreamTypes = ['aac','flv','mov','mp4','m4v','f4v','m4a','mp4v','3gp','3g2']; // Flash v9.0r115+ "moviestar" formats
120  this.netStreamPattern = new RegExp('\\.('+this.netStreamTypes.join('|')+')(\\?.*)?$','i');
121
122  this.filePattern = null;
123  this.features = {
124	buffering: false,
125    peakData: false,
126    waveformData: false,
127    eqData: false,
128    movieStar: false
129  };
130
131  this.sandbox = {
132    'type': null,
133    'types': {
134      'remote': 'remote (domain-based) rules',
135      'localWithFile': 'local with file access (no internet access)',
136      'localWithNetwork': 'local with network (internet access only, no local access)',
137      'localTrusted': 'local, trusted (local + internet access)'
138    },
139    'description': null,
140    'noRemote': null,
141    'noLocal': null
142  };
143
144  this._setVersionInfo = function() {
145    if (_s.flashVersion != 8 && _s.flashVersion != 9) {
146      alert('soundManager.flashVersion must be 8 or 9. "'+_s.flashVersion+'" is 
146invalid. Reverting to '+_s._defaultFlashVersion+'.');
147      _s.flashVersion = _s._defaultFlashVersion;
148    }
149    _s.version = _s.versionNumber+(_s.flashVersion==9?' (AS3/Flash 9)':' (AS2/Flash 8)');
150    // set up default options
151	if (_s.flashVersion > 8) {
152	  _s.defaultOptions = _s._mergeObjects(_s.defaultOptions,_s.flash9Options);
153	  _s.features.buffering = true;
154	}
155    if (_s.flashVersion > 8 && _s.useMovieStar) {
156      // flash 9+ support for movieStar formats as well as MP3
157      _s.defaultOptions = _s._mergeObjects(_s.defaultOptions,_s.movieStarOptions);
158      _s.filePatterns.flash9 = new RegExp('\\.(mp3|'+_s.netStreamTypes.join('|')+')(\\?.*)?$','i');
159      _s.features.movieStar = true;
160    } else {
161      _s.useMovieStar = false;
162      _s.features.movieStar = false;
163    }
164    _s.filePattern = _s.filePatterns[(_s.flashVersion!=8?'flash9':'flash8')];
165    _s.movieURL = (_s.flashVersion==8?'soundmanager2.swf':'soundmanager2_flash9.swf');
166    _s.features.peakData = _s.features.waveformData = _s.features.eqData = (_s.flashVersion>8);
167  };
168
169  this._overHTTP = (document.location?document.location.protocol.match(/http/i):null);
170  this._waitingforEI = false;
171  this._initPending = false;
172  this._tryInitOnFocus = (this.isSafari && typeof document.hasFocus == 'undefined');
173  this._isFocused = (typeof document.hasFocus != 'undefined'?document.hasFocus():null);
174  this._okToDisable = !this._tryInitOnFocus;
175
176  this.useAltURL = !this._overHTTP; // use altURL if not "online"
177
178  var flashCPLink = 'http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html';
179
180  this.strings = {
181    notReady: 'Not loaded yet - wait for soundManager.onload() before calling sound-related methods',
182    appXHTML: 'soundManager._createMovie(): appendChild/innerHTML set failed. May be app/xhtml+xml DOM-related.',
183    localFail: 'soundManager: Loading this page from local/network file system (not over HTTP?) Flash security likely restricting JS-Flash access. Consider adding current URL to "trusted locations" in the Flash player security settings manager at '+flashCPLink+', or simply serve this content over HTTP.',
184    waitFocus: 'soundManager: Special case: Waiting for focus-related event..',
185    waitImpatient: 'soundManager: Getting impatient, still waiting for Flash.. ;)',
186    waitForever: 'soundManager: Waiting indefinitely for Flash...',
187    needFunction: 'soundManager.onready(): Function object expected'
188  };
189
190  // --- public methods ---
191  
192  this.supported = function() {
193    return (_s._didInit && !_s._disabled);
194  };
195
196  this.getMovie = function(smID) {
197    return _s.isIE?window[smID]:(_s.isSafari?_$(smID)||document[smID]:_$(smID));
198  };
199
200  this.loadFromXML = function(sXmlUrl) {
201    try {
202      _s.o._loadFromXML(sXmlUrl);
203    } catch(e) {
204      _s._failSafely();
205      return true;
206    }
207  };
208
209  this.createSound = function(oOptions) {
210	var _cs = 'soundManager.createSound(): ';
211    if (!_s._didInit) {
212	  throw _s._complain(_cs+_s.strings.notReady,arguments.callee.caller);
213	}
214    if (arguments.length == 2) {
215      // function overloading in JS! :) ..assume simple createSound(id,url) use case
216      oOptions = {'id':arguments[0],'url':arguments[1]};
217    }
218    var thisOptions = _s._mergeObjects(oOptions); // inherit SM2 defaults
219    var _tO = thisOptions; // alias
220    if (_tO.id.toString().charAt(0).match(/^[0-9]$/)) { // hopefully this isn't buggy regexp-fu. :D
221	  var complaint = _cs+'Warning: Sound ID "'+_tO.id+'" should be a string, starting with a non-numeric character';
222	  _s._wD(complaint,2);
223	}
224    _s._wD(_cs+_tO.id+' ('+_tO.url+')',1);
225    if (_s._idCheck(_tO.id,true)) {
226      _s._wD(_cs+_tO.id+' exists',1);
227      return _s.sounds[_tO.id];
228    }
229    if (_s.flashVersion > 8 && _s.useMovieStar) {
230	  if (_tO.isMovieStar === null) {
231	    _tO.isMovieStar = (_tO.url.match(_s.netStreamPattern)?true:false);
232	  }
233	  if (_tO.isMovieStar) {
234	    _s._wD(_cs+'using MovieStar handling');
235	  }
236	  if (_tO.isMovieStar && (_tO.usePeakData || _tO.useWaveformData || _tO.useEQData)) {
237	    _s._wD('Warning: peak/waveform/eqData features unsupported for non-MP3 formats');
238	    _tO.usePeakData = false;
239		_tO.useWaveformData = false;
240		_tO.useEQData = false;
241	  }
242    }
243    _s.sounds[_tO.id] = new SMSound(_tO);
244    _s.soundIDs[_s.soundIDs.length] = _tO.id;
245    // AS2:
246    if (_s.flashVersion == 8) {
247      _s.o._createSound(_tO.id,_tO.onjustbeforefinishtime);
248    } else {
249      _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));
250    }
251    if (_tO.autoLoad || _tO.autoPlay) {
252      // TODO: does removing timeout here cause problems?
253        if (_s.sounds[_tO.id]) {
254          _s.sounds[_tO.id].load(_tO);
255        }
256    }
257    if (_tO.autoPlay) {
258	  _s.sounds[_tO.id].play();
259	}
260    return _s.sounds[_tO.id];
261  };
262
263  this.createVideo = function(oOptions) {
264    if (arguments.length==2) {
265      oOptions = {'id':arguments[0],'url':arguments[1]};
266    }
267    if (_s.flashVersion >= 9) {
268      oOptions.isMovieStar = true;
269      oOptions.useVideo = true;
270    } else {
271      _s._wD('soundManager.createVideo(): flash 9 required for video. Exiting.',2);
272      return false;
273    }
274    if (!_s.useMovieStar) {
275      _s._wD('soundManager.createVideo(): MovieStar mode not enabled. Exiting.',2);
276    }
277    return _s.createSound(oOptions);
278  };
279
280  this.destroySound = function(sID,bFromSound) {
281    // explicitly destroy a sound before normal page unload, etc.
282    if (!_s._idCheck(sID)) {
283      return false;
284    }
285    for (var i=0; i<_s.soundIDs.length; i++) {
286      if (_s.soundIDs[i] == sID) {
287	    _s.soundIDs.splice(i,1);
288        continue;
289      }
290    }
291    // conservative option: avoid crash with ze flash 8
292    // calling destroySound() within a sound onload() might crash firefox, certain flavours of winXP + flash 8??
293    // if (_s.flashVersion != 8) {
294      _s.sounds[sID].unload();
295    // }
296    if (!bFromSound) {
297      // ignore if being called from SMSound instance
298      _s.sounds[sID].destruct();
299    }
300    delete _s.sounds[sID];
301  };
302
303  this.destroyVideo = this.destroySound;
304
305  this.load = function(sID,oOptions) {
306    if (!_s._idCheck(sID)) {
307      return false;
308    }
309    _s.sounds[sID].load(oOptions);
310  };
311
312  this.unload = function(sID) {
313    if (!_s._idCheck(sID)) {
314      return false;
315    }
316    _s.sounds[sID].unload();
317  };
318
319  this.play = function(sID,oOptions) {
320    if (!_s._didInit) {
321	  throw _s._complain('soundManager.play(): '+_s.strings.notReady,arguments.callee.caller);
322	}
323    if (!_s._idCheck(sID)) {
324      if (typeof oOptions != 'Object') {
325		oOptions = {url:oOptions}; // overloading use case: play('mySound','/path/to/some.mp3');
326	  }
327      if (oOptions && oOptions.url) {
328        // overloading use case, creation + playing of sound: .play('someID',{url:'/path/to.mp3'});
329        _s._wD('soundController.play(): attempting to create "'+sID+'"',1);
330        oOptions.id = sID;
331        _s.createSound(oOptions);
332      } else {
333        return false;
334      }
335    }
336    _s.sounds[sID].play(oOptions);
337  };
338
339  this.start = this.play; // just for convenience
340
341  this.setPosition = function(sID,nMsecOffset) {
342    if (!_s._idCheck(sID)) {
343      return false;
344    }
345    _s.sounds[sID].setPosition(nMsecOffset);
346  };
347
348  this.stop = function(sID) {
349    if (!_s._idCheck(sID)) {
350	  return false;
351	}
352    _s._wD('soundManager.stop('+sID+')',1);
353    _s.sounds[sID].stop(); 
354  };
355
356  this.stopAll = function() {
357    _s._wD('soundManager.stopAll()',1);
358    for (var oSound in _s.sounds) {
359      if (_s.sounds[oSound] instanceof SMSound) {
360		_s.sounds[oSound].stop(); // apply only to sound objects
361	  }
362    }
363  };
364
365  this.pause = function(sID) {
366    if (!_s._idCheck(sID)) {
367	  return false;
368	}
369    _s.sounds[sID].pause();
370  };
371
372  this.pauseAll = function() {
373    for (var i=_s.soundIDs.length; i--;) {
374      _s.sounds[_s.soundIDs[i]].pause();
375    }
376  };
377
378  this.resume = function(sID) {
379    if (!_s._idCheck(sID)) {
380	  return false;
381	}
382    _s.sounds[sID].resume();
383  };
384
385  this.resumeAll = function() {
386    for (var i=_s.soundIDs.length; i--;) {
387      _s.sounds[_s.soundIDs[i]].resume();
388    }
389  };
390
391  this.togglePause = function(sID) {
392    if (!_s._idCheck(sID)) {
393	  return false;
394	}
395    _s.sounds[sID].togglePause();
396  };
397
398  this.setPan = function(sID,nPan) {
399    if (!_s._idCheck(sID)) {
400	  return false;
401	}
402    _s.sounds[sID].setPan(nPan);
403  };
404
405  this.setVolume = function(sID,nVol) {
406    if (!_s._idCheck(sID)) {
407	  return false;
408	}
409    _s.sounds[sID].setVolume(nVol);
410  };
411
412  this.mute = function(sID) {
413	if (typeof sID != 'string') {
414	  sID = null;
415	}
416    if (!sID) {
417      _s._wD('soundManager.mute(): Muting all sounds');
418      for (var i=_s.soundIDs.length; i--;) {
419        _s.sounds[_s.soundIDs[i]].mute();
420      }
421      _s.muted = true;
422    } else {
423      if (!_s._idCheck(sID)) {
424	    return false;
425	  }
426      _s._wD('soundManager.mute(): Muting "'+sID+'"');
427      _s.sounds[sID].mute();
428    }
429  };
430
431  this.muteAll = function() {
432    _s.mute();
433  };
434
435  this.unmute = function(sID) {
436    if (typeof sID != 'string') {
437	  sID = null;
438	}
439    if (!sID) {
440      _s._wD('soundManager.unmute(): Unmuting all sounds');
441      for (var i=_s.soundIDs.length; i--;) {
442        _s.sounds[_s.soundIDs[i]].unmute();
443      }
444      _s.muted = false;
445    } else {
446      if (!_s._idCheck(sID)) {
447		return false;
448	  }
449      _s._wD('soundManager.unmute(): Unmuting "'+sID+'"');
450      _s.sounds[sID].unmute();
451    }
452  };
453
454  this.unmuteAll = function() {
455    _s.unmute();
456  };
457
458  this.toggleMute = function(sID) {
459    if (!_s._idCheck(sID)) {
460	return false;
461    }
462    _s.sounds[sID].toggleMute();
463  };
464
465  this.getMemoryUse = function() {
466    if (_s.flashVersion == 8) {
467      // not supported in Flash 8
468      return 0;
469    }
470    if (_s.o) {
471      return parseInt(_s.o._getMemoryUse(),10);
472    }
473  };
474
475  this.disable = function(bNoDisable) {
476    // destroy all functions
477    if (typeof bNoDisable == 'undefined') {
478      bNoDisable = false;
479    }
480    if (_s._disabled) {
481	  return false;
482    }
483    _s._disabled = true;
484    _s._wD('soundManager.disable(): Shutting down',1);
485    for (var i=_s.soundIDs.length; i--;) {
486      _s._disableObject(_s.sounds[_s.soundIDs[i]]);
487    }
488    _s.initComplete(bNoDisable); // fire "complete", despite fail
489    // _s._disableObject(_s); // taken out to allow reboot()
490  };
491
492  this.canPlayURL = function(sURL) {
493    return (sURL?(sURL.match(_s.filePattern)?true:false):null);	
494  };
495
496  this.getSoundById = function(sID,suppressDebug) {
497    if (!sID) {
498	  throw new Error('SoundManager.getSoundById(): sID is null/undefined');
499	}
500    var result = _s.sounds[sID];
501    if (!result && !suppressDebug) {
502      _s._wD('"'+sID+'" is an invalid sound ID.',2);
503      // soundManager._wD('trace: '+arguments.callee.caller);
504    }
505    return result;
506  };
507
508  this.onready = function(oMethod,oScope) {
509    // queue a callback, with optional scope
510    // a status object will be passed to your handler
511	/*
512    soundManager.onready(function(oStatus) {
513	  alert('SM2 init success: '+oStatus.success);
514	});
515	*/
516    if (oMethod && oMethod instanceof Function) {
517      if (_s._didInit) {
518        _s._wD('soundManager.onready(): Queueing handler');
519      }
520      if (!oScope) {
521        oScope = window;
522      }
523      _s._addOnReady(oMethod,oScope);
524      _s._processOnReady();
525      return true;
526	} else {
527	  throw _s.strings.needFunction;
528	}
529  };
530
531  this.oninitmovie = function() {
532    // called after SWF has been appended to the DOM via JS (or retrieved from HTML)
533    // this is a stub for your own scripts.
534  };
535
536  this.onload = function() {
537    // window.onload() equivalent for SM2, ready to create sounds etc.
538    // this is a stub for your own scripts.
539    soundManager._wD('soundManager.onload()',1);
540  };
541
542  this.onerror = function() {
543    // stub for user handler, called when SM2 fails to load/init
544  };
545
546  // --- "private" methods ---
547
548  this._idCheck = this.getSoundById;
549
550  this._complain = function(sMsg,oCaller) {
551    // Try to create meaningful custom errors, w/stack trace to the "offending" line
552    var sPre = 'Error: ';
553    if (!oCaller) {
554      return new Error(sPre+sMsg);
555    }
556    var e = new Error(''); // make a mistake.
557    var stackMsg = null;
558    if (e.stack) {
559      // potentially dangerous: Try to return a meaningful stacktrace where provided (Mozilla)
560  	  try {
561	    var splitChar = '@';
562	    var stackTmp = e.stack.split(splitChar);
563	    stackMsg = stackTmp[4]; // try to return only the relevant bit, skipping internal SM2 shiz
564	  } catch(ee) {
565	    // oops.
566	    stackMsg = e.stack;
567	  }
568	}
569	if (typeof console != 'undefined' && typeof console.trace != 'undefined') {
570	  console.trace();
571	}
572	var errorDesc = sPre+sMsg+'. \nCaller: '+oCaller.toString()+(e.stack?' \nTop of stacktrace: '+stackMsg:(e.message?' \nMessage: '+e.message:''));
573	// See JS error/debug/console output for real error source, stack trace / message detail where possible.
574	return new Error(errorDesc);
575  };
576
577  var _doNothing = function() {
578    return false;
579  };
580
581  _doNothing._protected = true;
582
583  this._disableObject = function(o) {
584    for (var oProp in o) {
585      if (typeof o[oProp] == 'function' && typeof o[oProp]._protected == 'undefined') {
586		o[oProp] = _doNothing;
587	  }
588    }
589    oProp = null;
590  };
591
592  this._failSafely = function(bNoDisable) {
593    // general failure exception handler
594    if (typeof bNoDisable == 'undefined') {
595      bNoDisable = false;
596    }
597    if (!_s._disabled || bNoDisable) {
598      _s._wD('soundManager: Failed to initialise.',2);
599      _s.disable(bNoDisable);
600    }
601  };
602  
603  this._normalizeMovieURL = function(smURL) {
604    var urlParams = null;
605    if (smURL) {
606      if (smURL.match(/\.swf(\?.*)?$/i)) {
607        urlParams = smURL.substr(smURL.toLowerCase().lastIndexOf('.swf?')+4);
608        if (urlParams) {
609          return smURL; // assume user knows what they're doing
610        }
611      } else if (smURL.lastIndexOf('/') != smURL.length-1) {
612        smURL = smURL+'/';
613      }
614    }
615    return(smURL && smURL.lastIndexOf('/')!=-1?smURL.substr(0,smURL.lastIndexOf('/')+1):'./')+_s.movieURL;
616  };
617
618  this._getDocument = function() {
619    return (document.body?document.body:(document.documentElement?document.documentElement:document.getElementsByTagName('div')[0]));
620  };
621
622  this._getDocument._protected = true;
623
624  this._setPolling = function(bPolling,bHighPerformance) {
625    if (!_s.o || !_s.allowPolling) {
626	  return false;
627	}
628    _s.o._setPolling(bPolling,bHighPerformance);
629  };
630
631  this._createMovie = function(smID,smURL) {
632    var specialCase = null;
633    var remoteURL = (smURL?smURL:_s.url);
634    var localURL = (_s.altURL?_s.altURL:remoteURL);
635    if (_s.debugURLParam.test(window.location.href.toString())) {
636      _s.debugMode = true; // allow force of debug mode via URL
637    }
638    if (_s._didAppend && _s._appendSuccess) {
639	  return false; // ignore if already succeeded
640	}
641    _s._didAppend = true;
642	
643    // safety check for legacy (change to Flash 9 URL)
644    _s._setVersionInfo();
645    _s.url = _s._normalizeMovieURL(_s._overHTTP?remoteURL:localURL);
646    smURL = _s.url;
647
648    if (_s.useHighPerformance && _s.useMovieStar && _s.defaultOptions.useVideo === true) {
649      specialCase = 'soundManager note: disabling highPerformance, not applicable with movieStar mode + useVideo';
650      _s.useHighPerformance = false;
651    }
652
653    _s.wmode = (!_s.wmode && _s.useHighPerformance && !_s.useMovieStar?'transparent':_s.wmode);
654    if (_s.wmode !== null && _s.flashLoadTimeout !== 0 && !_s.useHighPerformance && !_s.isIE && navigator.platform.match(/win32/i)) {
655	  _s.specialWmodeCase = true;
656      // extra-special case: movie doesn't load until scrolled into view when using wmode = anything but 'window' here
657      // does not apply when using high performance (position:fixed means on-screen), OR infinite flash load timeout
658      _s._wD('soundManager note: Removing wmode, preventing off-screen 
658SWF loading issue');
659      _s.wmode = null;
660    }
661
662    if (_s.flashVersion == 8) {
663      _s.allowFullScreen = false;
664    }
665
666    var oEmbed = {
667      name: smID,
668      id: smID,
669      src: smURL,
670      width: '100%',
671      height: '100%',
672      quality: 'high',
673      allowScriptAccess: 'always',
674      bgcolor: _s.bgColor,
675      pluginspage: 'http://www.macromedia.com/go/getflashplayer',
676      type: 'application/x-shockwave-flash',
677      wmode: _s.wmode,
678      allowfullscreen: (_s.allowFullScreen?'true':'false')
679    };
680
681    if (!_s.wmode) {
682	  delete oEmbed.wmode; // don't write empty attribute
683    }
684
685    var oMovie = null;
686    var tmp = null;
687
688    if (_s.isIE) {
689      // IE is "special".
690      oMovie = document.createElement('div');
691      var movieHTML = '<object id="'+smID+'" data="'+smURL+'" type="application/x-shockwave-flash" width="100%" height="100%"><param name="movie" value="'+smURL+'" /><param name="AllowScriptAccess" value="always" /><param name="quality" value="high" />'+(_s.wmode?'<param name="wmode" value="'+_s.wmode+'" /> ':'')+'<param name="bgcolor" value="'+_s.bgColor+'" /><param name="allowFullScreen" value="'+(_s.allowFullScreen?'true':'false')+'" /><!-- --></object>';
692    } else {
693      oMovie = document.createElement('embed');
694      for (tmp in oEmbed) {
695	    if (oEmbed.hasOwnProperty(tmp)) {
696          oMovie.setAttribute(tmp,oEmbed[tmp]);
697	    }
698      }
699    }
700
701    var oD = document.createElement('div');
702    oD.id = _s.debugID+'-toggle';
703    var oToggle = {
704      position: 'fixed',
705      bottom: '0px',
706      right: '0px',
707      width: '1.2em',
708      height: '1.2em',
709      lineHeight: '1.2em',
710      margin: '2px',
711      textAlign: 'center',
712      border: '1px solid #999',
713      cursor: 'pointer',
714      background: '#fff',
715      color: '#333',
716      zIndex: 10001
717    };
718
719    oD.appendChild(document.createTextNode('-'));
720    oD.onclick = _s._toggleDebug;
721    oD.title = 'Toggle SM2 debug console';
722
723    if (navigator.userAgent.match(/msie 6/i)) {
724      oD.style.position = 'absolute';
725      oD.style.cursor = 'hand';
726    }
727
728    for (tmp in oToggle) {
729 	  if (oToggle.hasOwnProperty(tmp)) {
730        oD.style[tmp] = oToggle[tmp];
731	  }
732    }
733
734    var oTarget = _s._getDocument();
735
736    if (oTarget) {
737       
738      _s.oMC = _$('sm2-container')?_$('sm2-container'):document.createElement('div');
739
740      if (!_s.oMC.id) {
741        _s.oMC.id = 'sm2-container';
742        _s.oMC.className = 'movieContainer';
743        // "hide" flash movie
744        var s = null;
745        var oEl = null;
746        if (_s.useHighPerformance) {
747          s = {
748 	    position: 'fixed',
749 	    width: '8px',
750            height: '8px', // must be at least 6px for flash to run fast. odd? yes.
751            bottom: '0px',
752            left: '0px',
753	    overflow: 'hidden'
754	    // zIndex:-1 // sit behind everything else - potentially dangerous/buggy?
755          };
756        } else {
757          s = {
758            position: 'absolute',
759	    width: '8px',
760            height: '8px',
761            top: '-9999px',
762            left: '-9999px'
763          };
764        }
765        var x = null;
766        for (x in s) {
767		  if (s.hasOwnProperty(x)) {
768            _s.oMC.style[x] = s[x];
769		  }
770        }
771        try {
772		  if (!_s.isIE) {
773    	    _s.oMC.appendChild(oMovie);
774		  }
775          oTarget.appendChild(_s.oMC);
776		  if (_s.isIE) {
777			oEl = _s.oMC.appendChild(document.createElement('div'));
778			oEl.className = 'sm2-object-box';
779			oEl.innerHTML = movieHTML;
780          }
781          _s._appendSuccess = true;
782        } catch(e) {
783          throw new Error(_s.strings.appXHTML);
784        }
785      } else {
786        // it's already in the document.
787        _s.oMC.appendChild(oMovie);
788		if (_s.isIE) {
789		  oEl = _s.oMC.appendChild(document.createElement('div'));
790		  oEl.className = 'sm2-object-box';
791		  oEl.innerHTML = movieHTML;
792        }
793        _s._appendSuccess = true;
794      }
795
796      if (!_$(_s.debugID) && ((!_s._hasConsole||!_s.useConsole)||(_s.useConsole && _s._hasConsole && !_s.consoleOnly))) {
797        var oDebug = document.createElement('div');
798        oDebug.id = _s.debugID;
799        oDebug.style.display = (_s.debugMode?'block':'none');
800        if (_s.debugMode && !_$(oD.id)) {
801          try {
802            oTarget.appendChild(oD);
803          } catch(e2) {
804            throw new Error(_s.strings.appXHTML);
805          }
806          oTarget.appendChild(oDebug);
807        }
808      }
809      oTarget = null;
810    }
811
812    if (specialCase) {
813      _s._wD(specialCase);
814    }
815
816    _s._wD('-- SoundManager 2 '+_s.version+(_s.useMovieStar?', MovieStar mode':'')+(_s.useHighPerformance?', high performance mode, ':', ')+((_s.useFastPolling?'fast':'normal')+' polling mode')+(_s.wmode?', wmode: '+_s.wmode:'')+' --',1);
817    _s._wD('soundManager._createMovie(): Trying to load '+smURL+(!_s._overHTTP && _s.altURL?' (alternate URL)':''),1);
818  };
819
820  // aliased to this._wD()
821  this._writeDebug = function(sText,sType,bTimestamp) {
822    if (!_s.debugMode) {
823	  return false;
824	}
825    if (typeof bTimestamp != 'undefined' && bTimestamp) {
826      sText = sText + ' | '+new Date().getTime();
827    }
828    if (_s._hasConsole && _s.useConsole) {
829      var sMethod = _s._debugLevels[sType];
830      if (typeof console[sMethod] != 'undefined') {
831	    console[sMethod](sText);
832      } else {
833        console.log(sText);
834      }
835      if (_s.useConsoleOnly) {
836	return true;
837      }
838    }
839    var sDID = 'soundmanager-debug';
840    try {
841      var o = _$(sDID);
842      if (!o) {
843		return false;
844	  }
845      var oItem = document.createElement('div');
846      if (++_s._wdCount%2===0) {
847	    oItem.className = 'sm2-alt';
848      }
849      // sText = sText.replace(/\n/g,'<br />');
850      if (typeof sType == 'undefined') {
851        sType = 0;
852      } else {
853        sType = parseInt(sType,10);
854      }
855      oItem.appendChild(document.createTextNode(sText));
856      if (sType) {
857        if (sType >= 2) {
858		  oItem.style.fontWeight = 'bold';
859		}
860        if (sType == 3) {
861		  oItem.style.color = '#ff3333';
862		}
863      }
864      // o.appendChild(oItem); // top-to-bottom
865      o.insertBefore(oItem,o.firstChild); // bottom-to-top
866    } catch(e) {
867      // oh well
868    }
869    o = null;
870  };
871  this._writeDebug._protected = true;
872  this._wdCount = 0;
873  this._wdCount._protected = true;
874  this._wD = this._writeDebug;
875
876  this._wDAlert = function(sText) { alert(sText); };
877
878  if (window.location.href.indexOf('debug=alert')+1 && _s.debugMode) {
879    _s._wD = _s._wDAlert;
880  }
881
882  this._toggleDebug = function() {
883    var o = _$(_s.debugID);
884    var oT = _$(_s.debugID+'-toggle');
885    if (!o) {
886	  return false;
887	}
888    if (_s._debugOpen) {
889      // minimize
890      oT.innerHTML = '+';
891      o.style.display = 'none';
892    } else {
893      oT.innerHTML = '-';
894      o.style.display = 'block';
895    }
896    _s._debugOpen = !_s._debugOpen;
897  };
898
899  this._toggleDebug._protected = true;
900
901  this._debug = function() {
902    _s._wD('--- soundManager._debug(): Current sound objects ---',1);
903    for (var i=0,j=_s.soundIDs.length; i<j; i++) {
904      _s.sounds[_s.soundIDs[i]]._debug();
905    }
906  };
907
908  this._debugTS = function(sEventType,bSuccess,sMessage) {
909    // troubleshooter debug hooks
910    if (typeof sm2Debugger != 'undefined') {
911	  try {
912	    sm2Debugger.handleEvent(sEventType,bSuccess,sMessage);
913	  } catch(e) {
914	    // oh well	
915	  }
916    }
917  };
918
919  this._debugTS._protected = true;
920
921  this._mergeObjects = function(oMain,oAdd) {
922    // non-destructive merge
923    var o1 = {}; // clone o1
924    for (var i in oMain) {
925	  if (oMain.hasOwnProperty(i)) {
926        o1[i] = oMain[i];
927	  }
928    }
929    var o2 = (typeof oAdd == 'undefined'?_s.defaultOptions:oAdd);
930    for (var o in o2) {
931      if (o2.hasOwnProperty(o) && typeof o1[o] == 'undefined') {
932		o1[o] = o2[o];
933	  }
934    }
935    return o1;
936  };
937
938  this.createMovie = function(sURL) {
939    if (sURL) {
940      _s.url = sURL;
941    }
942    _s._initMovie();
943  };
944
945  this.go = this.createMovie; // nice alias
946
947  this._initMovie = function() {
948    // attempt to get, or create, movie
949    if (_s.o) {
950	  return false; // may already exist
951    }
952    _s.o = _s.getMovie(_s.id); // (inline markup)
953    if (!_s.o) {
954      if (!_s.oRemoved) {
955        // try to create
956        _s._createMovie(_s.id,_s.url);
957      } else {
958        // try to re-append removed movie after reboot()
959        if (!_s.isIE) {
960          _s.oMC.appendChild(_s.oRemoved);
961        } else {
962          _s.oMC.innerHTML = _s.oRemovedHTML;
963        }
964        _s.oRemoved = null;
965        _s._didAppend = true;
966      }
967      _s.o = _s.getMovie(_s.id);
968    }
969    if (_s.o) {
970      _s._wD('soundManager._initMovie(): Got '+_s.o.nodeName+' element ('+(_s._didAppend?'created via JS':'static HTML')+')',1);
971      if (_s.flashLoadTimeout>0) {
972        _s._wD('soundManager._initMovie(): Waiting for ExternalInterface call from Flash..');
973      }
974    }
975    if (typeof _s.oninitmovie == 'function') {
976	  setTimeout(_s.oninitmovie,1);
977    }
978  };
979
980  this.waitForExternalInterface = function() {
981    if (_s._waitingForEI) {
982	  return false;
983	}
984    _s._waitingForEI = true;
985    if (_s._tryInitOnFocus && !_s._isFocused) {
986      _s._wD(_s.strings.waitFocus);
987      return false;
988    }
989    if (_s.flashLoadTimeout>0) {
990      if (!_s._didInit) {
991        _s._wD(_s.strings.waitImpatient);
992      }
993      setTimeout(function() {
994        if (!_s._didInit) {
995          _s._wD('soundManager: No Flash response within reasonable time after document load.\nPossible causes: Loading '+_s.movieURL+' failed, Flash version under '+_s.flashVersion+', no support, flash blocked or JS-Flash security error.',2);
996          if (!_s._overHTTP) {
997          _s._wD(_s.strings.localFail,2);
998        }
999        _s._debugTS('flashtojs',false,': Timed out'+(_s._overHTTP)?' (Check flash security or flash blockers)':' (No plugin/missing SWF?)');
1000      }
1001      // if still not initialized and no other options, give up
1002      if (!_s._didInit && _s._okToDisable) {
1003	_s._failSafely(true); // don't disable, for reboot()
1004      }
1005    },_s.flashLoadTimeout);
1006    } else if (!_s._didInit) {
1007      _s._wD(_s.strings.waitForever);
1008    }
1009  };
1010
1011  this.handleFocus = function() {
1012    if (_s._isFocused || !_s._tryInitOnFocus) {
1013	  return true;
1014	}
1015    _s._okToDisable = true;
1016    _s._isFocused = true;
1017    _s._wD('soundManager.handleFocus()');
1018    if (_s._tryInitOnFocus) {
1019      // giant Safari 3.1 hack - assume window in focus if mouse is moving, since document.hasFocus() not currently implemented.
1020      window.removeEventListener('mousemove',_s.handleFocus,false);
1021    }
1022    // allow init to restart
1023    _s._waitingForEI = false;
1024    setTimeout(_s.waitForExternalInterface,500);
1025    // detach event
1026    if (window.removeEventListener) {
1027      window.removeEventListener('focus',_s.handleFocus,false);
1028    } else if (window.detachEvent) {
1029      window.detachEvent('onfocus',_s.handleFocus);
1030    }
1031  };
1032
1033  this.initComplete = function(bNoDisable) {
1034    if (_s._didInit) {
1035	  return false;
1036	}
1037    _s._didInit = true;
1038    _s._wD('-- SoundManager 2 '+(_s._disabled?'failed to load':'loaded')+' ('+(_s._disabled?'security/load error':'OK')+') --',1);
1039    if (_s._disabled || bNoDisable) {
1040      _s._wD('soundManager.initComplete(): calling soundManager.onerror()',1);
1041	  _s._processOnReady();
1042      _s._debugTS('onload',false);
1043      _s.onerror.apply(window);
1044      return false;
1045    } else {
1046	  _s._debugTS('onload',true);
1047    }
1048    if (_s.waitForWindowLoad && !_s._windowLoaded) {
1049      _s._wD('soundManager: Waiting for window.onload()');
1050      if (window.addEventListener) {
1051        window.addEventListener('load',_s._initUserOnload,false);
1052      } else if (window.attachEvent) {
1053        window.attachEvent('onload',_s._initUserOnload);
1054      }
1055      return false;
1056    } else {
1057      if (_s.waitForWindowLoad && _s._windowLoaded) {
1058        _s._wD('soundManager: Document already loaded');
1059      }
1060      _s._initUserOnload();
1061    }
1062  };
1063
1064  this._addOnReady = function(oMethod,oScope) {
1065    _s._onready.push({
1066	  'method': oMethod,
1067	  'scope': (oScope||null),
1068	  'fired': false
1069	});
1070  };
1071
1072  this._processOnReady = function() {
1073	if (!_s._didInit) {
1074	  // not ready yet.
1075	  return false;
1076	}
1077    var status = {
1078	  success: (!_s._disabled)
1079    };
1080    var queue = [];
1081    for (var i=0, j=_s._onready.length; i<j; i++) {
1082	  if (_s._onready[i].fired !== true) {
1083		queue.push(_s._onready[i]);
1084	  }
1085    }
1086	if (queue.length) {
1087	  _s._wD('soundManager: Firing '+queue.length+' onready() item'+(queue.length>1?'s':''));
1088	  for (i=0, j=queue.length; i<j; i++) {
1089	    if (queue[i].scope) {
1090		  queue[i].method.apply(queue[i].scope,[status]);
1091	    } else {
1092		  queue[i].method(status);
1093	    }
1094	    queue[i].fired = true;
1095	  }
1096	}
1097  };
1098
1099  this._initUserOnload = function() {
1100    window.setTimeout(function() {
1101      _s._processOnReady();
1102      _s._wD('soundManager.initComplete(): calling soundManager.onload()',1);
1103      // call user-defined "onload", scoped to window
1104      _s.onload.apply(window);
1105      _s._wD('soundManager.onload() complete',1);
1106    });
1107  };
1108
1109  this.init = function() {
1110    _s._wD('-- soundManager.init() --');
1111    // called after onload()
1112    _s._initMovie();
1113    if (_s._didInit) {
1114      _s._wD('soundManager.init(): Already called?');
1115      return false;
1116    }
1117    // event cleanup
1118    if (window.removeEventListener) {
1119      window.removeEventListener('load',_s.beginDelayedInit,false);
1120    } else if (window.detachEvent) {
1121      window.detachEvent('onload',_s.beginDelayedInit);
1122    }
1123    try {
1124      _s._wD('Attempting to call Flash from JS..');
1125      _s.o._externalInterfaceTest(false); // attempt to talk to Flash
1126      if (!_s.allowPolling) {
1127	    _s._wD('Polling (whileloading()/whileplaying() support) is disabled.',1);
1128	  }
1129      _s._setPolling(true,_s.useFastPolling?true:false);
1130	  if (!_s.debugMode) {
1131		_s.o._disableDebug();
1132	  }
1133      _s.enabled = true;
1134      _s._debugTS('jstoflash',true);
1135    } catch(e) {
1136	  _s._wD('js/flash exception: '+e.toString());
1137	  _s._debugTS('jstoflash',false);
1138      _s._failSafely(true); // don't disable, for reboot()
1139      _s.initComplete();
1140      return false;
1141    }
1142    _s.initComplete();
1143  };
1144
1145  this.beginDelayedInit = function() {
1146    _s._wD('soundManager.beginDelayedInit()');
1147    _s._windowLoaded = true;
1148    setTimeout(_s.waitForExternalInterface,500);
1149    setTimeout(_s.beginInit,20);
1150  };
1151
1152  this.beginInit = function() {
1153    if (_s._initPending) {
1154	  return false;
1155	}
1156    _s.createMovie(); // ensure creation if not already done
1157    _s._initMovie();
1158    _s._initPending = true;
1159    return true;
1160  };
1161
1162  this.domContentLoaded = function() {
1163    _s._wD('soundManager.domContentLoaded()');
1164    if (document.removeEventListener) {
1165	  document.removeEventListener('DOMContentLoaded',_s.domContentLoaded,false);
1166	}
1167    _s.go();
1168  };
1169
1170  this._externalInterfaceOK = function() {
1171    // callback from flash for confirming that movie loaded, EI is working etc.
1172    if (_s.swfLoaded) {
1173	  return false;
1174	}
1175    _s._wD('soundManager._externalInterfaceOK()');
1176    _s._debugTS('swf',true);
1177    _s._debugTS('flashtojs',true);
1178    _s.swfLoaded = true;
1179    _s._tryInitOnFocus = false;
1180
1181    if (_s.isIE) {
1182      // IE needs a timeout OR delay until window.onload - may need TODO: investigating
1183      setTimeout(_s.init,100);
1184    } else {
1185      _s.init();
1186    }
1187
1188  };
1189
1190  this._setSandboxType = function(sandboxType) {
1191    var sb = _s.sandbox;
1192    sb.type = sandboxType;
1193    sb.description = sb.types[(typeof sb.types[sandboxType] != 'undefined'?sandboxType:'unknown')];
1194    _s._wD('Flash security sandbox type: '+sb.type);
1195    if (sb.type == 'localWithFile') {
1196      sb.noRemote = true;
1197      sb.noLocal = false;
1198      _s._wD('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',2);
1199    } else if (sb.type == 'localWithNetwork') {
1200      sb.noRemote = false;
1201      sb.noLocal = true;
1202    } else if (sb.type == 'localTrusted') {
1203      sb.noRemote = false;
1204      sb.noLocal = false;
1205    }
1206  };
1207
1208  this.reboot = function() {
1209    // attempt to reset and init SM2
1210    _s._wD('soundManager.reboot()');
1211    if (_s.soundIDs.length) {
1212      _s._wD('Destroying '+_s.soundIDs.length+' SMSound objects...');
1213    }
1214    for (var i=_s.soundIDs.length; i--;) {
1215      _s.sounds[_s.soundIDs[i]].destruct();
1216    }
1217    // trash ze flash
1218    try {
1219      if (_s.isIE) {
1220        _s.oRemovedHTML = _s.o.innerHTML;
1221      }
1222      _s.oRemoved = _s.o.parentNode.removeChild(_s.o);
1223      _s._wD('Flash movie removed.');
1224    } catch(e) {
1225      // uh-oh.
1226      _s._wD('Warning: Failed to remove flash movie.',2);
1227    }
1228    _s.enabled = false;
1229    _s._didInit = false;
1230    _s._waitingForEI = false;
1231    _s._initPending = false;
1232    _s._didAppend = false;
1233    _s._appendSuccess = false;
1234    _s._disabled = false;
1235    _s._waitingforEI = true;
1236    _s.swfLoaded = false;
1237    _s.soundIDs = {};
1238    _s.sounds = [];
1239    _s.o = null;
1240    for (i=_s._onready.length; i--;) {
1241	  _s._onready[i].fired = false;
1242    }
1243    _s._wD('soundManager: Rebooting...');
1244    window.setTimeout(soundManager.beginDelayedInit,20);
1245  };
1246
1247  this.destruct = function() {
1248    _s._wD('soundManager.destruct()');
1249    _s.disable(true);
1250  };
1251  
1252  // SMSound (sound object)
1253  
1254  SMSound = function(oOptions) {
1255  var _t = this;
1256  this.sID = oOptions.id;
1257  this.url = oOptions.url;
1258  this.options = _s._mergeObjects(oOptions);
1259  this.instanceOptions = this.options; // per-play-instance-specific options
1260  this._iO = this.instanceOptions; // short alias
1261
1262  // assign property defaults (volume, pan etc.)
1263  this.pan = this.options.pan;
1264  this.volume = this.options.volume;
1265
1266  this._lastURL = null;
1267
1268  this._debug = function() {
1269    if (_s.debugMode) {
1270    var stuff = null;
1271    var msg = [];
1272    var sF = null;
1273    var sfBracket = null;
1274    var maxLength = 64; // # of characters of function code to show before truncating
1275    for (stuff in _t.options) {
1276      if (_t.options[stuff] !== null) {
1277        if (_t.options[stuff] instanceof Function) {
1278	      // handle functions specially
1279	      sF = _t.options[stuff].toString();
1280	      sF = sF.replace(/\s\s+/g,' '); // normalize spaces
1281	      sfBracket = sF.indexOf('{');
1282	      msg[msg.length] = ' '+stuff+': {'+sF.substr(sfBracket+1,(Math.min(Math.max(sF.indexOf('\n')-1,maxLength),maxLength))).replace(/\n/g,'')+'... }';
1283	    } else {
1284	      msg[msg.length] = ' '+stuff+': '+_t.options[stuff];
1285	    }
1286      }
1287    }
1288    _s._wD('SMSound() merged options: {\n'+msg.join(', \n')+'\n}');
1289    }
1290  };
1291
1292  this._debug();
1293
1294  this.id3 = {
1295   /* 
1296    Name/value pairs set via Flash when available - see reference for names:
1297    http://livedocs.macromedia.com/flash/8/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00001567.html
1298    (eg., this.id3.songname or this.id3['songname'])
1299   */
1300  };
1301
1302  this.resetProperties = function(bLoaded) {
1303    _t.bytesLoaded = null;
1304    _t.bytesTotal = null;
1305    _t.position = null;
1306    _t.duration = null;
1307    _t.durationEstimate = null;
1308    _t.loaded = false;
1309    _t.playState = 0;
1310    _t.paused = false;
1311    _t.readyState = 0; // 0 = uninitialised, 1 = loading, 2 = failed/error, 3 = loaded/success
1312    _t.muted = false;
1313    _t.didBeforeFinish = false;
1314    _t.didJustBeforeFinish = false;
1315    _t.isBuffering = false;
1316    _t.instanceOptions = {};
1317    _t.instanceCount = 0;
1318    _t.peakData = {
1319      left: 0,
1320      right: 0
1321    };
1322    _t.waveformData = {
1323	  left: [],
1324	  right: []
1325    };
1326    _t.eqData = [];
1327  };
1328
1329  _t.resetProperties();
1330
1331  // --- public methods ---
1332
1333  this.load = function(oOptions) {
1334    if (typeof oOptions != 'undefined') {
1335      _t._iO = _s._mergeObjects(oOptions);
1336      _t.instanceOptions = _t._iO;
1337    } else {
1338      oOptions = _t.options;
1339      _t._iO = oOptions;
1340      _t.instanceOptions = _t._iO;
1341      if (_t._lastURL && _t._lastURL != _t.url) {
1342        _s._wD('SMSound.load(): Using manually-assigned URL');
1343        _t._iO.url = _t.url;
1344        _t.url = null;
1345      }
1346    } 
1347
1348    if (typeof _t._iO.url == 'undefined') {
1349      _t._iO.url = _t.url;
1350    }
1351
1352    _s._wD('soundManager.load(): '+_t._iO.url,1);
1353    if (_t._iO.url == _t.url && _t.readyState !== 0 && _t.readyState != 2) {
1354      _s._wD('soundManager.load(): current URL already assigned.',1);
1355      return false;
1356    }
1357    _t.url = _t._iO.url;
1358    _t._lastURL = _t._iO.url;
1359    _t.loaded = false;
1360    _t.readyState = 1;
1361    _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?)
1362    try {
1363      if (_s.flashVersion==8) {
1364        _s.o._load(_t.sID,_t._iO.url,_t._iO.stream,_t._iO.autoPlay,(_t._iO.whileloading?1:0));
1365      } else {
1366        _s.o._load(_t.sID,_t._iO.url,_t._iO.stream?true:false,_t._iO.autoPlay?true:false); // ,(_tO.whileloading?true:false)
1367        if (_t._iO.isMovieStar && _t._iO.autoLoad && !_t._iO.autoPlay) {
1368          // special case: MPEG4 content must start playing to load, then pause to prevent playing.
1369          _t.pause();
1370        }
1371      }
1372    } catch(e) {
1373      _s._wD('SMSound.load(): Exception: JS-Flash communication failed, or JS error.',2);
1374      _s._debugTS('onload',false);
1375      _s.onerror();
1376      _s.disable();
1377    }
1378
1379  };
1380
1381  this.unload = function() {
1382    // Flash 8/AS2 can't "close" a stream - fake it by loading an empty MP3
1383    // Flash 9/AS3: Close stream, preventing further load
1384    if (_t.readyState !== 0) {
1385      _s._wD('SMSound.unload(): "'+_t.sID+'"');
1386      if (_t.readyState != 2) { // reset if not error
1387        _t.setPosition(0,true); // reset current sound positioning
1388      }
1389      _s.o._unload(_t.sID,_s.nullURL);
1390      // reset load/status flags
1391      _t.resetProperties();
1392    }
1393  };
1394
1395  this.destruct = function() {
1396    // kill sound within Flash
1397    _s._wD('SMSound.destruct(): "'+_t.sID+'"');
1398    _s.o._destroySound(_t.sID);
1399    _s.destroySound(_t.sID,true); // ensure deletion from controller
1400  };
1401
1402  this.play = function(oOptions) {
1403    if (!oOptions) {
1404	  oOptions = {};
1405    }
1406    _t._iO = _s._mergeObjects(oOptions,_t._iO);
1407    _t._iO = _s._mergeObjects(_t._iO,_t.options);
1408    _t.instanceOptions = _t._iO;
1409    if (_t.playState == 1) {
1410      var allowMulti = _t._iO.multiShot;
1411      if (!allowMulti) {
1412        _s._wD('SMSound.play(): "'+_t.sID+'" already playing (one-shot)',1);
1413        return false;
1414      } else {
1415        _s._wD('SMSound.play(): "'+_t.sID+'" already playing (multi-shot)',1);
1416      }
1417    }
1418    if (!_t.loaded) {
1419      if (_t.readyState === 0) {
1420        _s._wD('SMSound.play(): Attempting to load "'+_t.sID+'"',1);
1421        // try to get this sound playing ASAP
1422        _t._iO.stream = true;
1423        _t._iO.autoPlay = true;
1424        // TODO: need to investigate when false, double-playing
1425        // if (typeof oOptions.autoPlay=='undefined') _tO.autoPlay = true; // only set autoPlay if unspecified here
1426        _t.load(_t._iO); // try to get this sound playing ASAP
1427      } else if (_t.readyState == 2) {
1428        _s._wD('SMSound.play(): Could not load "'+_t.sID+'" - exiting',2);
1429        return false;
1430      } else {
1431        _s._wD('SMSound.play(): "'+_t.sID+'" is loading - attempting to play..',1);
1432      }
1433    } else {
1434      _s._wD('SMSound.play(): "'+_t.sID+'"');
1435    }
1436    if (_t.paused) {
1437      _t.resume();
1438    } else {
1439      _t.playState = 1;
1440      if (!_t.instanceCount || _s.flashVersion > 8) {
1441		_t.instanceCount++;
1442	  }
1443      _t.position = (typeof _t._iO.position != 'undefined' && !isNaN(_t._iO.position)?_t._iO.position:0);
1444      if (_t._iO.onplay) {
1445		_t._iO.onplay.apply(_t);
1446	  }
1447      _t.setVolume(_t._iO.volume,true); // restrict volume to instance options only
1448      _t.setPan(_t._iO.pan,true);
1449      _s.o._start(_t.sID,_t._iO.loop||1,(_s.flashVersion==9?_t.position:_t.position/1000));
1450    }
1451  };
1452
1453  this.start = this.play; // just for convenience
1454
1455  this.stop = function(bAll) {
1456    if (_t.playState == 1) {
1457      _t.playState = 0;
1458      _t.paused = false;
1459      // if (_s.defaultOptions.onstop) _s.defaultOptions.onstop.apply(_s);
1460      if (_t._iO.onstop) {
1461		_t._iO.onstop.apply(_t);
1462	  }
1463      _s.o._stop(_t.sID,bAll);
1464      _t.instanceCount = 0;
1465      _t._iO = {};
1466      // _t.instanceOptions = _t._iO;
1467    }
1468  };
1469
1470  this.setPosition = function(nMsecOffset,bNoDebug) {
1471    if (typeof nMsecOffset == 'undefined') {
1472      nMsecOffset = 0;
1473    }
1474    var offset = Math.min(_t.duration,Math.max(nMsecOffset,0)); // position >= 0 and <= current available (loaded) duration
1475    _t._iO.position = offset;
1476    if (!bNoDebug) {
1477      // _s._wD('SMSound.setPosition('+nMsecOffset+')'+(nMsecOffset != offset?', corrected value: '+offset:''));
1478    }
1479    _s.o._setPosition(_t.sID,(_s.flashVersion==9?_t._iO.position:_t._iO.position/1000),(_t.paused||!_t.playState)); // if paused or not playing, will not resume (by playing)
1480  };
1481
1482  this.pause = function() {
1483    if (_t.paused || _t.playState === 0) {
1484	  return false;
1485	}
1486    _s._wD('SMSound.pause()');
1487    _t.paused = true;
1488    _s.o._pause(_t.sID);
1489    if (_t._iO.onpause) {
1490	  _t._iO.onpause.apply(_t);
1491	}
1492  };
1493
1494  this.resume = function() {
1495    if (!_t.paused || _t.playState === 0) {
1496	  return false;
1497	}
1498    _s._wD('SMSound.resume()');
1499    _t.paused = false;
1500    _s.o._pause(_t.sID); // flash method is toggle-based (pause/resume)
1501    if (_t._iO.onresume) {
1502	  _t._iO.onresume.apply(_t);
1503	}
1504  };
1505
1506  this.togglePause = function() {
1507    _s._wD('SMSound.togglePause()');
1508    if (_t.playState === 0) {
1509      _t.play({position:(_s.flashVersion==9?_t.position:_t.position/1000)});
1510      return false;
1511    }
1512    if (_t.paused) {
1513      _t.resume();
1514    } else {
1515      _t.pause();
1516    }
1517  };
1518
1519  this.setPan = function(nPan,bInstanceOnly) {
1520    if (typeof nPan == 'undefined') {
1521      nPan = 0;
1522    }
1523    if (typeof bInstanceOnly == 'undefined') {
1524      bInstanceOnly = false;
1525    }
1526    _s.o._setPan(_t.sID,nPan);
1527    _t._iO.pan = nPan;
1528    if (!bInstanceOnly) {
1529      _t.pan = nPan;
1530    }
1531  };
1532
1533  this.setVolume = function(nVol,bInstanceOnly) {
1534    if (typeof nVol == 'undefined') {
1535      nVol = 100;
1536    }
1537    if (typeof bInstanceOnly == 'undefined') {
1538      bInstanceOnly = false;
1539    }
1540    _s.o._setVolume(_t.sID,(_s.muted&&!_t.muted)||_t.muted?0:nVol);
1541    _t._iO.volume = nVol;
1542    if (!bInstanceOnly) {
1543      _t.volume = nVol;
1544    }
1545  };
1546
1547  this.mute = function() {
1548    _t.muted = true;
1549    _s.o._setVolume(_t.sID,0);
1550  };
1551
1552  this.unmute = function() {
1553    _t.muted = false;
1554    var hasIO = typeof _t._iO.volume != 'undefined';
1555    _s.o._setVolume(_t.sID,hasIO?_t._iO.volume:_t.options.volume);
1556  };
1557
1558  this.toggleMute = function() {
1559    if (_t.muted) {
1560      _t.unmute(); 
1561    } else {
1562      _t.mute();
1563    }
1564  };
1565
1566  // --- "private" methods called by Flash ---
1567
1568  this._whileloading = function(nBytesLoaded,nBytesTotal,nDuration) {
1569    if (!_t._iO.isMovieStar) {
1570      _t.bytesLoaded = nBytesLoaded;
1571      _t.bytesTotal = nBytesTotal;
1572      _t.duration = Math.floor(nDuration);
1573      _t.durationEstimate = parseInt((_t.bytesTotal/_t.bytesLoaded)*_t.duration,10);
1574      if (_t.durationEstimate === undefined) {
1575	    // reported bug?
1576	    _t.durationEstimate = _t.duration;
1577      }
1578      if (_t.readyState != 3 && _t._iO.whileloading) {
1579  	    _t._iO.whileloading.apply(_t);
1580      }
1581    } else {
1582      _t.bytesLoaded = nBytesLoaded;
1583      _t.bytesTotal = nBytesTotal;
1584      _t.duration = Math.floor(nDuration);
1585      _t.durationEstimate = _t.duration;
1586      if (_t.readyState != 3 && _t._iO.whileloading) {
1587	_t._iO.whileloading.apply(_t);
1588      }
1589    }
1590  };
1591
1592  this._onid3 = function(oID3PropNames,oID3Data) {
1593    // oID3PropNames: string array (names)
1594    // ID3Data: string array (data)
1595    _s._wD('SMSound._onid3(): "'+this.sID+'" ID3 data received.');
1596    var oData = [];
1597    for (var i=0,j=oID3PropNames.length; i<j; i++) {
1598      oData[oID3PropNames[i]] = oID3Data[i];
1599      // _s._wD(oID3PropNames[i]+': '+oID3Data[i]);
1600    }
1601    _t.id3 = _s._mergeObjects(_t.id3,oData);
1602    if (_t._iO.onid3) {
1603      _t._iO.onid3.apply(_t);
1604    }
1605  };
1606
1607  this._whileplaying = function(nPosition,oPeakData,oWaveformDataLeft,oWaveformDataRight,oEQData) {
1608
1609    if (isNaN(nPosition) || nPosition === null) {
1610      return false; // Flash may return NaN at times
1611    }
1612    if (_t.playState === 0 && nPosition > 0) {
1613      // can happen at the end of a video where nPosition == 33 for some reason, after finishing.???
1614      // can also happen with a normal stop operation. This resets the position to 0.
1615      // _s._writeDebug('Note: Not playing, but position = '+nPosition);
1616      nPosition = 0;	
1617    }
1618    _t.position = nPosition;
1619	if (_t._iO.usePeakData && typeof oPeakData != 'undefined' && oPeakData) {
1620	  _t.peakData = {
1621	   left: oPeakData.leftPeak,
1622	   right: oPeakData.rightPeak
1623	  };
1624	}
1625	if (_t._iO.useWaveformData && typeof oWaveformDataLeft != 'undefined' && oWaveformDataLeft) {
1626	  _t.waveformData = {
1627	   left: oWaveformDataLeft.split(','),
1628	   right: oWaveformDataRight.split(',')
1629	  };
1630	}
1631	if (_t._iO.useEQData && typeof oEQData != 'undefined' && oEQData) {
1632	  _t.eqData = oEQData;
1633	}
1634    if (_t.playState == 1) {
1635	  // special case/hack: ensure buffering is false (instant load from cache, thus buffering stuck at 1?)
1636	  if (_t.isBuffering) {
1637		_t._onbufferchange(0);
1638	  }
1639      if (_t._iO.whileplaying) {
1640	    _t._iO.whileplaying.apply(_t); // flash may call after actual finish
1641      }
1642      if (_t.loaded && _t._iO.onbeforefinish && _t._iO.onbeforefinishtime && !_t.didBeforeFinish && _t.duration-_t.position <= _t._iO.onbeforefinishtime) {
1643        _s._wD('duration-position &lt;= onbeforefinishtime: '+_t.duration+' - '+_t.position+' &lt= '+_t._iO.onbeforefinishtime+' ('+(_t.duration-_t.position)+')');
1644        _t._onbeforefinish();
1645      }
1646    }
1647  };
1648
1649  this._onload = function(bSuccess) {
1650    bSuccess = (bSuccess==1?true:false);
1651    _s._wD('SMSound._onload(): "'+_t.sID+'"'+(bSuccess?' loaded.':' failed to load? - '+_t.url),(bSuccess?1:2));
1652    if (!bSuccess) {
1653      if (_s.sandbox.noRemote === true) {
1654        _s._wD('SMSound._onload(): Reminder: Flash security is denying network/internet access',1);
1655      }
1656      if (_s.sandbox.noLocal === true) {
1657        _s._wD('SMSound._onload(): Reminder: Flash security is denying local access',1);
1658      }
1659    }
1660    _t.loaded = bSuccess;
1661    _t.readyState = bSuccess?3:2;
1662    if (_t._iO.onload) {
1663      _t._iO.onload.apply(_t);
1664    }
1665  };
1666
1667  this._onbeforefinish = function() {
1668    if (!_t.didBeforeFinish) {
1669      _t.didBeforeFinish = true;
1670      if (_t._iO.onbeforefinish) {
1671        _s._wD('SMSound._onbeforefinish(): "'+_t.sID+'"');
1672        _t._iO.onbeforefinish.apply(_t);
1673      }
1674    }
1675  };
1676
1677  this._onjustbeforefinish = function(msOffset) {
1678    // msOffset: "end of sound" delay actual value (eg. 200 msec, value at event fire time was 187)
1679    if (!_t.didJustBeforeFinish) {
1680      _t.didJustBeforeFinish = true;
1681      if (_t._iO.onjustbeforefinish) {
1682        _s._wD('SMSound._onjustbeforefinish(): "'+_t.sID+'"');
1683        _t._iO.onjustbeforefinish.apply(_t);
1684      }
1685    }
1686  };
1687
1688  this._onfinish = function() {
1689    // sound has finished playing
1690
1691    // TODO: calling user-defined onfinish() should happen after setPosition(0)
1692    // OR: onfinish() and then setPosition(0) is bad.
1693    if (_t._iO.onbeforefinishcomplete) {
1694      _t._iO.onbeforefinishcomplete.apply(_t);
1695    }
1696    // reset some state items
1697    _t.didBeforeFinish = false;
1698    _t.didJustBeforeFinish = false;
1699    if (_t.instanceCount) {
1700      _t.instanceCount--;
1701      if (!_t.instanceCount) {
1702        // reset instance options
1703        // _t.setPosition(0);
1704        _t.playState = 0;
1705        _t.paused = false;
1706        _t.instanceCount = 0;
1707        _t.instanceOptions = {};
1708      }
1709	  if (!_t.instanceCount || _t._iO.multiShotEvents) {
1710        // fire onfinish for last, or every instance
1711        if (_t._iO.onfinish) {
1712          _s._wD('SMSound._onfinish(): "'+_t.sID+'"');
1713          _t._iO.onfinish.apply(_t);
1714        }
1715	  }
1716    } else {
1717	  if (_t.useVideo) {
1718	    // video has finished
1719	    // may need to reset position for next play call, "rewind"
1720	    // _t.setPosition(0);
1721	  }
1722      // _t.setPosition(0);
1723    }
1724
1725  };
1726
1727  this._onmetadata = function(oMetaData) {
1728    // movieStar mode only
1729    _s._wD('SMSound.onmetadata()');
1730    // Contains a subset of metadata. Note that files may have their own unique metadata.
1731    // http://livedocs.adobe.com/flash/9.0/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00000267.html
1732    if (!oMetaData.width && !oMetaData.height) {
1733	  _s._wD('No width/height given, assuming defaults');
1734	  oMetaData.width = 320;
1735	  oMetaData.height = 240;
1736    }
1737    _t.metadata = oMetaData; // potentially-large object from flash
1738    _t.width = oMetaData.width;
1739    _t.height = oMetaData.height;
1740    if (_t._iO.onmetadata) {
1741      _s._wD('SMSound._onmetadata(): "'+_t.sID+'"');
1742      _t._iO.onmetadata.apply(_t);
1743    }
1744    _s._wD('SMSound.onmetadata() complete');
1745  };
1746
1747  this._onbufferchange = function(bIsBuffering) {
1748    if (_t.playState === 0) {
1749      // ignore if not playing
1750      return false;
1751    }
1752    if (bIsBuffering == _t.isBuffering) {
1753      // ignore initial "false" default, if matching
1754	  _s._wD('_onbufferchange: ignoring false default / loaded sound');
1755      return false;
1756    }
1757    _t.isBuffering = (bIsBuffering==1?true:false);
1758    if (_t._iO.onbufferchange) {
1759      _s._wD('SMSound._onbufferchange(): '+bIsBuffering);
1760      _t._iO.onbufferchange.apply(_t);
1761    }
1762  };
1763
1764  this._ondataerror = function(sError) {
1765	// flash 9 wave/eq data handler
1766	if (_t.playState > 0) { // hack: called at start, and end from flash at/after onfinish().
1767	  _s._wD('SMSound._ondataerror(): '+sError);
1768	  if (_t._iO.ondataerror) {
1769	    _t._iO.ondataerror.apply(_t);	
1770	  }
1771	} else {
1772	  // _s._wD('SMSound._ondataerror(): ignoring');
1773	}
1774  };
1775
1776  }; // SMSound()
1777
1778  this._onfullscreenchange = function(bFullScreen) {
1779    _s._wD('onfullscreenchange(): '+bFullScreen);
1780    _s.isFullScreen = (bFullScreen==1?true:false);
1781    if (!_s.isFullScreen) {
1782	  // attempt to restore window focus after leaving full-screen
1783	  try {
1784		window.focus();
1785		_s._wD('window.focus()');
1786	  } catch(e) {
1787	    // oh well
1788	  }
1789    }
1790  };
1791
1792  // register a few event handlers
1793  if (window.addEventListener) {
1794    window.addEventListener('focus',_s.handleFocus,false);
1795    window.addEventListener('load',_s.beginDelayedInit,false);
1796    window.addEventListener('unload',_s.destruct,false);
1797    if (_s._tryInitOnFocus) {
1798      window.addEventListener('mousemove',_s.handleFocus,false); // massive Safari focus hack
1799    }
1800  } else if (window.attachEvent) {
1801    window.attachEvent('onfocus',_s.handleFocus);
1802    window.attachEvent('onload',_s.beginDelayedInit);
1803    window.attachEvent('unload',_s.destruct);
1804  } else {
1805    // no add/attachevent support - safe to assume no JS -> Flash either.
1806    _s._debugTS('onload',false);
1807    soundManager.onerror();
1808    soundManager.disable();
1809  }
1810
1811  if (document.addEventListener) {
1812	document.addEventListener('DOMContentLoaded',_s.domContentLoaded,false);
1813  }
1814
1815} // SoundManager()
1816
1817// set var SM2_DEFER = true; in your own script to prevent auto start-up
1818if (typeof SM2_DEFER == 'undefined' || !SM2_DEFER) {
1819  soundManager = new SoundManager();
1820}

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.