vendor/assets/javascripts/soundmanager2.js in soundmanager-rails-0.1.2 vs vendor/assets/javascripts/soundmanager2.js in soundmanager-rails-0.1.3

- old
+ new

@@ -6,19 +6,19 @@ * * Copyright (c) 2007, Scott Schiller. All rights reserved. * Code provided under the BSD License: * http://schillmania.com/projects/soundmanager2/license.txt * - * V2.97a.20120916 + * V2.97a.20130101 */ -/*global window, SM2_DEFER, sm2Debugger, console, document, navigator, setTimeout, setInterval, clearInterval, Audio */ +/*global window, SM2_DEFER, sm2Debugger, console, document, navigator, setTimeout, setInterval, clearInterval, Audio, opera */ /*jslint regexp: true, sloppy: true, white: true, nomen: true, plusplus: true */ /** * About this file - * --------------- + * ------------------------------------------------------------------------------------- * This is the fully-commented source version of the SoundManager 2 API, * recommended for use during development and testing. * * See soundmanager2-nodebug-jsmin.js for an optimized build (~11KB with gzip.) * http://schillmania.com/projects/soundmanager2/doc/getstarted/#basic-inclusion @@ -28,12 +28,14 @@ * debug blocks which are removed in the -nodebug builds, further optimizing code size. * * Also, as you may note: Whoa, reliable cross-platform/device audio support is hard! ;) */ -(function(window) { +(function(window, _undefined) { +"use strict"; + var soundManager = null; /** * The SoundManager constructor. * @@ -179,11 +181,11 @@ this.debugID = 'soundmanager-debug'; this.debugURLParam = /([#?&])debug=1/i; // dynamic attributes - this.versionNumber = 'V2.97a.20120916'; + this.versionNumber = 'V2.97a.20130101'; this.version = null; this.movieURL = null; this.altURL = null; this.swfLoaded = false; this.enabled = false; @@ -231,25 +233,10 @@ // </d> }; /** - * basic HTML5 Audio() support test - * try...catch because of IE 9 "not implemented" nonsense - * https://github.com/Modernizr/Modernizr/issues/224 - */ - - this.hasHTML5 = (function() { - try { - // new Audio(null) for stupid Opera 9.64 case, which throws not_enough_arguments exception otherwise. - return (typeof Audio !== 'undefined' && typeof (_isOpera && opera.version() < 10 ? new Audio(null) : new Audio()).canPlayType !== 'undefined'); - } catch(e) { - return false; - } - }()); - - /** * format support (html5/flash) * stores canPlayType() results based on audioFormats. * eg. { mp3: boolean, mp4: boolean } * treat as read-only. */ @@ -270,34 +257,32 @@ /** * a few private internals (OK, a lot. :D) */ var SMSound, - _s = this, _flash = null, _sm = 'soundManager', _smc = _sm+'::', _h5 = 'HTML5::', _id, _ua = navigator.userAgent, _win = window, _wl = _win.location.href.toString(), _doc = document, _doNothing, _setProperties, _init, _fV, _on_queue = [], _debugOpen = true, _debugTS, _didAppend = false, _appendSuccess = false, _didInit = false, _disabled = false, _windowLoaded = false, _wDS, _wdCount = 0, _initComplete, _mixin, _assign, _extraOptions, _addOnEvent, _processOnEvents, _initUserOnload, _delayWaitForEI, _waitForEI, _setVersionInfo, _handleFocus, _strings, _initMovie, _domContentLoaded, _winOnLoad, _didDCLoaded, _getDocument, _createMovie, _catchError, _setPolling, _initDebug, _debugLevels = ['log', 'info', 'warn', 'error'], _defaultFlashVersion = 8, _disableObject, _failSafely, _normalizeMovieURL, _oRemoved = null, _oRemovedHTML = null, _str, _flashBlockHandler, _getSWFCSS, _swfCSS, _toggleDebug, _loopFix, _policyFix, _complain, _idCheck, _waitingForEI = false, _initPending = false, _startTimer, _stopTimer, _timerExecute, _h5TimerCount = 0, _h5IntervalTimer = null, _parseURL, - _needsFlash = null, _featureCheck, _html5OK, _html5CanPlay, _html5Ext, _html5Unload, _domContentLoadedIE, _testHTML5, _event, _slice = Array.prototype.slice, _useGlobalHTML5Audio = false, _hasFlash, _detectFlash, _badSafariFix, _html5_events, _showSupport, - _is_iDevice = _ua.match(/(ipad|iphone|ipod)/i), _isIE = _ua.match(/msie/i), _isWebkit = _ua.match(/webkit/i), _isSafari = (_ua.match(/safari/i) && !_ua.match(/chrome/i)), _isOpera = (_ua.match(/opera/i)), - _mobileHTML5 = (_ua.match(/(mobile|pre\/|xoom)/i) || _is_iDevice), - _isBadSafari = (!_wl.match(/usehtml5audio/i) && !_wl.match(/sm2\-ignorebadua/i) && _isSafari && !_ua.match(/silk/i) && _ua.match(/OS X 10_6_([3-7])/i)), // Safari 4 and 5 (excluding Kindle Fire, "Silk") occasionally fail to load/play HTML5 audio on Snow Leopard 10.6.3 through 10.6.7 due to bug(s) in QuickTime X and/or other underlying frameworks. :/ Confirmed bug. https://bugs.webkit.org/show_bug.cgi?id=32159 - _hasConsole = (typeof console !== 'undefined' && typeof console.log !== 'undefined'), _isFocused = (typeof _doc.hasFocus !== 'undefined'?_doc.hasFocus():null), _tryInitOnFocus = (_isSafari && (typeof _doc.hasFocus === 'undefined' || !_doc.hasFocus())), _okToDisable = !_tryInitOnFocus, _flashMIME = /(mp3|mp4|mpa|m4a|m4b)/i, - _emptyURL = 'about:blank', // safe URL to unload, or load nothing from (flash 8 + most HTML5 UAs) - _overHTTP = (_doc.location?_doc.location.protocol.match(/http/i):null), - _http = (!_overHTTP ? 'http:/'+'/' : ''), + sm2 = this, globalHTML5Audio = null, flash = null, sm = 'soundManager', smc = sm + ': ', h5 = 'HTML5::', id, ua = navigator.userAgent, wl = window.location.href.toString(), doc = document, doNothing, setProperties, init, fV, on_queue = [], debugOpen = true, debugTS, didAppend = false, appendSuccess = false, didInit = false, disabled = false, windowLoaded = false, _wDS, wdCount = 0, initComplete, mixin, assign, extraOptions, addOnEvent, processOnEvents, initUserOnload, delayWaitForEI, waitForEI, setVersionInfo, handleFocus, strings, initMovie, preInit, domContentLoaded, winOnLoad, didDCLoaded, getDocument, createMovie, catchError, setPolling, initDebug, debugLevels = ['log', 'info', 'warn', 'error'], defaultFlashVersion = 8, disableObject, failSafely, normalizeMovieURL, oRemoved = null, oRemovedHTML = null, str, flashBlockHandler, getSWFCSS, swfCSS, toggleDebug, loopFix, policyFix, complain, idCheck, waitingForEI = false, initPending = false, startTimer, stopTimer, timerExecute, h5TimerCount = 0, h5IntervalTimer = null, parseURL, messages = [], + needsFlash = null, featureCheck, html5OK, html5CanPlay, html5Ext, html5Unload, domContentLoadedIE, testHTML5, event, slice = Array.prototype.slice, useGlobalHTML5Audio = false, lastGlobalHTML5URL, hasFlash, detectFlash, badSafariFix, html5_events, showSupport, flushMessages, + is_iDevice = ua.match(/(ipad|iphone|ipod)/i), isAndroid = ua.match(/android/i), isIE = ua.match(/msie/i), isWebkit = ua.match(/webkit/i), isSafari = (ua.match(/safari/i) && !ua.match(/chrome/i)), isOpera = (ua.match(/opera/i)), + mobileHTML5 = (ua.match(/(mobile|pre\/|xoom)/i) || is_iDevice || isAndroid), + isBadSafari = (!wl.match(/usehtml5audio/i) && !wl.match(/sm2\-ignorebadua/i) && isSafari && !ua.match(/silk/i) && ua.match(/OS X 10_6_([3-7])/i)), // Safari 4 and 5 (excluding Kindle Fire, "Silk") occasionally fail to load/play HTML5 audio on Snow Leopard 10.6.3 through 10.6.7 due to bug(s) in QuickTime X and/or other underlying frameworks. :/ Confirmed bug. https://bugs.webkit.org/show_bug.cgi?id=32159 + hasConsole = (window.console !== _undefined && console.log !== _undefined), isFocused = (doc.hasFocus !== _undefined?doc.hasFocus():null), tryInitOnFocus = (isSafari && (doc.hasFocus === _undefined || !doc.hasFocus())), okToDisable = !tryInitOnFocus, flashMIME = /(mp3|mp4|mpa|m4a|m4b)/i, + emptyURL = 'about:blank', // safe URL to unload, or load nothing from (flash 8 + most HTML5 UAs) + overHTTP = (doc.location?doc.location.protocol.match(/http/i):null), + http = (!overHTTP ? 'http:/'+'/' : ''), // mp3, mp4, aac etc. - _netStreamMimeTypes = /^\s*audio\/(?:x-)?(?:mpeg4|aac|flv|mov|mp4||m4v|m4a|m4b|mp4v|3gp|3g2)\s*(?:$|;)/i, + netStreamMimeTypes = /^\s*audio\/(?:x-)?(?:mpeg4|aac|flv|mov|mp4||m4v|m4a|m4b|mp4v|3gp|3g2)\s*(?:$|;)/i, // Flash v9.0r115+ "moviestar" formats - _netStreamTypes = ['mpeg4', 'aac', 'flv', 'mov', 'mp4', 'm4v', 'f4v', 'm4a', 'm4b', 'mp4v', '3gp', '3g2'], - _netStreamPattern = new RegExp('\\.(' + _netStreamTypes.join('|') + ')(\\?.*)?$', 'i'); + netStreamTypes = ['mpeg4', 'aac', 'flv', 'mov', 'mp4', 'm4v', 'f4v', 'm4a', 'm4b', 'mp4v', '3gp', '3g2'], + netStreamPattern = new RegExp('\\.(' + netStreamTypes.join('|') + ')(\\?.*)?$', 'i'); this.mimePattern = /^\s*audio\/(?:x-)?(?:mp(?:eg|3))\s*(?:$|;)/i; // default mp3 set // use altURL if not "online" - this.useAltURL = !_overHTTP; + this.useAltURL = !overHTTP; - this._global_a = null; + swfCSS = { - _swfCSS = { - 'swfBox': 'sm2-object-box', 'swfDefault': 'movieContainer', 'swfError': 'swf_error', // SWF loaded, but SM2 couldn't start (other error) 'swfTimedout': 'swf_timedout', 'swfLoaded': 'swf_loaded', @@ -306,24 +291,25 @@ 'highPerf': 'high_performance', 'flashDebug': 'flash_debug' }; - if (_mobileHTML5) { + /** + * basic HTML5 Audio() support test + * try...catch because of IE 9 "not implemented" nonsense + * https://github.com/Modernizr/Modernizr/issues/224 + */ - // prefer HTML5 for mobile + tablet-like devices, probably more reliable vs. flash at this point. - _s.useHTML5Audio = true; - _s.preferFlash = false; - - if (_is_iDevice) { - // by default, use global feature. iOS onfinish() -> next may fail otherwise. - _s.ignoreFlash = true; - _useGlobalHTML5Audio = true; + this.hasHTML5 = (function() { + try { + // new Audio(null) for stupid Opera 9.64 case, which throws not_enough_arguments exception otherwise. + return (Audio !== _undefined && (isOpera && opera !== _undefined && opera.version() < 10 ? new Audio(null) : new Audio()).canPlayType !== _undefined); + } catch(e) { + return false; } + }()); - } - /** * Public SoundManager API * ----------------------- */ @@ -334,50 +320,50 @@ * onready and ontimeout are also accepted parameters. call soundManager.setup() to see the full list. */ this.setup = function(options) { - var noURL = (!_s.url); + var noURL = (!sm2.url); // warn if flash options have already been applied - if (typeof options !== 'undefined' && _didInit && _needsFlash && _s.ok() && (typeof options.flashVersion !== 'undefined' || typeof options.url !== 'undefined')) { - _complain(_str('setupLate')); + if (options !== _undefined && didInit && needsFlash && sm2.ok() && (options.flashVersion !== _undefined || options.url !== _undefined || options.html5Test !== _undefined)) { + complain(str('setupLate')); } // TODO: defer: true? - _assign(options); + assign(options); // special case 1: "Late setup". SM2 loaded normally, but user didn't assign flash URL eg., setup({url:...}) before SM2 init. Treat as delayed init. - if (noURL && _didDCLoaded && typeof options.url !== 'undefined') { - _s.beginDelayedInit(); + if (noURL && didDCLoaded && options.url !== _undefined) { + sm2.beginDelayedInit(); } // special case 2: If lazy-loading SM2 (DOMContentLoaded has already happened) and user calls setup() with url: parameter, try to init ASAP. - if (!_didDCLoaded && typeof options.url !== 'undefined' && _doc.readyState === 'complete') { - setTimeout(_domContentLoaded, 1); + if (!didDCLoaded && options.url !== _undefined && doc.readyState === 'complete') { + setTimeout(domContentLoaded, 1); } - return _s; + return sm2; }; this.ok = function() { - return (_needsFlash?(_didInit && !_disabled):(_s.useHTML5Audio && _s.hasHTML5)); + return (needsFlash?(didInit && !disabled):(sm2.useHTML5Audio && sm2.hasHTML5)); }; this.supported = this.ok; // legacy this.getMovie = function(smID) { // safety net: some old browsers differ on SWF references, possibly related to ExternalInterface / flash version - return _id(smID) || _doc[smID] || _win[smID]; + return id(smID) || doc[smID] || window[smID]; }; /** * Creates a SMSound sound object instance. @@ -386,108 +372,105 @@ * @return {object} SMSound The new SMSound object. */ this.createSound = function(oOptions, _url) { - var _cs, _cs_string, thisOptions = null, oSound = null, _tO = null; + var cs, cs_string, options, oSound = null; // <d> - _cs = _sm+'.createSound(): '; - _cs_string = _cs + _str(!_didInit?'notReady':'notOK'); + cs = sm + '.createSound(): '; + cs_string = cs + str(!didInit?'notReady':'notOK'); // </d> - if (!_didInit || !_s.ok()) { - _complain(_cs_string); + if (!didInit || !sm2.ok()) { + complain(cs_string); return false; } - if (typeof _url !== 'undefined') { + if (_url !== _undefined) { // function overloading in JS! :) ..assume simple createSound(id,url) use case oOptions = { 'id': oOptions, 'url': _url }; } // inherit from defaultOptions - thisOptions = _mixin(oOptions); + options = mixin(oOptions); - thisOptions.url = _parseURL(thisOptions.url); + options.url = parseURL(options.url); - // local shortcut - _tO = thisOptions; - // <d> - if (_tO.id.toString().charAt(0).match(/^[0-9]$/)) { - _s._wD(_cs + _str('badID', _tO.id), 2); + if (options.id.toString().charAt(0).match(/^[0-9]$/)) { + sm2._wD(cs + str('badID', options.id), 2); } - _s._wD(_cs + _tO.id + ' (' + _tO.url + ')', 1); + sm2._wD(cs + options.id + ' (' + options.url + ')', 1); // </d> - if (_idCheck(_tO.id, true)) { - _s._wD(_cs + _tO.id + ' exists', 1); - return _s.sounds[_tO.id]; + if (idCheck(options.id, true)) { + sm2._wD(cs + options.id + ' exists', 1); + return sm2.sounds[options.id]; } function make() { - thisOptions = _loopFix(thisOptions); - _s.sounds[_tO.id] = new SMSound(_tO); - _s.soundIDs.push(_tO.id); - return _s.sounds[_tO.id]; + options = loopFix(options); + sm2.sounds[options.id] = new SMSound(options); + sm2.soundIDs.push(options.id); + return sm2.sounds[options.id]; } - if (_html5OK(_tO)) { + if (html5OK(options)) { oSound = make(); - _s._wD('Creating sound '+_tO.id+', using HTML5'); - oSound._setup_html5(_tO); + sm2._wD(options.id + ': Using HTML5'); + oSound._setup_html5(options); } else { - if (_fV > 8) { - if (_tO.isMovieStar === null) { + if (fV > 8) { + if (options.isMovieStar === null) { // attempt to detect MPEG-4 formats - _tO.isMovieStar = !!(_tO.serverURL || (_tO.type ? _tO.type.match(_netStreamMimeTypes) : false) || _tO.url.match(_netStreamPattern)); + options.isMovieStar = !!(options.serverURL || (options.type ? options.type.match(netStreamMimeTypes) : false) || options.url.match(netStreamPattern)); } // <d> - if (_tO.isMovieStar) { - _s._wD(_cs + 'using MovieStar handling'); - if (_tO.loops > 1) { + if (options.isMovieStar) { + sm2._wD(cs + 'using MovieStar handling'); + if (options.loops > 1) { _wDS('noNSLoop'); } } // </d> } - _tO = _policyFix(_tO, _cs); + options = policyFix(options, cs); oSound = make(); - if (_fV === 8) { - _flash._createSound(_tO.id, _tO.loops||1, _tO.usePolicyFile); + if (fV === 8) { + flash._createSound(options.id, options.loops||1, options.usePolicyFile); } else { - _flash._createSound(_tO.id, _tO.url, _tO.usePeakData, _tO.useWaveformData, _tO.useEQData, _tO.isMovieStar, (_tO.isMovieStar?_tO.bufferTime:false), _tO.loops||1, _tO.serverURL, _tO.duration||null, _tO.autoPlay, true, _tO.autoLoad, _tO.usePolicyFile); - if (!_tO.serverURL) { + flash._createSound(options.id, options.url, options.usePeakData, options.useWaveformData, options.useEQData, options.isMovieStar, (options.isMovieStar?options.bufferTime:false), options.loops||1, options.serverURL, options.duration||null, options.autoPlay, true, options.autoLoad, options.usePolicyFile); + if (!options.serverURL) { // We are connected immediately oSound.connected = true; - if (_tO.onconnect) { - _tO.onconnect.apply(oSound); + if (options.onconnect) { + options.onconnect.apply(oSound); } } } - if (!_tO.serverURL && (_tO.autoLoad || _tO.autoPlay)) { + if (!options.serverURL && (options.autoLoad || options.autoPlay)) { // call load for non-rtmp streams - oSound.load(_tO); + oSound.load(options); } } // rtmp will play in onconnect - if (!_tO.serverURL && _tO.autoPlay) { + if (!options.serverURL && options.autoPlay) { oSound.play(); } return oSound; @@ -501,36 +484,36 @@ this.destroySound = function(sID, _bFromSound) { // explicitly destroy a sound before normal page unload, etc. - if (!_idCheck(sID)) { + if (!idCheck(sID)) { return false; } - var oS = _s.sounds[sID], i; + var oS = sm2.sounds[sID], i; // Disable all callbacks while the sound is being destroyed oS._iO = {}; oS.stop(); oS.unload(); - for (i = 0; i < _s.soundIDs.length; i++) { - if (_s.soundIDs[i] === sID) { - _s.soundIDs.splice(i, 1); + for (i = 0; i < sm2.soundIDs.length; i++) { + if (sm2.soundIDs[i] === sID) { + sm2.soundIDs.splice(i, 1); break; } } if (!_bFromSound) { // ignore if being called from SMSound instance oS.destruct(true); } oS = null; - delete _s.sounds[sID]; + delete sm2.sounds[sID]; return true; }; @@ -541,14 +524,14 @@ * @param {object} oOptions Optional: Sound options */ this.load = function(sID, oOptions) { - if (!_idCheck(sID)) { + if (!idCheck(sID)) { return false; } - return _s.sounds[sID].load(oOptions); + return sm2.sounds[sID].load(oOptions); }; /** * Calls the unload() method of a SMSound object by ID. @@ -556,14 +539,14 @@ * @param {string} sID The ID of the sound */ this.unload = function(sID) { - if (!_idCheck(sID)) { + if (!idCheck(sID)) { return false; } - return _s.sounds[sID].unload(); + return sm2.sounds[sID].unload(); }; /** * Calls the onPosition() method of a SMSound object by ID. @@ -575,14 +558,14 @@ * @return {SMSound} The SMSound object */ this.onPosition = function(sID, nPosition, oMethod, oScope) { - if (!_idCheck(sID)) { + if (!idCheck(sID)) { return false; } - return _s.sounds[sID].onposition(nPosition, oMethod, oScope); + return sm2.sounds[sID].onposition(nPosition, oMethod, oScope); }; // legacy/backwards-compability: lower-case method name this.onposition = this.onPosition; @@ -596,14 +579,14 @@ * @return {SMSound} The SMSound object */ this.clearOnPosition = function(sID, nPosition, oMethod) { - if (!_idCheck(sID)) { + if (!idCheck(sID)) { return false; } - return _s.sounds[sID].clearOnPosition(nPosition, oMethod); + return sm2.sounds[sID].clearOnPosition(nPosition, oMethod); }; /** * Calls the play() method of a SMSound object by ID. @@ -615,32 +598,32 @@ this.play = function(sID, oOptions) { var result = false; - if (!_didInit || !_s.ok()) { - _complain(_sm+'.play(): ' + _str(!_didInit?'notReady':'notOK')); + if (!didInit || !sm2.ok()) { + complain(sm + '.play(): ' + str(!didInit?'notReady':'notOK')); return result; } - if (!_idCheck(sID)) { + if (!idCheck(sID)) { if (!(oOptions instanceof Object)) { // overloading use case: play('mySound','/path/to/some.mp3'); oOptions = { url: oOptions }; } if (oOptions && oOptions.url) { // overloading use case, create+play: .play('someID',{url:'/path/to.mp3'}); - _s._wD(_sm+'.play(): attempting to create "' + sID + '"', 1); + sm2._wD(sm + '.play(): attempting to create "' + sID + '"', 1); oOptions.id = sID; - result = _s.createSound(oOptions).play(); + result = sm2.createSound(oOptions).play(); } return result; } - return _s.sounds[sID].play(oOptions); + return sm2.sounds[sID].play(oOptions); }; this.start = this.play; // just for convenience @@ -652,14 +635,14 @@ * @return {SMSound} The SMSound object */ this.setPosition = function(sID, nMsecOffset) { - if (!_idCheck(sID)) { + if (!idCheck(sID)) { return false; } - return _s.sounds[sID].setPosition(nMsecOffset); + return sm2.sounds[sID].setPosition(nMsecOffset); }; /** * Calls the stop() method of a SMSound object by ID. @@ -668,32 +651,32 @@ * @return {SMSound} The SMSound object */ this.stop = function(sID) { - if (!_idCheck(sID)) { + if (!idCheck(sID)) { return false; } - _s._wD(_sm+'.stop(' + sID + ')', 1); - return _s.sounds[sID].stop(); + sm2._wD(sm + '.stop(' + sID + ')', 1); + return sm2.sounds[sID].stop(); }; /** * Stops all currently-playing sounds. */ this.stopAll = function() { var oSound; - _s._wD(_sm+'.stopAll()', 1); + sm2._wD(sm + '.stopAll()', 1); - for (oSound in _s.sounds) { - if (_s.sounds.hasOwnProperty(oSound)) { + for (oSound in sm2.sounds) { + if (sm2.sounds.hasOwnProperty(oSound)) { // apply only to sound objects - _s.sounds[oSound].stop(); + sm2.sounds[oSound].stop(); } } }; @@ -704,26 +687,26 @@ * @return {SMSound} The SMSound object */ this.pause = function(sID) { - if (!_idCheck(sID)) { + if (!idCheck(sID)) { return false; } - return _s.sounds[sID].pause(); + return sm2.sounds[sID].pause(); }; /** * Pauses all currently-playing sounds. */ this.pauseAll = function() { var i; - for (i = _s.soundIDs.length-1; i >= 0; i--) { - _s.sounds[_s.soundIDs[i]].pause(); + for (i = sm2.soundIDs.length-1; i >= 0; i--) { + sm2.sounds[sm2.soundIDs[i]].pause(); } }; /** @@ -733,26 +716,26 @@ * @return {SMSound} The SMSound object */ this.resume = function(sID) { - if (!_idCheck(sID)) { + if (!idCheck(sID)) { return false; } - return _s.sounds[sID].resume(); + return sm2.sounds[sID].resume(); }; /** * Resumes all currently-paused sounds. */ this.resumeAll = function() { var i; - for (i = _s.soundIDs.length-1; i >= 0; i--) { - _s.sounds[_s.soundIDs[i]].resume(); + for (i = sm2.soundIDs.length-1; i >= 0; i--) { + sm2.sounds[sm2.soundIDs[i]].resume(); } }; /** @@ -762,14 +745,14 @@ * @return {SMSound} The SMSound object */ this.togglePause = function(sID) { - if (!_idCheck(sID)) { + if (!idCheck(sID)) { return false; } - return _s.sounds[sID].togglePause(); + return sm2.sounds[sID].togglePause(); }; /** * Calls the setPan() method of a SMSound object by ID. @@ -779,14 +762,14 @@ * @return {SMSound} The SMSound object */ this.setPan = function(sID, nPan) { - if (!_idCheck(sID)) { + if (!idCheck(sID)) { return false; } - return _s.sounds[sID].setPan(nPan); + return sm2.sounds[sID].setPan(nPan); }; /** * Calls the setVolume() method of a SMSound object by ID. @@ -796,14 +779,14 @@ * @return {SMSound} The SMSound object */ this.setVolume = function(sID, nVol) { - if (!_idCheck(sID)) { + if (!idCheck(sID)) { return false; } - return _s.sounds[sID].setVolume(nVol); + return sm2.sounds[sID].setVolume(nVol); }; /** * Calls the mute() method of either a single SMSound object by ID, or all sound objects. @@ -813,26 +796,30 @@ this.mute = function(sID) { var i = 0; - if (typeof sID !== 'string') { + if (sID instanceof String) { sID = null; } if (!sID) { - _s._wD(_sm+'.mute(): Muting all sounds'); - for (i = _s.soundIDs.length-1; i >= 0; i--) { - _s.sounds[_s.soundIDs[i]].mute(); + + sm2._wD(sm + '.mute(): Muting all sounds'); + for (i = sm2.soundIDs.length-1; i >= 0; i--) { + sm2.sounds[sm2.soundIDs[i]].mute(); } - _s.muted = true; + sm2.muted = true; + } else { - if (!_idCheck(sID)) { + + if (!idCheck(sID)) { return false; } - _s._wD(_sm+'.mute(): Muting "' + sID + '"'); - return _s.sounds[sID].mute(); + sm2._wD(sm + '.mute(): Muting "' + sID + '"'); + return sm2.sounds[sID].mute(); + } return true; }; @@ -841,11 +828,11 @@ * Mutes all sounds. */ this.muteAll = function() { - _s.mute(); + sm2.mute(); }; /** * Calls the unmute() method of either a single SMSound object by ID, or all sound objects. @@ -855,29 +842,29 @@ this.unmute = function(sID) { var i; - if (typeof sID !== 'string') { + if (sID instanceof String) { sID = null; } if (!sID) { - _s._wD(_sm+'.unmute(): Unmuting all sounds'); - for (i = _s.soundIDs.length-1; i >= 0; i--) { - _s.sounds[_s.soundIDs[i]].unmute(); + sm2._wD(sm + '.unmute(): Unmuting all sounds'); + for (i = sm2.soundIDs.length-1; i >= 0; i--) { + sm2.sounds[sm2.soundIDs[i]].unmute(); } - _s.muted = false; + sm2.muted = false; } else { - if (!_idCheck(sID)) { + if (!idCheck(sID)) { return false; } - _s._wD(_sm+'.unmute(): Unmuting "' + sID + '"'); - return _s.sounds[sID].unmute(); + sm2._wD(sm + '.unmute(): Unmuting "' + sID + '"'); + return sm2.sounds[sID].unmute(); } return true; @@ -887,11 +874,11 @@ * Unmutes all sounds. */ this.unmuteAll = function() { - _s.unmute(); + sm2.unmute(); }; /** * Calls the toggleMute() method of a SMSound object by ID. @@ -900,14 +887,14 @@ * @return {SMSound} The SMSound object */ this.toggleMute = function(sID) { - if (!_idCheck(sID)) { + if (!idCheck(sID)) { return false; } - return _s.sounds[sID].toggleMute(); + return sm2.sounds[sID].toggleMute(); }; /** * Retrieves the memory used by the flash plugin. @@ -918,12 +905,12 @@ this.getMemoryUse = function() { // flash-only var ram = 0; - if (_flash && _fV !== 8) { - ram = parseInt(_flash._getMemoryUse(), 10); + if (flash && fV !== 8) { + ram = parseInt(flash._getMemoryUse(), 10); } return ram; }; @@ -935,28 +922,28 @@ this.disable = function(bNoDisable) { // destroy all functions var i; - if (typeof bNoDisable === 'undefined') { + if (bNoDisable === _undefined) { bNoDisable = false; } - if (_disabled) { + if (disabled) { return false; } - _disabled = true; + disabled = true; _wDS('shutdown', 1); - for (i = _s.soundIDs.length-1; i >= 0; i--) { - _disableObject(_s.sounds[_s.soundIDs[i]]); + for (i = sm2.soundIDs.length-1; i >= 0; i--) { + disableObject(sm2.sounds[sm2.soundIDs[i]]); } // fire "complete", despite fail - _initComplete(bNoDisable); - _event.remove(_win, 'load', _initUserOnload); + initComplete(bNoDisable); + event.remove(window, 'load', initUserOnload); return true; }; @@ -966,17 +953,17 @@ this.canPlayMIME = function(sMIME) { var result; - if (_s.hasHTML5) { - result = _html5CanPlay({type:sMIME}); + if (sm2.hasHTML5) { + result = html5CanPlay({type:sMIME}); } - if (!result && _needsFlash) { + if (!result && needsFlash) { // if flash 9, test netStream (movieStar) types as well. - result = (sMIME && _s.ok() ? !!((_fV > 8 ? sMIME.match(_netStreamMimeTypes) : null) || sMIME.match(_s.mimePattern)) : null); + result = (sMIME && sm2.ok() ? !!((fV > 8 ? sMIME.match(netStreamMimeTypes) : null) || sMIME.match(sm2.mimePattern)) : null); } return result; }; @@ -990,16 +977,16 @@ this.canPlayURL = function(sURL) { var result; - if (_s.hasHTML5) { - result = _html5CanPlay({url: sURL}); + if (sm2.hasHTML5) { + result = html5CanPlay({url: sURL}); } - if (!result && _needsFlash) { - result = (sURL && _s.ok() ? !!(sURL.match(_s.filePattern)) : null); + if (!result && needsFlash) { + result = (sURL && sm2.ok() ? !!(sURL.match(sm2.filePattern)) : null); } return result; }; @@ -1011,17 +998,17 @@ * @return {boolean} URL playability */ this.canPlayLink = function(oLink) { - if (typeof oLink.type !== 'undefined' && oLink.type) { - if (_s.canPlayMIME(oLink.type)) { + if (oLink.type !== _undefined && oLink.type) { + if (sm2.canPlayMIME(oLink.type)) { return true; } } - return _s.canPlayURL(oLink.href); + return sm2.canPlayURL(oLink.href); }; /** * Retrieves a SMSound object by ID. @@ -1031,18 +1018,18 @@ */ this.getSoundById = function(sID, _suppressDebug) { if (!sID) { - throw new Error(_sm+'.getSoundById(): sID is null/undefined'); + throw new Error(sm + '.getSoundById(): sID is null/_undefined'); } - var result = _s.sounds[sID]; + var result = sm2.sounds[sID]; // <d> if (!result && !_suppressDebug) { - _s._wD('"' + sID + '" is an invalid sound ID.', 2); + sm2._wD('"' + sID + '" is an invalid sound ID.', 2); } // </d> return result; @@ -1061,27 +1048,27 @@ result = false; if (typeof oMethod === 'function') { // <d> - if (_didInit) { - _s._wD(_str('queue', sType)); + if (didInit) { + sm2._wD(str('queue', sType)); } // </d> if (!oScope) { - oScope = _win; + oScope = window; } - _addOnEvent(sType, oMethod, oScope); - _processOnEvents(); + addOnEvent(sType, oMethod, oScope); + processOnEvents(); result = true; } else { - throw _str('needFunction', sType); + throw str('needFunction', sType); } return result; @@ -1100,27 +1087,27 @@ result = false; if (typeof oMethod === 'function') { // <d> - if (_didInit) { - _s._wD(_str('queue', sType)); + if (didInit) { + sm2._wD(str('queue', sType)); } // </d> if (!oScope) { - oScope = _win; + oScope = window; } - _addOnEvent(sType, oMethod, oScope); - _processOnEvents({type:sType}); + addOnEvent(sType, oMethod, oScope); + processOnEvents({type:sType}); result = true; } else { - throw _str('needFunction', sType); + throw str('needFunction', sType); } return result; @@ -1129,89 +1116,89 @@ /** * Writes console.log()-style debug output to a console or in-browser element. * Applies when debugMode = true * * @param {string} sText The console message - * @param {string} sType Optional: Log type of 'info', 'warn' or 'error' - * @param {object} Optional: The scope to apply to the callback + * @param {object} sType Optional string: Log type of 'info', 'warn' or 'error', or object (to be dumped) */ - this._writeDebug = function(sText, sType, _bTimestamp) { + this._writeDebug = function(sText, sType) { // pseudo-private console.log()-style output // <d> - var sDID = 'soundmanager-debug', o, oItem, sMethod; + var sDID = 'soundmanager-debug', o, oItem; - if (!_s.debugMode) { + if (!sm2.debugMode) { return false; } - if (typeof _bTimestamp !== 'undefined' && _bTimestamp) { - sText = sText + ' | ' + new Date().getTime(); - } - - if (_hasConsole && _s.useConsole) { - sMethod = _debugLevels[sType]; - if (typeof console[sMethod] !== 'undefined') { - console[sMethod](sText); + if (hasConsole && sm2.useConsole) { + if (sType && typeof sType === 'object') { + // object passed; dump to console. + console.log(sText, sType); + } else if (debugLevels[sType] !== _undefined) { + console[debugLevels[sType]](sText); } else { console.log(sText); } - if (_s.consoleOnly) { + if (sm2.consoleOnly) { return true; } } - try { + o = id(sDID); - o = _id(sDID); + if (!o) { + return false; + } - if (!o) { - return false; - } + oItem = doc.createElement('div'); - oItem = _doc.createElement('div'); + if (++wdCount % 2 === 0) { + oItem.className = 'sm2-alt'; + } - if (++_wdCount % 2 === 0) { - oItem.className = 'sm2-alt'; - } + if (sType === _undefined) { + sType = 0; + } else { + sType = parseInt(sType, 10); + } - if (typeof sType === 'undefined') { - sType = 0; - } else { - sType = parseInt(sType, 10); - } + oItem.appendChild(doc.createTextNode(sText)); - oItem.appendChild(_doc.createTextNode(sText)); - - if (sType) { - if (sType >= 2) { - oItem.style.fontWeight = 'bold'; - } - if (sType === 3) { - oItem.style.color = '#ff3333'; - } + if (sType) { + if (sType >= 2) { + oItem.style.fontWeight = 'bold'; } + if (sType === 3) { + oItem.style.color = '#ff3333'; + } + } - // top-to-bottom - // o.appendChild(oItem); + // top-to-bottom + // o.appendChild(oItem); - // bottom-to-top - o.insertBefore(oItem, o.firstChild); + // bottom-to-top + o.insertBefore(oItem, o.firstChild); - } catch(e) { - // oh well - } - o = null; // </d> return true; }; + // <d> + // last-resort debugging option + if (wl.indexOf('sm2-debug=alert') !== -1) { + this._writeDebug = function(sText) { + window.alert(sText); + }; + } + // </d> + // alias this._wD = this._writeDebug; /** * Provides debug / state information on all SMSound objects. @@ -1221,122 +1208,191 @@ // <d> var i, j; _wDS('currentObj', 1); - for (i = 0, j = _s.soundIDs.length; i < j; i++) { - _s.sounds[_s.soundIDs[i]]._debug(); + for (i = 0, j = sm2.soundIDs.length; i < j; i++) { + sm2.sounds[sm2.soundIDs[i]]._debug(); } // </d> }; /** * Restarts and re-initializes the SoundManager instance. + * + * @param {boolean} resetEvents Optional: When true, removes all registered onready and ontimeout event callbacks. + * @param {boolean} excludeInit Options: When true, does not call beginDelayedInit() (which would restart SM2). + * @return {object} soundManager The soundManager instance. */ - this.reboot = function() { + this.reboot = function(resetEvents, excludeInit) { - // attempt to reset and init SM2 - _s._wD(_sm+'.reboot()'); + // reset some (or all) state, and re-init unless otherwise specified. // <d> - if (_s.soundIDs.length) { - _s._wD('Destroying ' + _s.soundIDs.length + ' SMSound objects...'); + if (sm2.soundIDs.length) { + sm2._wD('Destroying ' + sm2.soundIDs.length + ' SMSound objects...'); } // </d> - var i, j; + var i, j, k; - for (i = _s.soundIDs.length-1; i >= 0; i--) { - _s.sounds[_s.soundIDs[i]].destruct(); + for (i = sm2.soundIDs.length-1; i >= 0; i--) { + sm2.sounds[sm2.soundIDs[i]].destruct(); } // trash ze flash - if (_flash) { + if (flash) { + try { - if (_isIE) { - _oRemovedHTML = _flash.innerHTML; + + if (isIE) { + oRemovedHTML = flash.innerHTML; } - _oRemoved = _flash.parentNode.removeChild(_flash); - _s._wD('Flash movie removed.'); + + oRemoved = flash.parentNode.removeChild(flash); + + _wDS('flRemoved'); + } catch(e) { - // uh-oh. + + // Remove failed? May be due to flash blockers silently removing the SWF object/embed node from the DOM. Warn and continue. + _wDS('badRemove', 2); + } + } // actually, force recreate of movie. - _oRemovedHTML = _oRemoved = _needsFlash = null; - _s.enabled = _didDCLoaded = _didInit = _waitingForEI = _initPending = _didAppend = _appendSuccess = _disabled = _s.swfLoaded = false; - _s.soundIDs = []; - _s.sounds = {}; - _flash = null; + oRemovedHTML = oRemoved = needsFlash = flash = null; - for (i in _on_queue) { - if (_on_queue.hasOwnProperty(i)) { - for (j = _on_queue[i].length-1; j >= 0; j--) { - _on_queue[i][j].fired = false; + sm2.enabled = didDCLoaded = didInit = waitingForEI = initPending = didAppend = appendSuccess = disabled = useGlobalHTML5Audio = sm2.swfLoaded = false; + + sm2.soundIDs = []; + sm2.sounds = {}; + + if (!resetEvents) { + // reset callbacks for onready, ontimeout etc. so that they will fire again on re-init + for (i in on_queue) { + if (on_queue.hasOwnProperty(i)) { + for (j = 0, k = on_queue[i].length; j < k; j++) { + on_queue[i][j].fired = false; + } } } + } else { + // remove all callbacks entirely + on_queue = []; } - _s._wD(_sm + ': Rebooting...'); - _win.setTimeout(_s.beginDelayedInit, 20); + // <d> + if (!excludeInit) { + sm2._wD(sm + ': Rebooting...'); + } + // </d> + // reset HTML5 and flash canPlay test results + + sm2.html5 = { + 'usingFlash': null + }; + + sm2.flash = {}; + + // reset device-specific HTML/flash mode switches + + sm2.html5Only = false; + sm2.ignoreFlash = false; + + window.setTimeout(function() { + + preInit(); + + // by default, re-init + + if (!excludeInit) { + sm2.beginDelayedInit(); + } + + }, 20); + + return sm2; + }; + this.reset = function() { + + /** + * Shuts down and restores the SoundManager instance to its original loaded state, without an explicit reboot. All onready/ontimeout handlers are removed. + * After this call, SM2 may be re-initialized via soundManager.beginDelayedInit(). + * @return {object} soundManager The soundManager instance. + */ + + _wDS('reset'); + return sm2.reboot(true, true); + + }; + /** * Undocumented: Determines the SM2 flash movie's load progress. * * @return {number or null} Percent loaded, or if invalid/unsupported, null. */ this.getMoviePercent = function() { - // interesting note: flash/ExternalInterface bridge methods are not typeof "function" nor instanceof Function, but are still valid. - return (_flash && typeof _flash.PercentLoaded !== 'undefined' ? _flash.PercentLoaded() : null); + /** + * Interesting syntax notes... + * Flash/ExternalInterface (ActiveX/NPAPI) bridge methods are not typeof "function" nor instanceof Function, but are still valid. + * Additionally, JSLint dislikes ('PercentLoaded' in flash)-style syntax and recommends hasOwnProperty(), which does not work in this case. + * Furthermore, using (flash && flash.PercentLoaded) causes IE to throw "object doesn't support this property or method". + * Thus, 'in' syntax must be used. + */ + return (flash && 'PercentLoaded' in flash ? flash.PercentLoaded() : null); // Yes, JSLint. See nearby comment in source for explanation. + }; /** * Additional helper for manually invoking SM2's init process after DOM Ready / window.onload(). */ this.beginDelayedInit = function() { - _windowLoaded = true; - _domContentLoaded(); + windowLoaded = true; + domContentLoaded(); setTimeout(function() { - if (_initPending) { + if (initPending) { return false; } - _createMovie(); - _initMovie(); - _initPending = true; + createMovie(); + initMovie(); + initPending = true; return true; }, 20); - _delayWaitForEI(); + delayWaitForEI(); }; /** * Destroys the SoundManager instance and all SMSound instances. */ this.destruct = function() { - _s._wD(_sm+'.destruct()'); - _s.disable(true); + sm2._wD(sm + '.destruct()'); + sm2.disable(true); }; /** * SMSound() (sound object) constructor @@ -1346,13 +1402,13 @@ * @return {SMSound} The new SMSound object */ SMSound = function(oOptions) { - var _t = this, _resetProperties, _add_html5_events, _remove_html5_events, _stop_html5_timer, _start_html5_timer, _attachOnPosition, _onplay_called = false, _onPositionItems = [], _onPositionFired = 0, _detachOnPosition, _applyFromTo, _lastURL = null, _lastHTML5State; + var s = this, resetProperties, add_html5_events, remove_html5_events, stop_html5_timer, start_html5_timer, attachOnPosition, onplay_called = false, onPositionItems = [], onPositionFired = 0, detachOnPosition, applyFromTo, lastURL = null, lastHTML5State; - _lastHTML5State = { + lastHTML5State = { // tracks duration + position (time) duration: null, time: null }; @@ -1360,11 +1416,11 @@ // legacy this.sID = this.id; this.url = oOptions.url; - this.options = _mixin(oOptions); + this.options = mixin(oOptions); // per-play-instance-specific options this.instanceOptions = this.options; // short alias @@ -1392,172 +1448,140 @@ */ this._debug = function() { // <d> - // pseudo-private console.log()-style output - - if (_s.debugMode) { - - var stuff = null, msg = [], sF, sfBracket, maxLength = 64; - - for (stuff in _t.options) { - if (_t.options[stuff] !== null) { - if (typeof _t.options[stuff] === 'function') { - // handle functions specially - sF = _t.options[stuff].toString(); - // normalize spaces - sF = sF.replace(/\s\s+/g, ' '); - sfBracket = sF.indexOf('{'); - msg.push(' ' + stuff + ': {' + sF.substr(sfBracket + 1, (Math.min(Math.max(sF.indexOf('\n') - 1, maxLength), maxLength))).replace(/\n/g, '') + '... }'); - } else { - msg.push(' ' + stuff + ': ' + _t.options[stuff]); - } - } - } - - _s._wD('SMSound() merged options: {\n' + msg.join(', \n') + '\n}'); - - } + sm2._wD(s.id + ': Merged options:', s.options); // </d> }; - // <d> - this._debug(); - // </d> - /** * Begins loading a sound per its *url*. * * @param {object} oOptions Optional: Sound options * @return {SMSound} The SMSound object */ this.load = function(oOptions) { - var oS = null, _iO; + var oSound = null, instanceOptions; - if (typeof oOptions !== 'undefined') { - _t._iO = _mixin(oOptions, _t.options); - _t.instanceOptions = _t._iO; + if (oOptions !== _undefined) { + s._iO = mixin(oOptions, s.options); } else { - oOptions = _t.options; - _t._iO = oOptions; - _t.instanceOptions = _t._iO; - if (_lastURL && _lastURL !== _t.url) { + oOptions = s.options; + s._iO = oOptions; + if (lastURL && lastURL !== s.url) { _wDS('manURL'); - _t._iO.url = _t.url; - _t.url = null; + s._iO.url = s.url; + s.url = null; } } - if (!_t._iO.url) { - _t._iO.url = _t.url; + if (!s._iO.url) { + s._iO.url = s.url; } - _t._iO.url = _parseURL(_t._iO.url); + s._iO.url = parseURL(s._iO.url); - _s._wD('SMSound.load(): ' + _t._iO.url, 1); + // ensure we're in sync + s.instanceOptions = s._iO; - if (_t._iO.url === _t.url && _t.readyState !== 0 && _t.readyState !== 2) { + // local shortcut + instanceOptions = s._iO; + + sm2._wD(s.id + ': load (' + instanceOptions.url + ')'); + + if (instanceOptions.url === s.url && s.readyState !== 0 && s.readyState !== 2) { _wDS('onURL', 1); // if loaded and an onload() exists, fire immediately. - if (_t.readyState === 3 && _t._iO.onload) { + if (s.readyState === 3 && instanceOptions.onload) { // assume success based on truthy duration. - _t._iO.onload.apply(_t, [(!!_t.duration)]); + instanceOptions.onload.apply(s, [(!!s.duration)]); } - return _t; + return s; } - // local shortcut - _iO = _t._iO; - - // make a local copy of the old url before we re-assign it - _lastURL = (_t.url && _t.url.toString ? _t.url.toString() : null); - // reset a few state properties - _t.loaded = false; - _t.readyState = 1; - _t.playState = 0; - _t.id3 = {}; + s.loaded = false; + s.readyState = 1; + s.playState = 0; + s.id3 = {}; // TODO: If switching from HTML5 -> flash (or vice versa), stop currently-playing audio. - if (_html5OK(_iO)) { + if (html5OK(instanceOptions)) { - oS = _t._setup_html5(_iO); + oSound = s._setup_html5(instanceOptions); - if (!oS._called_load) { + if (!oSound._called_load) { - _s._wD(_h5+'load: '+_t.id); + s._html5_canplay = false; - _t._html5_canplay = false; - // TODO: review called_load / html5_canplay logic // if url provided directly to load(), assign it here. - if (_t._a.src !== _iO.url) { + if (s.url !== instanceOptions.url) { - _s._wD(_wDS('manURL') + ': ' + _iO.url); + sm2._wD(_wDS('manURL') + ': ' + instanceOptions.url); - _t._a.src = _iO.url; + s._a.src = instanceOptions.url; // TODO: review / re-apply all relevant options (volume, loop, onposition etc.) // reset position for new URL - _t.setPosition(0); + s.setPosition(0); } // given explicit load call, try to preload. // early HTML5 implementation (non-standard) - _t._a.autobuffer = 'auto'; + s._a.autobuffer = 'auto'; // standard - _t._a.preload = 'auto'; + s._a.preload = 'auto'; - oS._called_load = true; + s._a._called_load = true; - if (_iO.autoPlay) { - _t.play(); + if (instanceOptions.autoPlay) { + s.play(); } } else { - _s._wD(_h5+'ignoring request to load again: '+_t.id); + sm2._wD(s.id + ': Ignoring request to load again'); } } else { try { - _t.isHTML5 = false; - _t._iO = _policyFix(_loopFix(_iO)); + s.isHTML5 = false; + s._iO = policyFix(loopFix(instanceOptions)); // re-assign local shortcut - _iO = _t._iO; - if (_fV === 8) { - _flash._load(_t.id, _iO.url, _iO.stream, _iO.autoPlay, (_iO.whileloading?1:0), _iO.loops||1, _iO.usePolicyFile); + instanceOptions = s._iO; + if (fV === 8) { + flash._load(s.id, instanceOptions.url, instanceOptions.stream, instanceOptions.autoPlay, instanceOptions.usePolicyFile); } else { - _flash._load(_t.id, _iO.url, !!(_iO.stream), !!(_iO.autoPlay), _iO.loops||1, !!(_iO.autoLoad), _iO.usePolicyFile); + flash._load(s.id, instanceOptions.url, !!(instanceOptions.stream), !!(instanceOptions.autoPlay), instanceOptions.loops||1, !!(instanceOptions.autoLoad), instanceOptions.usePolicyFile); } } catch(e) { _wDS('smError', 2); - _debugTS('onload', false); - _catchError({type:'SMSOUND_LOAD_JS_EXCEPTION', fatal:true}); - + debugTS('onload', false); + catchError({type:'SMSOUND_LOAD_JS_EXCEPTION', fatal:true}); } } // after all of this, ensure sound url is up to date. - _t.url = _iO.url; + s.url = instanceOptions.url; - return _t; + return s; }; /** * Unloads a sound, canceling any open HTTP requests. @@ -1569,82 +1593,82 @@ // Flash 8/AS2 can't "close" a stream - fake it by loading an empty URL // Flash 9/AS3: Close stream, preventing further load // HTML5: Most UAs will use empty URL - if (_t.readyState !== 0) { + if (s.readyState !== 0) { - _s._wD('SMSound.unload(): "' + _t.id + '"'); + sm2._wD(s.id + ': unload()'); - if (!_t.isHTML5) { + if (!s.isHTML5) { - if (_fV === 8) { - _flash._unload(_t.id, _emptyURL); + if (fV === 8) { + flash._unload(s.id, emptyURL); } else { - _flash._unload(_t.id); + flash._unload(s.id); } } else { - _stop_html5_timer(); + stop_html5_timer(); - if (_t._a) { + if (s._a) { - _t._a.pause(); - _html5Unload(_t._a, _emptyURL); + s._a.pause(); + html5Unload(s._a, emptyURL); - // reset local URL for next load / play call, too - _t.url = _emptyURL; + // update empty URL, too + lastURL = emptyURL; } } // reset load/status flags - _resetProperties(); + resetProperties(); } - return _t; + return s; }; /** * Unloads and destroys a sound. */ this.destruct = function(_bFromSM) { - _s._wD('SMSound.destruct(): "' + _t.id + '"'); + sm2._wD(s.id + ': Destruct'); - if (!_t.isHTML5) { + if (!s.isHTML5) { // kill sound within Flash // Disable the onfailure handler - _t._iO.onfailure = null; - _flash._destroySound(_t.id); + s._iO.onfailure = null; + flash._destroySound(s.id); } else { - _stop_html5_timer(); + stop_html5_timer(); - if (_t._a) { - _t._a.pause(); - _html5Unload(_t._a); - if (!_useGlobalHTML5Audio) { - _remove_html5_events(); + if (s._a) { + s._a.pause(); + html5Unload(s._a); + if (!useGlobalHTML5Audio) { + remove_html5_events(); } // break obvious circular reference - _t._a._t = null; - _t._a = null; + s._a._s = null; + s._a = null; } } if (!_bFromSM) { // ensure deletion from controller - _s.destroySound(_t.id, true); + sm2.destroySound(s.id, true); } }; @@ -1659,110 +1683,113 @@ var fN, allowMulti, a, onready, startOK = true, exit = null; // <d> - fN = 'SMSound.play(): '; + fN = s.id + ': play(): '; // </d> // default to true - _updatePlayState = (typeof _updatePlayState === 'undefined' ? true : _updatePlayState); + _updatePlayState = (_updatePlayState === _undefined ? true : _updatePlayState); if (!oOptions) { oOptions = {}; } // first, use local URL (if specified) - if (_t.url) { - _t._iO.url = _t.url; + if (s.url) { + s._iO.url = s.url; } // mix in any options defined at createSound() - _t._iO = _mixin(_t._iO, _t.options); + s._iO = mixin(s._iO, s.options); // mix in any options specific to this method - _t._iO = _mixin(oOptions, _t._iO); + s._iO = mixin(oOptions, s._iO); - _t._iO.url = _parseURL(_t._iO.url); + s._iO.url = parseURL(s._iO.url); - _t.instanceOptions = _t._iO; + s.instanceOptions = s._iO; // RTMP-only - if (_t._iO.serverURL && !_t.connected) { - if (!_t.getAutoPlay()) { - _s._wD(fN+' Netstream not connected yet - setting autoPlay'); - _t.setAutoPlay(true); + if (s._iO.serverURL && !s.connected) { + if (!s.getAutoPlay()) { + sm2._wD(fN +' Netstream not connected yet - setting autoPlay'); + s.setAutoPlay(true); } - // play will be called in _onconnect() - return _t; + // play will be called in onconnect() + return s; } - if (_html5OK(_t._iO)) { - _t._setup_html5(_t._iO); - _start_html5_timer(); + if (html5OK(s._iO)) { + s._setup_html5(s._iO); + start_html5_timer(); } - if (_t.playState === 1 && !_t.paused) { - allowMulti = _t._iO.multiShot; + if (s.playState === 1 && !s.paused) { + allowMulti = s._iO.multiShot; if (!allowMulti) { - _s._wD(fN + '"' + _t.id + '" already playing (one-shot)', 1); - exit = _t; + sm2._wD(fN + 'Already playing (one-shot)', 1); + exit = s; } else { - _s._wD(fN + '"' + _t.id + '" already playing (multi-shot)', 1); + sm2._wD(fN + 'Already playing (multi-shot)', 1); } } if (exit !== null) { return exit; } // edge case: play() with explicit URL parameter - if (oOptions.url && oOptions.url !== _t.url) { + if (oOptions.url && oOptions.url !== s.url) { // load using merged options - _t.load(_t._iO); + s.load(s._iO); } - if (!_t.loaded) { + if (!s.loaded) { - if (_t.readyState === 0) { + if (s.readyState === 0) { - _s._wD(fN + 'Attempting to load "' + _t.id + '"', 1); + sm2._wD(fN + 'Attempting to load'); // try to get this sound playing ASAP - if (!_t.isHTML5) { + if (!s.isHTML5) { // assign directly because setAutoPlay() increments the instanceCount - _t._iO.autoPlay = true; - _t.load(_t._iO); + s._iO.autoPlay = true; + s.load(s._iO); } else { // iOS needs this when recycling sounds, loading a new URL on an existing object. - _t.load(_t._iO); + s.load(s._iO); } - } else if (_t.readyState === 2) { + // HTML5 hack - re-set instanceOptions? + s.instanceOptions = s._iO; - _s._wD(fN + 'Could not load "' + _t.id + '" - exiting', 2); - exit = _t; + } else if (s.readyState === 2) { + sm2._wD(fN + 'Could not load - exiting', 2); + exit = s; + } else { - _s._wD(fN + '"' + _t.id + '" is loading - attempting to play..', 1); + sm2._wD(fN + 'Loading - attempting to play...'); } } else { - _s._wD(fN + '"' + _t.id + '"'); + sm2._wD(fN); } if (exit !== null) { return exit; } - if (!_t.isHTML5 && _fV === 9 && _t.position > 0 && _t.position === _t.duration) { + if (!s.isHTML5 && fV === 9 && s.position > 0 && s.position === s.duration) { // flash 9 needs a position reset if play() is called while at the end of a sound. - _s._wD(fN + '"' + _t.id + '": Sound at end, resetting to position:0'); + sm2._wD(fN + 'Sound at end, resetting to position:0'); oOptions.position = 0; } /** * Streams will pause when their buffer is full if they are being loaded. @@ -1771,49 +1798,50 @@ * So only call resume() if the position is > 0. * Another reason is because options like volume won't have been applied yet. * For normal sounds, just resume. */ - if (_t.paused && _t.position >= 0 && (!_t._iO.serverURL || _t.position > 0)) { + if (s.paused && s.position >= 0 && (!s._iO.serverURL || s.position > 0)) { // https://gist.github.com/37b17df75cc4d7a90bf6 - _s._wD(fN + '"' + _t.id + '" is resuming from paused state',1); - _t.resume(); + sm2._wD(fN + 'Resuming from paused state', 1); + s.resume(); } else { - _t._iO = _mixin(oOptions, _t._iO); + s._iO = mixin(oOptions, s._iO); // apply from/to parameters, if they exist (and not using RTMP) - if (_t._iO.from !== null && _t._iO.to !== null && _t.instanceCount === 0 && _t.playState === 0 && !_t._iO.serverURL) { + if (s._iO.from !== null && s._iO.to !== null && s.instanceCount === 0 && s.playState === 0 && !s._iO.serverURL) { onready = function() { // sound "canplay" or onload() // re-apply from/to to instance options, and start playback - _t._iO = _mixin(oOptions, _t._iO); - _t.play(_t._iO); + s._iO = mixin(oOptions, s._iO); + s.play(s._iO); }; // HTML5 needs to at least have "canplay" fired before seeking. - if (_t.isHTML5 && !_t._html5_canplay) { + if (s.isHTML5 && !s._html5_canplay) { // this hasn't been loaded yet. load it first, and then do this again. - _s._wD(fN+'Beginning load of "'+ _t.id+'" for from/to case'); + sm2._wD(fN + 'Beginning load for from/to case'); - _t.load({ - _oncanplay: onready + s.load({ + // TODO: was _oncanplay. Sounds wrong. + oncanplay: onready }); exit = false; - } else if (!_t.isHTML5 && !_t.loaded && (!_t.readyState || _t.readyState !== 2)) { + } else if (!s.isHTML5 && !s.loaded && (!s.readyState || s.readyState !== 2)) { // to be safe, preload the whole thing in Flash. - _s._wD(fN+'Preloading "'+ _t.id+'" for from/to case'); + sm2._wD(fN + 'Preloading for from/to case'); - _t.load({ + s.load({ onload: onready }); exit = false; @@ -1823,72 +1851,72 @@ return exit; } // otherwise, we're ready to go. re-apply local options, and continue - _t._iO = _applyFromTo(); + s._iO = applyFromTo(); } - _s._wD(fN+'"'+ _t.id+'" is starting to play'); + sm2._wD(fN + 'Starting to play'); - if (!_t.instanceCount || _t._iO.multiShotEvents || (!_t.isHTML5 && _fV > 8 && !_t.getAutoPlay())) { - _t.instanceCount++; + if (!s.instanceCount || s._iO.multiShotEvents || (!s.isHTML5 && fV > 8 && !s.getAutoPlay())) { + s.instanceCount++; } // if first play and onposition parameters exist, apply them now - if (_t._iO.onposition && _t.playState === 0) { - _attachOnPosition(_t); + if (s._iO.onposition && s.playState === 0) { + attachOnPosition(s); } - _t.playState = 1; - _t.paused = false; + s.playState = 1; + s.paused = false; - _t.position = (typeof _t._iO.position !== 'undefined' && !isNaN(_t._iO.position) ? _t._iO.position : 0); + s.position = (s._iO.position !== _undefined && !isNaN(s._iO.position) ? s._iO.position : 0); - if (!_t.isHTML5) { - _t._iO = _policyFix(_loopFix(_t._iO)); + if (!s.isHTML5) { + s._iO = policyFix(loopFix(s._iO)); } - if (_t._iO.onplay && _updatePlayState) { - _t._iO.onplay.apply(_t); - _onplay_called = true; + if (s._iO.onplay && _updatePlayState) { + s._iO.onplay.apply(s); + onplay_called = true; } - _t.setVolume(_t._iO.volume, true); - _t.setPan(_t._iO.pan, true); + s.setVolume(s._iO.volume, true); + s.setPan(s._iO.pan, true); - if (!_t.isHTML5) { + if (!s.isHTML5) { - startOK = _flash._start(_t.id, _t._iO.loops || 1, (_fV === 9 ? _t._iO.position : _t._iO.position / 1000), _t._iO.multiShot); + startOK = flash._start(s.id, s._iO.loops || 1, (fV === 9 ? s._iO.position : s._iO.position / 1000), s._iO.multiShot); - if (_fV === 9 && !startOK) { + if (fV === 9 && !startOK) { // edge case: no sound hardware, or 32-channel flash ceiling hit. // applies only to Flash 9, non-NetStream/MovieStar sounds. // http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/media/Sound.html#play%28%29 - _s._wD(fN+ _t.id+': No sound hardware, or 32-sound ceiling hit'); - if (_t._iO.onplayerror) { - _t._iO.onplayerror.apply(_t); + sm2._wD(fN + 'No sound hardware, or 32-sound ceiling hit'); + if (s._iO.onplayerror) { + s._iO.onplayerror.apply(s); } } } else { - _start_html5_timer(); + start_html5_timer(); - a = _t._setup_html5(); + a = s._setup_html5(); - _t.setPosition(_t._iO.position); + s.setPosition(s._iO.position); a.play(); } } - return _t; + return s; }; // just for convenience this.start = this.play; @@ -1900,76 +1928,79 @@ * @return {SMSound} The SMSound object */ this.stop = function(bAll) { - var _iO = _t._iO, _oP; + var instanceOptions = s._iO, + originalPosition; - if (_t.playState === 1) { + if (s.playState === 1) { - _t._onbufferchange(0); - _t._resetOnPosition(0); - _t.paused = false; + sm2._wD(s.id + ': stop()'); - if (!_t.isHTML5) { - _t.playState = 0; + s._onbufferchange(0); + s._resetOnPosition(0); + s.paused = false; + + if (!s.isHTML5) { + s.playState = 0; } // remove onPosition listeners, if any - _detachOnPosition(); + detachOnPosition(); // and "to" position, if set - if (_iO.to) { - _t.clearOnPosition(_iO.to); + if (instanceOptions.to) { + s.clearOnPosition(instanceOptions.to); } - if (!_t.isHTML5) { + if (!s.isHTML5) { - _flash._stop(_t.id, bAll); + flash._stop(s.id, bAll); // hack for netStream: just unload - if (_iO.serverURL) { - _t.unload(); + if (instanceOptions.serverURL) { + s.unload(); } } else { - if (_t._a) { + if (s._a) { - _oP = _t.position; + originalPosition = s.position; // act like Flash, though - _t.setPosition(0); + s.setPosition(0); // hack: reflect old position for onstop() (also like Flash) - _t.position = _oP; + s.position = originalPosition; // html5 has no stop() // NOTE: pausing means iOS requires interaction to resume. - _t._a.pause(); + s._a.pause(); - _t.playState = 0; + s.playState = 0; // and update UI - _t._onTimer(); + s._onTimer(); - _stop_html5_timer(); + stop_html5_timer(); } } - _t.instanceCount = 0; - _t._iO = {}; + s.instanceCount = 0; + s._iO = {}; - if (_iO.onstop) { - _iO.onstop.apply(_t); + if (instanceOptions.onstop) { + instanceOptions.onstop.apply(s); } } - return _t; + return s; }; /** * Undocumented/internal: Sets autoPlay for RTMP. @@ -1977,20 +2008,20 @@ * @param {boolean} autoPlay state */ this.setAutoPlay = function(autoPlay) { - _s._wD('sound '+_t.id+' turned autoplay ' + (autoPlay ? 'on' : 'off')); - _t._iO.autoPlay = autoPlay; + sm2._wD(s.id + ': Autoplay turned ' + (autoPlay ? 'on' : 'off')); + s._iO.autoPlay = autoPlay; - if (!_t.isHTML5) { - _flash._setAutoPlay(_t.id, autoPlay); + if (!s.isHTML5) { + flash._setAutoPlay(s.id, autoPlay); if (autoPlay) { // only increment the instanceCount if the sound isn't loaded (TODO: verify RTMP) - if (!_t.instanceCount && _t.readyState === 1) { - _t.instanceCount++; - _s._wD('sound '+_t.id+' incremented instance count to '+_t.instanceCount); + if (!s.instanceCount && s.readyState === 1) { + s.instanceCount++; + sm2._wD(s.id + ': Incremented instance count to '+s.instanceCount); } } } }; @@ -2001,11 +2032,11 @@ * @return {boolean} The current autoPlay value */ this.getAutoPlay = function() { - return _t._iO.autoPlay; + return s._iO.autoPlay; }; /** * Sets the position of a sound. @@ -2014,70 +2045,70 @@ * @return {SMSound} The SMSound object */ this.setPosition = function(nMsecOffset) { - if (typeof nMsecOffset === 'undefined') { + if (nMsecOffset === _undefined) { nMsecOffset = 0; } var original_pos, position, position1K, // Use the duration from the instance options, if we don't have a track duration yet. // position >= 0 and <= current available (loaded) duration - offset = (_t.isHTML5 ? Math.max(nMsecOffset, 0) : Math.min(_t.duration || _t._iO.duration, Math.max(nMsecOffset, 0))); + offset = (s.isHTML5 ? Math.max(nMsecOffset, 0) : Math.min(s.duration || s._iO.duration, Math.max(nMsecOffset, 0))); - original_pos = _t.position; - _t.position = offset; - position1K = _t.position/1000; - _t._resetOnPosition(_t.position); - _t._iO.position = offset; + original_pos = s.position; + s.position = offset; + position1K = s.position/1000; + s._resetOnPosition(s.position); + s._iO.position = offset; - if (!_t.isHTML5) { + if (!s.isHTML5) { - position = (_fV === 9 ? _t.position : position1K); - if (_t.readyState && _t.readyState !== 2) { + position = (fV === 9 ? s.position : position1K); + if (s.readyState && s.readyState !== 2) { // if paused or not playing, will not resume (by playing) - _flash._setPosition(_t.id, position, (_t.paused || !_t.playState), _t._iO.multiShot); + flash._setPosition(s.id, position, (s.paused || !s.playState), s._iO.multiShot); } - } else if (_t._a) { + } else if (s._a) { // Set the position in the canplay handler if the sound is not ready yet - if (_t._html5_canplay) { - if (_t._a.currentTime !== position1K) { + if (s._html5_canplay) { + if (s._a.currentTime !== position1K) { /** * DOM/JS errors/exceptions to watch out for: * if seek is beyond (loaded?) position, "DOM exception 11" * "INDEX_SIZE_ERR": DOM exception 1 */ - _s._wD('setPosition('+position1K+'): setting position'); + sm2._wD(s.id + ': setPosition('+position1K+')'); try { - _t._a.currentTime = position1K; - if (_t.playState === 0 || _t.paused) { + s._a.currentTime = position1K; + if (s.playState === 0 || s.paused) { // allow seek without auto-play/resume - _t._a.pause(); + s._a.pause(); } } catch(e) { - _s._wD('setPosition('+position1K+'): setting position failed: '+e.message, 2); + sm2._wD(s.id + ': setPosition(' + position1K + ') failed: ' + e.message, 2); } } } else { - _s._wD('setPosition('+position1K+'): delaying, sound not ready'); + sm2._wD(s.id + ': setPosition(' + position1K + '): Cannot seek yet, sound not ready'); } } - if (_t.isHTML5) { - if (_t.paused) { + if (s.isHTML5) { + if (s.paused) { // if paused, refresh UI right away // force update - _t._onTimer(true); + s._onTimer(true); } } - return _t; + return s; }; /** * Pauses sound playback. @@ -2085,31 +2116,31 @@ * @return {SMSound} The SMSound object */ this.pause = function(_bCallFlash) { - if (_t.paused || (_t.playState === 0 && _t.readyState !== 1)) { - return _t; + if (s.paused || (s.playState === 0 && s.readyState !== 1)) { + return s; } - _s._wD('SMSound.pause()'); - _t.paused = true; + sm2._wD(s.id + ': pause()'); + s.paused = true; - if (!_t.isHTML5) { - if (_bCallFlash || typeof _bCallFlash === 'undefined') { - _flash._pause(_t.id, _t._iO.multiShot); + if (!s.isHTML5) { + if (_bCallFlash || _bCallFlash === _undefined) { + flash._pause(s.id, s._iO.multiShot); } } else { - _t._setup_html5().pause(); - _stop_html5_timer(); + s._setup_html5().pause(); + stop_html5_timer(); } - if (_t._iO.onpause) { - _t._iO.onpause.apply(_t); + if (s._iO.onpause) { + s._iO.onpause.apply(s); } - return _t; + return s; }; /** * Resumes sound playback. @@ -2125,40 +2156,40 @@ * first time, I think it's more appropriate to call onplay() rather than onresume(). */ this.resume = function() { - var _iO = _t._iO; + var instanceOptions = s._iO; - if (!_t.paused) { - return _t; + if (!s.paused) { + return s; } - _s._wD('SMSound.resume()'); - _t.paused = false; - _t.playState = 1; + sm2._wD(s.id + ': resume()'); + s.paused = false; + s.playState = 1; - if (!_t.isHTML5) { - if (_iO.isMovieStar && !_iO.serverURL) { + if (!s.isHTML5) { + if (instanceOptions.isMovieStar && !instanceOptions.serverURL) { // Bizarre Webkit bug (Chrome reported via 8tracks.com dudes): AAC content paused for 30+ seconds(?) will not resume without a reposition. - _t.setPosition(_t.position); + s.setPosition(s.position); } // flash method is toggle-based (pause/resume) - _flash._pause(_t.id, _iO.multiShot); + flash._pause(s.id, instanceOptions.multiShot); } else { - _t._setup_html5().play(); - _start_html5_timer(); + s._setup_html5().play(); + start_html5_timer(); } - if (!_onplay_called && _iO.onplay) { - _iO.onplay.apply(_t); - _onplay_called = true; - } else if (_iO.onresume) { - _iO.onresume.apply(_t); + if (!onplay_called && instanceOptions.onplay) { + instanceOptions.onplay.apply(s); + onplay_called = true; + } else if (instanceOptions.onresume) { + instanceOptions.onresume.apply(s); } - return _t; + return s; }; /** * Toggles sound playback. @@ -2166,26 +2197,26 @@ * @return {SMSound} The SMSound object */ this.togglePause = function() { - _s._wD('SMSound.togglePause()'); + sm2._wD(s.id + ': togglePause()'); - if (_t.playState === 0) { - _t.play({ - position: (_fV === 9 && !_t.isHTML5 ? _t.position : _t.position / 1000) + if (s.playState === 0) { + s.play({ + position: (fV === 9 && !s.isHTML5 ? s.position : s.position / 1000) }); - return _t; + return s; } - if (_t.paused) { - _t.resume(); + if (s.paused) { + s.resume(); } else { - _t.pause(); + s.pause(); } - return _t; + return s; }; /** * Sets the panning (L-R) effect. @@ -2194,30 +2225,30 @@ * @return {SMSound} The SMSound object */ this.setPan = function(nPan, bInstanceOnly) { - if (typeof nPan === 'undefined') { + if (nPan === _undefined) { nPan = 0; } - if (typeof bInstanceOnly === 'undefined') { + if (bInstanceOnly === _undefined) { bInstanceOnly = false; } - if (!_t.isHTML5) { - _flash._setPan(_t.id, nPan); + if (!s.isHTML5) { + flash._setPan(s.id, nPan); } // else { no HTML5 pan? } - _t._iO.pan = nPan; + s._iO.pan = nPan; if (!bInstanceOnly) { - _t.pan = nPan; - _t.options.pan = nPan; + s.pan = nPan; + s.options.pan = nPan; } - return _t; + return s; }; /** * Sets the volume. @@ -2233,33 +2264,33 @@ * Hardware volume control overrides software, and volume * will always return 1 per Apple docs. (iOS 4 + 5.) * http://developer.apple.com/library/safari/documentation/AudioVideo/Conceptual/HTML-canvas-guide/AddingSoundtoCanvasAnimations/AddingSoundtoCanvasAnimations.html */ - if (typeof nVol === 'undefined') { + if (nVol === _undefined) { nVol = 100; } - if (typeof _bInstanceOnly === 'undefined') { + if (_bInstanceOnly === _undefined) { _bInstanceOnly = false; } - if (!_t.isHTML5) { - _flash._setVolume(_t.id, (_s.muted && !_t.muted) || _t.muted?0:nVol); - } else if (_t._a) { + if (!s.isHTML5) { + flash._setVolume(s.id, (sm2.muted && !s.muted) || s.muted?0:nVol); + } else if (s._a) { // valid range: 0-1 - _t._a.volume = Math.max(0, Math.min(1, nVol/100)); + s._a.volume = Math.max(0, Math.min(1, nVol/100)); } - _t._iO.volume = nVol; + s._iO.volume = nVol; if (!_bInstanceOnly) { - _t.volume = nVol; - _t.options.volume = nVol; + s.volume = nVol; + s.options.volume = nVol; } - return _t; + return s; }; /** * Mutes the sound. @@ -2267,19 +2298,19 @@ * @return {SMSound} The SMSound object */ this.mute = function() { - _t.muted = true; + s.muted = true; - if (!_t.isHTML5) { - _flash._setVolume(_t.id, 0); - } else if (_t._a) { - _t._a.muted = true; + if (!s.isHTML5) { + flash._setVolume(s.id, 0); + } else if (s._a) { + s._a.muted = true; } - return _t; + return s; }; /** * Unmutes the sound. @@ -2287,20 +2318,20 @@ * @return {SMSound} The SMSound object */ this.unmute = function() { - _t.muted = false; - var hasIO = (typeof _t._iO.volume !== 'undefined'); + s.muted = false; + var hasIO = (s._iO.volume !== _undefined); - if (!_t.isHTML5) { - _flash._setVolume(_t.id, hasIO?_t._iO.volume:_t.options.volume); - } else if (_t._a) { - _t._a.muted = false; + if (!s.isHTML5) { + flash._setVolume(s.id, hasIO?s._iO.volume:s.options.volume); + } else if (s._a) { + s._a.muted = false; } - return _t; + return s; }; /** * Toggles the muted state of a sound. @@ -2308,11 +2339,11 @@ * @return {SMSound} The SMSound object */ this.toggleMute = function() { - return (_t.muted?_t.unmute():_t.mute()); + return (s.muted?s.unmute():s.mute()); }; /** * Registers a callback to be fired when a sound reaches a given position during playback. @@ -2325,18 +2356,18 @@ this.onPosition = function(nPosition, oMethod, oScope) { // TODO: basic dupe checking? - _onPositionItems.push({ + onPositionItems.push({ position: parseInt(nPosition, 10), method: oMethod, - scope: (typeof oScope !== 'undefined' ? oScope : _t), + scope: (oScope !== _undefined ? oScope : s), fired: false }); - return _t; + return s; }; // legacy/backwards-compability: lower-case method name this.onposition = this.onPosition; @@ -2358,40 +2389,40 @@ if (isNaN(nPosition)) { // safety check return false; } - for (i=0; i < _onPositionItems.length; i++) { + for (i=0; i < onPositionItems.length; i++) { - if (nPosition === _onPositionItems[i].position) { + if (nPosition === onPositionItems[i].position) { // remove this item if no method was specified, or, if the method matches - if (!oMethod || (oMethod === _onPositionItems[i].method)) { - if (_onPositionItems[i].fired) { + if (!oMethod || (oMethod === onPositionItems[i].method)) { + if (onPositionItems[i].fired) { // decrement "fired" counter, too - _onPositionFired--; + onPositionFired--; } - _onPositionItems.splice(i, 1); + onPositionItems.splice(i, 1); } } } }; this._processOnPosition = function() { - var i, item, j = _onPositionItems.length; + var i, item, j = onPositionItems.length; - if (!j || !_t.playState || _onPositionFired >= j) { + if (!j || !s.playState || onPositionFired >= j) { return false; } for (i=j-1; i >= 0; i--) { - item = _onPositionItems[i]; - if (!item.fired && _t.position >= item.position) { + item = onPositionItems[i]; + if (!item.fired && s.position >= item.position) { item.fired = true; - _onPositionFired++; + onPositionFired++; item.method.apply(item.scope, [item.position]); } } return true; @@ -2399,21 +2430,21 @@ }; this._resetOnPosition = function(nPosition) { // reset "fired" for items interested in this position - var i, item, j = _onPositionItems.length; + var i, item, j = onPositionItems.length; if (!j) { return false; } for (i=j-1; i >= 0; i--) { - item = _onPositionItems[i]; + item = onPositionItems[i]; if (item.fired && nPosition <= item.position) { item.fired = false; - _onPositionFired--; + onPositionFired--; } } return true; @@ -2422,167 +2453,167 @@ /** * SMSound() private internals * -------------------------------- */ - _applyFromTo = function() { + applyFromTo = function() { - var _iO = _t._iO, - f = _iO.from, - t = _iO.to, + var instanceOptions = s._iO, + f = instanceOptions.from, + t = instanceOptions.to, start, end; end = function() { // end has been reached. - _s._wD(_t.id + ': "to" time of ' + t + ' reached.'); + sm2._wD(s.id + ': "To" time of ' + t + ' reached.'); // detach listener - _t.clearOnPosition(t, end); + s.clearOnPosition(t, end); // stop should clear this, too - _t.stop(); + s.stop(); }; start = function() { - _s._wD(_t.id + ': playing "from" ' + f); + sm2._wD(s.id + ': Playing "from" ' + f); // add listener for end if (t !== null && !isNaN(t)) { - _t.onPosition(t, end); + s.onPosition(t, end); } }; if (f !== null && !isNaN(f)) { // apply to instance options, guaranteeing correct start position. - _iO.position = f; + instanceOptions.position = f; // multiShot timing can't be tracked, so prevent that. - _iO.multiShot = false; + instanceOptions.multiShot = false; start(); } // return updated instanceOptions including starting position - return _iO; + return instanceOptions; }; - _attachOnPosition = function() { + attachOnPosition = function() { var item, - op = _t._iO.onposition; + op = s._iO.onposition; // attach onposition things, if any, now. if (op) { for (item in op) { if (op.hasOwnProperty(item)) { - _t.onPosition(parseInt(item, 10), op[item]); + s.onPosition(parseInt(item, 10), op[item]); } } } }; - _detachOnPosition = function() { + detachOnPosition = function() { var item, - op = _t._iO.onposition; + op = s._iO.onposition; // detach any onposition()-style listeners. if (op) { for (item in op) { if (op.hasOwnProperty(item)) { - _t.clearOnPosition(parseInt(item, 10)); + s.clearOnPosition(parseInt(item, 10)); } } } }; - _start_html5_timer = function() { + start_html5_timer = function() { - if (_t.isHTML5) { - _startTimer(_t); + if (s.isHTML5) { + startTimer(s); } }; - _stop_html5_timer = function() { + stop_html5_timer = function() { - if (_t.isHTML5) { - _stopTimer(_t); + if (s.isHTML5) { + stopTimer(s); } }; - _resetProperties = function(retainPosition) { + resetProperties = function(retainPosition) { if (!retainPosition) { - _onPositionItems = []; - _onPositionFired = 0; + onPositionItems = []; + onPositionFired = 0; } - _onplay_called = false; + onplay_called = false; - _t._hasTimer = null; - _t._a = null; - _t._html5_canplay = false; - _t.bytesLoaded = null; - _t.bytesTotal = null; - _t.duration = (_t._iO && _t._iO.duration ? _t._iO.duration : null); - _t.durationEstimate = null; - _t.buffered = []; + s._hasTimer = null; + s._a = null; + s._html5_canplay = false; + s.bytesLoaded = null; + s.bytesTotal = null; + s.duration = (s._iO && s._iO.duration ? s._iO.duration : null); + s.durationEstimate = null; + s.buffered = []; // legacy: 1D array - _t.eqData = []; + s.eqData = []; - _t.eqData.left = []; - _t.eqData.right = []; + s.eqData.left = []; + s.eqData.right = []; - _t.failures = 0; - _t.isBuffering = false; - _t.instanceOptions = {}; - _t.instanceCount = 0; - _t.loaded = false; - _t.metadata = {}; + s.failures = 0; + s.isBuffering = false; + s.instanceOptions = {}; + s.instanceCount = 0; + s.loaded = false; + s.metadata = {}; // 0 = uninitialised, 1 = loading, 2 = failed/error, 3 = loaded/success - _t.readyState = 0; + s.readyState = 0; - _t.muted = false; - _t.paused = false; + s.muted = false; + s.paused = false; - _t.peakData = { + s.peakData = { left: 0, right: 0 }; - _t.waveformData = { + s.waveformData = { left: [], right: [] }; - _t.playState = 0; - _t.position = null; + s.playState = 0; + s.position = null; - _t.id3 = {}; + s.id3 = {}; }; - _resetProperties(); + resetProperties(); /** * Pseudo-private SMSound internals * -------------------------------- */ @@ -2595,47 +2626,47 @@ * mimics flash and fires only when time/duration change, so as to be polling-friendly */ var duration, isNew = false, time, x = {}; - if (_t._hasTimer || bForce) { + if (s._hasTimer || bForce) { // TODO: May not need to track readyState (1 = loading) - if (_t._a && (bForce || ((_t.playState > 0 || _t.readyState === 1) && !_t.paused))) { + if (s._a && (bForce || ((s.playState > 0 || s.readyState === 1) && !s.paused))) { - duration = _t._get_html5_duration(); + duration = s._get_html5_duration(); - if (duration !== _lastHTML5State.duration) { + if (duration !== lastHTML5State.duration) { - _lastHTML5State.duration = duration; - _t.duration = duration; + lastHTML5State.duration = duration; + s.duration = duration; isNew = true; } // TODO: investigate why this goes wack if not set/re-set each time. - _t.durationEstimate = _t.duration; + s.durationEstimate = s.duration; - time = (_t._a.currentTime * 1000 || 0); + time = (s._a.currentTime * 1000 || 0); - if (time !== _lastHTML5State.time) { + if (time !== lastHTML5State.time) { - _lastHTML5State.time = time; + lastHTML5State.time = time; isNew = true; } if (isNew || bForce) { - _t._whileplaying(time,x,x,x,x); + s._whileplaying(time,x,x,x,x); } }/* else { - // _s._wD('_onTimer: Warn for "'+_t.id+'": '+(!_t._a?'Could not find element. ':'')+(_t.playState === 0?'playState bad, 0?':'playState = '+_t.playState+', OK')); + // sm2._wD('_onTimer: Warn for "'+s.id+'": '+(!s._a?'Could not find element. ':'')+(s.playState === 0?'playState bad, 0?':'playState = '+s.playState+', OK')); return false; }*/ @@ -2645,13 +2676,13 @@ }; this._get_html5_duration = function() { - var _iO = _t._iO, + var instanceOptions = s._iO, // if audio object exists, use its duration - else, instance option duration (if provided - it's a hack, really, and should be retired) OR null - d = (_t._a && _t._a.duration ? _t._a.duration*1000 : (_iO && _iO.duration ? _iO.duration : null)), + d = (s._a && s._a.duration ? s._a.duration*1000 : (instanceOptions && instanceOptions.duration ? instanceOptions.duration : null)), result = (d && !isNaN(d) && d !== Infinity ? d : null); return result; }; @@ -2663,172 +2694,185 @@ * note that loop is either off or infinite under HTML5, unlike Flash which allows arbitrary loop counts to be specified. */ // <d> if (!a.loop && nLoops > 1) { - _s._wD('Note: Native HTML5 looping is infinite.'); + sm2._wD('Note: Native HTML5 looping is infinite.', 1); } // </d> a.loop = (nLoops > 1 ? 'loop' : ''); }; this._setup_html5 = function(oOptions) { - var _iO = _mixin(_t._iO, oOptions), d = decodeURI, - _a = _useGlobalHTML5Audio ? _s._global_a : _t._a, - _dURL = d(_iO.url), - _oldIO = (_a && _a._t ? _a._t.instanceOptions : null), - result; + var instanceOptions = mixin(s._iO, oOptions), d = decodeURI, + a = useGlobalHTML5Audio ? globalHTML5Audio : s._a, + dURL = d(instanceOptions.url), + sameURL; - if (_a) { + /** + * "First things first, I, Poppa..." (reset the previous state of the old sound, if playing) + * Fixes case with devices that can only play one sound at a time + * Otherwise, other sounds in mid-play will be terminated without warning and in a stuck state + */ - if (_a._t) { + if (useGlobalHTML5Audio) { - if (!_useGlobalHTML5Audio && _dURL === d(_lastURL)) { + if (dURL === lastGlobalHTML5URL) { + // global HTML5 audio: re-use of URL + sameURL = true; + } - // same url, ignore request - result = _a; + } else if (dURL === lastURL) { - } else if (_useGlobalHTML5Audio && _oldIO.url === _iO.url && (!_lastURL || (_lastURL === _oldIO.url))) { + // options URL is the same as the "last" URL, and we used (loaded) it + sameURL = true; - // iOS-type reuse case - result = _a; + } - } + if (a) { - if (result) { + if (a._s) { - _t._apply_loop(_a, _iO.loops); - return result; + if (useGlobalHTML5Audio) { + if (a._s && a._s.playState && !sameURL) { + + // global HTML5 audio case, and loading a new URL. stop the currently-playing one. + a._s.stop(); + + } + + } else if (!useGlobalHTML5Audio && dURL === d(lastURL)) { + + // non-global HTML5 reuse case: same url, ignore request + s._apply_loop(a, instanceOptions.loops); + + return a; + } } - _s._wD('setting URL on existing object: ' + _dURL + (_lastURL ? ', old URL: ' + _lastURL : '')); + if (!sameURL) { - /** - * "First things first, I, Poppa.." (reset the previous state of the old sound, if playing) - * Fixes case with devices that can only play one sound at a time - * Otherwise, other sounds in mid-play will be terminated without warning and in a stuck state - */ + // don't retain onPosition() stuff with new URL. - if (_useGlobalHTML5Audio && _a._t && _a._t.playState && _iO.url !== _oldIO.url) { + resetProperties(false); - _a._t.stop(); + // assign new HTML5 URL - } + a.src = instanceOptions.url; - // reset load/playstate, onPosition etc. if the URL is new. - // somewhat-tricky object re-use vs. new SMSound object, old vs. new URL comparisons - _resetProperties((_oldIO && _oldIO.url ? _iO.url === _oldIO.url : (_lastURL ? _lastURL === _iO.url : false))); + s.url = instanceOptions.url; - _a.src = _iO.url; - _t.url = _iO.url; - _lastURL = _iO.url; - _a._called_load = false; + lastURL = instanceOptions.url; - } else { + lastGlobalHTML5URL = instanceOptions.url; - _wDS('h5a'); + a._called_load = false; - if (_iO.autoLoad || _iO.autoPlay) { + } - _t._a = new Audio(_iO.url); + } else { + if (instanceOptions.autoLoad || instanceOptions.autoPlay) { + + s._a = new Audio(instanceOptions.url); + } else { // null for stupid Opera 9.64 case - _t._a = (_isOpera && opera.version() < 10 ? new Audio(null) : new Audio()); + s._a = (isOpera && opera.version() < 10 ? new Audio(null) : new Audio()); } // assign local reference - _a = _t._a; + a = s._a; - _a._called_load = false; + a._called_load = false; - if (_useGlobalHTML5Audio) { + if (useGlobalHTML5Audio) { - _s._global_a = _a; + globalHTML5Audio = a; } } - _t.isHTML5 = true; + s.isHTML5 = true; // store a ref on the track - _t._a = _a; + s._a = a; // store a ref on the audio - _a._t = _t; + a._s = s; - _add_html5_events(); + add_html5_events(); - _t._apply_loop(_a, _iO.loops); + s._apply_loop(a, instanceOptions.loops); - if (_iO.autoLoad || _iO.autoPlay) { + if (instanceOptions.autoLoad || instanceOptions.autoPlay) { - _t.load(); + s.load(); } else { // early HTML5 implementation (non-standard) - _a.autobuffer = false; + a.autobuffer = false; // standard ('none' is also an option.) - _a.preload = 'auto'; + a.preload = 'auto'; } - return _a; + return a; }; - _add_html5_events = function() { + add_html5_events = function() { - if (_t._a._added_events) { + if (s._a._added_events) { return false; } var f; function add(oEvt, oFn, bCapture) { - return _t._a ? _t._a.addEventListener(oEvt, oFn, bCapture||false) : null; + return s._a ? s._a.addEventListener(oEvt, oFn, bCapture||false) : null; } - _t._a._added_events = true; + s._a._added_events = true; - for (f in _html5_events) { - if (_html5_events.hasOwnProperty(f)) { - add(f, _html5_events[f]); + for (f in html5_events) { + if (html5_events.hasOwnProperty(f)) { + add(f, html5_events[f]); } } return true; }; - _remove_html5_events = function() { + remove_html5_events = function() { // Remove event listeners var f; function remove(oEvt, oFn, bCapture) { - return (_t._a ? _t._a.removeEventListener(oEvt, oFn, bCapture||false) : null); + return (s._a ? s._a.removeEventListener(oEvt, oFn, bCapture||false) : null); } - _s._wD(_h5+'removing event listeners: '+_t.id); - _t._a._added_events = false; + sm2._wD(s.id + ': Removing event listeners'); + s._a._added_events = false; - for (f in _html5_events) { - if (_html5_events.hasOwnProperty(f)) { - remove(f, _html5_events[f]); + for (f in html5_events) { + if (html5_events.hasOwnProperty(f)) { + remove(f, html5_events[f]); } } }; @@ -2837,71 +2881,70 @@ * ------------------------------ */ this._onload = function(nSuccess) { - var fN, // check for duration to prevent false positives from flash 8 when loading from cache. - loadOK = (!!(nSuccess) || (!_t.isHTML5 && _fV === 8 && _t.duration)); + loadOK = !!nSuccess || (!s.isHTML5 && fV === 8 && s.duration); // <d> - fN = 'SMSound._onload(): '; - _s._wD(fN + '"' + _t.id + '"' + (loadOK?' loaded.':' failed to load? - ' + _t.url), (loadOK?1:2)); - if (!loadOK && !_t.isHTML5) { - if (_s.sandbox.noRemote === true) { - _s._wD(fN + _str('noNet'), 1); + fN = s.id + ': '; + sm2._wD(fN + (loadOK ? 'onload()' : 'Failed to load? - ' + s.url), (loadOK ? 1 : 2)); + if (!loadOK && !s.isHTML5) { + if (sm2.sandbox.noRemote === true) { + sm2._wD(fN + str('noNet'), 1); } - if (_s.sandbox.noLocal === true) { - _s._wD(fN + _str('noLocal'), 1); + if (sm2.sandbox.noLocal === true) { + sm2._wD(fN + str('noLocal'), 1); } } // </d> - _t.loaded = loadOK; - _t.readyState = loadOK?3:2; - _t._onbufferchange(0); + s.loaded = loadOK; + s.readyState = loadOK?3:2; + s._onbufferchange(0); - if (_t._iO.onload) { - _t._iO.onload.apply(_t, [loadOK]); + if (s._iO.onload) { + s._iO.onload.apply(s, [loadOK]); } return true; }; this._onbufferchange = function(nIsBuffering) { - if (_t.playState === 0) { + if (s.playState === 0) { // ignore if not playing return false; } - if ((nIsBuffering && _t.isBuffering) || (!nIsBuffering && !_t.isBuffering)) { + if ((nIsBuffering && s.isBuffering) || (!nIsBuffering && !s.isBuffering)) { return false; } - _t.isBuffering = (nIsBuffering === 1); - if (_t._iO.onbufferchange) { - _s._wD('SMSound._onbufferchange(): ' + nIsBuffering); - _t._iO.onbufferchange.apply(_t); + s.isBuffering = (nIsBuffering === 1); + if (s._iO.onbufferchange) { + sm2._wD(s.id + ': Buffer state change: ' + nIsBuffering); + s._iO.onbufferchange.apply(s); } return true; }; /** - * Notify Mobile Safari that user action is required - * to continue playing / loading the audio file. + * Playback may have stopped due to buffering, or related reason. + * This state can be encountered on iOS < 6 when auto-play is blocked. */ this._onsuspend = function() { - if (_t._iO.onsuspend) { - _s._wD('SMSound._onsuspend()'); - _t._iO.onsuspend.apply(_t); + if (s._iO.onsuspend) { + sm2._wD(s.id + ': Playback suspended'); + s._iO.onsuspend.apply(s); } return true; }; @@ -2911,159 +2954,159 @@ * at this point we just recreate failed sounds rather than trying to reconnect */ this._onfailure = function(msg, level, code) { - _t.failures++; - _s._wD('SMSound._onfailure(): "'+_t.id+'" count '+_t.failures); + s.failures++; + sm2._wD(s.id + ': Failures = ' + s.failures); - if (_t._iO.onfailure && _t.failures === 1) { - _t._iO.onfailure(_t, msg, level, code); + if (s._iO.onfailure && s.failures === 1) { + s._iO.onfailure(s, msg, level, code); } else { - _s._wD('SMSound._onfailure(): ignoring'); + sm2._wD(s.id + ': Ignoring failure'); } }; this._onfinish = function() { - // store local copy before it gets trashed.. - var _io_onfinish = _t._iO.onfinish; + // store local copy before it gets trashed... + var io_onfinish = s._iO.onfinish; - _t._onbufferchange(0); - _t._resetOnPosition(0); + s._onbufferchange(0); + s._resetOnPosition(0); // reset some state items - if (_t.instanceCount) { + if (s.instanceCount) { - _t.instanceCount--; + s.instanceCount--; - if (!_t.instanceCount) { + if (!s.instanceCount) { // remove onPosition listeners, if any - _detachOnPosition(); + detachOnPosition(); // reset instance options - _t.playState = 0; - _t.paused = false; - _t.instanceCount = 0; - _t.instanceOptions = {}; - _t._iO = {}; - _stop_html5_timer(); + s.playState = 0; + s.paused = false; + s.instanceCount = 0; + s.instanceOptions = {}; + s._iO = {}; + stop_html5_timer(); // reset position, too - if (_t.isHTML5) { - _t.position = 0; + if (s.isHTML5) { + s.position = 0; } } - if (!_t.instanceCount || _t._iO.multiShotEvents) { + if (!s.instanceCount || s._iO.multiShotEvents) { // fire onfinish for last, or every instance - if (_io_onfinish) { - _s._wD('SMSound._onfinish(): "' + _t.id + '"'); - _io_onfinish.apply(_t); + if (io_onfinish) { + sm2._wD(s.id + ': onfinish()'); + io_onfinish.apply(s); } } } }; this._whileloading = function(nBytesLoaded, nBytesTotal, nDuration, nBufferLength) { - var _iO = _t._iO; + var instanceOptions = s._iO; - _t.bytesLoaded = nBytesLoaded; - _t.bytesTotal = nBytesTotal; - _t.duration = Math.floor(nDuration); - _t.bufferLength = nBufferLength; + s.bytesLoaded = nBytesLoaded; + s.bytesTotal = nBytesTotal; + s.duration = Math.floor(nDuration); + s.bufferLength = nBufferLength; - if (!_t.isHTML5 && !_iO.isMovieStar) { + if (!s.isHTML5 && !instanceOptions.isMovieStar) { - if (_iO.duration) { + if (instanceOptions.duration) { // use duration from options, if specified and larger. nobody should be specifying duration in options, actually, and it should be retired. - _t.durationEstimate = (_t.duration > _iO.duration) ? _t.duration : _iO.duration; + s.durationEstimate = (s.duration > instanceOptions.duration) ? s.duration : instanceOptions.duration; } else { - _t.durationEstimate = parseInt((_t.bytesTotal / _t.bytesLoaded) * _t.duration, 10); + s.durationEstimate = parseInt((s.bytesTotal / s.bytesLoaded) * s.duration, 10); } } else { - _t.durationEstimate = _t.duration; + s.durationEstimate = s.duration; } // for flash, reflect sequential-load-style buffering - if (!_t.isHTML5) { - _t.buffered = [{ + if (!s.isHTML5) { + s.buffered = [{ 'start': 0, - 'end': _t.duration + 'end': s.duration }]; } // allow whileloading to fire even if "load" fired under HTML5, due to HTTP range/partials - if ((_t.readyState !== 3 || _t.isHTML5) && _iO.whileloading) { - _iO.whileloading.apply(_t); + if ((s.readyState !== 3 || s.isHTML5) && instanceOptions.whileloading) { + instanceOptions.whileloading.apply(s); } }; this._whileplaying = function(nPosition, oPeakData, oWaveformDataLeft, oWaveformDataRight, oEQData) { - var _iO = _t._iO, + var instanceOptions = s._iO, eqLeft; if (isNaN(nPosition) || nPosition === null) { // flash safety net return false; } // Safari HTML5 play() may return small -ve values when starting from position: 0, eg. -50.120396875. Unexpected/invalid per W3, I think. Normalize to 0. - _t.position = Math.max(0, nPosition); + s.position = Math.max(0, nPosition); - _t._processOnPosition(); + s._processOnPosition(); - if (!_t.isHTML5 && _fV > 8) { + if (!s.isHTML5 && fV > 8) { - if (_iO.usePeakData && typeof oPeakData !== 'undefined' && oPeakData) { - _t.peakData = { + if (instanceOptions.usePeakData && oPeakData !== _undefined && oPeakData) { + s.peakData = { left: oPeakData.leftPeak, right: oPeakData.rightPeak }; } - if (_iO.useWaveformData && typeof oWaveformDataLeft !== 'undefined' && oWaveformDataLeft) { - _t.waveformData = { + if (instanceOptions.useWaveformData && oWaveformDataLeft !== _undefined && oWaveformDataLeft) { + s.waveformData = { left: oWaveformDataLeft.split(','), right: oWaveformDataRight.split(',') }; } - if (_iO.useEQData) { - if (typeof oEQData !== 'undefined' && oEQData && oEQData.leftEQ) { + if (instanceOptions.useEQData) { + if (oEQData !== _undefined && oEQData && oEQData.leftEQ) { eqLeft = oEQData.leftEQ.split(','); - _t.eqData = eqLeft; - _t.eqData.left = eqLeft; - if (typeof oEQData.rightEQ !== 'undefined' && oEQData.rightEQ) { - _t.eqData.right = oEQData.rightEQ.split(','); + s.eqData = eqLeft; + s.eqData.left = eqLeft; + if (oEQData.rightEQ !== _undefined && oEQData.rightEQ) { + s.eqData.right = oEQData.rightEQ.split(','); } } } } - if (_t.playState === 1) { + if (s.playState === 1) { // special case/hack: ensure buffering is false if loading from cache (and not yet started) - if (!_t.isHTML5 && _fV === 8 && !_t.position && _t.isBuffering) { - _t._onbufferchange(0); + if (!s.isHTML5 && fV === 8 && !s.position && s.isBuffering) { + s._onbufferchange(0); } - if (_iO.whileplaying) { + if (instanceOptions.whileplaying) { // flash may call after actual finish - _iO.whileplaying.apply(_t); + instanceOptions.whileplaying.apply(s); } } return true; @@ -3076,19 +3119,19 @@ * internal: flash 9 + NetStream (MovieStar/RTMP-only) feature * * @param {object} oData */ - _s._wD('SMSound._oncaptiondata(): "' + this.id + '" caption data received.'); + sm2._wD(s.id + ': Caption data received.'); - _t.captiondata = oData; + s.captiondata = oData; - if (_t._iO.oncaptiondata) { - _t._iO.oncaptiondata.apply(_t, [oData]); + if (s._iO.oncaptiondata) { + s._iO.oncaptiondata.apply(s, [oData]); } - }; + }; this._onmetadata = function(oMDProps, oMDData) { /** * internal: flash 9 + NetStream (MovieStar/RTMP-only) feature @@ -3096,24 +3139,24 @@ * * @param {array} oMDProps (names) * @param {array} oMDData (values) */ - _s._wD('SMSound._onmetadata(): "' + this.id + '" metadata received.'); + sm2._wD(s.id + ': Metadata received.'); var oData = {}, i, j; for (i = 0, j = oMDProps.length; i < j; i++) { oData[oMDProps[i]] = oMDData[i]; } - _t.metadata = oData; + s.metadata = oData; - if (_t._iO.onmetadata) { - _t._iO.onmetadata.apply(_t); + if (s._iO.onmetadata) { + s._iO.onmetadata.apply(s); } - }; + }; this._onid3 = function(oID3Props, oID3Data) { /** * internal: flash 8 + flash 9 ID3 feature @@ -3121,107 +3164,111 @@ * * @param {array} oID3Props (names) * @param {array} oID3Data (values) */ - _s._wD('SMSound._onid3(): "' + this.id + '" ID3 data received.'); + sm2._wD(s.id + ': ID3 data received.'); var oData = [], i, j; for (i = 0, j = oID3Props.length; i < j; i++) { oData[oID3Props[i]] = oID3Data[i]; } - _t.id3 = _mixin(_t.id3, oData); + s.id3 = mixin(s.id3, oData); - if (_t._iO.onid3) { - _t._iO.onid3.apply(_t); + if (s._iO.onid3) { + s._iO.onid3.apply(s); } }; // flash/RTMP-only this._onconnect = function(bSuccess) { bSuccess = (bSuccess === 1); - _s._wD('SMSound._onconnect(): "'+_t.id+'"'+(bSuccess?' connected.':' failed to connect? - '+_t.url), (bSuccess?1:2)); - _t.connected = bSuccess; + sm2._wD(s.id + ': ' + (bSuccess ? 'Connected.' : 'Failed to connect? - ' + s.url), (bSuccess ? 1 : 2)); + s.connected = bSuccess; if (bSuccess) { - _t.failures = 0; + s.failures = 0; - if (_idCheck(_t.id)) { - if (_t.getAutoPlay()) { + if (idCheck(s.id)) { + if (s.getAutoPlay()) { // only update the play state if auto playing - _t.play(undefined, _t.getAutoPlay()); - } else if (_t._iO.autoLoad) { - _t.load(); + s.play(_undefined, s.getAutoPlay()); + } else if (s._iO.autoLoad) { + s.load(); } } - if (_t._iO.onconnect) { - _t._iO.onconnect.apply(_t, [bSuccess]); + if (s._iO.onconnect) { + s._iO.onconnect.apply(s, [bSuccess]); } } }; this._ondataerror = function(sError) { // flash 9 wave/eq data handler // hack: called at start, and end from flash at/after onfinish() - if (_t.playState > 0) { - _s._wD('SMSound._ondataerror(): ' + sError); - if (_t._iO.ondataerror) { - _t._iO.ondataerror.apply(_t); + if (s.playState > 0) { + sm2._wD(s.id + ': Data error: ' + sError); + if (s._iO.ondataerror) { + s._iO.ondataerror.apply(s); } } }; + // <d> + this._debug(); + // </d> + }; // SMSound() /** * Private SoundManager internals * ------------------------------ */ - _getDocument = function() { + getDocument = function() { - return (_doc.body || _doc._docElement || _doc.getElementsByTagName('div')[0]); + return (doc.body || doc._docElement || doc.getElementsByTagName('div')[0]); }; - _id = function(sID) { + id = function(sID) { - return _doc.getElementById(sID); + return doc.getElementById(sID); }; - _mixin = function(oMain, oAdd) { + mixin = function(oMain, oAdd) { // non-destructive merge var o1 = (oMain || {}), o2, o; // if unspecified, o2 is the default options object - o2 = (typeof oAdd === 'undefined' ? _s.defaultOptions : oAdd); + o2 = (oAdd === _undefined ? sm2.defaultOptions : oAdd); for (o in o2) { - if (o2.hasOwnProperty(o) && typeof o1[o] === 'undefined') { + if (o2.hasOwnProperty(o) && o1[o] === _undefined) { if (typeof o2[o] !== 'object' || o2[o] === null) { // assign directly o1[o] = o2[o]; } else { // recurse through o2 - o1[o] = _mixin(o1[o], o2[o]); + o1[o] = mixin(o1[o], o2[o]); } } @@ -3231,36 +3278,36 @@ }; // additional soundManager properties that soundManager.setup() will accept - _extraOptions = { + extraOptions = { 'onready': 1, 'ontimeout': 1, 'defaultOptions': 1, 'flash9Options': 1, 'movieStarOptions': 1 }; - _assign = function(o, oParent) { + assign = function(o, oParent) { /** * recursive assignment of properties, soundManager.setup() helper * allows property assignment based on whitelist */ var i, result = true, - hasParent = (typeof oParent !== 'undefined'), - setupOptions = _s.setupOptions, - extraOptions = _extraOptions; + hasParent = (oParent !== _undefined), + setupOptions = sm2.setupOptions, + bonusOptions = extraOptions; // <d> // if soundManager.setup() called, show accepted parameters. - if (typeof o === 'undefined') { + if (o === _undefined) { result = []; for (i in setupOptions) { @@ -3268,19 +3315,19 @@ result.push(i); } } - for (i in extraOptions) { + for (i in bonusOptions) { - if (extraOptions.hasOwnProperty(i)) { + if (bonusOptions.hasOwnProperty(i)) { - if (typeof _s[i] === 'object') { + if (typeof sm2[i] === 'object') { result.push(i+': {...}'); - } else if (_s[i] instanceof Function) { + } else if (sm2[i] instanceof Function) { result.push(i+': function() {...}'); } else { @@ -3290,11 +3337,11 @@ } } - _s._wD(_str('setup', result.join(', '))); + sm2._wD(str('setup', result.join(', '))); return false; } @@ -3304,70 +3351,70 @@ if (o.hasOwnProperty(i)) { // if not an {object} we want to recurse through... - if (typeof o[i] !== 'object' || o[i] === null || o[i] instanceof Array) { + if (typeof o[i] !== 'object' || o[i] === null || o[i] instanceof Array || o[i] instanceof RegExp) { // check "allowed" options - if (hasParent && typeof extraOptions[oParent] !== 'undefined') { + if (hasParent && bonusOptions[oParent] !== _undefined) { // valid recursive / nested object option, eg., { defaultOptions: { volume: 50 } } - _s[oParent][i] = o[i]; + sm2[oParent][i] = o[i]; - } else if (typeof setupOptions[i] !== 'undefined') { + } else if (setupOptions[i] !== _undefined) { // special case: assign to setupOptions object, which soundManager property references - _s.setupOptions[i] = o[i]; + sm2.setupOptions[i] = o[i]; // assign directly to soundManager, too - _s[i] = o[i]; + sm2[i] = o[i]; - } else if (typeof extraOptions[i] === 'undefined') { + } else if (bonusOptions[i] === _undefined) { // invalid or disallowed parameter. complain. - _complain(_str((typeof _s[i] === 'undefined' ? 'setupUndef' : 'setupError'), i), 2); + complain(str((sm2[i] === _undefined ? 'setupUndef' : 'setupError'), i), 2); result = false; } else { /** - * valid extraOptions parameter. + * valid extraOptions (bonusOptions) parameter. * is it a method, like onready/ontimeout? call it. * multiple parameters should be in an array, eg. soundManager.setup({onready: [myHandler, myScope]}); */ - if (_s[i] instanceof Function) { + if (sm2[i] instanceof Function) { - _s[i].apply(_s, (o[i] instanceof Array? o[i] : [o[i]])); + sm2[i].apply(sm2, (o[i] instanceof Array? o[i] : [o[i]])); } else { // good old-fashioned direct assignment - _s[i] = o[i]; + sm2[i] = o[i]; } } } else { // recursion case, eg., { defaultOptions: { ... } } - if (typeof extraOptions[i] === 'undefined') { + if (bonusOptions[i] === _undefined) { // invalid or disallowed parameter. complain. - _complain(_str((typeof _s[i] === 'undefined' ? 'setupUndef' : 'setupError'), i), 2); + complain(str((sm2[i] === _undefined ? 'setupUndef' : 'setupError'), i), 2); result = false; } else { // recurse through object - return _assign(o[i], i); + return assign(o[i], i); } } @@ -3377,33 +3424,35 @@ return result; }; - function _preferFlashCheck(kind) { + function preferFlashCheck(kind) { // whether flash should play a given type - return (_s.preferFlash && _hasFlash && !_s.ignoreFlash && (typeof _s.flash[kind] !== 'undefined' && _s.flash[kind])); + return (sm2.preferFlash && hasFlash && !sm2.ignoreFlash && (sm2.flash[kind] !== _undefined && sm2.flash[kind])); } /** * Internal DOM2-level event helpers * --------------------------------- */ - _event = (function() { + event = (function() { - var old = (_win.attachEvent), + // normalize event methods + var old = (window.attachEvent), evt = { add: (old?'attachEvent':'addEventListener'), remove: (old?'detachEvent':'removeEventListener') }; + // normalize "on" event prefix, optional capture argument function getArgs(oArgs) { - var args = _slice.call(oArgs), + var args = slice.call(oArgs), len = args.length; if (old) { // prefix args[1] = 'on' + args[1]; @@ -3419,14 +3468,16 @@ } function apply(args, sType) { + // normalize and call the event method, with the proper arguments var element = args.shift(), method = [evt[sType]]; if (old) { + // old IE can't do apply(). element[method](args[0], args[1]); } else { element[method].apply(element, args); } @@ -3454,25 +3505,25 @@ /** * Internal HTML5 event handling * ----------------------------- */ - function _html5_event(oFn) { + function html5_event(oFn) { - // wrap html5 event handlers so we don't call them on destroyed sounds + // wrap html5 event handlers so we don't call them on destroyed and/or unloaded sounds return function(e) { - var t = this._t, + var s = this._s, result; - if (!t || !t._a) { + if (!s || !s._a) { // <d> - if (t && t.id) { - _s._wD(_h5+'ignoring '+e.type+': '+t.id); + if (s && s.id) { + sm2._wD(s.id + ': Ignoring ' + e.type); } else { - _s._wD(_h5+'ignoring '+e.type); + sm2._wD(h5 + 'Ignoring ' + e.type); } // </d> result = null; } else { result = oFn.call(this, e); @@ -3482,163 +3533,163 @@ }; } - _html5_events = { + html5_events = { // HTML5 event-name-to-handler map - abort: _html5_event(function() { + abort: html5_event(function() { - _s._wD(_h5+'abort: '+this._t.id); + sm2._wD(this._s.id + ': abort'); }), // enough has loaded to play - canplay: _html5_event(function() { + canplay: html5_event(function() { - var t = this._t, + var s = this._s, position1K; - if (t._html5_canplay) { + if (s._html5_canplay) { // this event has already fired. ignore. return true; } - t._html5_canplay = true; - _s._wD(_h5+'canplay: '+t.id+', '+t.url); - t._onbufferchange(0); + s._html5_canplay = true; + sm2._wD(s.id + ': canplay'); + s._onbufferchange(0); // position according to instance options - position1K = (typeof t._iO.position !== 'undefined' && !isNaN(t._iO.position)?t._iO.position/1000:null); + position1K = (s._iO.position !== _undefined && !isNaN(s._iO.position)?s._iO.position/1000:null); // set the position if position was set before the sound loaded - if (t.position && this.currentTime !== position1K) { - _s._wD(_h5+'canplay: setting position to '+position1K); + if (s.position && this.currentTime !== position1K) { + sm2._wD(s.id + ': canplay: Setting position to ' + position1K); try { this.currentTime = position1K; } catch(ee) { - _s._wD(_h5+'setting position of ' + position1K + ' failed: '+ee.message, 2); + sm2._wD(s.id + ': canplay: Setting position of ' + position1K + ' failed: ' + ee.message, 2); } } // hack for HTML5 from/to case - if (t._iO._oncanplay) { - t._iO._oncanplay(); + if (s._iO._oncanplay) { + s._iO._oncanplay(); } }), - canplaythrough: _html5_event(function() { + canplaythrough: html5_event(function() { - var t = this._t; + var s = this._s; - if (!t.loaded) { - t._onbufferchange(0); - t._whileloading(t.bytesLoaded, t.bytesTotal, t._get_html5_duration()); - t._onload(true); + if (!s.loaded) { + s._onbufferchange(0); + s._whileloading(s.bytesLoaded, s.bytesTotal, s._get_html5_duration()); + s._onload(true); } }), // TODO: Reserved for potential use /* - emptied: _html5_event(function() { + emptied: html5_event(function() { - _s._wD(_h5+'emptied: '+this._t.id); + sm2._wD(this._s.id + ': emptied'); }), */ - ended: _html5_event(function() { + ended: html5_event(function() { - var t = this._t; + var s = this._s; - _s._wD(_h5+'ended: '+t.id); - t._onfinish(); + sm2._wD(s.id + ': ended'); + s._onfinish(); + }), - error: _html5_event(function() { + error: html5_event(function() { - _s._wD(_h5+'error: '+this.error.code); + sm2._wD(this._s.id + ': HTML5 error, code ' + this.error.code); // call load with error state? - this._t._onload(false); + this._s._onload(false); }), - loadeddata: _html5_event(function() { + loadeddata: html5_event(function() { - var t = this._t; + var s = this._s; - _s._wD(_h5+'loadeddata: '+this._t.id); + sm2._wD(s.id + ': loadeddata'); // safari seems to nicely report progress events, eventually totalling 100% - if (!t._loaded && !_isSafari) { - t.duration = t._get_html5_duration(); + if (!s._loaded && !isSafari) { + s.duration = s._get_html5_duration(); } }), - loadedmetadata: _html5_event(function() { + loadedmetadata: html5_event(function() { - _s._wD(_h5+'loadedmetadata: '+this._t.id); + sm2._wD(this._s.id + ': loadedmetadata'); }), - loadstart: _html5_event(function() { + loadstart: html5_event(function() { - _s._wD(_h5+'loadstart: '+this._t.id); + sm2._wD(this._s.id + ': loadstart'); // assume buffering at first - this._t._onbufferchange(1); + this._s._onbufferchange(1); }), - play: _html5_event(function() { + play: html5_event(function() { - _s._wD(_h5+'play: '+this._t.id+', '+this._t.url); + sm2._wD(this._s.id + ': play()'); // once play starts, no buffering - this._t._onbufferchange(0); + this._s._onbufferchange(0); }), - playing: _html5_event(function() { + playing: html5_event(function() { - _s._wD(_h5+'playing: '+this._t.id); - + sm2._wD(this._s.id + ': playing'); // once play starts, no buffering - this._t._onbufferchange(0); + this._s._onbufferchange(0); }), - progress: _html5_event(function(e) { + progress: html5_event(function(e) { // note: can fire repeatedly after "loaded" event, due to use of HTTP range/partials - var t = this._t, + var s = this._s, i, j, str, buffered = 0, isProgress = (e.type === 'progress'), ranges = e.target.buffered, // firefox 3.6 implements e.loaded/total (bytes) loaded = (e.loaded||0), total = (e.total||1), // HTML5 returns msec. SM2 API uses seconds for setPosition() etc., whether Flash or HTML5. scale = 1000; // reset the "buffered" (loaded byte ranges) array - t.buffered = []; + s.buffered = []; if (ranges && ranges.length) { // if loaded is 0, try TimeRanges implementation as % of load // https://developer.mozilla.org/en/DOM/TimeRanges // re-build "buffered" array for (i=0, j=ranges.length; i<j; i++) { - t.buffered.push({ + s.buffered.push({ 'start': ranges.start(i) * scale, 'end': ranges.end(i) * scale }); } @@ -3653,206 +3704,218 @@ str = []; j = ranges.length; for (i=0; i<j; i++) { str.push(e.target.buffered.start(i)*scale +'-'+ e.target.buffered.end(i)*scale); } - _s._wD(_h5+'progress: timeRanges: '+str.join(', ')); + sm2._wD(this._s.id + ': progress, timeRanges: ' + str.join(', ')); } if (isProgress && !isNaN(loaded)) { - _s._wD(_h5+'progress: '+t.id+': ' + Math.floor(loaded*100)+'% loaded'); + sm2._wD(this._s.id + ': progress, ' + Math.floor(loaded*100) + '% loaded'); } // </d> } if (!isNaN(loaded)) { // if progress, likely not buffering - t._onbufferchange(0); + s._onbufferchange(0); // TODO: prevent calls with duplicate values. - t._whileloading(loaded, total, t._get_html5_duration()); + s._whileloading(loaded, total, s._get_html5_duration()); if (loaded && total && loaded === total) { // in case "onload" doesn't fire (eg. gecko 1.9.2) - _html5_events.canplaythrough.call(this, e); + html5_events.canplaythrough.call(this, e); } } }), - ratechange: _html5_event(function() { + ratechange: html5_event(function() { - _s._wD(_h5+'ratechange: '+this._t.id); + sm2._wD(this._s.id + ': ratechange'); }), - suspend: _html5_event(function(e) { + suspend: html5_event(function(e) { // download paused/stopped, may have finished (eg. onload) - var t = this._t; + var s = this._s; - _s._wD(_h5+'suspend: '+t.id); - _html5_events.progress.call(this, e); - t._onsuspend(); + sm2._wD(this._s.id + ': suspend'); + html5_events.progress.call(this, e); + s._onsuspend(); }), - stalled: _html5_event(function() { + stalled: html5_event(function() { - _s._wD(_h5+'stalled: '+this._t.id); + sm2._wD(this._s.id + ': stalled'); }), - timeupdate: _html5_event(function() { + timeupdate: html5_event(function() { - this._t._onTimer(); + this._s._onTimer(); }), - waiting: _html5_event(function() { + waiting: html5_event(function() { - var t = this._t; + var s = this._s; // see also: seeking - _s._wD(_h5+'waiting: '+t.id); + sm2._wD(this._s.id + ': waiting'); // playback faster than download rate, etc. - t._onbufferchange(1); + s._onbufferchange(1); }) }; - _html5OK = function(iO) { + html5OK = function(iO) { // playability test based on URL or MIME type var result; - if (iO.serverURL || (iO.type && _preferFlashCheck(iO.type))) { + if (iO.serverURL || (iO.type && preferFlashCheck(iO.type))) { // RTMP, or preferring flash result = false; } else { // Use type, if specified. If HTML5-only mode, no other options, so just give 'er - result = ((iO.type ? _html5CanPlay({type:iO.type}) : _html5CanPlay({url:iO.url}) || _s.html5Only)); + result = ((iO.type ? html5CanPlay({type:iO.type}) : html5CanPlay({url:iO.url}) || sm2.html5Only)); } return result; }; - _html5Unload = function(oAudio, url) { + html5Unload = function(oAudio, url) { /** * Internal method: Unload media, and cancel any current/pending network requests. * Firefox can load an empty URL, which allegedly destroys the decoder and stops the download. * https://developer.mozilla.org/En/Using_audio_and_video_in_Firefox#Stopping_the_download_of_media * However, Firefox has been seen loading a relative URL from '' and thus requesting the hosting page on unload. * Other UA behaviour is unclear, so everyone else gets an about:blank-style URL. */ if (oAudio) { + // Firefox likes '' for unload (used to work?) - however, may request hosting page URL (bad.) Most other UAs dislike '' and fail to unload. oAudio.src = url; + + // reset some state, too + oAudio._called_load = false; + } + if (useGlobalHTML5Audio) { + + // ensure URL state is trashed, also + lastGlobalHTML5URL = null; + + } + }; - _html5CanPlay = function(o) { + html5CanPlay = function(o) { /** * Try to find MIME, test and return truthiness * o = { * url: '/path/to/an.mp3', * type: 'audio/mp3' * } */ - if (!_s.useHTML5Audio || !_s.hasHTML5) { + if (!sm2.useHTML5Audio || !sm2.hasHTML5) { return false; } var url = (o.url || null), mime = (o.type || null), - aF = _s.audioFormats, + aF = sm2.audioFormats, result, offset, fileExt, item; // account for known cases like audio/mp3 - if (mime && typeof _s.html5[mime] !== 'undefined') { - return (_s.html5[mime] && !_preferFlashCheck(mime)); + if (mime && sm2.html5[mime] !== _undefined) { + return (sm2.html5[mime] && !preferFlashCheck(mime)); } - if (!_html5Ext) { - _html5Ext = []; + if (!html5Ext) { + html5Ext = []; for (item in aF) { if (aF.hasOwnProperty(item)) { - _html5Ext.push(item); + html5Ext.push(item); if (aF[item].related) { - _html5Ext = _html5Ext.concat(aF[item].related); + html5Ext = html5Ext.concat(aF[item].related); } } } - _html5Ext = new RegExp('\\.('+_html5Ext.join('|')+')(\\?.*)?$','i'); + html5Ext = new RegExp('\\.('+html5Ext.join('|')+')(\\?.*)?$','i'); } // TODO: Strip URL queries, etc. - fileExt = (url ? url.toLowerCase().match(_html5Ext) : null); + fileExt = (url ? url.toLowerCase().match(html5Ext) : null); if (!fileExt || !fileExt.length) { if (!mime) { result = false; } else { // audio/mp3 -> mp3, result should be known offset = mime.indexOf(';'); - // strip "audio/X; codecs.." + // strip "audio/X; codecs..." fileExt = (offset !== -1?mime.substr(0,offset):mime).substr(6); } } else { // match the raw extension name - "mp3", for example fileExt = fileExt[1]; } - if (fileExt && typeof _s.html5[fileExt] !== 'undefined') { + if (fileExt && sm2.html5[fileExt] !== _undefined) { // result known - result = (_s.html5[fileExt] && !_preferFlashCheck(fileExt)); + result = (sm2.html5[fileExt] && !preferFlashCheck(fileExt)); } else { mime = 'audio/'+fileExt; - result = _s.html5.canPlayType({type:mime}); - _s.html5[fileExt] = result; - // _s._wD('canPlayType, found result: '+result); - result = (result && _s.html5[mime] && !_preferFlashCheck(mime)); + result = sm2.html5.canPlayType({type:mime}); + sm2.html5[fileExt] = result; + // sm2._wD('canPlayType, found result: ' + result); + result = (result && sm2.html5[mime] && !preferFlashCheck(mime)); } return result; }; - _testHTML5 = function() { + testHTML5 = function() { /** * Internal: Iterates over audioFormats, determining support eg. audio/mp3, audio/mpeg and so on * assigns results to html5[] and flash[]. */ - if (!_s.useHTML5Audio || !_s.hasHTML5) { + if (!sm2.useHTML5Audio || !sm2.hasHTML5) { return false; } // double-whammy: Opera 9.64 throws WRONG_ARGUMENTS_ERR if no parameter passed to Audio(), and Webkit + iOS happily tries to load "null" as a URL. :/ - var a = (typeof Audio !== 'undefined' ? (_isOpera && opera.version() < 10 ? new Audio(null) : new Audio()) : null), + var a = (Audio !== _undefined ? (isOpera && opera.version() < 10 ? new Audio(null) : new Audio()) : null), item, lookup, support = {}, aF, i; - function _cp(m) { + function cp(m) { var canPlay, i, j, result = false, isOK = false; @@ -3861,52 +3924,52 @@ } if (m instanceof Array) { // iterate through all mime types, return any successes for (i=0, j=m.length; i<j; i++) { - if (_s.html5[m[i]] || a.canPlayType(m[i]).match(_s.html5Test)) { + if (sm2.html5[m[i]] || a.canPlayType(m[i]).match(sm2.html5Test)) { isOK = true; - _s.html5[m[i]] = true; + sm2.html5[m[i]] = true; // note flash support, too - _s.flash[m[i]] = !!(m[i].match(_flashMIME)); + sm2.flash[m[i]] = !!(m[i].match(flashMIME)); } } result = isOK; } else { canPlay = (a && typeof a.canPlayType === 'function' ? a.canPlayType(m) : false); - result = !!(canPlay && (canPlay.match(_s.html5Test))); + result = !!(canPlay && (canPlay.match(sm2.html5Test))); } return result; } // test all registered formats + codecs - aF = _s.audioFormats; + aF = sm2.audioFormats; for (item in aF) { if (aF.hasOwnProperty(item)) { lookup = 'audio/' + item; - support[item] = _cp(aF[item].type); + support[item] = cp(aF[item].type); // write back generic type too, eg. audio/mp3 support[lookup] = support[item]; // assign flash - if (item.match(_flashMIME)) { + if (item.match(flashMIME)) { - _s.flash[item] = true; - _s.flash[lookup] = true; + sm2.flash[item] = true; + sm2.flash[lookup] = true; } else { - _s.flash[item] = false; - _s.flash[lookup] = false; + sm2.flash[item] = false; + sm2.flash[lookup] = false; } // assign result to related formats, too @@ -3914,97 +3977,95 @@ for (i=aF[item].related.length-1; i >= 0; i--) { // eg. audio/m4a support['audio/'+aF[item].related[i]] = support[item]; - _s.html5[aF[item].related[i]] = support[item]; - _s.flash[aF[item].related[i]] = support[item]; + sm2.html5[aF[item].related[i]] = support[item]; + sm2.flash[aF[item].related[i]] = support[item]; } } } } - support.canPlayType = (a?_cp:null); - _s.html5 = _mixin(_s.html5, support); + support.canPlayType = (a?cp:null); + sm2.html5 = mixin(sm2.html5, support); return true; }; - _strings = { + strings = { // <d> - notReady: 'Not loaded yet - wait for soundManager.onready()', + notReady: 'Unavailable - wait until onready() has fired.', notOK: 'Audio support is not available.', - domError: _smc + 'createMovie(): appendChild/innerHTML call failed. DOM not ready or other error.', - spcWmode: _smc + 'createMovie(): Removing wmode, preventing known SWF loading issue(s)', - swf404: _sm + ': Verify that %s is a valid path.', - tryDebug: 'Try ' + _sm + '.debugFlash = true for more security details (output goes to SWF.)', + domError: sm + 'exception caught while appending SWF to DOM.', + spcWmode: 'Removing wmode, preventing known SWF loading issue(s)', + swf404: smc + 'Verify that %s is a valid path.', + tryDebug: 'Try ' + sm + '.debugFlash = true for more security details (output goes to SWF.)', checkSWF: 'See SWF output for more debug info.', - localFail: _sm + ': Non-HTTP page (' + _doc.location.protocol + ' URL?) Review Flash player security settings for this special case:\nhttp://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html\nMay need to add/allow path, eg. c:/sm2/ or /users/me/sm2/', - waitFocus: _sm + ': Special case: Waiting for SWF to load with window focus...', - waitImpatient: _sm + ': Getting impatient, still waiting for Flash%s...', - waitForever: _sm + ': Waiting indefinitely for Flash (will recover if unblocked)...', - waitSWF: _sm + ': Retrying, waiting for 100% SWF load...', - needFunction: _sm + ': Function object expected for %s', + localFail: smc + 'Non-HTTP page (' + doc.location.protocol + ' URL?) Review Flash player security settings for this special case:\nhttp://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html\nMay need to add/allow path, eg. c:/sm2/ or /users/me/sm2/', + waitFocus: smc + 'Special case: Waiting for SWF to load with window focus...', + waitForever: smc + 'Waiting indefinitely for Flash (will recover if unblocked)...', + waitSWF: smc + 'Waiting for 100% SWF load...', + needFunction: smc + 'Function object expected for %s', badID: 'Warning: Sound ID "%s" should be a string, starting with a non-numeric character', - currentObj: '--- ' + _sm + '._debug(): Current sound objects ---', - waitEI: _smc + 'initMovie(): Waiting for ExternalInterface call from Flash...', - waitOnload: _sm + ': Waiting for window.onload()', - docLoaded: _sm + ': Document already loaded', - onload: _smc + 'initComplete(): calling soundManager.onload()', - onloadOK: _sm + '.onload() complete', - init: _smc + 'init()', - didInit: _smc + 'init(): Already called?', - flashJS: _sm + ': Attempting JS to Flash call...', + currentObj: smc + '_debug(): Current sound objects', + waitOnload: smc + 'Waiting for window.onload()', + docLoaded: smc + 'Document already loaded', + onload: smc + 'initComplete(): calling soundManager.onload()', + onloadOK: sm + '.onload() complete', + didInit: smc + 'init(): Already called?', secNote: 'Flash security note: Network/internet URLs will not load due to security restrictions. Access can be configured via Flash Player Global Security Settings Page: http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html', - badRemove: 'Warning: Failed to remove flash movie.', - shutdown: _sm + '.disable(): Shutting down', - queue: _sm + ': Queueing %s handler', - smFail: _sm + ': Failed to initialise.', + badRemove: smc + 'Failed to remove Flash node.', + shutdown: sm + '.disable(): Shutting down', + queue: smc + 'Queueing %s handler', smError: 'SMSound.load(): Exception: JS-Flash communication failed, or JS error.', - fbTimeout: 'No flash response, applying .'+_swfCSS.swfTimedout+' CSS...', + fbTimeout: 'No flash response, applying .'+swfCSS.swfTimedout+' CSS...', fbLoaded: 'Flash loaded', - fbHandler: _smc+'flashBlockHandler()', + flRemoved: smc + 'Flash movie removed.', + fbHandler: smc + 'flashBlockHandler()', manURL: 'SMSound.load(): Using manually-assigned URL', - onURL: _sm + '.load(): current URL already assigned.', - badFV: _sm + '.flashVersion must be 8 or 9. "%s" is invalid. Reverting to %s.', + onURL: sm + '.load(): current URL already assigned.', + badFV: sm + '.flashVersion must be 8 or 9. "%s" is invalid. Reverting to %s.', as2loop: 'Note: Setting stream:false so looping can work (flash 8 limitation)', noNSLoop: 'Note: Looping not implemented for MovieStar formats', needfl9: 'Note: Switching to flash 9, required for MP4 formats.', mfTimeout: 'Setting flashLoadTimeout = 0 (infinite) for off-screen, mobile flash case', - needFlash: _sm + ': Fatal error: Flash is needed to play some required formats, but is not available.', - gotFocus: _sm + ': Got window focus.', - mfOn: 'mobileFlash::enabling on-screen flash repositioning', + needFlash: smc + 'Fatal error: Flash is needed to play some required formats, but is not available.', + gotFocus: smc + 'Got window focus.', policy: 'Enabling usePolicyFile for data access', - setup: _sm + '.setup(): allowed parameters: %s', - setupError: _sm + '.setup(): "%s" cannot be assigned with this method.', - setupUndef: _sm + '.setup(): Could not find option "%s"', - setupLate: _sm + '.setup(): url + flashVersion changes will not take effect until reboot().', - h5a: 'creating HTML5 Audio() object', - noURL: _sm + ': Flash URL required. Call soundManager.setup({url:...}) to get started.' + setup: sm + '.setup(): allowed parameters: %s', + setupError: sm + '.setup(): "%s" cannot be assigned with this method.', + setupUndef: sm + '.setup(): Could not find option "%s"', + setupLate: sm + '.setup(): url, flashVersion and html5Test property changes will not take effect until reboot().', + noURL: smc + 'Flash URL required. Call soundManager.setup({url:...}) to get started.', + sm2Loaded: 'SoundManager 2: Ready.', + reset: sm + '.reset(): Removing event callbacks', + mobileUA: 'Mobile UA detected, preferring HTML5 by default.', + globalHTML5: 'Using singleton HTML5 Audio() pattern for this device.' // </d> }; - _str = function() { + str = function() { // internal string replace helper. // arguments: o [,items to replace] // <d> // real array, please - var args = _slice.call(arguments), + var args = slice.call(arguments), // first arg o = args.shift(), - str = (_strings && _strings[o]?_strings[o]:''), i, j; + str = (strings && strings[o]?strings[o]:''), i, j; if (str && args && args.length) { for (i = 0, j = args.length; i < j; i++) { str = str.replace('%s', args[i]); } } @@ -4012,81 +4073,80 @@ return str; // </d> }; - _loopFix = function(sOpt) { + loopFix = function(sOpt) { // flash 8 requires stream = false for looping to work - if (_fV === 8 && sOpt.loops > 1 && sOpt.stream) { + if (fV === 8 && sOpt.loops > 1 && sOpt.stream) { _wDS('as2loop'); sOpt.stream = false; } return sOpt; }; - _policyFix = function(sOpt, sPre) { + policyFix = function(sOpt, sPre) { if (sOpt && !sOpt.usePolicyFile && (sOpt.onid3 || sOpt.usePeakData || sOpt.useWaveformData || sOpt.useEQData)) { - _s._wD((sPre || '') + _str('policy')); + sm2._wD((sPre || '') + str('policy')); sOpt.usePolicyFile = true; } return sOpt; }; - _complain = function(sMsg) { + complain = function(sMsg) { // <d> - if (typeof console !== 'undefined' && typeof console.warn !== 'undefined') { + if (console !== _undefined && console.warn !== _undefined) { console.warn(sMsg); } else { - _s._wD(sMsg); + sm2._wD(sMsg); } // </d> }; - _doNothing = function() { + doNothing = function() { return false; }; - _disableObject = function(o) { + disableObject = function(o) { var oProp; for (oProp in o) { if (o.hasOwnProperty(oProp) && typeof o[oProp] === 'function') { - o[oProp] = _doNothing; + o[oProp] = doNothing; } } oProp = null; }; - _failSafely = function(bNoDisable) { + failSafely = function(bNoDisable) { // general failure exception handler - if (typeof bNoDisable === 'undefined') { + if (bNoDisable === _undefined) { bNoDisable = false; } - if (_disabled || bNoDisable) { - _wDS('smFail', 2); - _s.disable(bNoDisable); + if (disabled || bNoDisable) { + sm2.disable(bNoDisable); } }; - _normalizeMovieURL = function(smURL) { + normalizeMovieURL = function(smURL) { var urlParams = null, url; if (smURL) { if (smURL.match(/\.swf(\?.*)?$/i)) { @@ -4099,95 +4159,95 @@ // append trailing slash, if needed smURL += '/'; } } - url = (smURL && smURL.lastIndexOf('/') !== - 1 ? smURL.substr(0, smURL.lastIndexOf('/') + 1) : './') + _s.movieURL; + url = (smURL && smURL.lastIndexOf('/') !== - 1 ? smURL.substr(0, smURL.lastIndexOf('/') + 1) : './') + sm2.movieURL; - if (_s.noSWFCache) { + if (sm2.noSWFCache) { url += ('?ts=' + new Date().getTime()); } return url; }; - _setVersionInfo = function() { + setVersionInfo = function() { // short-hand for internal use - _fV = parseInt(_s.flashVersion, 10); + fV = parseInt(sm2.flashVersion, 10); - if (_fV !== 8 && _fV !== 9) { - _s._wD(_str('badFV', _fV, _defaultFlashVersion)); - _s.flashVersion = _fV = _defaultFlashVersion; + if (fV !== 8 && fV !== 9) { + sm2._wD(str('badFV', fV, defaultFlashVersion)); + sm2.flashVersion = fV = defaultFlashVersion; } // debug flash movie, if applicable - var isDebug = (_s.debugMode || _s.debugFlash?'_debug.swf':'.swf'); + var isDebug = (sm2.debugMode || sm2.debugFlash?'_debug.swf':'.swf'); - if (_s.useHTML5Audio && !_s.html5Only && _s.audioFormats.mp4.required && _fV < 9) { - _s._wD(_str('needfl9')); - _s.flashVersion = _fV = 9; + if (sm2.useHTML5Audio && !sm2.html5Only && sm2.audioFormats.mp4.required && fV < 9) { + sm2._wD(str('needfl9')); + sm2.flashVersion = fV = 9; } - _s.version = _s.versionNumber + (_s.html5Only?' (HTML5-only mode)':(_fV === 9?' (AS3/Flash 9)':' (AS2/Flash 8)')); + sm2.version = sm2.versionNumber + (sm2.html5Only?' (HTML5-only mode)':(fV === 9?' (AS3/Flash 9)':' (AS2/Flash 8)')); // set up default options - if (_fV > 8) { + if (fV > 8) { // +flash 9 base options - _s.defaultOptions = _mixin(_s.defaultOptions, _s.flash9Options); - _s.features.buffering = true; + sm2.defaultOptions = mixin(sm2.defaultOptions, sm2.flash9Options); + sm2.features.buffering = true; // +moviestar support - _s.defaultOptions = _mixin(_s.defaultOptions, _s.movieStarOptions); - _s.filePatterns.flash9 = new RegExp('\\.(mp3|' + _netStreamTypes.join('|') + ')(\\?.*)?$', 'i'); - _s.features.movieStar = true; + sm2.defaultOptions = mixin(sm2.defaultOptions, sm2.movieStarOptions); + sm2.filePatterns.flash9 = new RegExp('\\.(mp3|' + netStreamTypes.join('|') + ')(\\?.*)?$', 'i'); + sm2.features.movieStar = true; } else { - _s.features.movieStar = false; + sm2.features.movieStar = false; } // regExp for flash canPlay(), etc. - _s.filePattern = _s.filePatterns[(_fV !== 8?'flash9':'flash8')]; + sm2.filePattern = sm2.filePatterns[(fV !== 8?'flash9':'flash8')]; // if applicable, use _debug versions of SWFs - _s.movieURL = (_fV === 8?'soundmanager2.swf':'soundmanager2_flash9.swf').replace('.swf', isDebug); + sm2.movieURL = (fV === 8?'soundmanager2.swf':'soundmanager2_flash9.swf').replace('.swf', isDebug); - _s.features.peakData = _s.features.waveformData = _s.features.eqData = (_fV > 8); + sm2.features.peakData = sm2.features.waveformData = sm2.features.eqData = (fV > 8); }; - _setPolling = function(bPolling, bHighPerformance) { + setPolling = function(bPolling, bHighPerformance) { - if (!_flash) { + if (!flash) { return false; } - _flash._setPolling(bPolling, bHighPerformance); + flash._setPolling(bPolling, bHighPerformance); }; - _initDebug = function() { + initDebug = function() { // starts debug mode, creating output <div> for UAs without console object // allow force of debug mode via URL - if (_s.debugURLParam.test(_wl)) { - _s.debugMode = true; + if (sm2.debugURLParam.test(wl)) { + sm2.debugMode = true; } // <d> - if (_id(_s.debugID)) { + if (id(sm2.debugID)) { return false; } var oD, oDebug, oTarget, oToggle, tmp; - if (_s.debugMode && !_id(_s.debugID) && (!_hasConsole || !_s.useConsole || !_s.consoleOnly)) { + if (sm2.debugMode && !id(sm2.debugID) && (!hasConsole || !sm2.useConsole || !sm2.consoleOnly)) { - oD = _doc.createElement('div'); - oD.id = _s.debugID + '-toggle'; + oD = doc.createElement('div'); + oD.id = sm2.debugID + '-toggle'; oToggle = { 'position': 'fixed', 'bottom': '0px', 'right': '0px', @@ -4201,35 +4261,35 @@ 'background': '#fff', 'color': '#333', 'zIndex': 10001 }; - oD.appendChild(_doc.createTextNode('-')); - oD.onclick = _toggleDebug; + oD.appendChild(doc.createTextNode('-')); + oD.onclick = toggleDebug; oD.title = 'Toggle SM2 debug console'; - if (_ua.match(/msie 6/i)) { + if (ua.match(/msie 6/i)) { oD.style.position = 'absolute'; oD.style.cursor = 'hand'; } for (tmp in oToggle) { if (oToggle.hasOwnProperty(tmp)) { oD.style[tmp] = oToggle[tmp]; } } - oDebug = _doc.createElement('div'); - oDebug.id = _s.debugID; - oDebug.style.display = (_s.debugMode?'block':'none'); + oDebug = doc.createElement('div'); + oDebug.id = sm2.debugID; + oDebug.style.display = (sm2.debugMode?'block':'none'); - if (_s.debugMode && !_id(oD.id)) { + if (sm2.debugMode && !id(oD.id)) { try { - oTarget = _getDocument(); + oTarget = getDocument(); oTarget.appendChild(oD); } catch(e2) { - throw new Error(_str('domError')+' \n'+e2.toString()); + throw new Error(str('domError')+' \n'+e2.toString()); } oTarget.appendChild(oDebug); } } @@ -4237,52 +4297,46 @@ oTarget = null; // </d> }; - _idCheck = this.getSoundById; + idCheck = this.getSoundById; // <d> _wDS = function(o, errorLevel) { - return (!o ? '' : _s._wD(_str(o), errorLevel)); + return (!o ? '' : sm2._wD(str(o), errorLevel)); }; - // last-resort debugging option + toggleDebug = function() { - if (_wl.indexOf('sm2-debug=alert') + 1 && _s.debugMode) { - _s._wD = function(sText) {window.alert(sText);}; - } + var o = id(sm2.debugID), + oT = id(sm2.debugID + '-toggle'); - _toggleDebug = function() { - - var o = _id(_s.debugID), - oT = _id(_s.debugID + '-toggle'); - if (!o) { return false; } - if (_debugOpen) { + if (debugOpen) { // minimize oT.innerHTML = '+'; o.style.display = 'none'; } else { oT.innerHTML = '-'; o.style.display = 'block'; } - _debugOpen = !_debugOpen; + debugOpen = !debugOpen; }; - _debugTS = function(sEventType, bSuccess, sMessage) { + debugTS = function(sEventType, bSuccess, sMessage) { // troubleshooter debug hooks - if (typeof sm2Debugger !== 'undefined') { + if (window.sm2Debugger !== _undefined) { try { sm2Debugger.handleEvent(sEventType, bSuccess, sMessage); } catch(e) { // oh well } @@ -4291,119 +4345,119 @@ return true; }; // </d> - _getSWFCSS = function() { + getSWFCSS = function() { var css = []; - if (_s.debugMode) { - css.push(_swfCSS.sm2Debug); + if (sm2.debugMode) { + css.push(swfCSS.sm2Debug); } - if (_s.debugFlash) { - css.push(_swfCSS.flashDebug); + if (sm2.debugFlash) { + css.push(swfCSS.flashDebug); } - if (_s.useHighPerformance) { - css.push(_swfCSS.highPerf); + if (sm2.useHighPerformance) { + css.push(swfCSS.highPerf); } return css.join(' '); }; - _flashBlockHandler = function() { + flashBlockHandler = function() { // *possible* flash block situation. - var name = _str('fbHandler'), - p = _s.getMoviePercent(), - css = _swfCSS, + var name = str('fbHandler'), + p = sm2.getMoviePercent(), + css = swfCSS, error = {type:'FLASHBLOCK'}; - if (_s.html5Only) { + if (sm2.html5Only) { return false; } - if (!_s.ok()) { + if (!sm2.ok()) { - if (_needsFlash) { + if (needsFlash) { // make the movie more visible, so user can fix - _s.oMC.className = _getSWFCSS() + ' ' + css.swfDefault + ' ' + (p === null?css.swfTimedout:css.swfError); - _s._wD(name+': '+_str('fbTimeout')+(p?' ('+_str('fbLoaded')+')':'')); + sm2.oMC.className = getSWFCSS() + ' ' + css.swfDefault + ' ' + (p === null?css.swfTimedout:css.swfError); + sm2._wD(name + ': ' + str('fbTimeout') + (p ? ' (' + str('fbLoaded') + ')' : '')); } - _s.didFlashBlock = true; + sm2.didFlashBlock = true; // fire onready(), complain lightly - _processOnEvents({type:'ontimeout', ignoreInit:true, error:error}); - _catchError(error); + processOnEvents({type:'ontimeout', ignoreInit:true, error:error}); + catchError(error); } else { // SM2 loaded OK (or recovered) // <d> - if (_s.didFlashBlock) { - _s._wD(name+': Unblocked'); + if (sm2.didFlashBlock) { + sm2._wD(name + ': Unblocked'); } // </d> - if (_s.oMC) { - _s.oMC.className = [_getSWFCSS(), css.swfDefault, css.swfLoaded + (_s.didFlashBlock?' '+css.swfUnblocked:'')].join(' '); + if (sm2.oMC) { + sm2.oMC.className = [getSWFCSS(), css.swfDefault, css.swfLoaded + (sm2.didFlashBlock?' '+css.swfUnblocked:'')].join(' '); } } }; - _addOnEvent = function(sType, oMethod, oScope) { + addOnEvent = function(sType, oMethod, oScope) { - if (typeof _on_queue[sType] === 'undefined') { - _on_queue[sType] = []; + if (on_queue[sType] === _undefined) { + on_queue[sType] = []; } - _on_queue[sType].push({ + on_queue[sType].push({ 'method': oMethod, 'scope': (oScope || null), 'fired': false }); }; - _processOnEvents = function(oOptions) { + processOnEvents = function(oOptions) { // if unspecified, assume OK/error if (!oOptions) { oOptions = { - type: (_s.ok() ? 'onready' : 'ontimeout') + type: (sm2.ok() ? 'onready' : 'ontimeout') }; } - if (!_didInit && oOptions && !oOptions.ignoreInit) { + if (!didInit && oOptions && !oOptions.ignoreInit) { // not ready yet. return false; } - if (oOptions.type === 'ontimeout' && (_s.ok() || (_disabled && !oOptions.ignoreInit))) { + if (oOptions.type === 'ontimeout' && (sm2.ok() || (disabled && !oOptions.ignoreInit))) { // invalid case return false; } var status = { - success: (oOptions && oOptions.ignoreInit?_s.ok():!_disabled) + success: (oOptions && oOptions.ignoreInit?sm2.ok():!disabled) }, // queue specified by type, or none - srcQueue = (oOptions && oOptions.type?_on_queue[oOptions.type]||[]:[]), + srcQueue = (oOptions && oOptions.type?on_queue[oOptions.type]||[]:[]), queue = [], i, j, args = [status], - canRetry = (_needsFlash && _s.useFlashBlock && !_s.ok()); + canRetry = (needsFlash && !sm2.ok()); if (oOptions.error) { args[0].error = oOptions.error; } @@ -4412,153 +4466,156 @@ queue.push(srcQueue[i]); } } if (queue.length) { - _s._wD(_sm + ': Firing ' + queue.length + ' '+oOptions.type+'() item' + (queue.length === 1?'':'s')); + // sm2._wD(sm + ': Firing ' + queue.length + ' ' + oOptions.type + '() item' + (queue.length === 1 ? '' : 's')); for (i = 0, j = queue.length; i < j; i++) { if (queue[i].scope) { queue[i].method.apply(queue[i].scope, args); } else { queue[i].method.apply(this, args); } if (!canRetry) { - // flashblock case doesn't count here + // useFlashBlock and SWF timeout case doesn't count here. queue[i].fired = true; } } } return true; }; - _initUserOnload = function() { + initUserOnload = function() { - _win.setTimeout(function() { + window.setTimeout(function() { - if (_s.useFlashBlock) { - _flashBlockHandler(); + if (sm2.useFlashBlock) { + flashBlockHandler(); } - _processOnEvents(); + processOnEvents(); // call user-defined "onload", scoped to window - if (typeof _s.onload === 'function') { + if (typeof sm2.onload === 'function') { _wDS('onload', 1); - _s.onload.apply(_win); + sm2.onload.apply(window); _wDS('onloadOK', 1); } - if (_s.waitForWindowLoad) { - _event.add(_win, 'load', _initUserOnload); + if (sm2.waitForWindowLoad) { + event.add(window, 'load', initUserOnload); } },1); }; - _detectFlash = function() { + detectFlash = function() { // hat tip: Flash Detect library (BSD, (C) 2007) by Carl "DocYes" S. Yestrau - http://featureblend.com/javascript-flash-detection-library.html / http://featureblend.com/license.txt - if (typeof _hasFlash !== 'undefined') { + if (hasFlash !== _undefined) { // this work has already been done. - return _hasFlash; + return hasFlash; } - var hasPlugin = false, n = navigator, nP = n.plugins, obj, type, types, AX = _win.ActiveXObject; + var hasPlugin = false, n = navigator, nP = n.plugins, obj, type, types, AX = window.ActiveXObject; if (nP && nP.length) { type = 'application/x-shockwave-flash'; types = n.mimeTypes; if (types && types[type] && types[type].enabledPlugin && types[type].enabledPlugin.description) { hasPlugin = true; } - } else if (typeof AX !== 'undefined') { + } else if (AX !== _undefined && !ua.match(/MSAppHost/i)) { + // Windows 8 Store Apps (MSAppHost) are weird (compatibility?) and won't complain here, but will barf if Flash/ActiveX object is appended to the DOM. try { obj = new AX('ShockwaveFlash.ShockwaveFlash'); } catch(e) { // oh well } hasPlugin = (!!obj); + // cleanup, because it is ActiveX after all + obj = null; } - _hasFlash = hasPlugin; + hasFlash = hasPlugin; return hasPlugin; }; - _featureCheck = function() { + featureCheck = function() { var needsFlash, item, result = true, - formats = _s.audioFormats, + formats = sm2.audioFormats, // iPhone <= 3.1 has broken HTML5 audio(), but firmware 3.2 (original iPad) + iOS4 works. - isSpecial = (_is_iDevice && !!(_ua.match(/os (1|2|3_0|3_1)/i))); + isSpecial = (is_iDevice && !!(ua.match(/os (1|2|3_0|3_1)/i))); if (isSpecial) { // has Audio(), but is broken; let it load links directly. - _s.hasHTML5 = false; + sm2.hasHTML5 = false; // ignore flash case, however - _s.html5Only = true; + sm2.html5Only = true; - if (_s.oMC) { - _s.oMC.style.display = 'none'; + if (sm2.oMC) { + sm2.oMC.style.display = 'none'; } result = false; } else { - if (_s.useHTML5Audio) { + if (sm2.useHTML5Audio) { - if (!_s.html5 || !_s.html5.canPlayType) { - _s._wD('SoundManager: No HTML5 Audio() support detected.'); - _s.hasHTML5 = false; + if (!sm2.html5 || !sm2.html5.canPlayType) { + sm2._wD('SoundManager: No HTML5 Audio() support detected.'); + sm2.hasHTML5 = false; } // <d> - if (_isBadSafari) { - _s._wD(_smc+'Note: Buggy HTML5 Audio in Safari on this OS X release, see https://bugs.webkit.org/show_bug.cgi?id=32159 - '+(!_hasFlash?' would use flash fallback for MP3/MP4, but none detected.':'will use flash fallback for MP3/MP4, if available'),1); + if (isBadSafari) { + sm2._wD(smc + 'Note: Buggy HTML5 Audio in Safari on this OS X release, see https://bugs.webkit.org/show_bug.cgi?id=32159 - ' + (!hasFlash ?' would use flash fallback for MP3/MP4, but none detected.' : 'will use flash fallback for MP3/MP4, if available'), 1); } // </d> } } - if (_s.useHTML5Audio && _s.hasHTML5) { + if (sm2.useHTML5Audio && sm2.hasHTML5) { for (item in formats) { if (formats.hasOwnProperty(item)) { - if ((formats[item].required && !_s.html5.canPlayType(formats[item].type)) || (_s.preferFlash && (_s.flash[item] || _s.flash[formats[item].type]))) { + if ((formats[item].required && !sm2.html5.canPlayType(formats[item].type)) || (sm2.preferFlash && (sm2.flash[item] || sm2.flash[formats[item].type]))) { // flash may be required, or preferred for this format needsFlash = true; } } } } // sanity check... - if (_s.ignoreFlash) { + if (sm2.ignoreFlash) { needsFlash = false; } - _s.html5Only = (_s.hasHTML5 && _s.useHTML5Audio && !needsFlash); + sm2.html5Only = (sm2.hasHTML5 && sm2.useHTML5Audio && !needsFlash); - return (!_s.html5Only); + return (!sm2.html5Only); }; - _parseURL = function(url) { + parseURL = function(url) { /** * Internal: Finds and returns the first playable URL (or failing that, the first URL.) * @param {string or array} url A single URL string, OR, an array of URL strings or {url:'/path/to/resource', type:'audio/mp3'} objects. */ @@ -4570,16 +4627,16 @@ // find the first good one for (i=0, j=url.length; i<j; i++) { if (url[i] instanceof Object) { // MIME check - if (_s.canPlayMIME(url[i].type)) { + if (sm2.canPlayMIME(url[i].type)) { urlResult = i; break; } - } else if (_s.canPlayURL(url[i])) { + } else if (sm2.canPlayURL(url[i])) { // URL string check urlResult = i; break; } @@ -4602,125 +4659,125 @@ return result; }; - _startTimer = function(oSound) { + startTimer = function(oSound) { /** * attach a timer to this sound, and start an interval if needed */ if (!oSound._hasTimer) { oSound._hasTimer = true; - if (!_mobileHTML5 && _s.html5PollingInterval) { + if (!mobileHTML5 && sm2.html5PollingInterval) { - if (_h5IntervalTimer === null && _h5TimerCount === 0) { + if (h5IntervalTimer === null && h5TimerCount === 0) { - _h5IntervalTimer = _win.setInterval(_timerExecute, _s.html5PollingInterval); + h5IntervalTimer = window.setInterval(timerExecute, sm2.html5PollingInterval); } - _h5TimerCount++; + h5TimerCount++; } } }; - _stopTimer = function(oSound) { + stopTimer = function(oSound) { /** * detach a timer */ if (oSound._hasTimer) { oSound._hasTimer = false; - if (!_mobileHTML5 && _s.html5PollingInterval) { + if (!mobileHTML5 && sm2.html5PollingInterval) { // interval will stop itself at next execution. - _h5TimerCount--; + h5TimerCount--; } } }; - _timerExecute = function() { + timerExecute = function() { /** * manual polling for HTML5 progress events, ie., whileplaying() (can achieve greater precision than conservative default HTML5 interval) */ var i; - if (_h5IntervalTimer !== null && !_h5TimerCount) { + if (h5IntervalTimer !== null && !h5TimerCount) { // no active timers, stop polling interval. - _win.clearInterval(_h5IntervalTimer); + window.clearInterval(h5IntervalTimer); - _h5IntervalTimer = null; + h5IntervalTimer = null; return false; } // check all HTML5 sounds with timers - for (i = _s.soundIDs.length-1; i >= 0; i--) { + for (i = sm2.soundIDs.length-1; i >= 0; i--) { - if (_s.sounds[_s.soundIDs[i]].isHTML5 && _s.sounds[_s.soundIDs[i]]._hasTimer) { + if (sm2.sounds[sm2.soundIDs[i]].isHTML5 && sm2.sounds[sm2.soundIDs[i]]._hasTimer) { - _s.sounds[_s.soundIDs[i]]._onTimer(); + sm2.sounds[sm2.soundIDs[i]]._onTimer(); } } }; - _catchError = function(options) { + catchError = function(options) { - options = (typeof options !== 'undefined' ? options : {}); + options = (options !== _undefined ? options : {}); - if (typeof _s.onerror === 'function') { - _s.onerror.apply(_win, [{type:(typeof options.type !== 'undefined' ? options.type : null)}]); + if (typeof sm2.onerror === 'function') { + sm2.onerror.apply(window, [{type:(options.type !== _undefined ? options.type : null)}]); } - if (typeof options.fatal !== 'undefined' && options.fatal) { - _s.disable(); + if (options.fatal !== _undefined && options.fatal) { + sm2.disable(); } }; - _badSafariFix = function() { + badSafariFix = function() { // special case: "bad" Safari (OS X 10.3 - 10.7) must fall back to flash for MP3/MP4 - if (!_isBadSafari || !_detectFlash()) { + if (!isBadSafari || !detectFlash()) { // doesn't apply return false; } - var aF = _s.audioFormats, i, item; + var aF = sm2.audioFormats, i, item; for (item in aF) { if (aF.hasOwnProperty(item)) { if (item === 'mp3' || item === 'mp4') { - _s._wD(_sm+': Using flash fallback for '+item+' format'); - _s.html5[item] = false; + sm2._wD(sm + ': Using flash fallback for ' + item + ' format'); + sm2.html5[item] = false; // assign result to related formats, too if (aF[item] && aF[item].related) { for (i = aF[item].related.length-1; i >= 0; i--) { - _s.html5[aF[item].related[i]] = false; + sm2.html5[aF[item].related[i]] = false; } } } } } @@ -4733,17 +4790,15 @@ */ this._setSandboxType = function(sandboxType) { // <d> - var sb = _s.sandbox; + var sb = sm2.sandbox; sb.type = sandboxType; - sb.description = sb.types[(typeof sb.types[sandboxType] !== 'undefined'?sandboxType:'unknown')]; + sb.description = sb.types[(sb.types[sandboxType] !== _undefined?sandboxType:'unknown')]; - _s._wD('Flash security sandbox type: ' + sb.type); - if (sb.type === 'localWithFile') { sb.noRemote = true; sb.noLocal = false; _wDS('secNote', 2); @@ -4767,31 +4822,30 @@ // flash callback confirming flash loaded, EI working etc. // flashDate = approx. timing/delay info for JS/flash bridge // swfVersion: SWF build string - if (_s.swfLoaded) { + if (sm2.swfLoaded) { return false; } - var e, eiTime = new Date().getTime(); + var e; - _s._wD(_smc+'externalInterfaceOK()' + (flashDate?' (~' + (eiTime - flashDate) + ' ms)':'')); - _debugTS('swf', true); - _debugTS('flashtojs', true); - _s.swfLoaded = true; - _tryInitOnFocus = false; + debugTS('swf', true); + debugTS('flashtojs', true); + sm2.swfLoaded = true; + tryInitOnFocus = false; - if (_isBadSafari) { - _badSafariFix(); + if (isBadSafari) { + badSafariFix(); } // complain if JS + SWF build/version strings don't match, excluding +DEV builds // <d> - if (!swfVersion || swfVersion.replace(/\+dev/i,'') !== _s.versionNumber.replace(/\+dev/i, '')) { + if (!swfVersion || swfVersion.replace(/\+dev/i,'') !== sm2.versionNumber.replace(/\+dev/i, '')) { - e = _sm + ': Fatal: JavaScript file build "' + _s.versionNumber + '" does not match Flash SWF build "' + swfVersion + '" at ' + _s.url + '. Ensure both are up-to-date.'; + e = sm + ': Fatal: JavaScript file build "' + sm2.versionNumber + '" does not match Flash SWF build "' + swfVersion + '" at ' + sm2.url + '. Ensure both are up-to-date.'; // escape flash -> JS stack so this error fires in window. setTimeout(function versionMismatch() { throw new Error(e); }, 0); @@ -4801,150 +4855,204 @@ } // </d> // slight delay before init - setTimeout(_init, _isIE ? 100 : 1); + setTimeout(init, isIE ? 100 : 1); }; /** * Private initialization helpers * ------------------------------ */ - _createMovie = function(smID, smURL) { + createMovie = function(smID, smURL) { - if (_didAppend && _appendSuccess) { + if (didAppend && appendSuccess) { // ignore if already succeeded return false; } - function _initMsg() { - _s._wD('-- SoundManager 2 ' + _s.version + (!_s.html5Only && _s.useHTML5Audio?(_s.hasHTML5?' + HTML5 audio':', no HTML5 audio support'):'') + (!_s.html5Only ? (_s.useHighPerformance?', high performance mode, ':', ') + (( _s.flashPollingInterval ? 'custom (' + _s.flashPollingInterval + 'ms)' : 'normal') + ' polling') + (_s.wmode?', wmode: ' + _s.wmode:'') + (_s.debugFlash?', flash debug mode':'') + (_s.useFlashBlock?', flashBlock mode':'') : '') + ' --', 1); + function initMsg() { + + // <d> + + var options = [], title, str = [], delimiter = ' + '; + + title = 'SoundManager ' + sm2.version + (!sm2.html5Only && sm2.useHTML5Audio ? (sm2.hasHTML5 ? ' + HTML5 audio' : ', no HTML5 audio support') : ''); + + if (!sm2.html5Only) { + + if (sm2.preferFlash) { + options.push('preferFlash'); + } + + if (sm2.useHighPerformance) { + options.push('useHighPerformance'); + } + + if (sm2.flashPollingInterval) { + options.push('flashPollingInterval (' + sm2.flashPollingInterval + 'ms)'); + } + + if (sm2.html5PollingInterval) { + options.push('html5PollingInterval (' + sm2.html5PollingInterval + 'ms)'); + } + + if (sm2.wmode) { + options.push('wmode (' + sm2.wmode + ')'); + } + + if (sm2.debugFlash) { + options.push('debugFlash'); + } + + if (sm2.useFlashBlock) { + options.push('flashBlock'); + } + + } else { + + if (sm2.html5PollingInterval) { + options.push('html5PollingInterval (' + sm2.html5PollingInterval + 'ms)'); + } + + } + + if (options.length) { + str = str.concat([options.join(delimiter)]); + } + + sm2._wD(title + (str.length ? delimiter + str.join(', ') : ''), 1); + + showSupport(); + + // </d> + } - if (_s.html5Only) { + if (sm2.html5Only) { // 100% HTML5 mode - _setVersionInfo(); + setVersionInfo(); - _initMsg(); - _s.oMC = _id(_s.movieID); - _init(); + initMsg(); + sm2.oMC = id(sm2.movieID); + init(); // prevent multiple init attempts - _didAppend = true; + didAppend = true; - _appendSuccess = true; + appendSuccess = true; return false; } // flash path - var remoteURL = (smURL || _s.url), - localURL = (_s.altURL || remoteURL), + var remoteURL = (smURL || sm2.url), + localURL = (sm2.altURL || remoteURL), swfTitle = 'JS/Flash audio component (SoundManager 2)', - oTarget = _getDocument(), - extraClass = _getSWFCSS(), + oTarget = getDocument(), + extraClass = getSWFCSS(), isRTL = null, - html = _doc.getElementsByTagName('html')[0], + html = doc.getElementsByTagName('html')[0], oEmbed, oMovie, tmp, movieHTML, oEl, s, x, sClass; isRTL = (html && html.dir && html.dir.match(/rtl/i)); - smID = (typeof smID === 'undefined'?_s.id:smID); + smID = (smID === _undefined?sm2.id:smID); function param(name, value) { return '<param name="'+name+'" value="'+value+'" />'; } // safety check for legacy (change to Flash 9 URL) - _setVersionInfo(); - _s.url = _normalizeMovieURL(_overHTTP?remoteURL:localURL); - smURL = _s.url; + setVersionInfo(); + sm2.url = normalizeMovieURL(overHTTP?remoteURL:localURL); + smURL = sm2.url; - _s.wmode = (!_s.wmode && _s.useHighPerformance ? 'transparent' : _s.wmode); + sm2.wmode = (!sm2.wmode && sm2.useHighPerformance ? 'transparent' : sm2.wmode); - if (_s.wmode !== null && (_ua.match(/msie 8/i) || (!_isIE && !_s.useHighPerformance)) && navigator.platform.match(/win32|win64/i)) { + if (sm2.wmode !== null && (ua.match(/msie 8/i) || (!isIE && !sm2.useHighPerformance)) && navigator.platform.match(/win32|win64/i)) { /** * extra-special case: movie doesn't load until scrolled into view when using wmode = anything but 'window' here * does not apply when using high performance (position:fixed means on-screen), OR infinite flash load timeout * wmode breaks IE 8 on Vista + Win7 too in some cases, as of January 2011 (?) */ - _wDS('spcWmode'); - _s.wmode = null; + messages.push(strings.spcWmode); + sm2.wmode = null; } oEmbed = { 'name': smID, 'id': smID, 'src': smURL, 'quality': 'high', - 'allowScriptAccess': _s.allowScriptAccess, - 'bgcolor': _s.bgColor, - 'pluginspage': _http+'www.macromedia.com/go/getflashplayer', + 'allowScriptAccess': sm2.allowScriptAccess, + 'bgcolor': sm2.bgColor, + 'pluginspage': http+'www.macromedia.com/go/getflashplayer', 'title': swfTitle, 'type': 'application/x-shockwave-flash', - 'wmode': _s.wmode, + 'wmode': sm2.wmode, // http://help.adobe.com/en_US/as3/mobile/WS4bebcd66a74275c36cfb8137124318eebc6-7ffd.html 'hasPriority': 'true' }; - if (_s.debugFlash) { + if (sm2.debugFlash) { oEmbed.FlashVars = 'debug=1'; } - if (!_s.wmode) { + if (!sm2.wmode) { // don't write empty attribute delete oEmbed.wmode; } - if (_isIE) { + if (isIE) { // IE is "special". - oMovie = _doc.createElement('div'); + oMovie = doc.createElement('div'); movieHTML = [ - '<object id="' + smID + '" data="' + smURL + '" type="' + oEmbed.type + '" title="' + oEmbed.title +'" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="' + _http+'download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0">', + '<object id="' + smID + '" data="' + smURL + '" type="' + oEmbed.type + '" title="' + oEmbed.title +'" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="' + http+'download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0">', param('movie', smURL), - param('AllowScriptAccess', _s.allowScriptAccess), + param('AllowScriptAccess', sm2.allowScriptAccess), param('quality', oEmbed.quality), - (_s.wmode? param('wmode', _s.wmode): ''), - param('bgcolor', _s.bgColor), + (sm2.wmode? param('wmode', sm2.wmode): ''), + param('bgcolor', sm2.bgColor), param('hasPriority', 'true'), - (_s.debugFlash ? param('FlashVars', oEmbed.FlashVars) : ''), + (sm2.debugFlash ? param('FlashVars', oEmbed.FlashVars) : ''), '</object>' ].join(''); } else { - oMovie = _doc.createElement('embed'); + oMovie = doc.createElement('embed'); for (tmp in oEmbed) { if (oEmbed.hasOwnProperty(tmp)) { oMovie.setAttribute(tmp, oEmbed[tmp]); } } } - _initDebug(); - extraClass = _getSWFCSS(); - oTarget = _getDocument(); + initDebug(); + extraClass = getSWFCSS(); + oTarget = getDocument(); if (oTarget) { - _s.oMC = (_id(_s.movieID) || _doc.createElement('div')); + sm2.oMC = (id(sm2.movieID) || doc.createElement('div')); - if (!_s.oMC.id) { + if (!sm2.oMC.id) { - _s.oMC.id = _s.movieID; - _s.oMC.className = _swfCSS.swfDefault + ' ' + extraClass; + sm2.oMC.id = sm2.movieID; + sm2.oMC.className = swfCSS.swfDefault + ' ' + extraClass; s = null; oEl = null; - if (!_s.useFlashBlock) { - if (_s.useHighPerformance) { + if (!sm2.useFlashBlock) { + if (sm2.useHighPerformance) { // on-screen at all times s = { 'position': 'fixed', 'width': '8px', 'height': '8px', @@ -4966,76 +5074,76 @@ s.left = Math.abs(parseInt(s.left,10))+'px'; } } } - if (_isWebkit) { + if (isWebkit) { // soundcloud-reported render/crash fix, safari 5 - _s.oMC.style.zIndex = 10000; + sm2.oMC.style.zIndex = 10000; } - if (!_s.debugFlash) { + if (!sm2.debugFlash) { for (x in s) { if (s.hasOwnProperty(x)) { - _s.oMC.style[x] = s[x]; + sm2.oMC.style[x] = s[x]; } } } try { - if (!_isIE) { - _s.oMC.appendChild(oMovie); + if (!isIE) { + sm2.oMC.appendChild(oMovie); } - oTarget.appendChild(_s.oMC); - if (_isIE) { - oEl = _s.oMC.appendChild(_doc.createElement('div')); - oEl.className = _swfCSS.swfBox; + oTarget.appendChild(sm2.oMC); + if (isIE) { + oEl = sm2.oMC.appendChild(doc.createElement('div')); + oEl.className = swfCSS.swfBox; oEl.innerHTML = movieHTML; } - _appendSuccess = true; + appendSuccess = true; } catch(e) { - throw new Error(_str('domError')+' \n'+e.toString()); + throw new Error(str('domError')+' \n'+e.toString()); } } else { // SM2 container is already in the document (eg. flashblock use case) - sClass = _s.oMC.className; - _s.oMC.className = (sClass?sClass+' ':_swfCSS.swfDefault) + (extraClass?' '+extraClass:''); - _s.oMC.appendChild(oMovie); - if (_isIE) { - oEl = _s.oMC.appendChild(_doc.createElement('div')); - oEl.className = _swfCSS.swfBox; + sClass = sm2.oMC.className; + sm2.oMC.className = (sClass?sClass+' ':swfCSS.swfDefault) + (extraClass?' '+extraClass:''); + sm2.oMC.appendChild(oMovie); + if (isIE) { + oEl = sm2.oMC.appendChild(doc.createElement('div')); + oEl.className = swfCSS.swfBox; oEl.innerHTML = movieHTML; } - _appendSuccess = true; + appendSuccess = true; } } - _didAppend = true; - _initMsg(); - _s._wD(_smc+'createMovie(): Trying to load ' + smURL + (!_overHTTP && _s.altURL?' (alternate URL)':''), 1); + didAppend = true; + initMsg(); + // sm2._wD(sm + ': Trying to load ' + smURL + (!overHTTP && sm2.altURL ? ' (alternate URL)' : ''), 1); return true; }; - _initMovie = function() { + initMovie = function() { - if (_s.html5Only) { - _createMovie(); + if (sm2.html5Only) { + createMovie(); return false; } // attempt to get, or create, movie (may already exist) - if (_flash) { + if (flash) { return false; } - if (!_s.url) { + if (!sm2.url) { /** * Something isn't right - we've reached init, but the soundManager url property has not been set. * User has not called setup({url: ...}), or has not set soundManager.url (legacy use case) directly before init time. * Notify and exit. If user calls setup() with a url: property, init will be restarted as in the deferred loading case. @@ -5045,234 +5153,249 @@ return false; } // inline markup case - _flash = _s.getMovie(_s.id); + flash = sm2.getMovie(sm2.id); - if (!_flash) { - if (!_oRemoved) { + if (!flash) { + if (!oRemoved) { // try to create - _createMovie(_s.id, _s.url); + createMovie(sm2.id, sm2.url); } else { // try to re-append removed movie after reboot() - if (!_isIE) { - _s.oMC.appendChild(_oRemoved); + if (!isIE) { + sm2.oMC.appendChild(oRemoved); } else { - _s.oMC.innerHTML = _oRemovedHTML; + sm2.oMC.innerHTML = oRemovedHTML; } - _oRemoved = null; - _didAppend = true; + oRemoved = null; + didAppend = true; } - _flash = _s.getMovie(_s.id); + flash = sm2.getMovie(sm2.id); } - // <d> - if (_flash) { - _wDS('waitEI'); + if (typeof sm2.oninitmovie === 'function') { + setTimeout(sm2.oninitmovie, 1); } + + // <d> + flushMessages(); // </d> - if (typeof _s.oninitmovie === 'function') { - setTimeout(_s.oninitmovie, 1); - } - return true; }; - _delayWaitForEI = function() { + delayWaitForEI = function() { - setTimeout(_waitForEI, 1000); + setTimeout(waitForEI, 1000); }; - _waitForEI = function() { + waitForEI = function() { var p, loadIncomplete = false; - if (!_s.url) { + if (!sm2.url) { // No SWF url to load (noURL case) - exit for now. Will be retried when url is set. return false; } - if (_waitingForEI) { + if (waitingForEI) { return false; } - _waitingForEI = true; - _event.remove(_win, 'load', _delayWaitForEI); + waitingForEI = true; + event.remove(window, 'load', delayWaitForEI); - if (_tryInitOnFocus && !_isFocused) { + if (tryInitOnFocus && !isFocused) { // Safari won't load flash in background tabs, only when focused. _wDS('waitFocus'); return false; } - if (!_didInit) { - p = _s.getMoviePercent(); - _s._wD(_str('waitImpatient', (p > 0 ? ' (SWF ' + p + '% loaded)' : ''))); + if (!didInit) { + p = sm2.getMoviePercent(); if (p > 0 && p < 100) { loadIncomplete = true; } } setTimeout(function() { - p = _s.getMoviePercent(); + p = sm2.getMoviePercent(); if (loadIncomplete) { // special case: if movie *partially* loaded, retry until it's 100% before assuming failure. - _waitingForEI = false; - _s._wD(_str('waitSWF')); - _win.setTimeout(_delayWaitForEI, 1); + waitingForEI = false; + sm2._wD(str('waitSWF')); + window.setTimeout(delayWaitForEI, 1); return false; } // <d> - if (!_didInit) { - _s._wD(_sm + ': No Flash response within expected time.\nLikely causes: ' + (p === 0?'Loading ' + _s.movieURL + ' may have failed (and/or Flash ' + _fV + '+ not present?), ':'') + 'Flash blocked or JS-Flash security error.' + (_s.debugFlash?' ' + _str('checkSWF'):''), 2); - if (!_overHTTP && p) { + if (!didInit) { + sm2._wD(sm + ': No Flash response within expected time. Likely causes: ' + (p === 0 ? 'SWF load failed, ':'') + 'Flash blocked or JS-Flash security error.' + (sm2.debugFlash?' ' + str('checkSWF'):''), 2); + if (!overHTTP && p) { _wDS('localFail', 2); - if (!_s.debugFlash) { + if (!sm2.debugFlash) { _wDS('tryDebug', 2); } } if (p === 0) { // if 0 (not null), probably a 404. - _s._wD(_str('swf404', _s.url)); + sm2._wD(str('swf404', sm2.url), 1); } - _debugTS('flashtojs', false, ': Timed out' + _overHTTP?' (Check flash security or flash blockers)':' (No plugin/missing SWF?)'); + debugTS('flashtojs', false, ': Timed out' + overHTTP?' (Check flash security or flash blockers)':' (No plugin/missing SWF?)'); } // </d> // give up / time-out, depending - if (!_didInit && _okToDisable) { + if (!didInit && okToDisable) { if (p === null) { // SWF failed. Maybe blocked. - if (_s.useFlashBlock || _s.flashLoadTimeout === 0) { - if (_s.useFlashBlock) { - _flashBlockHandler(); + if (sm2.useFlashBlock || sm2.flashLoadTimeout === 0) { + if (sm2.useFlashBlock) { + flashBlockHandler(); } _wDS('waitForever'); } else { - // old SM2 behaviour, simply fail - _failSafely(true); + // no custom flash block handling, but SWF has timed out. Will recover if user unblocks / allows SWF load. + _wDS('waitForever'); + // fire any regular registered ontimeout() listeners. + processOnEvents({type:'ontimeout', ignoreInit: true}); } } else { // flash loaded? Shouldn't be a blocking issue, then. - if (_s.flashLoadTimeout === 0) { + if (sm2.flashLoadTimeout === 0) { _wDS('waitForever'); } else { - _failSafely(true); + failSafely(true); } } } - }, _s.flashLoadTimeout); + }, sm2.flashLoadTimeout); }; - _handleFocus = function() { + handleFocus = function() { function cleanup() { - _event.remove(_win, 'focus', _handleFocus); + event.remove(window, 'focus', handleFocus); } - if (_isFocused || !_tryInitOnFocus) { + if (isFocused || !tryInitOnFocus) { // already focused, or not special Safari background tab case cleanup(); return true; } - _okToDisable = true; - _isFocused = true; + okToDisable = true; + isFocused = true; _wDS('gotFocus'); // allow init to restart - _waitingForEI = false; + waitingForEI = false; // kick off ExternalInterface timeout, now that the SWF has started - _delayWaitForEI(); + delayWaitForEI(); cleanup(); return true; }; - _showSupport = function() { + flushMessages = function() { // <d> + // SM2 pre-init debug messages + if (messages.length) { + sm2._wD('SoundManager 2: ' + messages.join(' '), 1); + messages = []; + } + + // </d> + + }; + + showSupport = function() { + + // <d> + + flushMessages(); + var item, tests = []; - if (_s.useHTML5Audio && _s.hasHTML5) { - for (item in _s.audioFormats) { - if (_s.audioFormats.hasOwnProperty(item)) { - tests.push(item + ': ' + _s.html5[item] + (!_s.html5[item] && _hasFlash && _s.flash[item] ? ' (using flash)' : (_s.preferFlash && _s.flash[item] && _hasFlash ? ' (preferring flash)': (!_s.html5[item] ? ' (' + (_s.audioFormats[item].required ? 'required, ':'') + 'and no flash support)' : '')))); + if (sm2.useHTML5Audio && sm2.hasHTML5) { + for (item in sm2.audioFormats) { + if (sm2.audioFormats.hasOwnProperty(item)) { + tests.push(item + ' = ' + sm2.html5[item] + (!sm2.html5[item] && hasFlash && sm2.flash[item] ? ' (using flash)' : (sm2.preferFlash && sm2.flash[item] && hasFlash ? ' (preferring flash)': (!sm2.html5[item] ? ' (' + (sm2.audioFormats[item].required ? 'required, ':'') + 'and no flash support)' : '')))); } } - _s._wD('-- SoundManager 2: HTML5 support tests ('+_s.html5Test+'): '+tests.join(', ')+' --',1); + sm2._wD('SoundManager 2 HTML5 support: ' + tests.join(', '), 1); } // </d> }; - _initComplete = function(bNoDisable) { + initComplete = function(bNoDisable) { - if (_didInit) { + if (didInit) { return false; } - if (_s.html5Only) { + if (sm2.html5Only) { // all good. - _s._wD('-- SoundManager 2: loaded --'); - _didInit = true; - _initUserOnload(); - _debugTS('onload', true); + _wDS('sm2Loaded'); + didInit = true; + initUserOnload(); + debugTS('onload', true); return true; } - var wasTimeout = (_s.useFlashBlock && _s.flashLoadTimeout && !_s.getMoviePercent()), + var wasTimeout = (sm2.useFlashBlock && sm2.flashLoadTimeout && !sm2.getMoviePercent()), result = true, error; if (!wasTimeout) { - _didInit = true; - if (_disabled) { - error = {type: (!_hasFlash && _needsFlash ? 'NO_FLASH' : 'INIT_TIMEOUT')}; + didInit = true; + if (disabled) { + error = {type: (!hasFlash && needsFlash ? 'NO_FLASH' : 'INIT_TIMEOUT')}; } } - _s._wD('-- SoundManager 2 ' + (_disabled?'failed to load':'loaded') + ' (' + (_disabled?'Flash security/load error':'OK') + ') --', 1); + sm2._wD('SoundManager 2 ' + (disabled ? 'failed to load' : 'loaded') + ' (' + (disabled ? 'Flash security/load error' : 'OK') + ')', disabled ? 2: 1); - if (_disabled || bNoDisable) { - if (_s.useFlashBlock && _s.oMC) { - _s.oMC.className = _getSWFCSS() + ' ' + (_s.getMoviePercent() === null?_swfCSS.swfTimedout:_swfCSS.swfError); + if (disabled || bNoDisable) { + if (sm2.useFlashBlock && sm2.oMC) { + sm2.oMC.className = getSWFCSS() + ' ' + (sm2.getMoviePercent() === null?swfCSS.swfTimedout:swfCSS.swfError); } - _processOnEvents({type:'ontimeout', error:error, ignoreInit: true}); - _debugTS('onload', false); - _catchError(error); + processOnEvents({type:'ontimeout', error:error, ignoreInit: true}); + debugTS('onload', false); + catchError(error); result = false; } else { - _debugTS('onload', true); + debugTS('onload', true); } - if (!_disabled) { - if (_s.waitForWindowLoad && !_windowLoaded) { + if (!disabled) { + if (sm2.waitForWindowLoad && !windowLoaded) { _wDS('waitOnload'); - _event.add(_win, 'load', _initUserOnload); + event.add(window, 'load', initUserOnload); } else { // <d> - if (_s.waitForWindowLoad && _windowLoaded) { + if (sm2.waitForWindowLoad && windowLoaded) { _wDS('docLoaded'); } // </d> - _initUserOnload(); + initUserOnload(); } } return result; @@ -5281,126 +5404,122 @@ /** * apply top-level setupOptions object as local properties, eg., this.setupOptions.flashVersion -> this.flashVersion (soundManager.flashVersion) * this maintains backward compatibility, and allows properties to be defined separately for use by soundManager.setup(). */ - _setProperties = function() { + setProperties = function() { var i, - o = _s.setupOptions; + o = sm2.setupOptions; for (i in o) { if (o.hasOwnProperty(i)) { // assign local property if not already defined - if (typeof _s[i] === 'undefined') { + if (sm2[i] === _undefined) { - _s[i] = o[i]; + sm2[i] = o[i]; - } else if (_s[i] !== o[i]) { + } else if (sm2[i] !== o[i]) { // legacy support: write manually-assigned property (eg., soundManager.url) back to setupOptions to keep things in sync - _s.setupOptions[i] = _s[i]; + sm2.setupOptions[i] = sm2[i]; } } } }; - _init = function() { + init = function() { - _wDS('init'); - // called after onload() - if (_didInit) { + if (didInit) { _wDS('didInit'); return false; } - function _cleanup() { - _event.remove(_win, 'load', _s.beginDelayedInit); + function cleanup() { + event.remove(window, 'load', sm2.beginDelayedInit); } - if (_s.html5Only) { - if (!_didInit) { + if (sm2.html5Only) { + if (!didInit) { // we don't need no steenking flash! - _cleanup(); - _s.enabled = true; - _initComplete(); + cleanup(); + sm2.enabled = true; + initComplete(); } return true; } // flash path - _initMovie(); + initMovie(); try { - _wDS('flashJS'); - // attempt to talk to Flash - _flash._externalInterfaceTest(false); + flash._externalInterfaceTest(false); // apply user-specified polling interval, OR, if "high performance" set, faster vs. default polling // (determines frequency of whileloading/whileplaying callbacks, effectively driving UI framerates) - _setPolling(true, (_s.flashPollingInterval || (_s.useHighPerformance ? 10 : 50))); + setPolling(true, (sm2.flashPollingInterval || (sm2.useHighPerformance ? 10 : 50))); - if (!_s.debugMode) { + if (!sm2.debugMode) { // stop the SWF from making debug output calls to JS - _flash._disableDebug(); + flash._disableDebug(); } - _s.enabled = true; - _debugTS('jstoflash', true); + sm2.enabled = true; + debugTS('jstoflash', true); - if (!_s.html5Only) { + if (!sm2.html5Only) { // prevent browser from showing cached page state (or rather, restoring "suspended" page state) via back button, because flash may be dead // http://www.webkit.org/blog/516/webkit-page-cache-ii-the-unload-event/ - _event.add(_win, 'unload', _doNothing); + event.add(window, 'unload', doNothing); } } catch(e) { - _s._wD('js/flash exception: ' + e.toString()); - _debugTS('jstoflash', false); - _catchError({type:'JS_TO_FLASH_EXCEPTION', fatal:true}); + sm2._wD('js/flash exception: ' + e.toString()); + debugTS('jstoflash', false); + catchError({type:'JS_TO_FLASH_EXCEPTION', fatal:true}); // don't disable, for reboot() - _failSafely(true); - _initComplete(); + failSafely(true); + initComplete(); return false; } - _initComplete(); + initComplete(); // disconnect events - _cleanup(); + cleanup(); return true; }; - _domContentLoaded = function() { + domContentLoaded = function() { - if (_didDCLoaded) { + if (didDCLoaded) { return false; } - _didDCLoaded = true; + didDCLoaded = true; // assign top-level soundManager properties eg. soundManager.url - _setProperties(); + setProperties(); - _initDebug(); + initDebug(); /** * Temporary feature: allow force of HTML5 via URL params: sm2-usehtml5audio=0 or 1 * Ditto for sm2-preferFlash, too. */ @@ -5409,114 +5528,154 @@ var a = 'sm2-usehtml5audio=', a2 = 'sm2-preferflash=', b = null, b2 = null, - hasCon = (typeof console !== 'undefined' && typeof console.log === 'function'), - l = _wl.toLowerCase(); + hasCon = (window.console !== _undefined && typeof console.log === 'function'), + l = wl.toLowerCase(); if (l.indexOf(a) !== -1) { b = (l.charAt(l.indexOf(a)+a.length) === '1'); if (hasCon) { console.log((b?'Enabling ':'Disabling ')+'useHTML5Audio via URL parameter'); } - _s.setup({ + sm2.setup({ 'useHTML5Audio': b }); } if (l.indexOf(a2) !== -1) { b2 = (l.charAt(l.indexOf(a2)+a2.length) === '1'); if (hasCon) { console.log((b2?'Enabling ':'Disabling ')+'preferFlash via URL parameter'); } - _s.setup({ + sm2.setup({ 'preferFlash': b2 }); } }()); // </d> - if (!_hasFlash && _s.hasHTML5) { - _s._wD('SoundManager: No Flash detected'+(!_s.useHTML5Audio?', enabling HTML5.':'. Trying HTML5-only mode.')); - _s.setup({ + if (!hasFlash && sm2.hasHTML5) { + sm2._wD('SoundManager: No Flash detected' + (!sm2.useHTML5Audio ? ', enabling HTML5.' : '. Trying HTML5-only mode.'), 1); + sm2.setup({ 'useHTML5Audio': true, // make sure we aren't preferring flash, either // TODO: preferFlash should not matter if flash is not installed. Currently, stuff breaks without the below tweak. 'preferFlash': false }); } - _testHTML5(); - _s.html5.usingFlash = _featureCheck(); - _needsFlash = _s.html5.usingFlash; - _showSupport(); + testHTML5(); + sm2.html5.usingFlash = featureCheck(); + needsFlash = sm2.html5.usingFlash; - if (!_hasFlash && _needsFlash) { - _wDS('needFlash'); + if (!hasFlash && needsFlash) { + messages.push(strings.needFlash); // TODO: Fatal here vs. timeout approach, etc. // hack: fail sooner. - _s.setup({ + sm2.setup({ 'flashLoadTimeout': 1 }); } - if (_doc.removeEventListener) { - _doc.removeEventListener('DOMContentLoaded', _domContentLoaded, false); + if (doc.removeEventListener) { + doc.removeEventListener('DOMContentLoaded', domContentLoaded, false); } - _initMovie(); + initMovie(); + return true; }; - _domContentLoadedIE = function() { + domContentLoadedIE = function() { - if (_doc.readyState === 'complete') { - _domContentLoaded(); - _doc.detachEvent('onreadystatechange', _domContentLoadedIE); + if (doc.readyState === 'complete') { + domContentLoaded(); + doc.detachEvent('onreadystatechange', domContentLoadedIE); } return true; }; - _winOnLoad = function() { - // catch edge case of _initComplete() firing after window.load() - _windowLoaded = true; - _event.remove(_win, 'load', _winOnLoad); + winOnLoad = function() { + + // catch edge case of initComplete() firing after window.load() + windowLoaded = true; + event.remove(window, 'load', winOnLoad); + }; + /** + * miscellaneous run-time, pre-init stuff + */ + + preInit = function() { + + if (mobileHTML5) { + + // prefer HTML5 for mobile + tablet-like devices, probably more reliable vs. flash at this point. + + // <d> + if (!sm2.setupOptions.useHTML5Audio || sm2.setupOptions.preferFlash) { + // notify that defaults are being changed. + messages.push(strings.mobileUA); + } + // </d> + + sm2.setupOptions.useHTML5Audio = true; + sm2.setupOptions.preferFlash = false; + + if (is_iDevice || (isAndroid && !ua.match(/android\s2\.3/i))) { + // iOS and Android devices tend to work better with a single audio instance, specifically for chained playback of sounds in sequence. + // common use case: exiting sound onfinish() -> createSound() -> play() + // <d> + messages.push(strings.globalHTML5); + // </d> + if (is_iDevice) { + sm2.ignoreFlash = true; + } + useGlobalHTML5Audio = true; + } + + } + + }; + + preInit(); + // sniff up-front - _detectFlash(); + detectFlash(); // focus and window load, init (primarily flash-driven) - _event.add(_win, 'focus', _handleFocus); - _event.add(_win, 'load', _delayWaitForEI); - _event.add(_win, 'load', _winOnLoad); + event.add(window, 'focus', handleFocus); + event.add(window, 'load', delayWaitForEI); + event.add(window, 'load', winOnLoad); - if (_doc.addEventListener) { + if (doc.addEventListener) { - _doc.addEventListener('DOMContentLoaded', _domContentLoaded, false); + doc.addEventListener('DOMContentLoaded', domContentLoaded, false); - } else if (_doc.attachEvent) { + } else if (doc.attachEvent) { - _doc.attachEvent('onreadystatechange', _domContentLoadedIE); + doc.attachEvent('onreadystatechange', domContentLoadedIE); } else { // no add/attachevent support - safe to assume no JS -> Flash either - _debugTS('onload', false); - _catchError({type:'NO_DOM2_EVENTS', fatal:true}); + debugTS('onload', false); + catchError({type:'NO_DOM2_EVENTS', fatal:true}); } } // SoundManager() // SM2_DEFER details: http://www.schillmania.com/projects/soundmanager2/doc/getstarted/#lazy-loading -if (typeof SM2_DEFER === 'undefined' || !SM2_DEFER) { +if (window.SM2_DEFER === undefined || !SM2_DEFER) { soundManager = new SoundManager(); } /** * SoundManager public interfaces \ No newline at end of file