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 <= onbeforefinishtime: '+_t.duration+' - '+_t.position+' <= '+_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.