Mercurial > hg > Members > nobuyasu > presen
diff OpenSourceConference/ui/audio_support/soundmanager2.js @ 0:7451f481250b
add OSC presen
author | Nobuyasu Oshiro <dimolto@cr.ie.u-ryukyu.ac.jp> |
---|---|
date | Wed, 07 Sep 2011 21:43:27 +0900 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenSourceConference/ui/audio_support/soundmanager2.js Wed Sep 07 21:43:27 2011 +0900 @@ -0,0 +1,658 @@ +/* + SoundManager 2: Javascript Sound for the Web. + -------------------------------------------------- + http://www.schillmania.com/projects/soundmanager2/ + + Copyright (c) 2007, Scott Schiller. All rights reserved. + Code licensed under the BSD License: + http://www.schillmania.com/projects/soundmanager2/license.txt + + Beta V2.0b.20070118 +*/ + +function SoundManager(smURL,smID) { + var self = this; + this.enabled = false; + this.o = null; + this.url = (smURL||'ui/audio_support/soundmanager2.swf'); + this.id = (smID||'sm2movie'); + this.oMC = null; + this.sounds = []; + this.soundIDs = []; + this.allowPolling = true; // allow flash to poll for status update (required for "while playing", "progress" etc. to work.) + this.isIE = (navigator.userAgent.match(/MSIE/)); + this.isSafari = (navigator.userAgent.match(/safari/i)); + this._didAppend = false; + this._didInit = false; + this._disabled = false; + this.version = 'V2.0b.20070118'; + + this.defaultOptions = { + // ----------------- + 'debugMode': false, // enable debug/status messaging, handy for development/troubleshooting - will be written to a DIV with an ID of "soundmanager-debug", you can use CSS to make it pretty + '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) + 'stream': true, // allows playing before entire file has loaded (recommended) + 'autoPlay': false, // enable playing of file as soon as possible (much faster if "stream" is true) + 'onid3': null, // callback function for "ID3 data is added/available" + 'onload': null, // callback function for "load finished" + 'whileloading': null, // callback function for "download progress update" (X of Y bytes received) + 'onplay': null, // callback for "play" start + 'whileplaying': null, // callback during play (position update) + 'onstop': null, // callback for "user stop" + 'onfinish': null, // callback function for "sound finished playing" + 'onbeforefinish': null, // callback for "before sound finished playing (at [time])" + 'onbeforefinishtime': 2000, // offset (milliseconds) before end of sound to trigger beforefinish (eg. 1000 msec = 1 second) + 'onbeforefinishcomplete':null, // function to call when said sound finishes playing + 'onjustbeforefinish':null, // callback for [n] msec before end of current sound + 'onjustbeforefinishtime':200, // [n] - if not using, set to 0 (or null handler) and event will not fire. + 'multiShot': true, // let sounds "restart" or layer on top of each other when played multiple times, rather than one-shot/one at a time + 'pan': 0, // "pan" settings, left-to-right, -100 to 100 + 'volume': 100, // self-explanatory. 0-100, the latter being the max. + // ----------------- + 'foo': 'bar' // you don't need to change this one - it's actually an intentional filler. + } + + // --- public methods --- + + this.getMovie = function(smID) { + // return self.isIE?window[smID]:document[smID]; + return self.isIE?window[smID]:(self.isSafari?document[smID+'-embed']:document.getElementById(smID+'-embed')); + } + + this.loadFromXML = function(sXmlUrl) { + try { + self.o._loadFromXML(sXmlUrl); + } catch(e) { + self._failSafely(); + return true; + } + } + + this.createSound = function(oOptions) { + if (!self._didInit) throw new Error('soundManager.createSound(): Not loaded yet - wait for soundManager.onload() before calling sound-related methods'); + if (arguments.length==2) { + // function overloading in JS! :) ..assume simple createSound(id,url) use case + oOptions = {'id':arguments[0],'url':arguments[1]} + } + var thisOptions = self._mergeObjects(oOptions); + self._writeDebug('soundManager.createSound(): "<a href="#" onclick="soundManager.play(\''+thisOptions.id+'\');return false" title="play this sound">'+thisOptions.id+'</a>" ('+thisOptions.url+')'); + if (self._idCheck(thisOptions.id,true)) { + self._writeDebug('sound '+thisOptions.id+' already defined - exiting'); + return false; + } + self.sounds[thisOptions.id] = new SMSound(self,thisOptions); + self.soundIDs[self.soundIDs.length] = thisOptions.id; + try { + self.o._createSound(thisOptions.id,thisOptions.onjustbeforefinishtime); + } catch(e) { + self._failSafely(); + return true; + } + if (thisOptions.autoLoad || thisOptions.autoPlay) self.sounds[thisOptions.id].load(thisOptions); + if (thisOptions.autoPlay) self.sounds[thisOptions.id].playState = 1; // we can only assume this sound will be playing soon. + } + + this.load = function(sID,oOptions) { + if (!self._idCheck(sID)) return false; + self.sounds[sID].load(oOptions); + } + + this.unload = function(sID) { + if (!self._idCheck(sID)) return false; + self.sounds[sID].unload(); + } + + this.play = function(sID,oOptions) { + if (!self._idCheck(sID)) { + if (typeof oOptions != 'Object') oOptions = {url:oOptions}; // overloading use case: play('mySound','/path/to/some.mp3'); + if (oOptions && oOptions.url) { + // overloading use case, creation + playing of sound: .play('someID',{url:'/path/to.mp3'}); + self._writeDebug('soundController.play(): attempting to create "'+sID+'"'); + oOptions.id = sID; + self.createSound(oOptions); + } else { + return false; + } + } + self.sounds[sID].play(oOptions); + } + + this.start = this.play; // just for convenience + + this.setPosition = function(sID,nMsecOffset) { + if (!self._idCheck(sID)) return false; + self.sounds[sID].setPosition(nMsecOffset); + } + + this.stop = function(sID) { + if (!self._idCheck(sID)) return false; + self._writeDebug('soundManager.stop('+sID+')'); + self.sounds[sID].stop(); + } + + this.stopAll = function() { + for (var oSound in self.sounds) { + if (oSound instanceof SMSound) oSound.stop(); // apply only to sound objects + } + } + + this.pause = function(sID) { + if (!self._idCheck(sID)) return false; + self.sounds[sID].pause(); + } + + this.resume = function(sID) { + if (!self._idCheck(sID)) return false; + self.sounds[sID].resume(); + } + + this.togglePause = function(sID) { + if (!self._idCheck(sID)) return false; + self.sounds[sID].togglePause(); + } + + this.setPan = function(sID,nPan) { + if (!self._idCheck(sID)) return false; + self.sounds[sID].setPan(nPan); + } + + this.setVolume = function(sID,nVol) { + if (!self._idCheck(sID)) return false; + self.sounds[sID].setVolume(nVol); + } + + this.setPolling = function(bPolling) { + if (!self.o || !self.allowPolling) return false; + self._writeDebug('soundManager.setPolling('+bPolling+')'); + self.o._setPolling(bPolling); + } + + this.disable = function() { + // destroy all functions + if (self._disabled) return false; + self._disabled = true; + self._writeDebug('soundManager.disable(): Disabling all functions - future calls will return false.'); + for (var i=self.soundIDs.length; i--;) { + self._disableObject(self.sounds[self.soundIDs[i]]); + } + self.initComplete(); // fire "complete", despite fail + self._disableObject(self); + } + + this.getSoundById = function(sID,suppressDebug) { + if (!sID) throw new Error('SoundManager.getSoundById(): sID is null/undefined'); + var result = self.sounds[sID]; + if (!result && !suppressDebug) { + self._writeDebug('"'+sID+'" is an invalid sound ID.'); + // soundManager._writeDebug('trace: '+arguments.callee.caller); + } + return result; + } + + this.onload = function() { + onloadSM2(); + // window.onload() equivalent for SM2, ready to create sounds etc. + // this is a stub - you can override this in your own external script, eg. soundManager.onload = function() {} + // soundManager._writeDebug('<em>Warning</em>: soundManager.onload() is undefined.'); + } + + this.onerror = function() { + onerrorSM2(); + // stub for user handler, called when SM2 fails to load/init + } + + // --- "private" methods --- + + this._idCheck = this.getSoundById; + + this._disableObject = function(o) { + for (var oProp in o) { + if (typeof o[oProp] == 'function') o[oProp] = function(){return false;} + } + oProp = null; + } + + this._failSafely = function() { + // exception handler for "object doesn't support this property or method" + var flashCPLink = 'http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html'; + var fpgssTitle = 'You may need to whitelist this location/domain eg. file://C:/ or C:/ or mysite.com, or set ALWAYS ALLOW under the Flash Player Global Security Settings page. Note that this seems to apply only to file system viewing.'; + var flashCPL = '<a href="'+flashCPLink+'" title="'+fpgssTitle+'">view/edit</a>'; + var FPGSS = '<a href="'+flashCPLink+'" title="Flash Player Global Security Settings">FPGSS</a>'; + if (!self._disabled) { + self._writeDebug('soundManager: JS->Flash communication failed. Possible causes: flash/browser security restrictions ('+flashCPL+'), insufficient browser/plugin support, or .swf not found'); + self._writeDebug('Verify that the movie path of <em>'+self.url+'</em> is correct (<a href="'+self.url+'" title="If you get a 404/not found, fix it!">test link</a>)'); + if (self._didAppend) { + if (!document.domain) { + self._writeDebug('Loading from local file system? (document.domain appears to be null, this location may need whitelisting by '+FPGSS+')'); + self._writeDebug('Possible security/domain restrictions ('+flashCPL+'), should work when served by http on same domain'); + } + // self._writeDebug('Note: Movie added via JS method, static object/embed in-page markup method may work instead.'); + } + self.disable(); + } + } + + this._createMovie = function(smID,smURL) { + var useDOM = !self.isIE; // IE needs document.write() to work, long story short - lame + if (window.location.href.indexOf('debug=1')+1) self.defaultOptions.debugMode = true; // allow force of debug mode via URL + var html = ['<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0" width="16" height="16" id="'+smID+'"><param name="movie" value="'+smURL+'"><param name="quality" value="high"><param name="allowScriptAccess" value="always" /></object>','<embed name="'+smID+'-embed" id="'+smID+'-embed" src="'+smURL+'" width="1" height="1" quality="high" allowScriptAccess="always" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash"></embed>']; + var debugID = 'soundmanager-debug'; + var debugHTML = '<div id="'+debugID+'" style="display:'+(self.defaultOptions.debugMode?'block':'none')+'"></div>'; + if (useDOM) { + self.oMC = document.createElement('div'); + self.oMC.className = 'movieContainer'; + // "hide" flash movie + self.oMC.style.position = 'absolute'; + self.oMC.style.left = '-256px'; + self.oMC.style.width = '1px'; + self.oMC.style.height = '1px'; + self.oMC.innerHTML = html[self.isIE?0:1]; + var oTarget = (!self.isIE && document.body?document.body:document.getElementsByTagName('div')[0]); + oTarget.appendChild(self.oMC); // append to body here can throw "operation aborted" under IE + if (!document.getElementById(debugID)) { + var oDebug = document.createElement('div'); + oDebug.id = debugID; + oDebug.style.display = (self.defaultOptions.debugMode?'block':'none'); + oTarget.appendChild(oDebug); + } + oTarget = null; + } else { + // IE method - local file system, may cause strange JS error at line 53? + // I hate this method, but it appears to be the only one that will work (createElement works, but JS->Flash communication oddly fails when viewing locally.) + // Finally, IE doesn't do application/xhtml+xml yet (thus document.write() is still minimally acceptable here.) + if (document.getElementById(debugID)) debugHTML = ''; // avoid overwriting + document.write('<div style="position:absolute;left:-256px;top:-256px;width:1px;height:1px" class="movieContainer">'+html[self.isIE?0:1]+'</div>'+debugHTML); + } + self._didAppend = true; + self._writeDebug('-- SoundManager 2 Version '+self.version.substr(1)+' --'); + self._writeDebug('soundManager._createMovie(): trying to load <a href="'+smURL+'" title="Test this link (404=bad)">'+smURL+'</a>'); + } + + this._writeDebug = function(sText) { + if (!self.defaultOptions.debugMode) return false; + var sDID = 'soundmanager-debug'; + try { + var o = document.getElementById(sDID); + if (!o) { + // attempt to create soundManager debug element + var oNew = document.createElement('div'); + oNew.id = sDID; + // o = document.body.appendChild(oNew); + o = null; + if (!o) return false; + } + var p = document.createElement('div'); + p.innerHTML = sText; + // o.appendChild(p); // top-to-bottom + o.insertBefore(p,o.firstChild); // bottom-to-top + } catch(e) { + // oh well + } + o = null; + } + + this._debug = function() { + self._writeDebug('soundManager._debug(): sounds by id/url:'); + for (var i=0,j=self.soundIDs.length; i<j; i++) { + self._writeDebug(self.sounds[self.soundIDs[i]].sID+' | '+self.sounds[self.soundIDs[i]].url); + } + } + + this._mergeObjects = function(oMain,oAdd) { + // non-destructive merge + var o1 = oMain; + var o2 = (typeof oAdd == 'undefined'?self.defaultOptions:oAdd); + for (var o in o2) { + if (typeof o1[o] == 'undefined') o1[o] = o2[o]; + } + return o1; + } + + this.createMovie = function(sURL) { + self._writeDebug('soundManager.createMovie('+(sURL||'')+')'); + if (sURL) self.url = sURL; + self._initMovie(); + } + + this._initMovie = function() { + // attempt to get, or create, movie + if (self.o) return false; // pre-init may have fired this function before window.onload(), may already exist + self.o = self.getMovie(self.id); // try to get flash movie (inline markup) + if (!self.o) { + // try to create + self._createMovie(self.id,self.url); + self.o = self.getMovie(self.id); + } + if (!self.o) { + self._writeDebug('SoundManager(): Could not find object/embed element. Sound will be disabled.'); + self.disable(); + } else { + self._writeDebug('SoundManager(): Got '+self.o.nodeName+' element ('+(self._didAppend?'created via JS':'static HTML')+')'); + } + } + + this.initComplete = function() { + if (self._didInit) return false; + self._didInit = true; + self._writeDebug('-- SoundManager 2 '+(self._disabled?'failed to load':'loaded')+' ('+(self._disabled?'security/load error':'OK')+') --'); + if (self._disabled) { + self._writeDebug('soundManager.initComplete(): calling soundManager.onerror()'); + self.onerror.apply(window); + return false; + } + self._writeDebug('soundManager.initComplete(): calling soundManager.onload()'); + try { + // call user-defined "onload", scoped to window + self.onload.apply(window); + } catch(e) { + // something broke (likely JS error in user function) + self._writeDebug('soundManager.onload() threw an exception: '+e.message); + throw e; // (so browser/console gets message) + } + self._writeDebug('soundManager.onload() complete'); + } + + this.init = function() { + // called after onload() + self._initMovie(); + // event cleanup + if (window.removeEventListener) { + window.removeEventListener('load',self.beginInit,false); + } else if (window.detachEvent) { + window.detachEvent('onload',self.beginInit); + } + try { + self.o._externalInterfaceTest(); // attempt to talk to Flash + self._writeDebug('Flash ExternalInterface call (JS -> Flash) succeeded.'); + if (!self.allowPolling) self._writeDebug('Polling (whileloading/whileplaying support) is disabled.'); + self.setPolling(true); + self.enabled = true; + } catch(e) { + self._failSafely(); + self.initComplete(); + return false; + } + self.initComplete(); + } + + this.beginInit = function() { + self._initMovie(); + setTimeout(self.init,1000); // some delay required, otherwise JS<->Flash/ExternalInterface communication fails under non-IE (?!) + } + + this.destruct = function() { + if (self.isSafari) { + /* -- + Safari 1.3.2 (v312.6)/OSX 10.3.9 and perhaps newer will crash if a sound is actively loading when user exits/refreshes/leaves page + (Apparently related to ExternalInterface making calls to an unloading/unloaded page?) + Unloading sounds (detaching handlers and so on) may help to prevent this + -- */ + for (var i=self.soundIDs.length; i--;) { + if (self.sounds[self.soundIDs[i]].readyState == 1) self.sounds[self.soundIDs[i]].unload(); + } + } + self.disable(); + // self.o = null; + // self.oMC = null; + } + +} + +function SMSound(oSM,oOptions) { + var self = this; + var sm = oSM; + this.sID = oOptions.id; + this.url = oOptions.url; + this.options = sm._mergeObjects(oOptions); + this.id3 = { + /* + Name/value pairs set via Flash when available - see reference for names: + http://livedocs.macromedia.com/flash/8/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00001567.html + (eg., this.id3.songname or this.id3['songname']) + */ + } + + self.resetProperties = function(bLoaded) { + self.bytesLoaded = null; + self.bytesTotal = null; + self.position = null; + self.duration = null; + self.durationEstimate = null; + self.loaded = false; + self.loadSuccess = null; + self.playState = 0; + self.paused = false; + self.readyState = 0; // 0 = uninitialised, 1 = loading, 2 = failed/error, 3 = loaded/success + self.didBeforeFinish = false; + self.didJustBeforeFinish = false; + } + + self.resetProperties(); + + // --- public methods --- + + this.load = function(oOptions) { + self.loaded = false; + self.loadSuccess = null; + self.readyState = 1; + self.playState = (oOptions.autoPlay||false); // if autoPlay, assume "playing" is true (no way to detect when it actually starts in Flash unless onPlay is watched?) + var thisOptions = sm._mergeObjects(oOptions); + if (typeof thisOptions.url == 'undefined') thisOptions.url = self.url; + try { + sm._writeDebug('loading '+thisOptions.url); + sm.o._load(self.sID,thisOptions.url,thisOptions.stream,thisOptions.autoPlay,thisOptions.whileloading?1:0); + } catch(e) { + sm._writeDebug('SMSound().load(): JS->Flash communication failed.'); + } + } + + this.unload = function() { + // Flash 8/AS2 can't "close" a stream - fake it by loading an empty MP3 + sm._writeDebug('SMSound().unload()'); + self.setPosition(0); // reset current sound positioning + sm.o._unload(self.sID,'ui/audio_support/null.mp3'); + // reset load/status flags + self.resetProperties(); + } + + this.play = function(oOptions) { + if (!oOptions) oOptions = {}; + + // --- TODO: make event handlers specified via oOptions only apply to this instance of play() (eg. onfinish applies but will reset to default on finish) + if (oOptions.onfinish) self.options.onfinish = oOptions.onfinish; + if (oOptions.onbeforefinish) self.options.onbeforefinish = oOptions.onbeforefinish; + if (oOptions.onjustbeforefinish) self.options.onjustbeforefinish = oOptions.onjustbeforefinish; + // --- + + var thisOptions = sm._mergeObjects(oOptions); + if (self.playState == 1) { + // var allowMulti = typeof oOptions.multiShot!='undefined'?oOptions.multiShot:sm.defaultOptions.multiShot; + var allowMulti = thisOptions.multiShot; + if (!allowMulti) { + sm._writeDebug('SMSound.play(): "'+self.sID+'" already playing? (one-shot)'); + return false; + } else { + sm._writeDebug('SMSound.play(): "'+self.sID+'" already playing (multi-shot)'); + } + } + if (!self.loaded) { + if (self.readyState == 0) { + sm._writeDebug('SMSound.play(): .play() before load request. Attempting to load "'+self.sID+'"'); + // try to get this sound playing ASAP + thisOptions.stream = true; + thisOptions.autoPlay = true; + // TODO: need to investigate when false, double-playing + // if (typeof oOptions.autoPlay=='undefined') thisOptions.autoPlay = true; // only set autoPlay if unspecified here + self.load(thisOptions); // try to get this sound playing ASAP + } else if (self.readyState == 2) { + sm._writeDebug('SMSound.play(): Could not load "'+self.sID+'" - exiting'); + return false; + } else { + sm._writeDebug('SMSound.play(): "'+self.sID+'" is loading - attempting to play..'); + } + } else { + sm._writeDebug('SMSound.play(): "'+self.sID+'"'); + } + if (self.paused) { + self.resume(); + } else { + self.playState = 1; + self.position = (thisOptions.offset||0); + if (thisOptions.onplay) thisOptions.onplay.apply(self); + self.setVolume(thisOptions.volume); + self.setPan(thisOptions.pan); + if (!thisOptions.autoPlay) { + sm._writeDebug('starting sound '+self.sID); + sm.o._start(self.sID,thisOptions.loop||1,self.position); // TODO: verify !autoPlay doesn't cause issue + } + } + } + + this.start = this.play; // just for convenience + + this.stop = function(bAll) { + if (self.playState == 1) { + self.playState = 0; + self.paused = false; + if (sm.defaultOptions.onstop) sm.defaultOptions.onstop.apply(self); + sm.o._stop(self.sID); + } + } + + this.setPosition = function(nMsecOffset) { + // sm._writeDebug('setPosition('+nMsecOffset+')'); + sm.o._setPosition(self.sID,nMsecOffset/1000,self.paused||!self.playState); // if paused or not playing, will not resume (by playing) + } + + this.pause = function() { + if (self.paused) return false; + sm._writeDebug('SMSound.pause()'); + self.paused = true; + sm.o._pause(self.sID); + } + + this.resume = function() { + if (!self.paused) return false; + sm._writeDebug('SMSound.resume()'); + self.paused = false; + sm.o._pause(self.sID); // flash method is toggle-based (pause/resume) + } + + this.togglePause = function() { + // if playing, pauses - if paused, resumes playing. + sm._writeDebug('SMSound.togglePause()'); + if (!self.playState) { + // self.setPosition(); + self.play({offset:self.position/1000}); + return false; + } + if (self.paused) { + sm._writeDebug('SMSound.togglePause(): resuming..'); + self.resume(); + } else { + sm._writeDebug('SMSound.togglePause(): pausing..'); + self.pause(); + } + } + + this.setPan = function(nPan) { + if (typeof nPan == 'undefined') nPan = 0; + sm.o._setPan(self.sID,nPan); + self.options.pan = nPan; + } + + this.setVolume = function(nVol) { + if (typeof nVol == 'undefined') nVol = 100; + sm.o._setVolume(self.sID,nVol); + self.options.volume = nVol; + } + + // --- "private" methods called by Flash --- + + this._whileloading = function(nBytesLoaded,nBytesTotal,nDuration) { + self.bytesLoaded = nBytesLoaded; + self.bytesTotal = nBytesTotal; + self.duration = nDuration; + self.durationEstimate = parseInt((self.bytesTotal/self.bytesLoaded)*self.duration); // estimate total time (will only be accurate with CBR MP3s.) + if (self.readyState != 3 && self.options.whileloading) self.options.whileloading.apply(self); + // soundManager._writeDebug('duration/durationEst: '+self.duration+' / '+self.durationEstimate); + } + + this._onid3 = function(oID3PropNames,oID3Data) { + // oID3PropNames: string array (names) + // ID3Data: string array (data) + sm._writeDebug('SMSound()._onid3(): "'+this.sID+'" ID3 data received.'); + var oData = []; + for (var i=0,j=oID3PropNames.length; i<j; i++) { + oData[oID3PropNames[i]] = oID3Data[i]; + // sm._writeDebug(oID3PropNames[i]+': '+oID3Data[i]); + } + self.id3 = sm._mergeObjects(self.id3,oData); + if (self.options.onid3) self.options.onid3.apply(self); + } + + this._whileplaying = function(nPosition) { + if (isNaN(nPosition) || nPosition == null) return false; // Flash may return NaN at times + self.position = nPosition; + if (self.playState == 1) { + if (self.options.whileplaying) self.options.whileplaying.apply(self); // flash may call after actual finish + if (self.loaded && self.options.onbeforefinish && self.options.onbeforefinishtime && !self.didBeforeFinish && self.duration-self.position <= self.options.onbeforefinishtime) { + sm._writeDebug('duration-position <= onbeforefinishtime: '+self.duration+' - '+self.position+' <= '+self.options.onbeforefinishtime+' ('+(self.duration-self.position)+')'); + self._onbeforefinish(); + } + } + } + + this._onload = function(bSuccess) { + bSuccess = (bSuccess==1?true:false); + sm._writeDebug('SMSound._onload(): "'+self.sID+'"'+(bSuccess?' loaded.':' failed to load (or loaded from cache - weird bug) - [<a href="'+self.url+'">test URL</a>]')); + self.loaded = bSuccess; + self.loadSuccess = bSuccess; + self.readyState = bSuccess?3:2; + if (self.options.onload) self.options.onload.apply(self); + } + + this._onbeforefinish = function() { + if (!self.didBeforeFinish) { + self.didBeforeFinish = true; + if (self.options.onbeforefinish) self.options.onbeforefinish.apply(self); + } + } + + this._onjustbeforefinish = function(msOffset) { + // msOffset: "end of sound" delay actual value (eg. 200 msec, value at event fire time was 187) + if (!self.didJustBeforeFinish) { + self.didJustBeforeFinish = true; + soundManager._writeDebug('SMSound._onjustbeforefinish()'); + if (self.options.onjustbeforefinish) self.options.onjustbeforefinish.apply(self);; + } + } + + this._onfinish = function() { + // sound has finished playing + sm._writeDebug('SMSound._onfinish(): "'+self.sID+'" finished playing'); + self.playState = 0; + self.paused = false; + if (self.options.onfinish) self.options.onfinish.apply(self); + if (self.options.onbeforefinishcomplete) self.options.onbeforefinishcomplete.apply(self); + // reset some state items + self.setPosition(0); + self.didBeforeFinish = false; + self.didJustBeforeFinish = false; + } + +} + +var soundManager = new SoundManager(); + +// attach onload handler +if (window.addEventListener) { + window.addEventListener('load',soundManager.beginInit,false); + window.addEventListener('beforeunload',soundManager.destruct,false); +} else if (window.attachEvent) { + window.attachEvent('onload',soundManager.beginInit); + window.attachEvent('beforeunload',soundManager.destruct); +} else { + // no add/attachevent support - safe to assume no JS->Flash either. + soundManager.disable(); +} \ No newline at end of file