vendor/assets/javascripts/soundmanager2.js in soundmanager-rails-1.0.0 vs vendor/assets/javascripts/soundmanager2.js in soundmanager-rails-1.0.1

- old
+ new

@@ -6,11 +6,11 @@ * * Copyright (c) 2007, Scott Schiller. All rights reserved. * Code provided under the BSD License: * http://schillmania.com/projects/soundmanager2/license.txt * - * V2.97a.20130512 + * V2.97a.20131201 */ /*global window, SM2_DEFER, sm2Debugger, console, document, navigator, setTimeout, setInterval, clearInterval, Audio, opera */ /*jslint regexp: true, sloppy: true, white: true, nomen: true, plusplus: true, todo: true */ @@ -71,11 +71,11 @@ 'wmode': null, // flash rendering mode - null, 'transparent', or 'opaque' (last two allow z-index to work) 'allowScriptAccess': 'always', // for scripting the SWF (object/embed property), 'always' or 'sameDomain' 'useFlashBlock': false, // *requires flashblock.css, see demos* - allow recovery from flash blockers. Wait indefinitely and apply timeout CSS to SWF, if applicable. 'useHTML5Audio': true, // use HTML5 Audio() where API is supported (most Safari, Chrome versions), Firefox (no MP3/MP4.) Ideally, transparent vs. Flash API where possible. 'html5Test': /^(probably|maybe)$/i, // HTML5 Audio() format support test. Use /^probably$/i; if you want to be more conservative. - 'preferFlash': true, // overrides useHTML5audio. if true and flash support present, will try to use flash for MP3/MP4 as needed since HTML5 audio support is still quirky in browsers. + 'preferFlash': false, // overrides useHTML5audio, will use Flash for MP3/MP4/AAC if present. Potential option if HTML5 playback with these formats is quirky. 'noSWFCache': false, // if true, appends ?ts={date} to break aggressive SWF caching. 'idPrefix': 'sound' // if an id is not provided to createSound(), this prefix is used for generated IDs - 'sound0', 'sound1' etc. }; @@ -187,11 +187,11 @@ this.debugID = 'soundmanager-debug'; this.debugURLParam = /([#?&])debug=1/i; // dynamic attributes - this.versionNumber = 'V2.97a.20130512'; + this.versionNumber = 'V2.97a.20131201'; this.version = null; this.movieURL = null; this.altURL = null; this.swfLoaded = false; this.enabled = false; @@ -263,17 +263,18 @@ /** * a few private internals (OK, a lot. :D) */ var SMSound, - 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 = [], + 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, rebootIntoHTML5, 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 = [], canIgnoreFlash, needsFlash = null, featureCheck, html5OK, html5CanPlay, html5Ext, html5Unload, domContentLoadedIE, testHTML5, event, slice = Array.prototype.slice, useGlobalHTML5Audio = false, lastGlobalHTML5URL, hasFlash, detectFlash, badSafariFix, html5_events, showSupport, flushMessages, wrapCallback, idCounter = 0, - 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)), isFirefox = (ua.match(/firefox/i)), + 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, msecScale = 1000, emptyURL = 'about:blank', // safe URL to unload, or load nothing from (flash 8 + most HTML5 UAs) + emptyWAV = 'data:audio/wave;base64,/UklGRiYAAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQIAAAD//w==', // tiny WAV for HTML5 unloading 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, // Flash v9.0r115+ "moviestar" formats @@ -1007,11 +1008,11 @@ result = html5CanPlay({type:sMIME}); } if (!result && needsFlash) { // if flash 9, test netStream (movieStar) types as well. - result = (sMIME && sm2.ok() ? !!((fV > 8 ? sMIME.match(netStreamMimeTypes) : null) || sMIME.match(sm2.mimePattern)) : null); + result = (sMIME && sm2.ok() ? !!((fV > 8 ? sMIME.match(netStreamMimeTypes) : null) || sMIME.match(sm2.mimePattern)) : null); // TODO: make less "weird" (per JSLint) } return result; }; @@ -1610,14 +1611,10 @@ // reference: http://msdn.microsoft.com/en-us/library/ie/ff974759%28v=vs.85%29.aspx s._a.preload = 'auto'; s._a._called_load = true; - if (instanceOptions.autoPlay) { - s.play(); - } - } else { sm2._wD(s.id + ': Ignoring request to load again'); } @@ -1927,12 +1924,12 @@ // this hasn't been loaded yet. load it first, and then do this again. sm2._wD(fN + 'Beginning load for from/to case'); s.load({ - // TODO: was _oncanplay. Sounds wrong. - oncanplay: onready + // note: custom HTML5-only event added for from/to implementation. + _oncanplay: onready }); exit = false; } else if (!s.isHTML5 && !s.loaded && (!s.readyState || s.readyState !== 2)) { @@ -2024,11 +2021,11 @@ sm2._wD(s.id + ': Cloning Audio() for instance #' + s.instanceCount + '...'); audioClone = new Audio(s._iO.url); onended = function() { - event.remove(audioClone, 'onended', onended); + event.remove(audioClone, 'ended', onended); s._onfinish(s); // cleanup html5Unload(audioClone); audioClone = null; }; @@ -2043,10 +2040,20 @@ audioClone.play(); }; event.add(audioClone, 'ended', onended); + // apply volume to clones, too + if (s._iO.volume !== undefined) { + audioClone.volume = Math.max(0, Math.min(1, s._iO.volume/100)); + } + + // playing multiple muted sounds? if you do this, you're weird ;) - but let's cover it. + if (s.muted) { + audioClone.muted = true; + } + if (s._iO.position) { // HTML5 audio can't seek before onplay() event has fired. // wait for canplay, then seek to position and start playback. event.add(audioClone, 'canplay', oncanplay); } else { @@ -2429,10 +2436,14 @@ } if (!s.isHTML5) { flash._setVolume(s.id, (sm2.muted && !s.muted) || s.muted?0:nVol); } else if (s._a) { + if (sm2.muted && !s.muted) { + s.muted = true; + s._a.muted = true; + } // valid range: 0-1 s._a.volume = Math.max(0, Math.min(1, nVol/100)); } s._iO.volume = nVol; @@ -2563,24 +2574,25 @@ }; this._processOnPosition = function() { var i, item, j = onPositionItems.length; - + if (!j || !s.playState || onPositionFired >= j) { return false; } for (i=j-1; i >= 0; i--) { item = onPositionItems[i]; if (!item.fired && s.position >= item.position) { item.fired = true; onPositionFired++; item.method.apply(item.scope, [item.position]); + j = onPositionItems.length; // reset j -- onPositionItems.length can be changed in the item callback above... occasionally breaking the loop. } } - + return true; }; this._resetOnPosition = function(nPosition) { @@ -2666,11 +2678,11 @@ if (op) { for (item in op) { if (op.hasOwnProperty(item)) { - s.onPosition(parseInt(item, 10), op[item]); + s.onPosition(parseInt(item, 10), op[item]); } } } @@ -2909,13 +2921,15 @@ } if (!sameURL) { - // don't retain onPosition() stuff with new URL. + // don't retain onPosition() stuff with new URLs. - resetProperties(false); + if (lastURL) { + resetProperties(false); + } // assign new HTML5 URL a.src = instanceOptions.url; @@ -2932,10 +2946,11 @@ } else { if (instanceOptions.autoLoad || instanceOptions.autoPlay) { s._a = new Audio(instanceOptions.url); + s._a.load(); } else { // null for stupid Opera 9.64 case s._a = (isOpera && opera.version() < 10 ? new Audio(null) : new Audio()); @@ -3273,11 +3288,11 @@ this._oncaptiondata = function(oData) { /** * internal: flash 9 + NetStream (MovieStar/RTMP-only) feature - * + * * @param {object} oData */ sm2._wD(s.id + ': Caption data received.'); @@ -3292,11 +3307,11 @@ this._onmetadata = function(oMDProps, oMDData) { /** * internal: flash 9 + NetStream (MovieStar/RTMP-only) feature * RTMP may include song title, MovieStar content may include encoding info - * + * * @param {array} oMDProps (names) * @param {array} oMDData (values) */ sm2._wD(s.id + ': Metadata received.'); @@ -3317,11 +3332,11 @@ this._onid3 = function(oID3Props, oID3Data) { /** * internal: flash 8 + flash 9 ID3 feature * may include artist, song title etc. - * + * * @param {array} oID3Props (names) * @param {array} oID3Data (values) */ sm2._wD(s.id + ': ID3 data received.'); @@ -3392,11 +3407,11 @@ * ------------------------------ */ getDocument = function() { - return (doc.body || doc._docElement || doc.getElementsByTagName('div')[0]); + return (doc.body || doc.getElementsByTagName('div')[0]); }; id = function(sID) { @@ -3849,11 +3864,11 @@ progress: html5_event(function(e) { // note: can fire repeatedly after "loaded" event, due to use of HTTP range/partials var s = this._s, - i, j, str, buffered = 0, + i, j, progStr, 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); @@ -3881,16 +3896,16 @@ // linear case, buffer sum; does not account for seeking and HTTP partials / byte ranges loaded = Math.min(1, buffered/(e.target.duration*msecScale)); // <d> if (isProgress && ranges.length > 1) { - str = []; + progStr = []; j = ranges.length; for (i=0; i<j; i++) { - str.push(e.target.buffered.start(i)*msecScale +'-'+ e.target.buffered.end(i)*msecScale); + progStr.push(e.target.buffered.start(i)*msecScale +'-'+ e.target.buffered.end(i)*msecScale); } - sm2._wD(this._s.id + ': progress, timeRanges: ' + str.join(', ')); + sm2._wD(this._s.id + ': progress, timeRanges: ' + progStr.join(', ')); } if (isProgress && !isNaN(loaded)) { sm2._wD(this._s.id + ': progress, ' + Math.floor(loaded*100) + '% loaded'); } @@ -3995,12 +4010,13 @@ var url; if (oAudio) { - // Firefox likes '' for unload (used to work, but no longer?) - however, may request hosting page URL (bad.) Most other UAs dislike '' and fail to unload. - url = (isSafari && !is_iDevice ? null : (isFirefox ? emptyURL : null)); + // Firefox and Chrome accept short WAVe data: URIs. Chome dislikes audio/wav, but accepts audio/wav for data: MIME. + // Desktop Safari complains / fails on data: URI, so it gets about:blank. + url = (isSafari ? emptyURL : (sm2.html5.canPlayType('audio/wav') ? emptyWAV : emptyURL)); oAudio.src = url; // reset some state, too if (oAudio._called_unload !== undefined) { @@ -4111,11 +4127,11 @@ var a = (Audio !== _undefined ? (isOpera && opera.version() < 10 ? new Audio(null) : new Audio()) : null), item, lookup, support = {}, aF, i; function cp(m) { - var canPlay, i, j, + var canPlay, j, result = false, isOK = false; if (!a || typeof a.canPlayType !== 'function') { return result; @@ -4255,24 +4271,29 @@ // internal string replace helper. // arguments: o [,items to replace] // <d> + var args, + i, j, o, + sstr; + // real array, please - var args = slice.call(arguments), + args = slice.call(arguments); - // first arg - o = args.shift(), + // first argument + o = args.shift(); - str = (strings && strings[o]?strings[o]:''), i, j; - if (str && args && args.length) { + sstr = (strings && strings[o] ? strings[o] : ''); + + if (sstr && args && args.length) { for (i = 0, j = args.length; i < j; i++) { - str = str.replace('%s', args[i]); + sstr = sstr.replace('%s', args[i]); } } - return str; + return sstr; // </d> }; loopFix = function(sOpt) { @@ -4887,11 +4908,11 @@ if (!mobileHTML5 && sm2.html5PollingInterval) { if (h5IntervalTimer === null && h5TimerCount === 0) { h5IntervalTimer = setInterval(timerExecute, sm2.html5PollingInterval); - + } h5TimerCount++; } @@ -5087,11 +5108,14 @@ function initMsg() { // <d> - var options = [], title, str = [], delimiter = ' + '; + var options = [], + title, + msg = [], + delimiter = ' + '; title = 'SoundManager ' + sm2.version + (!sm2.html5Only && sm2.useHTML5Audio ? (sm2.hasHTML5 ? ' + HTML5 audio' : ', no HTML5 audio support') : ''); if (!sm2.html5Only) { @@ -5130,14 +5154,14 @@ } } if (options.length) { - str = str.concat([options.join(delimiter)]); + msg = msg.concat([options.join(delimiter)]); } - sm2._wD(title + (str.length ? delimiter + str.join(', ') : ''), 1); + sm2._wD(title + (msg.length ? delimiter + msg.join(', ') : ''), 1); showSupport(); // </d> @@ -5402,10 +5426,31 @@ setTimeout(waitForEI, 1000); }; + rebootIntoHTML5 = function() { + + // special case: try for a reboot with preferFlash: false, if 100% HTML5 mode is possible and useFlashBlock is not enabled. + + window.setTimeout(function() { + + complain(smc + 'useFlashBlock is false, 100% HTML5 mode is possible. Rebooting with preferFlash: false...'); + + sm2.setup({ + preferFlash: false + }).reboot(); + + // if for some reason you want to detect this case, use an ontimeout() callback and look for html5Only and didFlashBlock == true. + sm2.didFlashBlock = true; + + sm2.beginDelayedInit(); + + }, 1); + + }; + waitForEI = function() { var p, loadIncomplete = false; @@ -5419,11 +5464,11 @@ } waitingForEI = true; event.remove(window, 'load', delayWaitForEI); - if (tryInitOnFocus && !isFocused) { + if (hasFlash && tryInitOnFocus && !isFocused) { // Safari won't load flash in background tabs, only when focused. _wDS('waitFocus'); return false; } @@ -5446,32 +5491,42 @@ return false; } // <d> 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 (!sm2.debugFlash) { _wDS('tryDebug', 2); } + } + if (p === 0) { + // if 0 (not null), probably a 404. sm2._wD(str('swf404', sm2.url), 1); + } + 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 (p === null) { - // SWF failed. Maybe blocked. + // SWF failed to report load progress. Possibly blocked. if (sm2.useFlashBlock || sm2.flashLoadTimeout === 0) { if (sm2.useFlashBlock) { @@ -5485,53 +5540,47 @@ // no custom flash block handling, but SWF has timed out. Will recover if user unblocks / allows SWF load. if (!sm2.useFlashBlock && canIgnoreFlash) { - // special case: try for a reboot with preferFlash: false, if 100% HTML5 mode is possible and useFlashBlock is not enabled. + rebootIntoHTML5(); - window.setTimeout(function() { - - complain(smc + 'useFlashBlock is false, 100% HTML5 mode is possible. Rebooting with preferFlash: false...'); - - sm2.setup({ - preferFlash: false - }).reboot(); - - // if for some reason you want to detect this case, use an ontimeout() callback and look for html5Only and didFlashBlock == true. - sm2.didFlashBlock = true; - - sm2.beginDelayedInit(); - - }, 1); - } else { _wDS('waitForever'); // fire any regular registered ontimeout() listeners. - processOnEvents({type:'ontimeout', ignoreInit: true}); + processOnEvents({type:'ontimeout', ignoreInit: true, error: {type: 'INIT_FLASHBLOCK'}}); } } } else { - // flash loaded? Shouldn't be a blocking issue, then. + // SWF loaded? Shouldn't be a blocking issue, then. if (sm2.flashLoadTimeout === 0) { _wDS('waitForever'); } else { - failSafely(true); + if (!sm2.useFlashBlock && canIgnoreFlash) { + rebootIntoHTML5(); + + } else { + + failSafely(true); + + } + } } + } }, sm2.flashLoadTimeout); }; @@ -5617,15 +5666,14 @@ result = true, error; if (!wasTimeout) { didInit = true; - if (disabled) { - error = {type: (!hasFlash && needsFlash ? 'NO_FLASH' : 'INIT_TIMEOUT')}; - } } + error = {type: (!hasFlash && needsFlash ? 'NO_FLASH' : 'INIT_TIMEOUT')}; + sm2._wD('SoundManager 2 ' + (disabled ? 'failed to load' : 'loaded') + ' (' + (disabled ? 'Flash security/load error' : 'OK') + ')', disabled ? 2: 1); if (disabled || bNoDisable) { if (sm2.useFlashBlock && sm2.oMC) { sm2.oMC.className = getSWFCSS() + ' ' + (sm2.getMoviePercent() === null?swfCSS.swfTimedout:swfCSS.swfError); @@ -5781,11 +5829,11 @@ // <d> (function(){ var a = 'sm2-usehtml5audio=', a2 = 'sm2-preferflash=', - b = null, + b = null, b2 = null, l = wl.toLowerCase(); if (l.indexOf(a) !== -1) { b = (l.charAt(l.indexOf(a)+a.length) === '1'); @@ -5809,11 +5857,11 @@ }()); // </d> if (!hasFlash && sm2.hasHTML5) { - sm2._wD('SoundManager: No Flash detected' + (!sm2.useHTML5Audio ? ', enabling HTML5.' : '. Trying HTML5-only mode.'), 1); + sm2._wD('SoundManager 2: 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 @@ -5936,6 +5984,6 @@ */ window.SoundManager = SoundManager; // constructor window.soundManager = soundManager; // public API, flash callbacks etc. -}(window)); \ No newline at end of file +}(window));