SoundManager2-2.97a.20150601/000077500000000000000000000000001253275212400151615ustar00rootroot00000000000000SoundManager2-2.97a.20150601/.gitignore000066400000000000000000000001171253275212400171500ustar00rootroot00000000000000.DS_Store build.sh make-docs.sh make-rollups.sh tmp .build.properties download SoundManager2-2.97a.20150601/README.rdoc000066400000000000000000000062251253275212400167740ustar00rootroot00000000000000== SoundManager 2: JavaScript Sound for the Web By wrapping and extending HTML5 and Flash Audio APIs, SoundManager 2 brings reliable cross-platform audio to JavaScript. == HTML5 Audio() Support * 100% Flash-free MP3 + MP4/AAC where supported * Compatible with Apple iPad 3.2, iPhone/iOS 4 and newer * Fallback to Flash for MP3/MP4 support, as needed * SM2 API is transparent; HTML5/flash switching handled internally * HTML5 API support approximates Flash 8 API features * Some other formats (WAV/OGG) supported via HTML5, depending on browser * See "useHTML5Audio" property for implementation details == Basic API Features (Flash 8) * Load, stop, play, pause, mute, seek, pan and volume control of sounds from Javascript * Events: onload, whileloading, whileplaying, onfinish and more * ID3V1 and ID3V2 tag support for MP3s (title, artist, genre etc.) == Shiny Flash 9 Features * RTMP / Flash Media Server streaming support * MPEG-4 (AAC, HE-AAC, H.264) audio support * "MultiShot" play (layered/chorusing effects) * Waveform/frequency spectrum data * Peak (L/R channel volume) data * Audio buffering state/event handling == General Tech Stuff * Full API Documentation with examples and notes * console.log()-style debug output and troubleshooting tools * Community-based discussion/support == As Heard On The Internets A few nifty sites that have implemented SM2 for driving audio: * Tidal * Beats * Songza * Earbits * freesound.org * last.fm * 8tracks * Discogs * The Hype Machine * nyan.cat == Project home, documentation, live demos etc.: http://www.schillmania.com/projects/soundmanager2/ == Compiling JS builds (-nodebug, -jsmin) and Flash components, AS2/AS3 to SWF An Ant build file defines the tasks for compiling JS and SWF components, useful if you make changes to the SM2 source and want to recompile. Google's Closure Compiler is used for the JS. AS2 compilation is done by MTASC, and AS3 is handled by Adobe's Open Source Flex SDK (mxmlc) compiler. Refer to the build.xml file for compiler downloads and path definitions. == Versioning / Development Notes Releases are versioned by date, e.g., V2.97a.20110424 and are tagged as such.* The latest official release is always on trunk/master. Post-release development builds may be available on the appropriate +DEV branch, eg., V2.97a.20110801+DEV == Forks and Pull Requests Firstly, thank you for wanting to contribute! Bug fixes and tweaks are welcomed, particularly if they follow the general coding style of the project. If making a pull request, use the project's current +DEV development branch as the merge target instead of "master", if possible (please and thank-you.) * SoundManager 2 has been at "version" 2.97 for a long time, because 2.97 was arguably the best llama-ass-whipping version of WinAmp. (WinAmp 3 was not as good, and WinAmp 5 was "the best of 2 and 3 combined.") This MP3 player was my favourite Windows app during the 90's, and is missed as there's nothing quite like it on OS X where I spend most of my time these days. SoundManager2-2.97a.20150601/bower.json000066400000000000000000000006211253275212400171710ustar00rootroot00000000000000{ "name": "SoundManager2", "version": "2.97.20150601", "homepage": "https://github.com/scottschiller/SoundManager2", "main": "script/soundmanager2.js", "ignore": [ "**/.*", "node_modules", "bower_components", "demo", "doc", "test", "tests", "tasks", "component.json", "composer.json", "CONTRIBUTING.md", "package.js", "package.json" ] } SoundManager2-2.97a.20150601/build.xml000066400000000000000000000313271253275212400170100ustar00rootroot00000000000000 ${closure-compiler.jar} Removing existing JS builds... Compressing minified JS... /** /** @license Making no-debug JS... this.debugMode = true; this.debugMode = false; Compressing no-debug JS... Retrieved SM2 license: ${sm2_license} soundmanager2.js sizes (full, -jsmin, -nodebug, -nodebug-jsmin): Building debug version, Flash 8/AS2... Building debug version, Flash 9/AS3... Making no-debug .AS... var debugEnabled = true; var debugEnabled = false; writeDebug( // flashDebug( // <d> /* // </d> */ writeDebug( // writeDebug( public var debugEnabled: Boolean = true; public var debugEnabled: Boolean = false; flashDebug( // flashDebug( // <d> /* // </d> */ writeDebug( // writeDebug( flashDebug( // flashDebug( writeDebug( // writeDebug( // <d> /* // </d> */ Making cross-domain .AS.. Building no-debug SWFs... Building debug cross-domain SWFs... Building no-debug cross-domain SWFs... SoundManager2-2.97a.20150601/component.json000077500000000000000000000010711253275212400200600ustar00rootroot00000000000000{ "name": "soundmanager2", "repo": "scottschiller/soundmanager2", "description": "A JavaScript Sound API supporting MP3, MPEG4 and HTML5 audio + RTMP, providing reliable cross-browser/platform audio control in as little as 11 KB.", "version": "V2.97a.20150601-nodebug-jsmin", "keywords": [ "audio", "soundmanager2", "soundmanager" ], "license": "BSD", "scripts": [ "script/soundmanager2-nodebug-jsmin.js" ], "main": "script/soundmanager2-nodebug-jsmin.js", "files":[ "swf/soundmanager2.swf", "swf/soundmanager2_flash9.swf" ] }SoundManager2-2.97a.20150601/license.txt000066400000000000000000000030731253275212400173470ustar00rootroot00000000000000Software License Agreement (BSD License) Copyright (c) 2007, Scott Schiller (schillmania.com) All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of schillmania.com nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission from schillmania.com. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.SoundManager2-2.97a.20150601/package.json000066400000000000000000000015661253275212400174570ustar00rootroot00000000000000{ "name": "SoundManager2", "version": "2.97.20150601", "description": "A JavaScript Sound API supporting MP3, MPEG4 and HTML5 audio + RTMP, providing reliable cross-browser/platform audio control in as little as 11 KB.", "main": "script/soundmanager2.js", "directories": { "lib": "./script" }, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { "type": "git", "url": "https://github.com/scottschiller/SoundManager2/" }, "keywords": [ "browser", "audio", "sound", "mp3", "mpeg4", "html5" ], "author": { "name": "Scott Schiller", "email": "spam@schillmania.com", "url": "http://schillmania.com/" }, "license": "BSD", "bugs": { "url": "https://github.com/scottschiller/SoundManager2/issues/" }, "homepage": "http://www.schillmania.com/projects/soundmanager2/" } SoundManager2-2.97a.20150601/script/000077500000000000000000000000001253275212400164655ustar00rootroot00000000000000SoundManager2-2.97a.20150601/script/soundmanager2-jsmin.js000066400000000000000000001436701253275212400227210ustar00rootroot00000000000000/** @license SoundManager 2: JavaScript Sound for the Web ---------------------------------------------- http://schillmania.com/projects/soundmanager2/ Copyright (c) 2007, Scott Schiller. All rights reserved. Code provided under the BSD License: http://schillmania.com/projects/soundmanager2/license.txt V2.97a.20150601 */ (function(h,g){function K(sb,K){function ha(b){return c.preferFlash&&H&&!c.ignoreFlash&&c.flash[b]!==g&&c.flash[b]}function r(b){return function(d){var e=this._s;e&&e._a?d=b.call(this,d):(e&&e.id?c._wD(e.id+": Ignoring "+d.type):c._wD("HTML5::Ignoring "+d.type),d=null);return d}}this.setupOptions={url:sb||null,flashVersion:8,debugMode:!0,debugFlash:!1,useConsole:!0,consoleOnly:!0,waitForWindowLoad:!1,bgColor:"#ffffff",useHighPerformance:!1,flashPollingInterval:null,html5PollingInterval:null,flashLoadTimeout:1E3, wmode:null,allowScriptAccess:"always",useFlashBlock:!1,useHTML5Audio:!0,forceUseGlobalHTML5Audio:!1,ignoreMobileRestrictions:!1,html5Test:/^(probably|maybe)$/i,preferFlash:!1,noSWFCache:!1,idPrefix:"sound"};this.defaultOptions={autoLoad:!1,autoPlay:!1,from:null,loops:1,onid3:null,onload:null,whileloading:null,onplay:null,onpause:null,onresume:null,whileplaying:null,onposition:null,onstop:null,onfailure:null,onfinish:null,multiShot:!0,multiShotEvents:!1,position:null,pan:0,stream:!0,to:null,type:null, usePolicyFile:!1,volume:100};this.flash9Options={isMovieStar:null,usePeakData:!1,useWaveformData:!1,useEQData:!1,onbufferchange:null,ondataerror:null};this.movieStarOptions={bufferTime:3,serverURL:null,onconnect:null,duration:null};this.audioFormats={mp3:{type:['audio/mpeg; codecs="mp3"',"audio/mpeg","audio/mp3","audio/MPA","audio/mpa-robust"],required:!0},mp4:{related:["aac","m4a","m4b"],type:['audio/mp4; codecs="mp4a.40.2"',"audio/aac","audio/x-m4a","audio/MP4A-LATM","audio/mpeg4-generic"],required:!1}, ogg:{type:["audio/ogg; codecs=vorbis"],required:!1},opus:{type:["audio/ogg; codecs=opus","audio/opus"],required:!1},wav:{type:['audio/wav; codecs="1"',"audio/wav","audio/wave","audio/x-wav"],required:!1}};this.movieID="sm2-container";this.id=K||"sm2movie";this.debugID="soundmanager-debug";this.debugURLParam=/([#?&])debug=1/i;this.versionNumber="V2.97a.20150601";this.altURL=this.movieURL=this.version=null;this.enabled=this.swfLoaded=!1;this.oMC=null;this.sounds={};this.soundIDs=[];this.didFlashBlock= this.muted=!1;this.filePattern=null;this.filePatterns={flash8:/\.mp3(\?.*)?$/i,flash9:/\.mp3(\?.*)?$/i};this.features={buffering:!1,peakData:!1,waveformData:!1,eqData:!1,movieStar:!1};this.sandbox={type:null,types:{remote:"remote (domain-based) rules",localWithFile:"local with file access (no internet access)",localWithNetwork:"local with network (internet access only, no local access)",localTrusted:"local, trusted (local+internet access)"},description:null,noRemote:null,noLocal:null};this.html5= {usingFlash:null};this.flash={};this.ignoreFlash=this.html5Only=!1;var W,c=this,Xa=null,l=null,z,v=navigator.userAgent,ia=h.location.href.toString(),m=document,xa,Ya,ya,n,I=[],za=!0,D,X=!1,Y=!1,q=!1,y=!1,ja=!1,p,tb=0,Z,A,Aa,R,Ba,O,S,T,Za,Ca,Da,ka,F,la,P,Ea,aa,ma,na,U,$a,Fa,ab=["log","info","warn","error"],bb,Ga,cb,ba=null,Ha=null,t,Ia,V,db,oa,pa,L,w,ca=!1,Ja=!1,eb,fb,gb,qa=0,da=null,ra,Q=[],ea,u=null,hb,sa,fa,M,ta,Ka,ib,x,jb=Array.prototype.slice,C=!1,La,H,Ma,kb,J,lb,Na,ua,mb=0,Oa,Pa=v.match(/(ipad|iphone|ipod)/i), nb=v.match(/android/i),N=v.match(/msie/i),ub=v.match(/webkit/i),va=v.match(/safari/i)&&!v.match(/chrome/i),Qa=v.match(/opera/i),Ra=v.match(/(mobile|pre\/|xoom)/i)||Pa||nb,Sa=!ia.match(/usehtml5audio/i)&&!ia.match(/sm2\-ignorebadua/i)&&va&&!v.match(/silk/i)&&v.match(/OS X 10_6_([3-7])/i),Ta=h.console!==g&&console.log!==g,Ua=m.hasFocus!==g?m.hasFocus():null,wa=va&&(m.hasFocus===g||!m.hasFocus()),ob=!wa,pb=/(mp3|mp4|mpa|m4a|m4b)/i,ga=m.location?m.location.protocol.match(/http/i):null,vb=ga?"":"http://", qb=/^\s*audio\/(?:x-)?(?:mpeg4|aac|flv|mov|mp4||m4v|m4a|m4b|mp4v|3gp|3g2)\s*(?:$|;)/i,rb="mpeg4 aac flv mov mp4 m4v f4v m4a m4b mp4v 3gp 3g2".split(" "),wb=new RegExp("\\.("+rb.join("|")+")(\\?.*)?$","i");this.mimePattern=/^\s*audio\/(?:x-)?(?:mp(?:eg|3))\s*(?:$|;)/i;this.useAltURL=!ga;var Va;try{Va=Audio!==g&&(Qa&&opera!==g&&10>opera.version()?new Audio(null):new Audio).canPlayType!==g}catch(xb){Va=!1}this.hasHTML5=Va;this.setup=function(b){var d=!c.url;b!==g&&q&&u&&c.ok()&&(b.flashVersion!==g|| b.url!==g||b.html5Test!==g)&&L(t("setupLate"));Aa(b);c.setupOptions.useHTML5Audio&&!C&&c.setupOptions.forceUseGlobalHTML5Audio&&(Q.push(F.globalHTML5),C=!0);!Oa&&Ra&&(c.setupOptions.ignoreMobileRestrictions&&Q.push(F.ignoreMobile),c.setupOptions.useHTML5Audio&&!c.setupOptions.preferFlash||c._wD(F.mobileUA),c.setupOptions.useHTML5Audio=!0,c.setupOptions.preferFlash=!1,nb&&!v.match(/android\s2\.3/i)&&(c._wD(F.globalHTML5),Pa&&(c.ignoreFlash=!0),C=!0));b&&(d&&aa&&b.url!==g&&c.beginDelayedInit(),aa|| b.url===g||"complete"!==m.readyState||setTimeout(P,1));Oa=!0;return c};this.supported=this.ok=function(){return u?q&&!y:c.useHTML5Audio&&c.hasHTML5};this.getMovie=function(c){return z(c)||m[c]||h[c]};this.createSound=function(b,d){function e(){f=oa(f);c.sounds[f.id]=new W(f);c.soundIDs.push(f.id);return c.sounds[f.id]}var a,f;a=null;a="soundManager.createSound(): "+t(q?"notOK":"notReady");if(!q||!c.ok())return L(a),!1;d!==g&&(b={id:b,url:d});f=A(b);f.url=ra(f.url);f.id===g&&(f.id=c.setupOptions.idPrefix+ mb++);f.id.toString().charAt(0).match(/^[0-9]$/)&&c._wD("soundManager.createSound(): "+t("badID",f.id),2);c._wD("soundManager.createSound(): "+f.id+(f.url?" ("+f.url+")":""),1);if(w(f.id,!0))return c._wD("soundManager.createSound(): "+f.id+" exists",1),c.sounds[f.id];if(sa(f))a=e(),c.html5Only||c._wD(f.id+": Using HTML5"),a._setup_html5(f);else{if(c.html5Only)return c._wD(f.id+": No HTML5 support for this sound, and no Flash. Exiting."),e();if(c.html5.usingFlash&&f.url&&f.url.match(/data\:/i))return c._wD(f.id+ ": data: URIs not supported via Flash. Exiting."),e();8a.instanceCount?(m(),e=a._setup_html5(),a.setPosition(a._iO.position),e.play()):(c._wD(a.id+": Cloning Audio() for instance #"+ a.instanceCount+"..."),k=new Audio(a._iO.url),G=function(){x.remove(k,"ended",G);a._onfinish(a);ta(k);k=null},h=function(){x.remove(k,"canplay",h);try{k.currentTime=a._iO.position/1E3}catch(c){L(a.id+": multiShot play() failed to apply position of "+a._iO.position/1E3)}k.play()},x.add(k,"ended",G),a._iO.volume!==g&&(k.volume=Math.max(0,Math.min(1,a._iO.volume/100))),a.muted&&(k.muted=!0),a._iO.position?x.add(k,"canplay",h):k.play()):(B=l._start(a.id,a._iO.loops||1,9===n?a.position:a.position/1E3, a._iO.multiShot||!1),9!==n||B||(c._wD(e+"No sound hardware, or 32-sound ceiling hit",2),a._iO.onplayerror&&a._iO.onplayerror.apply(a)))}return a};this.stop=function(b){var d=a._iO;1===a.playState&&(c._wD(a.id+": stop()"),a._onbufferchange(0),a._resetOnPosition(0),a.paused=!1,a.isHTML5||(a.playState=0),Wa(),d.to&&a.clearOnPosition(d.to),a.isHTML5?a._a&&(b=a.position,a.setPosition(0),a.position=b,a._a.pause(),a.playState=0,a._onTimer(),G()):(l._stop(a.id,b),d.serverURL&&a.unload()),a.instanceCount= 0,a._iO={},d.onstop&&d.onstop.apply(a));return a};this.setAutoPlay=function(b){c._wD(a.id+": Autoplay turned "+(b?"on":"off"));a._iO.autoPlay=b;a.isHTML5||(l._setAutoPlay(a.id,b),b&&!a.instanceCount&&1===a.readyState&&(a.instanceCount++,c._wD(a.id+": Incremented instance count to "+a.instanceCount)))};this.getAutoPlay=function(){return a._iO.autoPlay};this.setPosition=function(b){b===g&&(b=0);var d=a.isHTML5?Math.max(b,0):Math.min(a.duration||a._iO.duration,Math.max(b,0));a.position=d;b=a.position/ 1E3;a._resetOnPosition(a.position);a._iO.position=d;if(!a.isHTML5)b=9===n?a.position:b,a.readyState&&2!==a.readyState&&l._setPosition(a.id,b,a.paused||!a.playState,a._iO.multiShot);else if(a._a){if(a._html5_canplay){if(a._a.currentTime!==b){c._wD(a.id+": setPosition("+b+")");try{a._a.currentTime=b,(0===a.playState||a.paused)&&a._a.pause()}catch(e){c._wD(a.id+": setPosition("+b+") failed: "+e.message,2)}}}else if(b)return c._wD(a.id+": setPosition("+b+"): Cannot seek yet, sound not ready",2),a;a.paused&& a._onTimer(!0)}return a};this.pause=function(b){if(a.paused||0===a.playState&&1!==a.readyState)return a;c._wD(a.id+": pause()");a.paused=!0;a.isHTML5?(a._setup_html5().pause(),G()):(b||b===g)&&l._pause(a.id,a._iO.multiShot);a._iO.onpause&&a._iO.onpause.apply(a);return a};this.resume=function(){var b=a._iO;if(!a.paused)return a;c._wD(a.id+": resume()");a.paused=!1;a.playState=1;a.isHTML5?(a._setup_html5().play(),m()):(b.isMovieStar&&!b.serverURL&&a.setPosition(a.position),l._pause(a.id,b.multiShot)); !r&&b.onplay?(b.onplay.apply(a),r=!0):b.onresume&&b.onresume.apply(a);return a};this.togglePause=function(){c._wD(a.id+": togglePause()");if(0===a.playState)return a.play({position:9!==n||a.isHTML5?a.position/1E3:a.position}),a;a.paused?a.resume():a.pause();return a};this.setPan=function(c,b){c===g&&(c=0);b===g&&(b=!1);a.isHTML5||l._setPan(a.id,c);a._iO.pan=c;b||(a.pan=c,a.options.pan=c);return a};this.setVolume=function(b,d){b===g&&(b=100);d===g&&(d=!1);a.isHTML5?a._a&&(c.muted&&!a.muted&&(a.muted= !0,a._a.muted=!0),a._a.volume=Math.max(0,Math.min(1,b/100))):l._setVolume(a.id,c.muted&&!a.muted||a.muted?0:b);a._iO.volume=b;d||(a.volume=b,a.options.volume=b);return a};this.mute=function(){a.muted=!0;a.isHTML5?a._a&&(a._a.muted=!0):l._setVolume(a.id,0);return a};this.unmute=function(){a.muted=!1;var b=a._iO.volume!==g;a.isHTML5?a._a&&(a._a.muted=!1):l._setVolume(a.id,b?a._iO.volume:a.options.volume);return a};this.toggleMute=function(){return a.muted?a.unmute():a.mute()};this.onposition=this.onPosition= function(b,c,d){E.push({position:parseInt(b,10),method:c,scope:d!==g?d:a,fired:!1});return a};this.clearOnPosition=function(a,b){var c;a=parseInt(a,10);if(isNaN(a))return!1;for(c=0;c=b)return!1;for(--b;0<=b;b--)c=E[b],!c.fired&&a.position>=c.position&&(c.fired=!0,v++,c.method.apply(c.scope,[c.position]));return!0};this._resetOnPosition= function(a){var b,c;b=E.length;if(!b)return!1;for(--b;0<=b;b--)c=E[b],c.fired&&a<=c.position&&(c.fired=!1,v--);return!0};y=function(){var b=a._iO,d=b.from,e=b.to,f,g;g=function(){c._wD(a.id+': "To" time of '+e+" reached.");a.clearOnPosition(e,g);a.stop()};f=function(){c._wD(a.id+': Playing "from" '+d);if(null!==e&&!isNaN(e))a.onPosition(e,g)};null===d||isNaN(d)||(b.position=d,b.multiShot=!1,f());return b};q=function(){var b,c=a._iO.onposition;if(c)for(b in c)if(c.hasOwnProperty(b))a.onPosition(parseInt(b, 10),c[b])};Wa=function(){var b,c=a._iO.onposition;if(c)for(b in c)c.hasOwnProperty(b)&&a.clearOnPosition(parseInt(b,10))};m=function(){a.isHTML5&&eb(a)};G=function(){a.isHTML5&&fb(a)};f=function(b){b||(E=[],v=0);r=!1;a._hasTimer=null;a._a=null;a._html5_canplay=!1;a.bytesLoaded=null;a.bytesTotal=null;a.duration=a._iO&&a._iO.duration?a._iO.duration:null;a.durationEstimate=null;a.buffered=[];a.eqData=[];a.eqData.left=[];a.eqData.right=[];a.failures=0;a.isBuffering=!1;a.instanceOptions={};a.instanceCount= 0;a.loaded=!1;a.metadata={};a.readyState=0;a.muted=!1;a.paused=!1;a.peakData={left:0,right:0};a.waveformData={left:[],right:[]};a.playState=0;a.position=null;a.id3={}};f();this._onTimer=function(b){var c,f=!1,g={};if(a._hasTimer||b)return a._a&&(b||(0opera.version()?new Audio(null):new Audio,c=a._a,c._called_load=!1,C&&(Xa=c);a.isHTML5=!0;a._a=c;c._s=a;h();a._apply_loop(c,b.loops);b.autoLoad||b.autoPlay?a.load():(c.autobuffer=!1,c.preload="auto");return c};h=function(){if(a._a._added_events)return!1;var b;a._a._added_events=!0;for(b in J)J.hasOwnProperty(b)&&a._a&&a._a.addEventListener(b,J[b],!1);return!0};k=function(){var b; c._wD(a.id+": Removing event listeners");a._a._added_events=!1;for(b in J)J.hasOwnProperty(b)&&a._a&&a._a.removeEventListener(b,J[b],!1)};this._onload=function(b){var d=!!b||!a.isHTML5&&8===n&&a.duration;b=a.id+": ";c._wD(b+(d?"onload()":"Failed to load / invalid sound?"+(a.duration?" -":" Zero-length duration reported.")+" ("+a.url+")"),d?1:2);d||a.isHTML5||(!0===c.sandbox.noRemote&&c._wD(b+t("noNet"),1),!0===c.sandbox.noLocal&&c._wD(b+t("noLocal"),1));a.loaded=d;a.readyState=d?3:2;a._onbufferchange(0); a._iO.onload&&ua(a,function(){a._iO.onload.apply(a,[d])});return!0};this._onbufferchange=function(b){if(0===a.playState||b&&a.isBuffering||!b&&!a.isBuffering)return!1;a.isBuffering=1===b;a._iO.onbufferchange&&(c._wD(a.id+": Buffer state change: "+b),a._iO.onbufferchange.apply(a,[b]));return!0};this._onsuspend=function(){a._iO.onsuspend&&(c._wD(a.id+": Playback suspended"),a._iO.onsuspend.apply(a));return!0};this._onfailure=function(b,d,e){a.failures++;c._wD(a.id+": Failure ("+a.failures+"): "+b); if(a._iO.onfailure&&1===a.failures)a._iO.onfailure(b,d,e);else c._wD(a.id+": Ignoring failure")};this._onwarning=function(b,c,d){if(a._iO.onwarning)a._iO.onwarning(b,c,d)};this._onfinish=function(){var b=a._iO.onfinish;a._onbufferchange(0);a._resetOnPosition(0);a.instanceCount&&(a.instanceCount--,a.instanceCount||(Wa(),a.playState=0,a.paused=!1,a.instanceCount=0,a.instanceOptions={},a._iO={},G(),a.isHTML5&&(a.position=0)),a.instanceCount&&!a._iO.multiShotEvents||!b||(c._wD(a.id+": onfinish()"),ua(a, function(){b.apply(a)})))};this._whileloading=function(b,c,d,e){var f=a._iO;a.bytesLoaded=b;a.bytesTotal=c;a.duration=Math.floor(d);a.bufferLength=e;a.durationEstimate=a.isHTML5||f.isMovieStar?a.duration:f.duration?a.duration>f.duration?a.duration:f.duration:parseInt(a.bytesTotal/a.bytesLoaded*a.duration,10);a.isHTML5||(a.buffered=[{start:0,end:a.duration}]);(3!==a.readyState||a.isHTML5)&&f.whileloading&&f.whileloading.apply(a)};this._whileplaying=function(b,c,d,e,f){var k=a._iO;if(isNaN(b)||null=== b)return!1;a.position=Math.max(0,b);a._processOnPosition();!a.isHTML5&&8opera.version()?new Audio(null):new Audio:null,e,a,f={},h,k;h=c.audioFormats;for(e in h)if(h.hasOwnProperty(e)&&(a="audio/"+e,f[e]=b(h[e].type),f[a]=f[e],e.match(pb)?(c.flash[e]=!0,c.flash[a]=!0):(c.flash[e]=!1,c.flash[a]=!1),h[e]&&h[e].related))for(k=h[e].related.length-1;0<=k;k--)f["audio/"+h[e].related[k]]= f[e],c.html5[h[e].related[k]]=f[e],c.flash[h[e].related[k]]=f[e];f.canPlayType=d?b:null;c.html5=A(c.html5,f);c.html5.usingFlash=hb();u=c.html5.usingFlash;return!0};F={notReady:"Unavailable - wait until onready() has fired.",notOK:"Audio support is not available.",domError:"soundManagerexception caught while appending SWF to DOM.",spcWmode:"Removing wmode, preventing known SWF loading issue(s)",swf404:"soundManager: Verify that %s is a valid path.",tryDebug:"Try soundManager.debugFlash = true for more security details (output goes to SWF.)", checkSWF:"See SWF output for more debug info.",localFail:"soundManager: Non-HTTP page ("+m.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:"soundManager: Special case: Waiting for SWF to load with window focus...",waitForever:"soundManager: Waiting indefinitely for Flash (will recover if unblocked)...", waitSWF:"soundManager: Waiting for 100% SWF load...",needFunction:"soundManager: Function object expected for %s",badID:'Sound ID "%s" should be a string, starting with a non-numeric character',currentObj:"soundManager: _debug(): Current sound objects",waitOnload:"soundManager: Waiting for window.onload()",docLoaded:"soundManager: Document already loaded",onload:"soundManager: initComplete(): calling soundManager.onload()",onloadOK:"soundManager.onload() complete",didInit:"soundManager: 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:"soundManager: Failed to remove Flash node.",shutdown:"soundManager.disable(): Shutting down",queue:"soundManager: Queueing %s handler",smError:"SMSound.load(): Exception: JS-Flash communication failed, or JS error.",fbTimeout:"No flash response, applying .swf_timedout CSS...", fbLoaded:"Flash loaded",fbHandler:"soundManager: flashBlockHandler()",manURL:"SMSound.load(): Using manually-assigned URL",onURL:"soundManager.load(): current URL already assigned.",badFV:'soundManager.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:"soundManager: Fatal error: Flash is needed to play some required formats, but is not available.",gotFocus:"soundManager: Got window focus.",policy:"Enabling usePolicyFile for data access",setup:"soundManager.setup(): allowed parameters: %s",setupError:'soundManager.setup(): "%s" cannot be assigned with this method.',setupUndef:'soundManager.setup(): Could not find option "%s"',setupLate:"soundManager.setup(): url, flashVersion and html5Test property changes will not take effect until reboot().", noURL:"soundManager: Flash URL required. Call soundManager.setup({url:...}) to get started.",sm2Loaded:"SoundManager 2: Ready. "+String.fromCharCode(10003),reset:"soundManager.reset(): Removing event callbacks",mobileUA:"Mobile UA detected, preferring HTML5 by default.",globalHTML5:"Using singleton HTML5 Audio() pattern for this device.",ignoreMobile:"Ignoring mobile restrictions for this device."};t=function(){var b,c,e,a;b=jb.call(arguments);c=b.shift();if((a=F&&F[c]?F[c]:"")&&b&&b.length)for(c= 0,e=b.length;cn&&(c._wD(t("needfl9")),c.flashVersion=n=9);c.version=c.versionNumber+(c.html5Only?" (HTML5-only mode)":9===n?" (AS3/Flash 9)":" (AS2/Flash 8)");8'}if(X&&Y)return!1;if(c.html5Only)return Da(),e(),c.oMC=z(c.movieID),ya(),Y=X=!0,!1;var f=d||c.url,h=c.altURL||f,k=ma(),l=V(), n=null,n=m.getElementsByTagName("html")[0],p,r,q,n=n&&n.dir&&n.dir.match(/rtl/i);b=b===g?c.id:b;Da();c.url=cb(ga?f:h);d=c.url;c.wmode=!c.wmode&&c.useHighPerformance?"transparent":c.wmode;null!==c.wmode&&(v.match(/msie 8/i)||!N&&!c.useHighPerformance)&&navigator.platform.match(/win32|win64/i)&&(Q.push(F.spcWmode),c.wmode=null);k={name:b,id:b,src:d,quality:"high",allowScriptAccess:c.allowScriptAccess,bgcolor:c.bgColor,pluginspage:vb+"www.macromedia.com/go/getflashplayer",title:"JS/Flash audio component (SoundManager 2)", type:"application/x-shockwave-flash",wmode:c.wmode,hasPriority:"true"};c.debugFlash&&(k.FlashVars="debug=1");c.wmode||delete k.wmode;if(N)f=m.createElement("div"),r=['',a("movie",d),a("AllowScriptAccess",c.allowScriptAccess),a("quality",k.quality),c.wmode?a("wmode",c.wmode):"",a("bgcolor", c.bgColor),a("hasPriority","true"),c.debugFlash?a("FlashVars",k.FlashVars):"",""].join("");else for(p in f=m.createElement("embed"),k)k.hasOwnProperty(p)&&f.setAttribute(p,k[p]);Fa();l=V();if(k=ma())if(c.oMC=z(c.movieID)||m.createElement("div"),c.oMC.id)q=c.oMC.className,c.oMC.className=(q?q+" ":"movieContainer")+(l?" "+l:""),c.oMC.appendChild(f),N&&(p=c.oMC.appendChild(m.createElement("div")),p.className="sm2-object-box",p.innerHTML=r),Y=!0;else{c.oMC.id=c.movieID;c.oMC.className="movieContainer "+ l;p=l=null;c.useFlashBlock||(c.useHighPerformance?l={position:"fixed",width:"8px",height:"8px",bottom:"0px",left:"0px",overflow:"hidden"}:(l={position:"absolute",width:"6px",height:"6px",top:"-9999px",left:"-9999px"},n&&(l.left=Math.abs(parseInt(l.left,10))+"px")));ub&&(c.oMC.style.zIndex=1E4);if(!c.debugFlash)for(q in l)l.hasOwnProperty(q)&&(c.oMC.style[q]=l[q]);try{N||c.oMC.appendChild(f),k.appendChild(c.oMC),N&&(p=c.oMC.appendChild(m.createElement("div")),p.className="sm2-object-box",p.innerHTML= r),Y=!0}catch(u){throw Error(t("domError")+" \n"+u.toString());}}X=!0;e();return!0};la=function(){if(c.html5Only)return na(),!1;if(l)return!1;if(!c.url)return p("noURL"),!1;l=c.getMovie(c.id);l||(ba?(N?c.oMC.innerHTML=Ha:c.oMC.appendChild(ba),ba=null,X=!0):na(c.id,c.url),l=c.getMovie(c.id));"function"===typeof c.oninitmovie&&setTimeout(c.oninitmovie,1);Na();return!0};T=function(){setTimeout(Za,1E3)};Ca=function(){h.setTimeout(function(){L("soundManager: useFlashBlock is false, 100% HTML5 mode is possible. Rebooting with preferFlash: false..."); c.setup({preferFlash:!1}).reboot();c.didFlashBlock=!0;c.beginDelayedInit()},1)};Za=function(){var b,d=!1;if(!c.url||ca)return!1;ca=!0;x.remove(h,"load",T);if(H&&wa&&!Ua)return p("waitFocus"),!1;q||(b=c.getMoviePercent(),0b&&(d=!0));setTimeout(function(){b=c.getMoviePercent();if(d)return ca=!1,c._wD(t("waitSWF")),h.setTimeout(T,1),!1;q||(c._wD("soundManager: No Flash response within expected time. Likely causes: "+(0===b?"SWF load failed, ":"")+"Flash blocked or JS-Flash security error."+(c.debugFlash? " "+t("checkSWF"):""),2),!ga&&b&&(p("localFail",2),c.debugFlash||p("tryDebug",2)),0===b&&c._wD(t("swf404",c.url),1),D("flashtojs",!1,": Timed out"+(ga?" (Check flash security or flash blockers)":" (No plugin/missing SWF?)")));!q&&ob&&(null===b?c.useFlashBlock||0===c.flashLoadTimeout?(c.useFlashBlock&&Ia(),p("waitForever")):!c.useFlashBlock&&ea?Ca():(p("waitForever"),O({type:"ontimeout",ignoreInit:!0,error:{type:"INIT_FLASHBLOCK"}})):0===c.flashLoadTimeout?p("waitForever"):!c.useFlashBlock&&ea?Ca(): Ga(!0))},c.flashLoadTimeout)};ka=function(){if(Ua||!wa)return x.remove(h,"focus",ka),!0;Ua=ob=!0;p("gotFocus");ca=!1;T();x.remove(h,"focus",ka);return!0};Na=function(){Q.length&&(c._wD("SoundManager 2: "+Q.join(" "),1),Q=[])};lb=function(){Na();var b,d=[];if(c.useHTML5Audio&&c.hasHTML5){for(b in c.audioFormats)c.audioFormats.hasOwnProperty(b)&&d.push(b+" = "+c.html5[b]+(!c.html5[b]&&u&&c.flash[b]?" (using flash)":c.preferFlash&&c.flash[b]&&u?" (preferring flash)":c.html5[b]?"":" ("+(c.audioFormats[b].required? "required, ":"")+"and no flash support)"));c._wD("SoundManager 2 HTML5 support: "+d.join(", "),1)}};Z=function(b){if(q)return!1;if(c.html5Only)return p("sm2Loaded",1),q=!0,S(),D("onload",!0),!0;var d=!0,e;c.useFlashBlock&&c.flashLoadTimeout&&!c.getMoviePercent()||(q=!0);e={type:!H&&u?"NO_FLASH":"INIT_TIMEOUT"};c._wD("SoundManager 2 "+(y?"failed to load":"loaded")+" ("+(y?"Flash security/load error":"OK")+") "+String.fromCharCode(y?10006:10003),y?2:1);y||b?(c.useFlashBlock&&c.oMC&&(c.oMC.className= V()+" "+(null===c.getMoviePercent()?"swf_timedout":"swf_error")),O({type:"ontimeout",error:e,ignoreInit:!0}),D("onload",!1),U(e),d=!1):D("onload",!0);y||(c.waitForWindowLoad&&!ja?(p("waitOnload"),x.add(h,"load",S)):(c.waitForWindowLoad&&ja&&p("docLoaded"),S()));return d};Ya=function(){var b,d=c.setupOptions;for(b in d)d.hasOwnProperty(b)&&(c[b]===g?c[b]=d[b]:c[b]!==d[b]&&(c.setupOptions[b]=c[b]))};ya=function(){if(q)return p("didInit"),!1;if(c.html5Only)return q||(x.remove(h,"load",c.beginDelayedInit), c.enabled=!0,Z()),!0;la();try{l._externalInterfaceTest(!1),$a(!0,c.flashPollingInterval||(c.useHighPerformance?10:50)),c.debugMode||l._disableDebug(),c.enabled=!0,D("jstoflash",!0),c.html5Only||x.add(h,"unload",xa)}catch(b){return c._wD("js/flash exception: "+b.toString()),D("jstoflash",!1),U({type:"JS_TO_FLASH_EXCEPTION",fatal:!0}),Ga(!0),Z(),!1}Z();x.remove(h,"load",c.beginDelayedInit);return!0};P=function(){if(aa)return!1;aa=!0;Ya();Fa();!H&&c.hasHTML5&&(c._wD("SoundManager 2: No Flash detected"+ (c.useHTML5Audio?". Trying HTML5-only mode.":", enabling HTML5."),1),c.setup({useHTML5Audio:!0,preferFlash:!1}));ib();!H&&u&&(Q.push(F.needFlash),c.setup({flashLoadTimeout:1}));m.removeEventListener&&m.removeEventListener("DOMContentLoaded",P,!1);la();return!0};Ka=function(){"complete"===m.readyState&&(P(),m.detachEvent("onreadystatechange",Ka));return!0};Ea=function(){ja=!0;P();x.remove(h,"load",Ea)};Ma();x.add(h,"focus",ka);x.add(h,"load",T);x.add(h,"load",Ea);m.addEventListener?m.addEventListener("DOMContentLoaded", P,!1):m.attachEvent?m.attachEvent("onreadystatechange",Ka):(D("onload",!1),U({type:"NO_DOM2_EVENTS",fatal:!0}))}if(!h||!h.document)throw Error("SoundManager requires a browser with window and document objects.");var W=null;h.SM2_DEFER!==g&&SM2_DEFER||(W=new K);"object"===typeof module&&module&&"object"===typeof module.exports?(module.exports.SoundManager=K,module.exports.soundManager=W):"function"===typeof define&&define.amd&&define(function(){return{constructor:K,getInstance:function(g){!h.soundManager&& g instanceof Function&&(g=g(K),g instanceof K&&(h.soundManager=g));return h.soundManager}}});h.SoundManager=K;h.soundManager=W})(window);SoundManager2-2.97a.20150601/script/soundmanager2-nodebug-jsmin.js000066400000000000000000001070121253275212400243300ustar00rootroot00000000000000/** @license * * SoundManager 2: JavaScript Sound for the Web * ---------------------------------------------- * http://schillmania.com/projects/soundmanager2/ * * Copyright (c) 2007, Scott Schiller. All rights reserved. * Code provided under the BSD License: * http://schillmania.com/projects/soundmanager2/license.txt * * V2.97a.20150601 */ (function(h,g){function w(gb,w){function Y(b){return c.preferFlash&&z&&!c.ignoreFlash&&c.flash[b]!==g&&c.flash[b]}function r(b){return function(c){var d=this._s;return d&&d._a?b.call(this,c):null}}this.setupOptions={url:gb||null,flashVersion:8,debugMode:!0,debugFlash:!1,useConsole:!0,consoleOnly:!0,waitForWindowLoad:!1,bgColor:"#ffffff",useHighPerformance:!1,flashPollingInterval:null,html5PollingInterval:null,flashLoadTimeout:1E3,wmode:null,allowScriptAccess:"always",useFlashBlock:!1,useHTML5Audio:!0, forceUseGlobalHTML5Audio:!1,ignoreMobileRestrictions:!1,html5Test:/^(probably|maybe)$/i,preferFlash:!1,noSWFCache:!1,idPrefix:"sound"};this.defaultOptions={autoLoad:!1,autoPlay:!1,from:null,loops:1,onid3:null,onload:null,whileloading:null,onplay:null,onpause:null,onresume:null,whileplaying:null,onposition:null,onstop:null,onfailure:null,onfinish:null,multiShot:!0,multiShotEvents:!1,position:null,pan:0,stream:!0,to:null,type:null,usePolicyFile:!1,volume:100};this.flash9Options={isMovieStar:null,usePeakData:!1, useWaveformData:!1,useEQData:!1,onbufferchange:null,ondataerror:null};this.movieStarOptions={bufferTime:3,serverURL:null,onconnect:null,duration:null};this.audioFormats={mp3:{type:['audio/mpeg; codecs="mp3"',"audio/mpeg","audio/mp3","audio/MPA","audio/mpa-robust"],required:!0},mp4:{related:["aac","m4a","m4b"],type:['audio/mp4; codecs="mp4a.40.2"',"audio/aac","audio/x-m4a","audio/MP4A-LATM","audio/mpeg4-generic"],required:!1},ogg:{type:["audio/ogg; codecs=vorbis"],required:!1},opus:{type:["audio/ogg; codecs=opus", "audio/opus"],required:!1},wav:{type:['audio/wav; codecs="1"',"audio/wav","audio/wave","audio/x-wav"],required:!1}};this.movieID="sm2-container";this.id=w||"sm2movie";this.debugID="soundmanager-debug";this.debugURLParam=/([#?&])debug=1/i;this.versionNumber="V2.97a.20150601";this.altURL=this.movieURL=this.version=null;this.enabled=this.swfLoaded=!1;this.oMC=null;this.sounds={};this.soundIDs=[];this.didFlashBlock=this.muted=!1;this.filePattern=null;this.filePatterns={flash8:/\.mp3(\?.*)?$/i,flash9:/\.mp3(\?.*)?$/i}; this.features={buffering:!1,peakData:!1,waveformData:!1,eqData:!1,movieStar:!1};this.sandbox={};this.html5={usingFlash:null};this.flash={};this.ignoreFlash=this.html5Only=!1;var M,c=this,Na=null,k=null,Z,u=navigator.userAgent,Oa=h.location.href.toString(),p=document,oa,Pa,pa,m,y=[],N=!1,O=!1,l=!1,A=!1,qa=!1,P,x,ra,aa,sa,F,H,I,Qa,ta,ua,ba,Q,ca,G,va,R,wa,da,J,Ra,xa,Sa,ya,Ta,S=null,za=null,T,Aa,K,ea,fa,q,U=!1,Ba=!1,Ua,Va,Wa,ga=0,V=null,ha,ia=[],W,v=null,Xa,ja,X,D,ka,Ca,Ya,t,hb=Array.prototype.slice, B=!1,Da,z,Ea,Za,C,la,$a=0,Fa,Ga=u.match(/(ipad|iphone|ipod)/i),ab=u.match(/android/i),E=u.match(/msie/i),ib=u.match(/webkit/i),ma=u.match(/safari/i)&&!u.match(/chrome/i),Ha=u.match(/opera/i),Ia=u.match(/(mobile|pre\/|xoom)/i)||Ga||ab,bb=!Oa.match(/usehtml5audio/i)&&!Oa.match(/sm2\-ignorebadua/i)&&ma&&!u.match(/silk/i)&&u.match(/OS X 10_6_([3-7])/i),Ja=p.hasFocus!==g?p.hasFocus():null,na=ma&&(p.hasFocus===g||!p.hasFocus()),cb=!na,db=/(mp3|mp4|mpa|m4a|m4b)/i,Ka=p.location?p.location.protocol.match(/http/i): null,jb=Ka?"":"http://",eb=/^\s*audio\/(?:x-)?(?:mpeg4|aac|flv|mov|mp4||m4v|m4a|m4b|mp4v|3gp|3g2)\s*(?:$|;)/i,fb="mpeg4 aac flv mov mp4 m4v f4v m4a m4b mp4v 3gp 3g2".split(" "),kb=new RegExp("\\.("+fb.join("|")+")(\\?.*)?$","i");this.mimePattern=/^\s*audio\/(?:x-)?(?:mp(?:eg|3))\s*(?:$|;)/i;this.useAltURL=!Ka;var La;try{La=Audio!==g&&(Ha&&opera!==g&&10>opera.version()?new Audio(null):new Audio).canPlayType!==g}catch(lb){La=!1}this.hasHTML5=La;this.setup=function(b){var e=!c.url;b!==g&&l&&v&&c.ok(); ra(b);c.setupOptions.useHTML5Audio&&!B&&c.setupOptions.forceUseGlobalHTML5Audio&&(ia.push(Q.globalHTML5),B=!0);!Fa&&Ia&&(c.setupOptions.ignoreMobileRestrictions&&ia.push(Q.ignoreMobile),c.setupOptions.useHTML5Audio=!0,c.setupOptions.preferFlash=!1,ab&&!u.match(/android\s2\.3/i)&&(Ga&&(c.ignoreFlash=!0),B=!0));b&&(e&&R&&b.url!==g&&c.beginDelayedInit(),R||b.url===g||"complete"!==p.readyState||setTimeout(G,1));Fa=!0;return c};this.supported=this.ok=function(){return v?l&&!A:c.useHTML5Audio&&c.hasHTML5}; this.getMovie=function(b){return Z(b)||p[b]||h[b]};this.createSound=function(b,e){function d(){a=ea(a);c.sounds[a.id]=new M(a);c.soundIDs.push(a.id);return c.sounds[a.id]}var a,f=null;if(!l||!c.ok())return!1;e!==g&&(b={id:b,url:e});a=x(b);a.url=ha(a.url);a.id===g&&(a.id=c.setupOptions.idPrefix+$a++);if(q(a.id,!0))return c.sounds[a.id];if(ja(a))f=d(),f._setup_html5(a);else{if(c.html5Only||c.html5.usingFlash&&a.url&&a.url.match(/data\:/i))return d();8a.instanceCount?(p(),f=a._setup_html5(),a.setPosition(a._iO.position),f.play()):(n=new Audio(a._iO.url),h=function(){t.remove(n,"ended",h);a._onfinish(a);ka(n);n=null},Ma=function(){t.remove(n,"canplay",Ma);try{n.currentTime=a._iO.position/1E3}catch(b){}n.play()},t.add(n,"ended",h),a._iO.volume!==g&&(n.volume=Math.max(0,Math.min(1,a._iO.volume/100))),a.muted&&(n.muted=!0),a._iO.position?t.add(n, "canplay",Ma):n.play()):(f=k._start(a.id,a._iO.loops||1,9===m?a.position:a.position/1E3,a._iO.multiShot||!1),9!==m||f||a._iO.onplayerror&&a._iO.onplayerror.apply(a))}return a};this.stop=function(b){var c=a._iO;1===a.playState&&(a._onbufferchange(0),a._resetOnPosition(0),a.paused=!1,a.isHTML5||(a.playState=0),y(),c.to&&a.clearOnPosition(c.to),a.isHTML5?a._a&&(b=a.position,a.setPosition(0),a.position=b,a._a.pause(),a.playState=0,a._onTimer(),L()):(k._stop(a.id,b),c.serverURL&&a.unload()),a.instanceCount= 0,a._iO={},c.onstop&&c.onstop.apply(a));return a};this.setAutoPlay=function(b){a._iO.autoPlay=b;a.isHTML5||(k._setAutoPlay(a.id,b),b&&(a.instanceCount||1!==a.readyState||a.instanceCount++))};this.getAutoPlay=function(){return a._iO.autoPlay};this.setPosition=function(b){b===g&&(b=0);var c=a.isHTML5?Math.max(b,0):Math.min(a.duration||a._iO.duration,Math.max(b,0));a.position=c;b=a.position/1E3;a._resetOnPosition(a.position);a._iO.position=c;if(!a.isHTML5)b=9===m?a.position:b,a.readyState&&2!==a.readyState&& k._setPosition(a.id,b,a.paused||!a.playState,a._iO.multiShot);else if(a._a){if(a._html5_canplay){if(a._a.currentTime!==b)try{a._a.currentTime=b,(0===a.playState||a.paused)&&a._a.pause()}catch(e){}}else if(b)return a;a.paused&&a._onTimer(!0)}return a};this.pause=function(b){if(a.paused||0===a.playState&&1!==a.readyState)return a;a.paused=!0;a.isHTML5?(a._setup_html5().pause(),L()):(b||b===g)&&k._pause(a.id,a._iO.multiShot);a._iO.onpause&&a._iO.onpause.apply(a);return a};this.resume=function(){var b= a._iO;if(!a.paused)return a;a.paused=!1;a.playState=1;a.isHTML5?(a._setup_html5().play(),p()):(b.isMovieStar&&!b.serverURL&&a.setPosition(a.position),k._pause(a.id,b.multiShot));!u&&b.onplay?(b.onplay.apply(a),u=!0):b.onresume&&b.onresume.apply(a);return a};this.togglePause=function(){if(0===a.playState)return a.play({position:9!==m||a.isHTML5?a.position/1E3:a.position}),a;a.paused?a.resume():a.pause();return a};this.setPan=function(b,c){b===g&&(b=0);c===g&&(c=!1);a.isHTML5||k._setPan(a.id,b);a._iO.pan= b;c||(a.pan=b,a.options.pan=b);return a};this.setVolume=function(b,e){b===g&&(b=100);e===g&&(e=!1);a.isHTML5?a._a&&(c.muted&&!a.muted&&(a.muted=!0,a._a.muted=!0),a._a.volume=Math.max(0,Math.min(1,b/100))):k._setVolume(a.id,c.muted&&!a.muted||a.muted?0:b);a._iO.volume=b;e||(a.volume=b,a.options.volume=b);return a};this.mute=function(){a.muted=!0;a.isHTML5?a._a&&(a._a.muted=!0):k._setVolume(a.id,0);return a};this.unmute=function(){a.muted=!1;var b=a._iO.volume!==g;a.isHTML5?a._a&&(a._a.muted=!1):k._setVolume(a.id, b?a._iO.volume:a.options.volume);return a};this.toggleMute=function(){return a.muted?a.unmute():a.mute()};this.onposition=this.onPosition=function(b,c,e){l.push({position:parseInt(b,10),method:c,scope:e!==g?e:a,fired:!1});return a};this.clearOnPosition=function(a,b){var c;a=parseInt(a,10);if(isNaN(a))return!1;for(c=0;c=b)return!1;for(--b;0<= b;b--)c=l[b],!c.fired&&a.position>=c.position&&(c.fired=!0,v++,c.method.apply(c.scope,[c.position]));return!0};this._resetOnPosition=function(a){var b,c;b=l.length;if(!b)return!1;for(--b;0<=b;b--)c=l[b],c.fired&&a<=c.position&&(c.fired=!1,v--);return!0};A=function(){var b=a._iO,c=b.from,e=b.to,d,f;f=function(){a.clearOnPosition(e,f);a.stop()};d=function(){if(null!==e&&!isNaN(e))a.onPosition(e,f)};null===c||isNaN(c)||(b.position=c,b.multiShot=!1,d());return b};r=function(){var b,c=a._iO.onposition; if(c)for(b in c)if(c.hasOwnProperty(b))a.onPosition(parseInt(b,10),c[b])};y=function(){var b,c=a._iO.onposition;if(c)for(b in c)c.hasOwnProperty(b)&&a.clearOnPosition(parseInt(b,10))};p=function(){a.isHTML5&&Ua(a)};L=function(){a.isHTML5&&Va(a)};f=function(b){b||(l=[],v=0);u=!1;a._hasTimer=null;a._a=null;a._html5_canplay=!1;a.bytesLoaded=null;a.bytesTotal=null;a.duration=a._iO&&a._iO.duration?a._iO.duration:null;a.durationEstimate=null;a.buffered=[];a.eqData=[];a.eqData.left=[];a.eqData.right=[]; a.failures=0;a.isBuffering=!1;a.instanceOptions={};a.instanceCount=0;a.loaded=!1;a.metadata={};a.readyState=0;a.muted=!1;a.paused=!1;a.peakData={left:0,right:0};a.waveformData={left:[],right:[]};a.playState=0;a.position=null;a.id3={}};f();this._onTimer=function(b){var c,f=!1,g={};if(a._hasTimer||b)return a._a&&(b||(0opera.version()?new Audio(null):new Audio,c=a._a,c._called_load=!1,B&&(Na=c);a.isHTML5=!0;a._a=c;c._s=a;n();a._apply_loop(c,b.loops);b.autoLoad||b.autoPlay?a.load():(c.autobuffer=!1,c.preload="auto");return c};n=function(){if(a._a._added_events)return!1;var b;a._a._added_events=!0;for(b in C)C.hasOwnProperty(b)&&a._a&&a._a.addEventListener(b,C[b],!1);return!0};h=function(){var b; a._a._added_events=!1;for(b in C)C.hasOwnProperty(b)&&a._a&&a._a.removeEventListener(b,C[b],!1)};this._onload=function(b){var c=!!b||!a.isHTML5&&8===m&&a.duration;a.loaded=c;a.readyState=c?3:2;a._onbufferchange(0);a._iO.onload&&la(a,function(){a._iO.onload.apply(a,[c])});return!0};this._onbufferchange=function(b){if(0===a.playState||b&&a.isBuffering||!b&&!a.isBuffering)return!1;a.isBuffering=1===b;a._iO.onbufferchange&&a._iO.onbufferchange.apply(a,[b]);return!0};this._onsuspend=function(){a._iO.onsuspend&& a._iO.onsuspend.apply(a);return!0};this._onfailure=function(b,c,e){a.failures++;if(a._iO.onfailure&&1===a.failures)a._iO.onfailure(b,c,e)};this._onwarning=function(b,c,e){if(a._iO.onwarning)a._iO.onwarning(b,c,e)};this._onfinish=function(){var b=a._iO.onfinish;a._onbufferchange(0);a._resetOnPosition(0);a.instanceCount&&(a.instanceCount--,a.instanceCount||(y(),a.playState=0,a.paused=!1,a.instanceCount=0,a.instanceOptions={},a._iO={},L(),a.isHTML5&&(a.position=0)),(!a.instanceCount||a._iO.multiShotEvents)&& b&&la(a,function(){b.apply(a)}))};this._whileloading=function(b,c,e,d){var f=a._iO;a.bytesLoaded=b;a.bytesTotal=c;a.duration=Math.floor(e);a.bufferLength=d;a.durationEstimate=a.isHTML5||f.isMovieStar?a.duration:f.duration?a.duration>f.duration?a.duration:f.duration:parseInt(a.bytesTotal/a.bytesLoaded*a.duration,10);a.isHTML5||(a.buffered=[{start:0,end:a.duration}]);(3!==a.readyState||a.isHTML5)&&f.whileloading&&f.whileloading.apply(a)};this._whileplaying=function(b,c,e,d,f){var n=a._iO;if(isNaN(b)|| null===b)return!1;a.position=Math.max(0,b);a._processOnPosition();!a.isHTML5&&8opera.version()?new Audio(null):new Audio:null,d,a,f={},n,h;n=c.audioFormats;for(d in n)if(n.hasOwnProperty(d)&&(a="audio/"+d,f[d]=b(n[d].type),f[a]=f[d],d.match(db)?(c.flash[d]=!0,c.flash[a]=!0):(c.flash[d]=!1,c.flash[a]=!1),n[d]&&n[d].related))for(h= n[d].related.length-1;0<=h;h--)f["audio/"+n[d].related[h]]=f[d],c.html5[n[d].related[h]]=f[d],c.flash[n[d].related[h]]=f[d];f.canPlayType=e?b:null;c.html5=x(c.html5,f);c.html5.usingFlash=Xa();v=c.html5.usingFlash;return!0};Q={};T=function(){};ea=function(b){8===m&&1m&&(c.flashVersion=m=9);c.version=c.versionNumber+(c.html5Only?" (HTML5-only mode)":9===m?" (AS3/Flash 9)":" (AS2/Flash 8)");8'}if(N&&O)return!1;if(c.html5Only)return ua(),c.oMC=Z(c.movieID),pa(),O=N=!0,!1;var a=e||c.url,f=c.altURL||a,h=wa(),k=K(),m=null,m=p.getElementsByTagName("html")[0],l,r,q,m=m&&m.dir&&m.dir.match(/rtl/i);b=b===g?c.id:b;ua();c.url=Ta(Ka?a:f);e=c.url;c.wmode=!c.wmode&&c.useHighPerformance?"transparent":c.wmode;null!==c.wmode&&(u.match(/msie 8/i)||!E&&!c.useHighPerformance)&&navigator.platform.match(/win32|win64/i)&&(ia.push(Q.spcWmode),c.wmode=null);h={name:b, id:b,src:e,quality:"high",allowScriptAccess:c.allowScriptAccess,bgcolor:c.bgColor,pluginspage:jb+"www.macromedia.com/go/getflashplayer",title:"JS/Flash audio component (SoundManager 2)",type:"application/x-shockwave-flash",wmode:c.wmode,hasPriority:"true"};c.debugFlash&&(h.FlashVars="debug=1");c.wmode||delete h.wmode;if(E)a=p.createElement("div"),r=['', d("movie",e),d("AllowScriptAccess",c.allowScriptAccess),d("quality",h.quality),c.wmode?d("wmode",c.wmode):"",d("bgcolor",c.bgColor),d("hasPriority","true"),c.debugFlash?d("FlashVars",h.FlashVars):"",""].join("");else for(l in a=p.createElement("embed"),h)h.hasOwnProperty(l)&&a.setAttribute(l,h[l]);xa();k=K();if(h=wa())if(c.oMC=Z(c.movieID)||p.createElement("div"),c.oMC.id)q=c.oMC.className,c.oMC.className=(q?q+" ":"movieContainer")+(k?" "+k:""),c.oMC.appendChild(a),E&&(l=c.oMC.appendChild(p.createElement("div")), l.className="sm2-object-box",l.innerHTML=r),O=!0;else{c.oMC.id=c.movieID;c.oMC.className="movieContainer "+k;l=k=null;c.useFlashBlock||(c.useHighPerformance?k={position:"fixed",width:"8px",height:"8px",bottom:"0px",left:"0px",overflow:"hidden"}:(k={position:"absolute",width:"6px",height:"6px",top:"-9999px",left:"-9999px"},m&&(k.left=Math.abs(parseInt(k.left,10))+"px")));ib&&(c.oMC.style.zIndex=1E4);if(!c.debugFlash)for(q in k)k.hasOwnProperty(q)&&(c.oMC.style[q]=k[q]);try{E||c.oMC.appendChild(a), h.appendChild(c.oMC),E&&(l=c.oMC.appendChild(p.createElement("div")),l.className="sm2-object-box",l.innerHTML=r),O=!0}catch(t){throw Error(T("domError")+" \n"+t.toString());}}return N=!0};ca=function(){if(c.html5Only)return da(),!1;if(k||!c.url)return!1;k=c.getMovie(c.id);k||(S?(E?c.oMC.innerHTML=za:c.oMC.appendChild(S),S=null,N=!0):da(c.id,c.url),k=c.getMovie(c.id));"function"===typeof c.oninitmovie&&setTimeout(c.oninitmovie,1);return!0};I=function(){setTimeout(Qa,1E3)};ta=function(){h.setTimeout(function(){c.setup({preferFlash:!1}).reboot(); c.didFlashBlock=!0;c.beginDelayedInit()},1)};Qa=function(){var b,e=!1;if(!c.url||U)return!1;U=!0;t.remove(h,"load",I);if(z&&na&&!Ja)return!1;l||(b=c.getMoviePercent(),0b&&(e=!0));setTimeout(function(){b=c.getMoviePercent();if(e)return U=!1,h.setTimeout(I,1),!1;!l&&cb&&(null===b?c.useFlashBlock||0===c.flashLoadTimeout?c.useFlashBlock&&Aa():!c.useFlashBlock&&W?ta():F({type:"ontimeout",ignoreInit:!0,error:{type:"INIT_FLASHBLOCK"}}):0!==c.flashLoadTimeout&&(!c.useFlashBlock&&W?ta():ya(!0)))}, c.flashLoadTimeout)};ba=function(){if(Ja||!na)return t.remove(h,"focus",ba),!0;Ja=cb=!0;U=!1;I();t.remove(h,"focus",ba);return!0};P=function(b){if(l)return!1;if(c.html5Only)return l=!0,H(),!0;var e=!0,d;c.useFlashBlock&&c.flashLoadTimeout&&!c.getMoviePercent()||(l=!0);d={type:!z&&v?"NO_FLASH":"INIT_TIMEOUT"};if(A||b)c.useFlashBlock&&c.oMC&&(c.oMC.className=K()+" "+(null===c.getMoviePercent()?"swf_timedout":"swf_error")),F({type:"ontimeout",error:d,ignoreInit:!0}),J(d),e=!1;A||(c.waitForWindowLoad&& !qa?t.add(h,"load",H):H());return e};Pa=function(){var b,e=c.setupOptions;for(b in e)e.hasOwnProperty(b)&&(c[b]===g?c[b]=e[b]:c[b]!==e[b]&&(c.setupOptions[b]=c[b]))};pa=function(){if(l)return!1;if(c.html5Only)return l||(t.remove(h,"load",c.beginDelayedInit),c.enabled=!0,P()),!0;ca();try{k._externalInterfaceTest(!1),Ra(!0,c.flashPollingInterval||(c.useHighPerformance?10:50)),c.debugMode||k._disableDebug(),c.enabled=!0,c.html5Only||t.add(h,"unload",oa)}catch(b){return J({type:"JS_TO_FLASH_EXCEPTION", fatal:!0}),ya(!0),P(),!1}P();t.remove(h,"load",c.beginDelayedInit);return!0};G=function(){if(R)return!1;R=!0;Pa();xa();!z&&c.hasHTML5&&c.setup({useHTML5Audio:!0,preferFlash:!1});Ya();!z&&v&&(ia.push(Q.needFlash),c.setup({flashLoadTimeout:1}));p.removeEventListener&&p.removeEventListener("DOMContentLoaded",G,!1);ca();return!0};Ca=function(){"complete"===p.readyState&&(G(),p.detachEvent("onreadystatechange",Ca));return!0};va=function(){qa=!0;G();t.remove(h,"load",va)};Ea();t.add(h,"focus",ba);t.add(h, "load",I);t.add(h,"load",va);p.addEventListener?p.addEventListener("DOMContentLoaded",G,!1):p.attachEvent?p.attachEvent("onreadystatechange",Ca):J({type:"NO_DOM2_EVENTS",fatal:!0})}if(!h||!h.document)throw Error("SoundManager requires a browser with window and document objects.");var M=null;h.SM2_DEFER!==g&&SM2_DEFER||(M=new w);"object"===typeof module&&module&&"object"===typeof module.exports?(module.exports.SoundManager=w,module.exports.soundManager=M):"function"===typeof define&&define.amd&&define(function(){return{constructor:w, getInstance:function(g){!h.soundManager&&g instanceof Function&&(g=g(w),g instanceof w&&(h.soundManager=g));return h.soundManager}}});h.SoundManager=w;h.soundManager=M})(window);SoundManager2-2.97a.20150601/script/soundmanager2-nodebug.js000066400000000000000000002424221253275212400232170ustar00rootroot00000000000000/** @license * * SoundManager 2: JavaScript Sound for the Web * ---------------------------------------------- * http://schillmania.com/projects/soundmanager2/ * * Copyright (c) 2007, Scott Schiller. All rights reserved. * Code provided under the BSD License: * http://schillmania.com/projects/soundmanager2/license.txt * * V2.97a.20150601 */ /*global window, SM2_DEFER, sm2Debugger, console, document, navigator, setTimeout, setInterval, clearInterval, Audio, opera, module, define */ /*jslint regexp: true, sloppy: true, white: true, nomen: true, plusplus: true, todo: true */ (function(window, _undefined) { "use strict"; if (!window || !window.document) { throw new Error('SoundManager requires a browser with window and document objects.'); } var soundManager = null; function SoundManager(smURL, smID) { this.setupOptions = { 'url': (smURL || null), 'flashVersion': 8, 'debugMode': true, 'debugFlash': false, 'useConsole': true, 'consoleOnly': true, 'waitForWindowLoad': false, 'bgColor': '#ffffff', 'useHighPerformance': false, 'flashPollingInterval': null, 'html5PollingInterval': null, 'flashLoadTimeout': 1000, 'wmode': null, 'allowScriptAccess': 'always', 'useFlashBlock': false, 'useHTML5Audio': true, 'forceUseGlobalHTML5Audio': false, 'ignoreMobileRestrictions': false, 'html5Test': /^(probably|maybe)$/i, 'preferFlash': false, 'noSWFCache': false, 'idPrefix': 'sound' }; this.defaultOptions = { 'autoLoad': false, 'autoPlay': false, 'from': null, 'loops': 1, 'onid3': null, 'onload': null, 'whileloading': null, 'onplay': null, 'onpause': null, 'onresume': null, 'whileplaying': null, 'onposition': null, 'onstop': null, 'onfailure': null, 'onfinish': null, 'multiShot': true, 'multiShotEvents': false, 'position': null, 'pan': 0, 'stream': true, 'to': null, 'type': null, 'usePolicyFile': false, 'volume': 100 }; this.flash9Options = { 'isMovieStar': null, 'usePeakData': false, 'useWaveformData': false, 'useEQData': false, 'onbufferchange': null, 'ondataerror': null }; this.movieStarOptions = { 'bufferTime': 3, 'serverURL': null, 'onconnect': null, 'duration': null }; this.audioFormats = { 'mp3': { 'type': ['audio/mpeg; codecs="mp3"', 'audio/mpeg', 'audio/mp3', 'audio/MPA', 'audio/mpa-robust'], 'required': true }, 'mp4': { 'related': ['aac','m4a','m4b'], 'type': ['audio/mp4; codecs="mp4a.40.2"', 'audio/aac', 'audio/x-m4a', 'audio/MP4A-LATM', 'audio/mpeg4-generic'], 'required': false }, 'ogg': { 'type': ['audio/ogg; codecs=vorbis'], 'required': false }, 'opus': { 'type': ['audio/ogg; codecs=opus', 'audio/opus'], 'required': false }, 'wav': { 'type': ['audio/wav; codecs="1"', 'audio/wav', 'audio/wave', 'audio/x-wav'], 'required': false } }; this.movieID = 'sm2-container'; this.id = (smID || 'sm2movie'); this.debugID = 'soundmanager-debug'; this.debugURLParam = /([#?&])debug=1/i; this.versionNumber = 'V2.97a.20150601'; this.version = null; this.movieURL = null; this.altURL = null; this.swfLoaded = false; this.enabled = false; this.oMC = null; this.sounds = {}; this.soundIDs = []; this.muted = false; this.didFlashBlock = false; this.filePattern = null; this.filePatterns = { 'flash8': /\.mp3(\?.*)?$/i, 'flash9': /\.mp3(\?.*)?$/i }; this.features = { 'buffering': false, 'peakData': false, 'waveformData': false, 'eqData': false, 'movieStar': false }; this.sandbox = { }; this.html5 = { 'usingFlash': null }; this.flash = {}; this.html5Only = false; this.ignoreFlash = false; 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, rebootIntoHTML5, 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, 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, didSetup, msecScale = 1000, 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)), 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', emptyWAV = 'data:audio/wave;base64,/UklGRiYAAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQIAAAD//w==', overHTTP = (doc.location ? doc.location.protocol.match(/http/i) : null), http = (!overHTTP ? 'http:/'+'/' : ''), netStreamMimeTypes = /^\s*audio\/(?:x-)?(?:mpeg4|aac|flv|mov|mp4||m4v|m4a|m4b|mp4v|3gp|3g2)\s*(?:$|;)/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; this.useAltURL = !overHTTP; swfCSS = { 'swfBox': 'sm2-object-box', 'swfDefault': 'movieContainer', 'swfError': 'swf_error', 'swfTimedout': 'swf_timedout', 'swfLoaded': 'swf_loaded', 'swfUnblocked': 'swf_unblocked', 'sm2Debug': 'sm2_debug', 'highPerf': 'high_performance', 'flashDebug': 'flash_debug' }; this.hasHTML5 = (function() { try { return (Audio !== _undefined && (isOpera && opera !== _undefined && opera.version() < 10 ? new Audio(null) : new Audio()).canPlayType !== _undefined); } catch(e) { return false; } }()); this.setup = function(options) { var noURL = (!sm2.url); if (options !== _undefined && didInit && needsFlash && sm2.ok() && (options.flashVersion !== _undefined || options.url !== _undefined || options.html5Test !== _undefined)) { } assign(options); if (sm2.setupOptions.useHTML5Audio && !useGlobalHTML5Audio && sm2.setupOptions.forceUseGlobalHTML5Audio) { messages.push(strings.globalHTML5); useGlobalHTML5Audio = true; } if (!didSetup && mobileHTML5) { if (sm2.setupOptions.ignoreMobileRestrictions) { messages.push(strings.ignoreMobile); } sm2.setupOptions.useHTML5Audio = true; sm2.setupOptions.preferFlash = false; if ((isAndroid && !ua.match(/android\s2\.3/i))) { if (is_iDevice) { sm2.ignoreFlash = true; } useGlobalHTML5Audio = true; } } if (options) { if (noURL && didDCLoaded && options.url !== _undefined) { sm2.beginDelayedInit(); } if (!didDCLoaded && options.url !== _undefined && doc.readyState === 'complete') { setTimeout(domContentLoaded, 1); } } didSetup = true; return sm2; }; this.ok = function() { return (needsFlash ? (didInit && !disabled) : (sm2.useHTML5Audio && sm2.hasHTML5)); }; this.supported = this.ok; this.getMovie = function(smID) { return id(smID) || doc[smID] || window[smID]; }; this.createSound = function(oOptions, _url) { var cs, cs_string, options, oSound = null; if (!didInit || !sm2.ok()) { return false; } if (_url !== _undefined) { oOptions = { 'id': oOptions, 'url': _url }; } options = mixin(oOptions); options.url = parseURL(options.url); if (options.id === _undefined) { options.id = sm2.setupOptions.idPrefix + (idCounter++); } if (idCheck(options.id, true)) { return sm2.sounds[options.id]; } function make() { options = loopFix(options); sm2.sounds[options.id] = new SMSound(options); sm2.soundIDs.push(options.id); return sm2.sounds[options.id]; } if (html5OK(options)) { oSound = make(); oSound._setup_html5(options); } else { if (sm2.html5Only) { return make(); } if (sm2.html5.usingFlash && options.url && options.url.match(/data\:/i)) { return make(); } if (fV > 8) { if (options.isMovieStar === null) { options.isMovieStar = !!(options.serverURL || (options.type ? options.type.match(netStreamMimeTypes) : false) || (options.url && options.url.match(netStreamPattern))); } } options = policyFix(options, cs); oSound = make(); if (fV === 8) { flash._createSound(options.id, options.loops || 1, options.usePolicyFile); } else { 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) { oSound.connected = true; if (options.onconnect) { options.onconnect.apply(oSound); } } } if (!options.serverURL && (options.autoLoad || options.autoPlay)) { oSound.load(options); } } if (!options.serverURL && options.autoPlay) { oSound.play(); } return oSound; }; this.destroySound = function(sID, _bFromSound) { if (!idCheck(sID)) { return false; } var oS = sm2.sounds[sID], i; oS.stop(); oS._iO = {}; oS.unload(); for (i = 0; i < sm2.soundIDs.length; i++) { if (sm2.soundIDs[i] === sID) { sm2.soundIDs.splice(i, 1); break; } } if (!_bFromSound) { oS.destruct(true); } oS = null; delete sm2.sounds[sID]; return true; }; this.load = function(sID, oOptions) { if (!idCheck(sID)) { return false; } return sm2.sounds[sID].load(oOptions); }; this.unload = function(sID) { if (!idCheck(sID)) { return false; } return sm2.sounds[sID].unload(); }; this.onPosition = function(sID, nPosition, oMethod, oScope) { if (!idCheck(sID)) { return false; } return sm2.sounds[sID].onposition(nPosition, oMethod, oScope); }; this.onposition = this.onPosition; this.clearOnPosition = function(sID, nPosition, oMethod) { if (!idCheck(sID)) { return false; } return sm2.sounds[sID].clearOnPosition(nPosition, oMethod); }; this.play = function(sID, oOptions) { var result = null, overloaded = (oOptions && !(oOptions instanceof Object)); if (!didInit || !sm2.ok()) { return false; } if (!idCheck(sID, overloaded)) { if (!overloaded) { return false; } if (overloaded) { oOptions = { url: oOptions }; } if (oOptions && oOptions.url) { oOptions.id = sID; result = sm2.createSound(oOptions).play(); } } else if (overloaded) { oOptions = { url: oOptions }; } if (result === null) { result = sm2.sounds[sID].play(oOptions); } return result; }; this.start = this.play; this.setPosition = function(sID, nMsecOffset) { if (!idCheck(sID)) { return false; } return sm2.sounds[sID].setPosition(nMsecOffset); }; this.stop = function(sID) { if (!idCheck(sID)) { return false; } return sm2.sounds[sID].stop(); }; this.stopAll = function() { var oSound; for (oSound in sm2.sounds) { if (sm2.sounds.hasOwnProperty(oSound)) { sm2.sounds[oSound].stop(); } } }; this.pause = function(sID) { if (!idCheck(sID)) { return false; } return sm2.sounds[sID].pause(); }; this.pauseAll = function() { var i; for (i = sm2.soundIDs.length - 1; i >= 0; i--) { sm2.sounds[sm2.soundIDs[i]].pause(); } }; this.resume = function(sID) { if (!idCheck(sID)) { return false; } return sm2.sounds[sID].resume(); }; this.resumeAll = function() { var i; for (i = sm2.soundIDs.length- 1 ; i >= 0; i--) { sm2.sounds[sm2.soundIDs[i]].resume(); } }; this.togglePause = function(sID) { if (!idCheck(sID)) { return false; } return sm2.sounds[sID].togglePause(); }; this.setPan = function(sID, nPan) { if (!idCheck(sID)) { return false; } return sm2.sounds[sID].setPan(nPan); }; this.setVolume = function(sID, nVol) { var i, j; if (sID !== _undefined && !isNaN(sID) && nVol === _undefined) { for (i = 0, j = sm2.soundIDs.length; i < j; i++) { sm2.sounds[sm2.soundIDs[i]].setVolume(sID); } return; } if (!idCheck(sID)) { return false; } return sm2.sounds[sID].setVolume(nVol); }; this.mute = function(sID) { var i = 0; if (sID instanceof String) { sID = null; } if (!sID) { for (i = sm2.soundIDs.length - 1; i >= 0; i--) { sm2.sounds[sm2.soundIDs[i]].mute(); } sm2.muted = true; } else { if (!idCheck(sID)) { return false; } return sm2.sounds[sID].mute(); } return true; }; this.muteAll = function() { sm2.mute(); }; this.unmute = function(sID) { var i; if (sID instanceof String) { sID = null; } if (!sID) { for (i = sm2.soundIDs.length - 1; i >= 0; i--) { sm2.sounds[sm2.soundIDs[i]].unmute(); } sm2.muted = false; } else { if (!idCheck(sID)) { return false; } return sm2.sounds[sID].unmute(); } return true; }; this.unmuteAll = function() { sm2.unmute(); }; this.toggleMute = function(sID) { if (!idCheck(sID)) { return false; } return sm2.sounds[sID].toggleMute(); }; this.getMemoryUse = function() { var ram = 0; if (flash && fV !== 8) { ram = parseInt(flash._getMemoryUse(), 10); } return ram; }; this.disable = function(bNoDisable) { var i; if (bNoDisable === _undefined) { bNoDisable = false; } if (disabled) { return false; } disabled = true; for (i = sm2.soundIDs.length - 1; i >= 0; i--) { disableObject(sm2.sounds[sm2.soundIDs[i]]); } initComplete(bNoDisable); event.remove(window, 'load', initUserOnload); return true; }; this.canPlayMIME = function(sMIME) { var result; if (sm2.hasHTML5) { result = html5CanPlay({ type: sMIME }); } if (!result && needsFlash) { result = (sMIME && sm2.ok() ? !!((fV > 8 ? sMIME.match(netStreamMimeTypes) : null) || sMIME.match(sm2.mimePattern)) : null); } return result; }; this.canPlayURL = function(sURL) { var result; if (sm2.hasHTML5) { result = html5CanPlay({ url: sURL }); } if (!result && needsFlash) { result = (sURL && sm2.ok() ? !!(sURL.match(sm2.filePattern)) : null); } return result; }; this.canPlayLink = function(oLink) { if (oLink.type !== _undefined && oLink.type) { if (sm2.canPlayMIME(oLink.type)) { return true; } } return sm2.canPlayURL(oLink.href); }; this.getSoundById = function(sID, _suppressDebug) { if (!sID) { return null; } var result = sm2.sounds[sID]; return result; }; this.onready = function(oMethod, oScope) { var sType = 'onready', result = false; if (typeof oMethod === 'function') { if (!oScope) { oScope = window; } addOnEvent(sType, oMethod, oScope); processOnEvents(); result = true; } else { throw str('needFunction', sType); } return result; }; this.ontimeout = function(oMethod, oScope) { var sType = 'ontimeout', result = false; if (typeof oMethod === 'function') { if (!oScope) { oScope = window; } addOnEvent(sType, oMethod, oScope); processOnEvents({type:sType}); result = true; } else { throw str('needFunction', sType); } return result; }; this._writeDebug = function(sText, sTypeOrObject) { return true; }; this._wD = this._writeDebug; this._debug = function() { }; this.reboot = function(resetEvents, excludeInit) { var i, j, k; for (i = sm2.soundIDs.length- 1 ; i >= 0; i--) { sm2.sounds[sm2.soundIDs[i]].destruct(); } if (flash) { try { if (isIE) { oRemovedHTML = flash.innerHTML; } oRemoved = flash.parentNode.removeChild(flash); } catch(e) { } } oRemovedHTML = oRemoved = needsFlash = flash = null; sm2.enabled = didDCLoaded = didInit = waitingForEI = initPending = didAppend = appendSuccess = disabled = useGlobalHTML5Audio = sm2.swfLoaded = false; sm2.soundIDs = []; sm2.sounds = {}; idCounter = 0; didSetup = false; if (!resetEvents) { 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 { on_queue = []; } sm2.html5 = { 'usingFlash': null }; sm2.flash = {}; sm2.html5Only = false; sm2.ignoreFlash = false; window.setTimeout(function() { if (!excludeInit) { sm2.beginDelayedInit(); } }, 20); return sm2; }; this.reset = function() { return sm2.reboot(true, true); }; this.getMoviePercent = function() { return (flash && 'PercentLoaded' in flash ? flash.PercentLoaded() : null); }; this.beginDelayedInit = function() { windowLoaded = true; domContentLoaded(); setTimeout(function() { if (initPending) { return false; } createMovie(); initMovie(); initPending = true; return true; }, 20); delayWaitForEI(); }; this.destruct = function() { sm2.disable(true); }; SMSound = function(oOptions) { 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, urlOmitted; lastHTML5State = { duration: null, time: null }; this.id = oOptions.id; this.sID = this.id; this.url = oOptions.url; this.options = mixin(oOptions); this.instanceOptions = this.options; this._iO = this.instanceOptions; this.pan = this.options.pan; this.volume = this.options.volume; this.isHTML5 = false; this._a = null; urlOmitted = (this.url ? false : true); this.id3 = {}; this._debug = function() { }; this.load = function(oOptions) { var oSound = null, instanceOptions; if (oOptions !== _undefined) { s._iO = mixin(oOptions, s.options); } else { oOptions = s.options; s._iO = oOptions; if (lastURL && lastURL !== s.url) { s._iO.url = s.url; s.url = null; } } if (!s._iO.url) { s._iO.url = s.url; } s._iO.url = parseURL(s._iO.url); s.instanceOptions = s._iO; instanceOptions = s._iO; if (!instanceOptions.url && !s.url) { return s; } if (instanceOptions.url === s.url && s.readyState !== 0 && s.readyState !== 2) { if (s.readyState === 3 && instanceOptions.onload) { wrapCallback(s, function() { instanceOptions.onload.apply(s, [(!!s.duration)]); }); } return s; } s.loaded = false; s.readyState = 1; s.playState = 0; s.id3 = {}; if (html5OK(instanceOptions)) { oSound = s._setup_html5(instanceOptions); if (!oSound._called_load) { s._html5_canplay = false; if (s.url !== instanceOptions.url) { s._a.src = instanceOptions.url; s.setPosition(0); } s._a.autobuffer = 'auto'; s._a.preload = 'auto'; s._a._called_load = true; } else { } } else { if (sm2.html5Only) { return s; } if (s._iO.url && s._iO.url.match(/data\:/i)) { return s; } try { s.isHTML5 = false; s._iO = policyFix(loopFix(instanceOptions)); if (s._iO.autoPlay && (s._iO.position || s._iO.from)) { s._iO.autoPlay = false; } instanceOptions = s._iO; if (fV === 8) { flash._load(s.id, instanceOptions.url, instanceOptions.stream, instanceOptions.autoPlay, instanceOptions.usePolicyFile); } else { flash._load(s.id, instanceOptions.url, !!(instanceOptions.stream), !!(instanceOptions.autoPlay), instanceOptions.loops || 1, !!(instanceOptions.autoLoad), instanceOptions.usePolicyFile); } } catch(e) { catchError({ type: 'SMSOUND_LOAD_JS_EXCEPTION', fatal: true }); } } s.url = instanceOptions.url; return s; }; this.unload = function() { if (s.readyState !== 0) { if (!s.isHTML5) { if (fV === 8) { flash._unload(s.id, emptyURL); } else { flash._unload(s.id); } } else { stop_html5_timer(); if (s._a) { s._a.pause(); lastURL = html5Unload(s._a); } } resetProperties(); } return s; }; this.destruct = function(_bFromSM) { if (!s.isHTML5) { s._iO.onfailure = null; flash._destroySound(s.id); } else { stop_html5_timer(); if (s._a) { s._a.pause(); html5Unload(s._a); if (!useGlobalHTML5Audio) { remove_html5_events(); } s._a._s = null; s._a = null; } } if (!_bFromSM) { sm2.destroySound(s.id, true); } }; this.play = function(oOptions, _updatePlayState) { var fN, allowMulti, a, onready, audioClone, onended, oncanplay, startOK = true, exit = null; _updatePlayState = (_updatePlayState === _undefined ? true : _updatePlayState); if (!oOptions) { oOptions = {}; } if (s.url) { s._iO.url = s.url; } s._iO = mixin(s._iO, s.options); s._iO = mixin(oOptions, s._iO); s._iO.url = parseURL(s._iO.url); s.instanceOptions = s._iO; if (!s.isHTML5 && s._iO.serverURL && !s.connected) { if (!s.getAutoPlay()) { s.setAutoPlay(true); } return s; } if (html5OK(s._iO)) { s._setup_html5(s._iO); start_html5_timer(); } if (s.playState === 1 && !s.paused) { allowMulti = s._iO.multiShot; if (!allowMulti) { if (s.isHTML5) { s.setPosition(s._iO.position); } exit = s; } else { } } if (exit !== null) { return exit; } if (oOptions.url && oOptions.url !== s.url) { if (!s.readyState && !s.isHTML5 && fV === 8 && urlOmitted) { urlOmitted = false; } else { s.load(s._iO); } } if (!s.loaded) { if (s.readyState === 0) { if (!s.isHTML5 && !sm2.html5Only) { s._iO.autoPlay = true; s.load(s._iO); } else if (s.isHTML5) { s.load(s._iO); } else { exit = s; } s.instanceOptions = s._iO; } else if (s.readyState === 2) { exit = s; } else { } } else { } if (exit !== null) { return exit; } if (!s.isHTML5 && fV === 9 && s.position > 0 && s.position === s.duration) { oOptions.position = 0; } if (s.paused && s.position >= 0 && (!s._iO.serverURL || s.position > 0)) { s.resume(); } else { s._iO = mixin(oOptions, s._iO); if (((!s.isHTML5 && s._iO.position !== null && s._iO.position > 0) || (s._iO.from !== null && s._iO.from > 0) || s._iO.to !== null) && s.instanceCount === 0 && s.playState === 0 && !s._iO.serverURL) { onready = function() { s._iO = mixin(oOptions, s._iO); s.play(s._iO); }; if (s.isHTML5 && !s._html5_canplay) { s.load({ _oncanplay: onready }); exit = false; } else if (!s.isHTML5 && !s.loaded && (!s.readyState || s.readyState !== 2)) { s.load({ onload: onready }); exit = false; } if (exit !== null) { return exit; } s._iO = applyFromTo(); } if (!s.instanceCount || s._iO.multiShotEvents || (s.isHTML5 && s._iO.multiShot && !useGlobalHTML5Audio) || (!s.isHTML5 && fV > 8 && !s.getAutoPlay())) { s.instanceCount++; } if (s._iO.onposition && s.playState === 0) { attachOnPosition(s); } s.playState = 1; s.paused = false; s.position = (s._iO.position !== _undefined && !isNaN(s._iO.position) ? s._iO.position : 0); if (!s.isHTML5) { s._iO = policyFix(loopFix(s._iO)); } if (s._iO.onplay && _updatePlayState) { s._iO.onplay.apply(s); onplay_called = true; } s.setVolume(s._iO.volume, true); s.setPan(s._iO.pan, true); if (!s.isHTML5) { startOK = flash._start(s.id, s._iO.loops || 1, (fV === 9 ? s.position : s.position / msecScale), s._iO.multiShot || false); if (fV === 9 && !startOK) { if (s._iO.onplayerror) { s._iO.onplayerror.apply(s); } } } else { if (s.instanceCount < 2) { start_html5_timer(); a = s._setup_html5(); s.setPosition(s._iO.position); a.play(); } else { audioClone = new Audio(s._iO.url); onended = function() { event.remove(audioClone, 'ended', onended); s._onfinish(s); html5Unload(audioClone); audioClone = null; }; oncanplay = function() { event.remove(audioClone, 'canplay', oncanplay); try { audioClone.currentTime = s._iO.position/msecScale; } catch(err) { } audioClone.play(); }; event.add(audioClone, 'ended', onended); if (s._iO.volume !== _undefined) { audioClone.volume = Math.max(0, Math.min(1, s._iO.volume/100)); } if (s.muted) { audioClone.muted = true; } if (s._iO.position) { event.add(audioClone, 'canplay', oncanplay); } else { audioClone.play(); } } } } return s; }; this.start = this.play; this.stop = function(bAll) { var instanceOptions = s._iO, originalPosition; if (s.playState === 1) { s._onbufferchange(0); s._resetOnPosition(0); s.paused = false; if (!s.isHTML5) { s.playState = 0; } detachOnPosition(); if (instanceOptions.to) { s.clearOnPosition(instanceOptions.to); } if (!s.isHTML5) { flash._stop(s.id, bAll); if (instanceOptions.serverURL) { s.unload(); } } else { if (s._a) { originalPosition = s.position; s.setPosition(0); s.position = originalPosition; s._a.pause(); s.playState = 0; s._onTimer(); stop_html5_timer(); } } s.instanceCount = 0; s._iO = {}; if (instanceOptions.onstop) { instanceOptions.onstop.apply(s); } } return s; }; this.setAutoPlay = function(autoPlay) { s._iO.autoPlay = autoPlay; if (!s.isHTML5) { flash._setAutoPlay(s.id, autoPlay); if (autoPlay) { if (!s.instanceCount && s.readyState === 1) { s.instanceCount++; } } } }; this.getAutoPlay = function() { return s._iO.autoPlay; }; this.setPosition = function(nMsecOffset) { if (nMsecOffset === _undefined) { nMsecOffset = 0; } var position, position1K, offset = (s.isHTML5 ? Math.max(nMsecOffset, 0) : Math.min(s.duration || s._iO.duration, Math.max(nMsecOffset, 0))); s.position = offset; position1K = s.position/msecScale; s._resetOnPosition(s.position); s._iO.position = offset; if (!s.isHTML5) { position = (fV === 9 ? s.position : position1K); if (s.readyState && s.readyState !== 2) { flash._setPosition(s.id, position, (s.paused || !s.playState), s._iO.multiShot); } } else if (s._a) { if (s._html5_canplay) { if (s._a.currentTime !== position1K) { try { s._a.currentTime = position1K; if (s.playState === 0 || s.paused) { s._a.pause(); } } catch(e) { } } } else if (position1K) { return s; } if (s.paused) { s._onTimer(true); } } return s; }; this.pause = function(_bCallFlash) { if (s.paused || (s.playState === 0 && s.readyState !== 1)) { return s; } s.paused = true; if (!s.isHTML5) { if (_bCallFlash || _bCallFlash === _undefined) { flash._pause(s.id, s._iO.multiShot); } } else { s._setup_html5().pause(); stop_html5_timer(); } if (s._iO.onpause) { s._iO.onpause.apply(s); } return s; }; this.resume = function() { var instanceOptions = s._iO; if (!s.paused) { return s; } s.paused = false; s.playState = 1; if (!s.isHTML5) { if (instanceOptions.isMovieStar && !instanceOptions.serverURL) { s.setPosition(s.position); } flash._pause(s.id, instanceOptions.multiShot); } else { s._setup_html5().play(); start_html5_timer(); } if (!onplay_called && instanceOptions.onplay) { instanceOptions.onplay.apply(s); onplay_called = true; } else if (instanceOptions.onresume) { instanceOptions.onresume.apply(s); } return s; }; this.togglePause = function() { if (s.playState === 0) { s.play({ position: (fV === 9 && !s.isHTML5 ? s.position : s.position / msecScale) }); return s; } if (s.paused) { s.resume(); } else { s.pause(); } return s; }; this.setPan = function(nPan, bInstanceOnly) { if (nPan === _undefined) { nPan = 0; } if (bInstanceOnly === _undefined) { bInstanceOnly = false; } if (!s.isHTML5) { flash._setPan(s.id, nPan); } s._iO.pan = nPan; if (!bInstanceOnly) { s.pan = nPan; s.options.pan = nPan; } return s; }; this.setVolume = function(nVol, _bInstanceOnly) { if (nVol === _undefined) { nVol = 100; } if (_bInstanceOnly === _undefined) { _bInstanceOnly = false; } 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; } s._a.volume = Math.max(0, Math.min(1, nVol/100)); } s._iO.volume = nVol; if (!_bInstanceOnly) { s.volume = nVol; s.options.volume = nVol; } return s; }; this.mute = function() { s.muted = true; if (!s.isHTML5) { flash._setVolume(s.id, 0); } else if (s._a) { s._a.muted = true; } return s; }; this.unmute = function() { s.muted = false; var hasIO = (s._iO.volume !== _undefined); if (!s.isHTML5) { flash._setVolume(s.id, hasIO ? s._iO.volume : s.options.volume); } else if (s._a) { s._a.muted = false; } return s; }; this.toggleMute = function() { return (s.muted ? s.unmute() : s.mute()); }; this.onPosition = function(nPosition, oMethod, oScope) { onPositionItems.push({ position: parseInt(nPosition, 10), method: oMethod, scope: (oScope !== _undefined ? oScope : s), fired: false }); return s; }; this.onposition = this.onPosition; this.clearOnPosition = function(nPosition, oMethod) { var i; nPosition = parseInt(nPosition, 10); if (isNaN(nPosition)) { return false; } for (i=0; i < onPositionItems.length; i++) { if (nPosition === onPositionItems[i].position) { if (!oMethod || (oMethod === onPositionItems[i].method)) { if (onPositionItems[i].fired) { onPositionFired--; } onPositionItems.splice(i, 1); } } } }; 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; } } return true; }; this._resetOnPosition = function(nPosition) { var i, item, j = onPositionItems.length; if (!j) { return false; } for (i = j - 1; i >= 0; i--) { item = onPositionItems[i]; if (item.fired && nPosition <= item.position) { item.fired = false; onPositionFired--; } } return true; }; applyFromTo = function() { var instanceOptions = s._iO, f = instanceOptions.from, t = instanceOptions.to, start, end; end = function() { s.clearOnPosition(t, end); s.stop(); }; start = function() { if (t !== null && !isNaN(t)) { s.onPosition(t, end); } }; if (f !== null && !isNaN(f)) { instanceOptions.position = f; instanceOptions.multiShot = false; start(); } return instanceOptions; }; attachOnPosition = function() { var item, op = s._iO.onposition; if (op) { for (item in op) { if (op.hasOwnProperty(item)) { s.onPosition(parseInt(item, 10), op[item]); } } } }; detachOnPosition = function() { var item, op = s._iO.onposition; if (op) { for (item in op) { if (op.hasOwnProperty(item)) { s.clearOnPosition(parseInt(item, 10)); } } } }; start_html5_timer = function() { if (s.isHTML5) { startTimer(s); } }; stop_html5_timer = function() { if (s.isHTML5) { stopTimer(s); } }; resetProperties = function(retainPosition) { if (!retainPosition) { onPositionItems = []; onPositionFired = 0; } onplay_called = false; 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 = []; s.eqData = []; s.eqData.left = []; s.eqData.right = []; s.failures = 0; s.isBuffering = false; s.instanceOptions = {}; s.instanceCount = 0; s.loaded = false; s.metadata = {}; s.readyState = 0; s.muted = false; s.paused = false; s.peakData = { left: 0, right: 0 }; s.waveformData = { left: [], right: [] }; s.playState = 0; s.position = null; s.id3 = {}; }; resetProperties(); this._onTimer = function(bForce) { var duration, isNew = false, time, x = {}; if (s._hasTimer || bForce) { if (s._a && (bForce || ((s.playState > 0 || s.readyState === 1) && !s.paused))) { duration = s._get_html5_duration(); if (duration !== lastHTML5State.duration) { lastHTML5State.duration = duration; s.duration = duration; isNew = true; } s.durationEstimate = s.duration; time = (s._a.currentTime * msecScale || 0); if (time !== lastHTML5State.time) { lastHTML5State.time = time; isNew = true; } if (isNew || bForce) { s._whileplaying(time, x, x, x, x); } } return isNew; } }; this._get_html5_duration = function() { var instanceOptions = s._iO, d = (s._a && s._a.duration ? s._a.duration * msecScale : (instanceOptions && instanceOptions.duration ? instanceOptions.duration : null)), result = (d && !isNaN(d) && d !== Infinity ? d : null); return result; }; this._apply_loop = function(a, nLoops) { a.loop = (nLoops > 1 ? 'loop' : ''); }; this._setup_html5 = function(oOptions) { var instanceOptions = mixin(s._iO, oOptions), a = useGlobalHTML5Audio ? globalHTML5Audio : s._a, dURL = decodeURI(instanceOptions.url), sameURL; if (useGlobalHTML5Audio) { if (dURL === decodeURI(lastGlobalHTML5URL)) { sameURL = true; } } else if (dURL === decodeURI(lastURL)) { sameURL = true; } if (a) { if (a._s) { if (useGlobalHTML5Audio) { if (a._s && a._s.playState && !sameURL) { a._s.stop(); } } else if (!useGlobalHTML5Audio && dURL === decodeURI(lastURL)) { s._apply_loop(a, instanceOptions.loops); return a; } } if (!sameURL) { if (lastURL) { resetProperties(false); } a.src = instanceOptions.url; s.url = instanceOptions.url; lastURL = instanceOptions.url; lastGlobalHTML5URL = instanceOptions.url; a._called_load = false; } } else { if (instanceOptions.autoLoad || instanceOptions.autoPlay) { s._a = new Audio(instanceOptions.url); s._a.load(); } else { s._a = (isOpera && opera.version() < 10 ? new Audio(null) : new Audio()); } a = s._a; a._called_load = false; if (useGlobalHTML5Audio) { globalHTML5Audio = a; } } s.isHTML5 = true; s._a = a; a._s = s; add_html5_events(); s._apply_loop(a, instanceOptions.loops); if (instanceOptions.autoLoad || instanceOptions.autoPlay) { s.load(); } else { a.autobuffer = false; a.preload = 'auto'; } return a; }; add_html5_events = function() { if (s._a._added_events) { return false; } var f; function add(oEvt, oFn, bCapture) { return s._a ? s._a.addEventListener(oEvt, oFn, bCapture || false) : null; } s._a._added_events = true; for (f in html5_events) { if (html5_events.hasOwnProperty(f)) { add(f, html5_events[f]); } } return true; }; remove_html5_events = function() { var f; function remove(oEvt, oFn, bCapture) { return (s._a ? s._a.removeEventListener(oEvt, oFn, bCapture || false) : null); } s._a._added_events = false; for (f in html5_events) { if (html5_events.hasOwnProperty(f)) { remove(f, html5_events[f]); } } }; this._onload = function(nSuccess) { var fN, loadOK = !!nSuccess || (!s.isHTML5 && fV === 8 && s.duration); s.loaded = loadOK; s.readyState = (loadOK ? 3 : 2); s._onbufferchange(0); if (s._iO.onload) { wrapCallback(s, function() { s._iO.onload.apply(s, [loadOK]); }); } return true; }; this._onbufferchange = function(nIsBuffering) { if (s.playState === 0) { return false; } if ((nIsBuffering && s.isBuffering) || (!nIsBuffering && !s.isBuffering)) { return false; } s.isBuffering = (nIsBuffering === 1); if (s._iO.onbufferchange) { s._iO.onbufferchange.apply(s, [nIsBuffering]); } return true; }; this._onsuspend = function() { if (s._iO.onsuspend) { s._iO.onsuspend.apply(s); } return true; }; this._onfailure = function(msg, level, code) { s.failures++; if (s._iO.onfailure && s.failures === 1) { s._iO.onfailure(msg, level, code); } else { } }; this._onwarning = function(msg, level, code) { if (s._iO.onwarning) { s._iO.onwarning(msg, level, code); } }; this._onfinish = function() { var io_onfinish = s._iO.onfinish; s._onbufferchange(0); s._resetOnPosition(0); if (s.instanceCount) { s.instanceCount--; if (!s.instanceCount) { detachOnPosition(); s.playState = 0; s.paused = false; s.instanceCount = 0; s.instanceOptions = {}; s._iO = {}; stop_html5_timer(); if (s.isHTML5) { s.position = 0; } } if (!s.instanceCount || s._iO.multiShotEvents) { if (io_onfinish) { wrapCallback(s, function() { io_onfinish.apply(s); }); } } } }; this._whileloading = function(nBytesLoaded, nBytesTotal, nDuration, nBufferLength) { var instanceOptions = s._iO; s.bytesLoaded = nBytesLoaded; s.bytesTotal = nBytesTotal; s.duration = Math.floor(nDuration); s.bufferLength = nBufferLength; if (!s.isHTML5 && !instanceOptions.isMovieStar) { if (instanceOptions.duration) { s.durationEstimate = (s.duration > instanceOptions.duration) ? s.duration : instanceOptions.duration; } else { s.durationEstimate = parseInt((s.bytesTotal / s.bytesLoaded) * s.duration, 10); } } else { s.durationEstimate = s.duration; } if (!s.isHTML5) { s.buffered = [{ 'start': 0, 'end': s.duration }]; } if ((s.readyState !== 3 || s.isHTML5) && instanceOptions.whileloading) { instanceOptions.whileloading.apply(s); } }; this._whileplaying = function(nPosition, oPeakData, oWaveformDataLeft, oWaveformDataRight, oEQData) { var instanceOptions = s._iO, eqLeft; if (isNaN(nPosition) || nPosition === null) { return false; } s.position = Math.max(0, nPosition); s._processOnPosition(); if (!s.isHTML5 && fV > 8) { if (instanceOptions.usePeakData && oPeakData !== _undefined && oPeakData) { s.peakData = { left: oPeakData.leftPeak, right: oPeakData.rightPeak }; } if (instanceOptions.useWaveformData && oWaveformDataLeft !== _undefined && oWaveformDataLeft) { s.waveformData = { left: oWaveformDataLeft.split(','), right: oWaveformDataRight.split(',') }; } if (instanceOptions.useEQData) { if (oEQData !== _undefined && oEQData && oEQData.leftEQ) { eqLeft = oEQData.leftEQ.split(','); s.eqData = eqLeft; s.eqData.left = eqLeft; if (oEQData.rightEQ !== _undefined && oEQData.rightEQ) { s.eqData.right = oEQData.rightEQ.split(','); } } } } if (s.playState === 1) { if (!s.isHTML5 && fV === 8 && !s.position && s.isBuffering) { s._onbufferchange(0); } if (instanceOptions.whileplaying) { instanceOptions.whileplaying.apply(s); } } return true; }; this._oncaptiondata = function(oData) { s.captiondata = oData; if (s._iO.oncaptiondata) { s._iO.oncaptiondata.apply(s, [oData]); } }; this._onmetadata = function(oMDProps, oMDData) { var oData = {}, i, j; for (i = 0, j = oMDProps.length; i < j; i++) { oData[oMDProps[i]] = oMDData[i]; } s.metadata = oData; if (s._iO.onmetadata) { s._iO.onmetadata.call(s, s.metadata); } }; this._onid3 = function(oID3Props, oID3Data) { var oData = [], i, j; for (i = 0, j = oID3Props.length; i < j; i++) { oData[oID3Props[i]] = oID3Data[i]; } s.id3 = mixin(s.id3, oData); if (s._iO.onid3) { s._iO.onid3.apply(s); } }; this._onconnect = function(bSuccess) { bSuccess = (bSuccess === 1); s.connected = bSuccess; if (bSuccess) { s.failures = 0; if (idCheck(s.id)) { if (s.getAutoPlay()) { s.play(_undefined, s.getAutoPlay()); } else if (s._iO.autoLoad) { s.load(); } } if (s._iO.onconnect) { s._iO.onconnect.apply(s, [bSuccess]); } } }; this._ondataerror = function(sError) { if (s.playState > 0) { if (s._iO.ondataerror) { s._iO.ondataerror.apply(s); } } }; }; getDocument = function() { return (doc.body || doc.getElementsByTagName('div')[0]); }; id = function(sID) { return doc.getElementById(sID); }; mixin = function(oMain, oAdd) { var o1 = (oMain || {}), o2, o; o2 = (oAdd === _undefined ? sm2.defaultOptions : oAdd); for (o in o2) { if (o2.hasOwnProperty(o) && o1[o] === _undefined) { if (typeof o2[o] !== 'object' || o2[o] === null) { o1[o] = o2[o]; } else { o1[o] = mixin(o1[o], o2[o]); } } } return o1; }; wrapCallback = function(oSound, callback) { if (!oSound.isHTML5 && fV === 8) { window.setTimeout(callback, 0); } else { callback(); } }; extraOptions = { 'onready': 1, 'ontimeout': 1, 'defaultOptions': 1, 'flash9Options': 1, 'movieStarOptions': 1 }; assign = function(o, oParent) { var i, result = true, hasParent = (oParent !== _undefined), setupOptions = sm2.setupOptions, bonusOptions = extraOptions; for (i in o) { if (o.hasOwnProperty(i)) { if (typeof o[i] !== 'object' || o[i] === null || o[i] instanceof Array || o[i] instanceof RegExp) { if (hasParent && bonusOptions[oParent] !== _undefined) { sm2[oParent][i] = o[i]; } else if (setupOptions[i] !== _undefined) { sm2.setupOptions[i] = o[i]; sm2[i] = o[i]; } else if (bonusOptions[i] === _undefined) { result = false; } else { if (sm2[i] instanceof Function) { sm2[i].apply(sm2, (o[i] instanceof Array ? o[i] : [o[i]])); } else { sm2[i] = o[i]; } } } else { if (bonusOptions[i] === _undefined) { result = false; } else { return assign(o[i], i); } } } } return result; }; function preferFlashCheck(kind) { return (sm2.preferFlash && hasFlash && !sm2.ignoreFlash && (sm2.flash[kind] !== _undefined && sm2.flash[kind])); } event = (function() { var old = (window.attachEvent), evt = { add: (old ? 'attachEvent' : 'addEventListener'), remove: (old ? 'detachEvent' : 'removeEventListener') }; function getArgs(oArgs) { var args = slice.call(oArgs), len = args.length; if (old) { args[1] = 'on' + args[1]; if (len > 3) { args.pop(); } } else if (len === 3) { args.push(false); } return args; } function apply(args, sType) { var element = args.shift(), method = [evt[sType]]; if (old) { element[method](args[0], args[1]); } else { element[method].apply(element, args); } } function add() { apply(getArgs(arguments), 'add'); } function remove() { apply(getArgs(arguments), 'remove'); } return { 'add': add, 'remove': remove }; }()); function html5_event(oFn) { return function(e) { var s = this._s, result; if (!s || !s._a) { result = null; } else { result = oFn.call(this, e); } return result; }; } html5_events = { abort: html5_event(function() { }), canplay: html5_event(function() { var s = this._s, position1K; if (s._html5_canplay) { return true; } s._html5_canplay = true; s._onbufferchange(0); position1K = (s._iO.position !== _undefined && !isNaN(s._iO.position) ? s._iO.position/msecScale : null); if (this.currentTime !== position1K) { try { this.currentTime = position1K; } catch(ee) { } } if (s._iO._oncanplay) { s._iO._oncanplay(); } }), canplaythrough: html5_event(function() { var s = this._s; if (!s.loaded) { s._onbufferchange(0); s._whileloading(s.bytesLoaded, s.bytesTotal, s._get_html5_duration()); s._onload(true); } }), durationchange: html5_event(function() { var s = this._s, duration; duration = s._get_html5_duration(); if (!isNaN(duration) && duration !== s.duration) { s.durationEstimate = s.duration = duration; } }), ended: html5_event(function() { var s = this._s; s._onfinish(); }), error: html5_event(function() { this._s._onload(false); }), loadeddata: html5_event(function() { var s = this._s; if (!s._loaded && !isSafari) { s.duration = s._get_html5_duration(); } }), loadedmetadata: html5_event(function() { }), loadstart: html5_event(function() { this._s._onbufferchange(1); }), play: html5_event(function() { this._s._onbufferchange(0); }), playing: html5_event(function() { this._s._onbufferchange(0); }), progress: html5_event(function(e) { var s = this._s, i, j, progStr, buffered = 0, isProgress = (e.type === 'progress'), ranges = e.target.buffered, loaded = (e.loaded || 0), total = (e.total || 1); s.buffered = []; if (ranges && ranges.length) { for (i = 0, j = ranges.length; i < j; i++) { s.buffered.push({ 'start': ranges.start(i) * msecScale, 'end': ranges.end(i) * msecScale }); } buffered = (ranges.end(0) - ranges.start(0)) * msecScale; loaded = Math.min(1, buffered / (e.target.duration * msecScale)); } if (!isNaN(loaded)) { s._whileloading(loaded, total, s._get_html5_duration()); if (loaded && total && loaded === total) { html5_events.canplaythrough.call(this, e); } } }), ratechange: html5_event(function() { }), suspend: html5_event(function(e) { var s = this._s; html5_events.progress.call(this, e); s._onsuspend(); }), stalled: html5_event(function() { }), timeupdate: html5_event(function() { this._s._onTimer(); }), waiting: html5_event(function() { var s = this._s; s._onbufferchange(1); }) }; html5OK = function(iO) { var result; if (!iO || (!iO.type && !iO.url && !iO.serverURL)) { result = false; } else if (iO.serverURL || (iO.type && preferFlashCheck(iO.type))) { result = false; } else { result = ((iO.type ? html5CanPlay({type:iO.type}) : html5CanPlay({url:iO.url}) || sm2.html5Only || iO.url.match(/data\:/i))); } return result; }; html5Unload = function(oAudio) { var url; if (oAudio) { url = (isSafari ? emptyURL : (sm2.html5.canPlayType('audio/wav') ? emptyWAV : emptyURL)); oAudio.src = url; if (oAudio._called_unload !== _undefined) { oAudio._called_load = false; } } if (useGlobalHTML5Audio) { lastGlobalHTML5URL = null; } return url; }; html5CanPlay = function(o) { if (!sm2.useHTML5Audio || !sm2.hasHTML5) { return false; } var url = (o.url || null), mime = (o.type || null), aF = sm2.audioFormats, result, offset, fileExt, item; if (mime && sm2.html5[mime] !== _undefined) { return (sm2.html5[mime] && !preferFlashCheck(mime)); } if (!html5Ext) { html5Ext = []; for (item in aF) { if (aF.hasOwnProperty(item)) { html5Ext.push(item); if (aF[item].related) { html5Ext = html5Ext.concat(aF[item].related); } } } html5Ext = new RegExp('\\.('+html5Ext.join('|')+')(\\?.*)?$','i'); } fileExt = (url ? url.toLowerCase().match(html5Ext) : null); if (!fileExt || !fileExt.length) { if (!mime) { result = false; } else { offset = mime.indexOf(';'); fileExt = (offset !== -1 ? mime.substr(0,offset) : mime).substr(6); } } else { fileExt = fileExt[1]; } if (fileExt && sm2.html5[fileExt] !== _undefined) { result = (sm2.html5[fileExt] && !preferFlashCheck(fileExt)); } else { mime = 'audio/' + fileExt; result = sm2.html5.canPlayType({type:mime}); sm2.html5[fileExt] = result; result = (result && sm2.html5[mime] && !preferFlashCheck(mime)); } return result; }; testHTML5 = function() { if (!sm2.useHTML5Audio || !sm2.hasHTML5) { sm2.html5.usingFlash = true; needsFlash = true; return false; } var a = (Audio !== _undefined ? (isOpera && opera.version() < 10 ? new Audio(null) : new Audio()) : null), item, lookup, support = {}, aF, i; function cp(m) { var canPlay, j, result = false, isOK = false; if (!a || typeof a.canPlayType !== 'function') { return result; } if (m instanceof Array) { for (i = 0, j = m.length; i < j; i++) { if (sm2.html5[m[i]] || a.canPlayType(m[i]).match(sm2.html5Test)) { isOK = true; sm2.html5[m[i]] = true; 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(sm2.html5Test))); } return result; } aF = sm2.audioFormats; for (item in aF) { if (aF.hasOwnProperty(item)) { lookup = 'audio/' + item; support[item] = cp(aF[item].type); support[lookup] = support[item]; if (item.match(flashMIME)) { sm2.flash[item] = true; sm2.flash[lookup] = true; } else { sm2.flash[item] = false; sm2.flash[lookup] = false; } if (aF[item] && aF[item].related) { for (i = aF[item].related.length - 1; i >= 0; i--) { support['audio/' + 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); sm2.html5 = mixin(sm2.html5, support); sm2.html5.usingFlash = featureCheck(); needsFlash = sm2.html5.usingFlash; return true; }; strings = { }; str = function() { }; loopFix = function(sOpt) { if (fV === 8 && sOpt.loops > 1 && sOpt.stream) { sOpt.stream = false; } return sOpt; }; policyFix = function(sOpt, sPre) { if (sOpt && !sOpt.usePolicyFile && (sOpt.onid3 || sOpt.usePeakData || sOpt.useWaveformData || sOpt.useEQData)) { sOpt.usePolicyFile = true; } return sOpt; }; complain = function(sMsg) { }; doNothing = function() { return false; }; disableObject = function(o) { var oProp; for (oProp in o) { if (o.hasOwnProperty(oProp) && typeof o[oProp] === 'function') { o[oProp] = doNothing; } } oProp = null; }; failSafely = function(bNoDisable) { if (bNoDisable === _undefined) { bNoDisable = false; } if (disabled || bNoDisable) { sm2.disable(bNoDisable); } }; normalizeMovieURL = function(smURL) { var urlParams = null, url; if (smURL) { if (smURL.match(/\.swf(\?.*)?$/i)) { urlParams = smURL.substr(smURL.toLowerCase().lastIndexOf('.swf?') + 4); if (urlParams) { return smURL; } } else if (smURL.lastIndexOf('/') !== smURL.length - 1) { smURL += '/'; } } url = (smURL && smURL.lastIndexOf('/') !== - 1 ? smURL.substr(0, smURL.lastIndexOf('/') + 1) : './') + sm2.movieURL; if (sm2.noSWFCache) { url += ('?ts=' + new Date().getTime()); } return url; }; setVersionInfo = function() { fV = parseInt(sm2.flashVersion, 10); if (fV !== 8 && fV !== 9) { sm2.flashVersion = fV = defaultFlashVersion; } var isDebug = (sm2.debugMode || sm2.debugFlash ? '_debug.swf' : '.swf'); if (sm2.useHTML5Audio && !sm2.html5Only && sm2.audioFormats.mp4.required && fV < 9) { sm2.flashVersion = fV = 9; } sm2.version = sm2.versionNumber + (sm2.html5Only ? ' (HTML5-only mode)' : (fV === 9 ? ' (AS3/Flash 9)' : ' (AS2/Flash 8)')); if (fV > 8) { sm2.defaultOptions = mixin(sm2.defaultOptions, sm2.flash9Options); sm2.features.buffering = true; sm2.defaultOptions = mixin(sm2.defaultOptions, sm2.movieStarOptions); sm2.filePatterns.flash9 = new RegExp('\\.(mp3|' + netStreamTypes.join('|') + ')(\\?.*)?$', 'i'); sm2.features.movieStar = true; } else { sm2.features.movieStar = false; } sm2.filePattern = sm2.filePatterns[(fV !== 8 ? 'flash9' : 'flash8')]; sm2.movieURL = (fV === 8 ? 'soundmanager2.swf' : 'soundmanager2_flash9.swf').replace('.swf', isDebug); sm2.features.peakData = sm2.features.waveformData = sm2.features.eqData = (fV > 8); }; setPolling = function(bPolling, bHighPerformance) { if (!flash) { return false; } flash._setPolling(bPolling, bHighPerformance); }; initDebug = function() { }; idCheck = this.getSoundById; getSWFCSS = function() { var css = []; if (sm2.debugMode) { css.push(swfCSS.sm2Debug); } if (sm2.debugFlash) { css.push(swfCSS.flashDebug); } if (sm2.useHighPerformance) { css.push(swfCSS.highPerf); } return css.join(' '); }; flashBlockHandler = function() { var name = str('fbHandler'), p = sm2.getMoviePercent(), css = swfCSS, error = { type:'FLASHBLOCK' }; if (sm2.html5Only) { return false; } if (!sm2.ok()) { if (needsFlash) { sm2.oMC.className = getSWFCSS() + ' ' + css.swfDefault + ' ' + (p === null ? css.swfTimedout : css.swfError); } sm2.didFlashBlock = true; processOnEvents({ type: 'ontimeout', ignoreInit: true, error: error }); catchError(error); } else { if (sm2.oMC) { sm2.oMC.className = [getSWFCSS(), css.swfDefault, css.swfLoaded + (sm2.didFlashBlock ? ' ' + css.swfUnblocked : '')].join(' '); } } }; addOnEvent = function(sType, oMethod, oScope) { if (on_queue[sType] === _undefined) { on_queue[sType] = []; } on_queue[sType].push({ 'method': oMethod, 'scope': (oScope || null), 'fired': false }); }; processOnEvents = function(oOptions) { if (!oOptions) { oOptions = { type: (sm2.ok() ? 'onready' : 'ontimeout') }; } if (!didInit && oOptions && !oOptions.ignoreInit) { return false; } if (oOptions.type === 'ontimeout' && (sm2.ok() || (disabled && !oOptions.ignoreInit))) { return false; } var status = { success: (oOptions && oOptions.ignoreInit ? sm2.ok() : !disabled) }, srcQueue = (oOptions && oOptions.type ? on_queue[oOptions.type] || [] : []), queue = [], i, j, args = [status], canRetry = (needsFlash && !sm2.ok()); if (oOptions.error) { args[0].error = oOptions.error; } for (i = 0, j = srcQueue.length; i < j; i++) { if (srcQueue[i].fired !== true) { queue.push(srcQueue[i]); } } if (queue.length) { 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) { queue[i].fired = true; } } } return true; }; initUserOnload = function() { window.setTimeout(function() { if (sm2.useFlashBlock) { flashBlockHandler(); } processOnEvents(); if (typeof sm2.onload === 'function') { sm2.onload.apply(window); } if (sm2.waitForWindowLoad) { event.add(window, 'load', initUserOnload); } }, 1); }; detectFlash = function() { if (hasFlash !== _undefined) { return hasFlash; } 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 (AX !== _undefined && !ua.match(/MSAppHost/i)) { try { obj = new AX('ShockwaveFlash.ShockwaveFlash'); } catch(e) { obj = null; } hasPlugin = (!!obj); obj = null; } hasFlash = hasPlugin; return hasPlugin; }; featureCheck = function() { var flashNeeded, item, formats = sm2.audioFormats, isSpecial = (is_iDevice && !!(ua.match(/os (1|2|3_0|3_1)\s/i))); if (isSpecial) { sm2.hasHTML5 = false; sm2.html5Only = true; if (sm2.oMC) { sm2.oMC.style.display = 'none'; } } else { if (sm2.useHTML5Audio) { if (!sm2.html5 || !sm2.html5.canPlayType) { sm2.hasHTML5 = false; } } } if (sm2.useHTML5Audio && sm2.hasHTML5) { canIgnoreFlash = true; for (item in formats) { if (formats.hasOwnProperty(item)) { if (formats[item].required) { if (!sm2.html5.canPlayType(formats[item].type)) { canIgnoreFlash = false; flashNeeded = true; } else if (sm2.preferFlash && (sm2.flash[item] || sm2.flash[formats[item].type])) { flashNeeded = true; } } } } } if (sm2.ignoreFlash) { flashNeeded = false; canIgnoreFlash = true; } sm2.html5Only = (sm2.hasHTML5 && sm2.useHTML5Audio && !flashNeeded); return (!sm2.html5Only); }; parseURL = function(url) { var i, j, urlResult = 0, result; if (url instanceof Array) { for (i = 0, j = url.length; i < j; i++) { if (url[i] instanceof Object) { if (sm2.canPlayMIME(url[i].type)) { urlResult = i; break; } } else if (sm2.canPlayURL(url[i])) { urlResult = i; break; } } if (url[urlResult].url) { url[urlResult] = url[urlResult].url; } result = url[urlResult]; } else { result = url; } return result; }; startTimer = function(oSound) { if (!oSound._hasTimer) { oSound._hasTimer = true; if (!mobileHTML5 && sm2.html5PollingInterval) { if (h5IntervalTimer === null && h5TimerCount === 0) { h5IntervalTimer = setInterval(timerExecute, sm2.html5PollingInterval); } h5TimerCount++; } } }; stopTimer = function(oSound) { if (oSound._hasTimer) { oSound._hasTimer = false; if (!mobileHTML5 && sm2.html5PollingInterval) { h5TimerCount--; } } }; timerExecute = function() { var i; if (h5IntervalTimer !== null && !h5TimerCount) { clearInterval(h5IntervalTimer); h5IntervalTimer = null; return false; } for (i = sm2.soundIDs.length - 1; i >= 0; i--) { if (sm2.sounds[sm2.soundIDs[i]].isHTML5 && sm2.sounds[sm2.soundIDs[i]]._hasTimer) { sm2.sounds[sm2.soundIDs[i]]._onTimer(); } } }; catchError = function(options) { options = (options !== _undefined ? options : {}); if (typeof sm2.onerror === 'function') { sm2.onerror.apply(window, [{ type: (options.type !== _undefined ? options.type : null) }]); } if (options.fatal !== _undefined && options.fatal) { sm2.disable(); } }; badSafariFix = function() { if (!isBadSafari || !detectFlash()) { return false; } var aF = sm2.audioFormats, i, item; for (item in aF) { if (aF.hasOwnProperty(item)) { if (item === 'mp3' || item === 'mp4') { sm2.html5[item] = false; if (aF[item] && aF[item].related) { for (i = aF[item].related.length - 1; i >= 0; i--) { sm2.html5[aF[item].related[i]] = false; } } } } } }; this._setSandboxType = function(sandboxType) { }; this._externalInterfaceOK = function(swfVersion) { if (sm2.swfLoaded) { return false; } var e; sm2.swfLoaded = true; tryInitOnFocus = false; if (isBadSafari) { badSafariFix(); } setTimeout(init, isIE ? 100 : 1); }; createMovie = function(smID, smURL) { if (didAppend && appendSuccess) { return false; } function initMsg() { } if (sm2.html5Only) { setVersionInfo(); initMsg(); sm2.oMC = id(sm2.movieID); init(); didAppend = true; appendSuccess = true; return false; } var remoteURL = (smURL || sm2.url), localURL = (sm2.altURL || remoteURL), swfTitle = 'JS/Flash audio component (SoundManager 2)', oTarget = getDocument(), extraClass = getSWFCSS(), isRTL = null, html = doc.getElementsByTagName('html')[0], oEmbed, oMovie, tmp, movieHTML, oEl, s, x, sClass; isRTL = (html && html.dir && html.dir.match(/rtl/i)); smID = (smID === _undefined ? sm2.id : smID); function param(name, value) { return ''; } setVersionInfo(); sm2.url = normalizeMovieURL(overHTTP ? remoteURL : localURL); smURL = sm2.url; sm2.wmode = (!sm2.wmode && sm2.useHighPerformance ? 'transparent' : sm2.wmode); if (sm2.wmode !== null && (ua.match(/msie 8/i) || (!isIE && !sm2.useHighPerformance)) && navigator.platform.match(/win32|win64/i)) { messages.push(strings.spcWmode); sm2.wmode = null; } oEmbed = { 'name': smID, 'id': smID, 'src': smURL, 'quality': 'high', 'allowScriptAccess': sm2.allowScriptAccess, 'bgcolor': sm2.bgColor, 'pluginspage': http + 'www.macromedia.com/go/getflashplayer', 'title': swfTitle, 'type': 'application/x-shockwave-flash', 'wmode': sm2.wmode, 'hasPriority': 'true' }; if (sm2.debugFlash) { oEmbed.FlashVars = 'debug=1'; } if (!sm2.wmode) { delete oEmbed.wmode; } if (isIE) { oMovie = doc.createElement('div'); movieHTML = [ '', param('movie', smURL), param('AllowScriptAccess', sm2.allowScriptAccess), param('quality', oEmbed.quality), (sm2.wmode? param('wmode', sm2.wmode): ''), param('bgcolor', sm2.bgColor), param('hasPriority', 'true'), (sm2.debugFlash ? param('FlashVars', oEmbed.FlashVars) : ''), '' ].join(''); } else { oMovie = doc.createElement('embed'); for (tmp in oEmbed) { if (oEmbed.hasOwnProperty(tmp)) { oMovie.setAttribute(tmp, oEmbed[tmp]); } } } initDebug(); extraClass = getSWFCSS(); oTarget = getDocument(); if (oTarget) { sm2.oMC = (id(sm2.movieID) || doc.createElement('div')); if (!sm2.oMC.id) { sm2.oMC.id = sm2.movieID; sm2.oMC.className = swfCSS.swfDefault + ' ' + extraClass; s = null; oEl = null; if (!sm2.useFlashBlock) { if (sm2.useHighPerformance) { s = { 'position': 'fixed', 'width': '8px', 'height': '8px', 'bottom': '0px', 'left': '0px', 'overflow': 'hidden' }; } else { s = { 'position': 'absolute', 'width': '6px', 'height': '6px', 'top': '-9999px', 'left': '-9999px' }; if (isRTL) { s.left = Math.abs(parseInt(s.left, 10)) + 'px'; } } } if (isWebkit) { sm2.oMC.style.zIndex = 10000; } if (!sm2.debugFlash) { for (x in s) { if (s.hasOwnProperty(x)) { sm2.oMC.style[x] = s[x]; } } } try { if (!isIE) { sm2.oMC.appendChild(oMovie); } oTarget.appendChild(sm2.oMC); if (isIE) { oEl = sm2.oMC.appendChild(doc.createElement('div')); oEl.className = swfCSS.swfBox; oEl.innerHTML = movieHTML; } appendSuccess = true; } catch(e) { throw new Error(str('domError') + ' \n' + e.toString()); } } else { 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; } } didAppend = true; initMsg(); return true; }; initMovie = function() { if (sm2.html5Only) { createMovie(); return false; } if (flash) { return false; } if (!sm2.url) { return false; } flash = sm2.getMovie(sm2.id); if (!flash) { if (!oRemoved) { createMovie(sm2.id, sm2.url); } else { if (!isIE) { sm2.oMC.appendChild(oRemoved); } else { sm2.oMC.innerHTML = oRemovedHTML; } oRemoved = null; didAppend = true; } flash = sm2.getMovie(sm2.id); } if (typeof sm2.oninitmovie === 'function') { setTimeout(sm2.oninitmovie, 1); } return true; }; delayWaitForEI = function() { setTimeout(waitForEI, 1000); }; rebootIntoHTML5 = function() { window.setTimeout(function() { sm2.setup({ preferFlash: false }).reboot(); sm2.didFlashBlock = true; sm2.beginDelayedInit(); }, 1); }; waitForEI = function() { var p, loadIncomplete = false; if (!sm2.url) { return false; } if (waitingForEI) { return false; } waitingForEI = true; event.remove(window, 'load', delayWaitForEI); if (hasFlash && tryInitOnFocus && !isFocused) { return false; } if (!didInit) { p = sm2.getMoviePercent(); if (p > 0 && p < 100) { loadIncomplete = true; } } setTimeout(function() { p = sm2.getMoviePercent(); if (loadIncomplete) { waitingForEI = false; window.setTimeout(delayWaitForEI, 1); return false; } if (!didInit && okToDisable) { if (p === null) { if (sm2.useFlashBlock || sm2.flashLoadTimeout === 0) { if (sm2.useFlashBlock) { flashBlockHandler(); } } else { if (!sm2.useFlashBlock && canIgnoreFlash) { rebootIntoHTML5(); } else { processOnEvents({ type: 'ontimeout', ignoreInit: true, error: { type: 'INIT_FLASHBLOCK' } }); } } } else { if (sm2.flashLoadTimeout === 0) { } else { if (!sm2.useFlashBlock && canIgnoreFlash) { rebootIntoHTML5(); } else { failSafely(true); } } } } }, sm2.flashLoadTimeout); }; handleFocus = function() { function cleanup() { event.remove(window, 'focus', handleFocus); } if (isFocused || !tryInitOnFocus) { cleanup(); return true; } okToDisable = true; isFocused = true; waitingForEI = false; delayWaitForEI(); cleanup(); return true; }; flushMessages = function() { }; showSupport = function() { }; initComplete = function(bNoDisable) { if (didInit) { return false; } if (sm2.html5Only) { didInit = true; initUserOnload(); return true; } var wasTimeout = (sm2.useFlashBlock && sm2.flashLoadTimeout && !sm2.getMoviePercent()), result = true, error; if (!wasTimeout) { didInit = true; } error = { type: (!hasFlash && needsFlash ? 'NO_FLASH' : 'INIT_TIMEOUT') }; 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 }); catchError(error); result = false; } else { } if (!disabled) { if (sm2.waitForWindowLoad && !windowLoaded) { event.add(window, 'load', initUserOnload); } else { initUserOnload(); } } return result; }; setProperties = function() { var i, o = sm2.setupOptions; for (i in o) { if (o.hasOwnProperty(i)) { if (sm2[i] === _undefined) { sm2[i] = o[i]; } else if (sm2[i] !== o[i]) { sm2.setupOptions[i] = sm2[i]; } } } }; init = function() { if (didInit) { return false; } function cleanup() { event.remove(window, 'load', sm2.beginDelayedInit); } if (sm2.html5Only) { if (!didInit) { cleanup(); sm2.enabled = true; initComplete(); } return true; } initMovie(); try { flash._externalInterfaceTest(false); setPolling(true, (sm2.flashPollingInterval || (sm2.useHighPerformance ? 10 : 50))); if (!sm2.debugMode) { flash._disableDebug(); } sm2.enabled = true; if (!sm2.html5Only) { event.add(window, 'unload', doNothing); } } catch(e) { catchError({ type: 'JS_TO_FLASH_EXCEPTION', fatal: true }); failSafely(true); initComplete(); return false; } initComplete(); cleanup(); return true; }; domContentLoaded = function() { if (didDCLoaded) { return false; } didDCLoaded = true; setProperties(); initDebug(); if (!hasFlash && sm2.hasHTML5) { sm2.setup({ 'useHTML5Audio': true, 'preferFlash': false }); } testHTML5(); if (!hasFlash && needsFlash) { messages.push(strings.needFlash); sm2.setup({ 'flashLoadTimeout': 1 }); } if (doc.removeEventListener) { doc.removeEventListener('DOMContentLoaded', domContentLoaded, false); } initMovie(); return true; }; domContentLoadedIE = function() { if (doc.readyState === 'complete') { domContentLoaded(); doc.detachEvent('onreadystatechange', domContentLoadedIE); } return true; }; winOnLoad = function() { windowLoaded = true; domContentLoaded(); event.remove(window, 'load', winOnLoad); }; detectFlash(); event.add(window, 'focus', handleFocus); event.add(window, 'load', delayWaitForEI); event.add(window, 'load', winOnLoad); if (doc.addEventListener) { doc.addEventListener('DOMContentLoaded', domContentLoaded, false); } else if (doc.attachEvent) { doc.attachEvent('onreadystatechange', domContentLoadedIE); } else { catchError({ type: 'NO_DOM2_EVENTS', fatal: true }); } } // SM2_DEFER details: http://www.schillmania.com/projects/soundmanager2/doc/getstarted/#lazy-loading if (window.SM2_DEFER === _undefined || !SM2_DEFER) { soundManager = new SoundManager(); } if (typeof module === 'object' && module && typeof module.exports === 'object') { module.exports.SoundManager = SoundManager; module.exports.soundManager = soundManager; } else if (typeof define === 'function' && define.amd) { define(function() { function getInstance(smBuilder) { if (!window.soundManager && smBuilder instanceof Function) { var instance = smBuilder(SoundManager); if (instance instanceof SoundManager) { window.soundManager = instance; } } return window.soundManager; } return { constructor: SoundManager, getInstance: getInstance } }); } // standard browser case // constructor window.SoundManager = SoundManager; // public API, flash callbacks etc. window.soundManager = soundManager; }(window)); SoundManager2-2.97a.20150601/script/soundmanager2.js000066400000000000000000004765471253275212400216170ustar00rootroot00000000000000/** @license * * SoundManager 2: JavaScript Sound for the Web * ---------------------------------------------- * http://schillmania.com/projects/soundmanager2/ * * Copyright (c) 2007, Scott Schiller. All rights reserved. * Code provided under the BSD License: * http://schillmania.com/projects/soundmanager2/license.txt * * V2.97a.20150601 */ /*global window, SM2_DEFER, sm2Debugger, console, document, navigator, setTimeout, setInterval, clearInterval, Audio, opera, module, define */ /*jslint regexp: true, sloppy: true, white: true, nomen: true, plusplus: true, todo: 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 * Alternately, serve this file with gzip for 75% compression savings (~30KB over HTTP.) * * You may notice and comments in this source; these are delimiters for * 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, _undefined) { "use strict"; if (!window || !window.document) { // Don't cross the [environment] streams. SM2 expects to be running in a browser, not under node.js etc. // Additionally, if a browser somehow manages to fail this test, as Egon said: "It would be bad." throw new Error('SoundManager requires a browser with window and document objects.'); } var soundManager = null; /** * The SoundManager constructor. * * @constructor * @param {string} smURL Optional: Path to SWF files * @param {string} smID Optional: The ID to use for the SWF container element * @this {SoundManager} * @return {SoundManager} The new SoundManager instance */ function SoundManager(smURL, smID) { /** * soundManager configuration options list * defines top-level configuration properties to be applied to the soundManager instance (eg. soundManager.flashVersion) * to set these properties, use the setup() method - eg., soundManager.setup({url: '/swf/', flashVersion: 9}) */ this.setupOptions = { 'url': (smURL || null), // path (directory) where SoundManager 2 SWFs exist, eg., /path/to/swfs/ 'flashVersion': 8, // flash build to use (8 or 9.) Some API features require 9. 'debugMode': true, // enable debugging output (console.log() with HTML fallback) 'debugFlash': false, // enable debugging output inside SWF, troubleshoot Flash/browser issues 'useConsole': true, // use console.log() if available (otherwise, writes to #soundmanager-debug element) 'consoleOnly': true, // if console is being used, do not create/write to #soundmanager-debug 'waitForWindowLoad': false, // force SM2 to wait for window.onload() before trying to call soundManager.onload() 'bgColor': '#ffffff', // SWF background color. N/A when wmode = 'transparent' 'useHighPerformance': false, // position:fixed flash movie can help increase js/flash speed, minimize lag 'flashPollingInterval': null, // msec affecting whileplaying/loading callback frequency. If null, default of 50 msec is used. 'html5PollingInterval': null, // msec affecting whileplaying() for HTML5 audio, excluding mobile devices. If null, native HTML5 update events are used. 'flashLoadTimeout': 1000, // msec to wait for flash movie to load before failing (0 = infinity) '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 (MP3/MP4 support varies.) Ideally, transparent vs. Flash API where possible. 'forceUseGlobalHTML5Audio': false, // if true, a single Audio() object is used for all sounds - and only one can play at a time. 'ignoreMobileRestrictions': false, // if true, SM2 will not apply global HTML5 audio rules to mobile UAs. iOS WebViews purportedly allow multiple Audio() objects, auto-play etc. 'html5Test': /^(probably|maybe)$/i, // HTML5 Audio() format support test. Use /^probably$/i; if you want to be more conservative. '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. }; this.defaultOptions = { /** * the default configuration for sound objects made with createSound() and related methods * eg., volume, auto-load behaviour and so forth */ 'autoLoad': false, // enable automatic loading (otherwise .load() will be called on demand with .play(), the latter being nicer on bandwidth - if you want to .load yourself, you also can) 'autoPlay': false, // enable playing of file as soon as possible (much faster if "stream" is true) 'from': null, // position to start playback within a sound (msec), default = beginning 'loops': 1, // how many times to repeat the sound (position will wrap around to 0, setPosition() will break out of loop when >0) 'onid3': null, // callback function for "ID3 data is added/available" 'onload': null, // callback function for "load finished" 'whileloading': null, // callback function for "download progress update" (X of Y bytes received) 'onplay': null, // callback for "play" start 'onpause': null, // callback for "pause" 'onresume': null, // callback for "resume" (pause toggle) 'whileplaying': null, // callback during play (position update) 'onposition': null, // object containing times and function callbacks for positions of interest 'onstop': null, // callback for "user stop" 'onfailure': null, // callback function for when playing fails 'onfinish': null, // callback function for "sound finished playing" 'multiShot': true, // let sounds "restart" or layer on top of each other when played multiple times, rather than one-shot/one at a time 'multiShotEvents': false, // fire multiple sound events (currently onfinish() only) when multiShot is enabled 'position': null, // offset (milliseconds) to seek to within loaded sound data. 'pan': 0, // "pan" settings, left-to-right, -100 to 100 'stream': true, // allows playing before entire file has loaded (recommended) 'to': null, // position to end playback within a sound (msec), default = end 'type': null, // MIME-like hint for file pattern / canPlay() tests, eg. audio/mp3 'usePolicyFile': false, // enable crossdomain.xml request for audio on remote domains (for ID3/waveform access) 'volume': 100 // self-explanatory. 0-100, the latter being the max. }; this.flash9Options = { /** * flash 9-only options, * merged into defaultOptions if flash 9 is being used */ 'isMovieStar': null, // "MovieStar" MPEG4 audio mode. Null (default) = auto detect MP4, AAC etc. based on URL. true = force on, ignore URL 'usePeakData': false, // enable left/right channel peak (level) data 'useWaveformData': false, // enable sound spectrum (raw waveform data) - NOTE: May increase CPU load. 'useEQData': false, // enable sound EQ (frequency spectrum data) - NOTE: May increase CPU load. 'onbufferchange': null, // callback for "isBuffering" property change 'ondataerror': null // callback for waveform/eq data access error (flash playing audio in other tabs/domains) }; this.movieStarOptions = { /** * flash 9.0r115+ MPEG4 audio options, * merged into defaultOptions if flash 9+movieStar mode is enabled */ 'bufferTime': 3, // seconds of data to buffer before playback begins (null = flash default of 0.1 seconds - if AAC playback is gappy, try increasing.) 'serverURL': null, // rtmp: FMS or FMIS server to connect to, required when requesting media via RTMP or one of its variants 'onconnect': null, // rtmp: callback for connection to flash media server 'duration': null // rtmp: song duration (msec) }; this.audioFormats = { /** * determines HTML5 support + flash requirements. * if no support (via flash and/or HTML5) for a "required" format, SM2 will fail to start. * flash fallback is used for MP3 or MP4 if HTML5 can't play it (or if preferFlash = true) */ 'mp3': { 'type': ['audio/mpeg; codecs="mp3"', 'audio/mpeg', 'audio/mp3', 'audio/MPA', 'audio/mpa-robust'], 'required': true }, 'mp4': { 'related': ['aac','m4a','m4b'], // additional formats under the MP4 container 'type': ['audio/mp4; codecs="mp4a.40.2"', 'audio/aac', 'audio/x-m4a', 'audio/MP4A-LATM', 'audio/mpeg4-generic'], 'required': false }, 'ogg': { 'type': ['audio/ogg; codecs=vorbis'], 'required': false }, 'opus': { 'type': ['audio/ogg; codecs=opus', 'audio/opus'], 'required': false }, 'wav': { 'type': ['audio/wav; codecs="1"', 'audio/wav', 'audio/wave', 'audio/x-wav'], 'required': false } }; // HTML attributes (id + class names) for the SWF container this.movieID = 'sm2-container'; this.id = (smID || 'sm2movie'); this.debugID = 'soundmanager-debug'; this.debugURLParam = /([#?&])debug=1/i; // dynamic attributes this.versionNumber = 'V2.97a.20150601'; this.version = null; this.movieURL = null; this.altURL = null; this.swfLoaded = false; this.enabled = false; this.oMC = null; this.sounds = {}; this.soundIDs = []; this.muted = false; this.didFlashBlock = false; this.filePattern = null; this.filePatterns = { 'flash8': /\.mp3(\?.*)?$/i, 'flash9': /\.mp3(\?.*)?$/i }; // support indicators, set at init this.features = { 'buffering': false, 'peakData': false, 'waveformData': false, 'eqData': false, 'movieStar': false }; // flash sandbox info, used primarily in troubleshooting this.sandbox = { // 'type': null, 'types': { 'remote': 'remote (domain-based) rules', 'localWithFile': 'local with file access (no internet access)', 'localWithNetwork': 'local with network (internet access only, no local access)', 'localTrusted': 'local, trusted (local+internet access)' }, 'description': null, 'noRemote': null, 'noLocal': null // }; /** * format support (html5/flash) * stores canPlayType() results based on audioFormats. * eg. { mp3: boolean, mp4: boolean } * treat as read-only. */ this.html5 = { 'usingFlash': null // set if/when flash fallback is needed }; // file type support hash this.flash = {}; // determined at init time this.html5Only = false; // used for special cases (eg. iPad/iPhone/palm OS?) this.ignoreFlash = false; /** * 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, rebootIntoHTML5, 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, 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, didSetup, msecScale = 1000, 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) 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 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; 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', 'swfUnblocked': 'swf_unblocked', // or loaded OK 'sm2Debug': 'sm2_debug', 'highPerf': 'high_performance', 'flashDebug': 'flash_debug' }; /** * 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 (Audio !== _undefined && (isOpera && opera !== _undefined && opera.version() < 10 ? new Audio(null) : new Audio()).canPlayType !== _undefined); } catch(e) { return false; } }()); /** * Public SoundManager API * ----------------------- */ /** * Configures top-level soundManager properties. * * @param {object} options Option parameters, eg. { flashVersion: 9, url: '/path/to/swfs/' } * onready and ontimeout are also accepted parameters. call soundManager.setup() to see the full list. */ this.setup = function(options) { var noURL = (!sm2.url); // warn if flash options have already been applied if (options !== _undefined && didInit && needsFlash && sm2.ok() && (options.flashVersion !== _undefined || options.url !== _undefined || options.html5Test !== _undefined)) { complain(str('setupLate')); } // TODO: defer: true? assign(options); // force the singleton HTML5 pattern? if (sm2.setupOptions.useHTML5Audio && !useGlobalHTML5Audio && sm2.setupOptions.forceUseGlobalHTML5Audio) { messages.push(strings.globalHTML5); useGlobalHTML5Audio = true; } // don't apply if (!didSetup && mobileHTML5) { if (sm2.setupOptions.ignoreMobileRestrictions) { messages.push(strings.ignoreMobile); } // prefer HTML5 for mobile + tablet-like devices, probably more reliable vs. flash at this point. // if (!sm2.setupOptions.useHTML5Audio || sm2.setupOptions.preferFlash) { // notify that defaults are being changed. sm2._wD(strings.mobileUA); } // sm2.setupOptions.useHTML5Audio = true; sm2.setupOptions.preferFlash = false; if ((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() // sm2._wD(strings.globalHTML5); // if (is_iDevice) { sm2.ignoreFlash = true; } useGlobalHTML5Audio = true; } } // 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 (options) { 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 && options.url !== _undefined && doc.readyState === 'complete') { setTimeout(domContentLoaded, 1); } } didSetup = true; return sm2; }; this.ok = function() { 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] || window[smID]; }; /** * Creates a SMSound sound object instance. Can also be overloaded, e.g., createSound('mySound', '/some.mp3'); * * @param {object} oOptions Sound options (at minimum, url parameter is required.) * @return {object} SMSound The new SMSound object. */ this.createSound = function(oOptions, _url) { var cs, cs_string, options, oSound = null; // cs = sm + '.createSound(): '; cs_string = cs + str(!didInit ? 'notReady' : 'notOK'); // if (!didInit || !sm2.ok()) { complain(cs_string); return false; } if (_url !== _undefined) { // function overloading in JS! :) ... assume simple createSound(id, url) use case. oOptions = { 'id': oOptions, 'url': _url }; } // inherit from defaultOptions options = mixin(oOptions); options.url = parseURL(options.url); // generate an id, if needed. if (options.id === _undefined) { options.id = sm2.setupOptions.idPrefix + (idCounter++); } // if (options.id.toString().charAt(0).match(/^[0-9]$/)) { sm2._wD(cs + str('badID', options.id), 2); } sm2._wD(cs + options.id + (options.url ? ' (' + options.url + ')' : ''), 1); // if (idCheck(options.id, true)) { sm2._wD(cs + options.id + ' exists', 1); return sm2.sounds[options.id]; } function make() { options = loopFix(options); sm2.sounds[options.id] = new SMSound(options); sm2.soundIDs.push(options.id); return sm2.sounds[options.id]; } if (html5OK(options)) { oSound = make(); // if (!sm2.html5Only) { sm2._wD(options.id + ': Using HTML5'); } // oSound._setup_html5(options); } else { if (sm2.html5Only) { sm2._wD(options.id + ': No HTML5 support for this sound, and no Flash. Exiting.'); return make(); } // TODO: Move HTML5/flash checks into generic URL parsing/handling function. if (sm2.html5.usingFlash && options.url && options.url.match(/data\:/i)) { // data: URIs not supported by Flash, either. sm2._wD(options.id + ': data: URIs not supported via Flash. Exiting.'); return make(); } if (fV > 8) { if (options.isMovieStar === null) { // attempt to detect MPEG-4 formats options.isMovieStar = !!(options.serverURL || (options.type ? options.type.match(netStreamMimeTypes) : false) || (options.url && options.url.match(netStreamPattern))); } // if (options.isMovieStar) { sm2._wD(cs + 'using MovieStar handling'); if (options.loops > 1) { _wDS('noNSLoop'); } } // } options = policyFix(options, cs); oSound = make(); if (fV === 8) { flash._createSound(options.id, options.loops || 1, options.usePolicyFile); } else { 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 (options.onconnect) { options.onconnect.apply(oSound); } } } if (!options.serverURL && (options.autoLoad || options.autoPlay)) { // call load for non-rtmp streams oSound.load(options); } } // rtmp will play in onconnect if (!options.serverURL && options.autoPlay) { oSound.play(); } return oSound; }; /** * Destroys a SMSound sound object instance. * * @param {string} sID The ID of the sound to destroy */ this.destroySound = function(sID, _bFromSound) { // explicitly destroy a sound before normal page unload, etc. if (!idCheck(sID)) { return false; } var oS = sm2.sounds[sID], i; oS.stop(); // Disable all callbacks after stop(), when the sound is being destroyed oS._iO = {}; oS.unload(); 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 sm2.sounds[sID]; return true; }; /** * Calls the load() method of a SMSound object by ID. * * @param {string} sID The ID of the sound * @param {object} oOptions Optional: Sound options */ this.load = function(sID, oOptions) { if (!idCheck(sID)) { return false; } return sm2.sounds[sID].load(oOptions); }; /** * Calls the unload() method of a SMSound object by ID. * * @param {string} sID The ID of the sound */ this.unload = function(sID) { if (!idCheck(sID)) { return false; } return sm2.sounds[sID].unload(); }; /** * Calls the onPosition() method of a SMSound object by ID. * * @param {string} sID The ID of the sound * @param {number} nPosition The position to watch for * @param {function} oMethod The relevant callback to fire * @param {object} oScope Optional: The scope to apply the callback to * @return {SMSound} The SMSound object */ this.onPosition = function(sID, nPosition, oMethod, oScope) { if (!idCheck(sID)) { return false; } return sm2.sounds[sID].onposition(nPosition, oMethod, oScope); }; // legacy/backwards-compability: lower-case method name this.onposition = this.onPosition; /** * Calls the clearOnPosition() method of a SMSound object by ID. * * @param {string} sID The ID of the sound * @param {number} nPosition The position to watch for * @param {function} oMethod Optional: The relevant callback to fire * @return {SMSound} The SMSound object */ this.clearOnPosition = function(sID, nPosition, oMethod) { if (!idCheck(sID)) { return false; } return sm2.sounds[sID].clearOnPosition(nPosition, oMethod); }; /** * Calls the play() method of a SMSound object by ID. * * @param {string} sID The ID of the sound * @param {object} oOptions Optional: Sound options * @return {SMSound} The SMSound object */ this.play = function(sID, oOptions) { var result = null, // legacy function-overloading use case: play('mySound', '/path/to/some.mp3'); overloaded = (oOptions && !(oOptions instanceof Object)); if (!didInit || !sm2.ok()) { complain(sm + '.play(): ' + str(!didInit?'notReady':'notOK')); return false; } if (!idCheck(sID, overloaded)) { if (!overloaded) { // no sound found for the given ID. Bail. return false; } if (overloaded) { oOptions = { url: oOptions }; } if (oOptions && oOptions.url) { // overloading use case, create+play: .play('someID', {url:'/path/to.mp3'}); sm2._wD(sm + '.play(): Attempting to create "' + sID + '"', 1); oOptions.id = sID; result = sm2.createSound(oOptions).play(); } } else if (overloaded) { // existing sound object case oOptions = { url: oOptions }; } if (result === null) { // default case result = sm2.sounds[sID].play(oOptions); } return result; }; // just for convenience this.start = this.play; /** * Calls the setPosition() method of a SMSound object by ID. * * @param {string} sID The ID of the sound * @param {number} nMsecOffset Position (milliseconds) * @return {SMSound} The SMSound object */ this.setPosition = function(sID, nMsecOffset) { if (!idCheck(sID)) { return false; } return sm2.sounds[sID].setPosition(nMsecOffset); }; /** * Calls the stop() method of a SMSound object by ID. * * @param {string} sID The ID of the sound * @return {SMSound} The SMSound object */ this.stop = function(sID) { if (!idCheck(sID)) { return false; } sm2._wD(sm + '.stop(' + sID + ')', 1); return sm2.sounds[sID].stop(); }; /** * Stops all currently-playing sounds. */ this.stopAll = function() { var oSound; sm2._wD(sm + '.stopAll()', 1); for (oSound in sm2.sounds) { if (sm2.sounds.hasOwnProperty(oSound)) { // apply only to sound objects sm2.sounds[oSound].stop(); } } }; /** * Calls the pause() method of a SMSound object by ID. * * @param {string} sID The ID of the sound * @return {SMSound} The SMSound object */ this.pause = function(sID) { if (!idCheck(sID)) { return false; } return sm2.sounds[sID].pause(); }; /** * Pauses all currently-playing sounds. */ this.pauseAll = function() { var i; for (i = sm2.soundIDs.length - 1; i >= 0; i--) { sm2.sounds[sm2.soundIDs[i]].pause(); } }; /** * Calls the resume() method of a SMSound object by ID. * * @param {string} sID The ID of the sound * @return {SMSound} The SMSound object */ this.resume = function(sID) { if (!idCheck(sID)) { return false; } return sm2.sounds[sID].resume(); }; /** * Resumes all currently-paused sounds. */ this.resumeAll = function() { var i; for (i = sm2.soundIDs.length- 1 ; i >= 0; i--) { sm2.sounds[sm2.soundIDs[i]].resume(); } }; /** * Calls the togglePause() method of a SMSound object by ID. * * @param {string} sID The ID of the sound * @return {SMSound} The SMSound object */ this.togglePause = function(sID) { if (!idCheck(sID)) { return false; } return sm2.sounds[sID].togglePause(); }; /** * Calls the setPan() method of a SMSound object by ID. * * @param {string} sID The ID of the sound * @param {number} nPan The pan value (-100 to 100) * @return {SMSound} The SMSound object */ this.setPan = function(sID, nPan) { if (!idCheck(sID)) { return false; } return sm2.sounds[sID].setPan(nPan); }; /** * Calls the setVolume() method of a SMSound object by ID * Overloaded case: pass only volume argument eg., setVolume(50) to apply to all sounds. * * @param {string} sID The ID of the sound * @param {number} nVol The volume value (0 to 100) * @return {SMSound} The SMSound object */ this.setVolume = function(sID, nVol) { // setVolume(50) function overloading case - apply to all sounds var i, j; if (sID !== _undefined && !isNaN(sID) && nVol === _undefined) { for (i = 0, j = sm2.soundIDs.length; i < j; i++) { sm2.sounds[sm2.soundIDs[i]].setVolume(sID); } return; } // setVolume('mySound', 50) case if (!idCheck(sID)) { return false; } return sm2.sounds[sID].setVolume(nVol); }; /** * Calls the mute() method of either a single SMSound object by ID, or all sound objects. * * @param {string} sID Optional: The ID of the sound (if omitted, all sounds will be used.) */ this.mute = function(sID) { var i = 0; if (sID instanceof String) { sID = null; } if (!sID) { sm2._wD(sm + '.mute(): Muting all sounds'); for (i = sm2.soundIDs.length - 1; i >= 0; i--) { sm2.sounds[sm2.soundIDs[i]].mute(); } sm2.muted = true; } else { if (!idCheck(sID)) { return false; } sm2._wD(sm + '.mute(): Muting "' + sID + '"'); return sm2.sounds[sID].mute(); } return true; }; /** * Mutes all sounds. */ this.muteAll = function() { sm2.mute(); }; /** * Calls the unmute() method of either a single SMSound object by ID, or all sound objects. * * @param {string} sID Optional: The ID of the sound (if omitted, all sounds will be used.) */ this.unmute = function(sID) { var i; if (sID instanceof String) { sID = null; } if (!sID) { sm2._wD(sm + '.unmute(): Unmuting all sounds'); for (i = sm2.soundIDs.length - 1; i >= 0; i--) { sm2.sounds[sm2.soundIDs[i]].unmute(); } sm2.muted = false; } else { if (!idCheck(sID)) { return false; } sm2._wD(sm + '.unmute(): Unmuting "' + sID + '"'); return sm2.sounds[sID].unmute(); } return true; }; /** * Unmutes all sounds. */ this.unmuteAll = function() { sm2.unmute(); }; /** * Calls the toggleMute() method of a SMSound object by ID. * * @param {string} sID The ID of the sound * @return {SMSound} The SMSound object */ this.toggleMute = function(sID) { if (!idCheck(sID)) { return false; } return sm2.sounds[sID].toggleMute(); }; /** * Retrieves the memory used by the flash plugin. * * @return {number} The amount of memory in use */ this.getMemoryUse = function() { // flash-only var ram = 0; if (flash && fV !== 8) { ram = parseInt(flash._getMemoryUse(), 10); } return ram; }; /** * Undocumented: NOPs soundManager and all SMSound objects. */ this.disable = function(bNoDisable) { // destroy all functions var i; if (bNoDisable === _undefined) { bNoDisable = false; } if (disabled) { return false; } disabled = true; _wDS('shutdown', 1); for (i = sm2.soundIDs.length - 1; i >= 0; i--) { disableObject(sm2.sounds[sm2.soundIDs[i]]); } // fire "complete", despite fail initComplete(bNoDisable); event.remove(window, 'load', initUserOnload); return true; }; /** * Determines playability of a MIME type, eg. 'audio/mp3'. */ this.canPlayMIME = function(sMIME) { var result; if (sm2.hasHTML5) { 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); // TODO: make less "weird" (per JSLint) } return result; }; /** * Determines playability of a URL based on audio support. * * @param {string} sURL The URL to test * @return {boolean} URL playability */ this.canPlayURL = function(sURL) { var result; if (sm2.hasHTML5) { result = html5CanPlay({ url: sURL }); } if (!result && needsFlash) { result = (sURL && sm2.ok() ? !!(sURL.match(sm2.filePattern)) : null); } return result; }; /** * Determines playability of an HTML DOM <a> object (or similar object literal) based on audio support. * * @param {object} oLink an HTML DOM <a> object or object literal including href and/or type attributes * @return {boolean} URL playability */ this.canPlayLink = function(oLink) { if (oLink.type !== _undefined && oLink.type) { if (sm2.canPlayMIME(oLink.type)) { return true; } } return sm2.canPlayURL(oLink.href); }; /** * Retrieves a SMSound object by ID. * * @param {string} sID The ID of the sound * @return {SMSound} The SMSound object */ this.getSoundById = function(sID, _suppressDebug) { if (!sID) { return null; } var result = sm2.sounds[sID]; // if (!result && !_suppressDebug) { sm2._wD(sm + '.getSoundById(): Sound "' + sID + '" not found.', 2); } // return result; }; /** * Queues a callback for execution when SoundManager has successfully initialized. * * @param {function} oMethod The callback method to fire * @param {object} oScope Optional: The scope to apply to the callback */ this.onready = function(oMethod, oScope) { var sType = 'onready', result = false; if (typeof oMethod === 'function') { // if (didInit) { sm2._wD(str('queue', sType)); } // if (!oScope) { oScope = window; } addOnEvent(sType, oMethod, oScope); processOnEvents(); result = true; } else { throw str('needFunction', sType); } return result; }; /** * Queues a callback for execution when SoundManager has failed to initialize. * * @param {function} oMethod The callback method to fire * @param {object} oScope Optional: The scope to apply to the callback */ this.ontimeout = function(oMethod, oScope) { var sType = 'ontimeout', result = false; if (typeof oMethod === 'function') { // if (didInit) { sm2._wD(str('queue', sType)); } // if (!oScope) { oScope = window; } addOnEvent(sType, oMethod, oScope); processOnEvents({type:sType}); result = true; } else { throw str('needFunction', sType); } return result; }; /** * Writes console.log()-style debug output to a console or in-browser element. * Applies when debugMode = true * * @param {string} sText The console message * @param {object} nType Optional log level (number), or object. Number case: Log type/style where 0 = 'info', 1 = 'warn', 2 = 'error'. Object case: Object to be dumped. */ this._writeDebug = function(sText, sTypeOrObject) { // pseudo-private console.log()-style output // var sDID = 'soundmanager-debug', o, oItem; if (!sm2.setupOptions.debugMode) { return false; } if (hasConsole && sm2.useConsole) { if (sTypeOrObject && typeof sTypeOrObject === 'object') { // object passed; dump to console. console.log(sText, sTypeOrObject); } else if (debugLevels[sTypeOrObject] !== _undefined) { console[debugLevels[sTypeOrObject]](sText); } else { console.log(sText); } if (sm2.consoleOnly) { return true; } } o = id(sDID); if (!o) { return false; } oItem = doc.createElement('div'); if (++wdCount % 2 === 0) { oItem.className = 'sm2-alt'; } if (sTypeOrObject === _undefined) { sTypeOrObject = 0; } else { sTypeOrObject = parseInt(sTypeOrObject, 10); } oItem.appendChild(doc.createTextNode(sText)); if (sTypeOrObject) { if (sTypeOrObject >= 2) { oItem.style.fontWeight = 'bold'; } if (sTypeOrObject === 3) { oItem.style.color = '#ff3333'; } } // top-to-bottom // o.appendChild(oItem); // bottom-to-top o.insertBefore(oItem, o.firstChild); o = null; // return true; }; // // last-resort debugging option if (wl.indexOf('sm2-debug=alert') !== -1) { this._writeDebug = function(sText) { window.alert(sText); }; } // // alias this._wD = this._writeDebug; /** * Provides debug / state information on all SMSound objects. */ this._debug = function() { // var i, j; _wDS('currentObj', 1); for (i = 0, j = sm2.soundIDs.length; i < j; i++) { sm2.sounds[sm2.soundIDs[i]]._debug(); } // }; /** * 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(resetEvents, excludeInit) { // reset some (or all) state, and re-init unless otherwise specified. // if (sm2.soundIDs.length) { sm2._wD('Destroying ' + sm2.soundIDs.length + ' SMSound object' + (sm2.soundIDs.length !== 1 ? 's' : '') + '...'); } // var i, j, k; for (i = sm2.soundIDs.length- 1 ; i >= 0; i--) { sm2.sounds[sm2.soundIDs[i]].destruct(); } // trash ze flash (remove from the DOM) if (flash) { try { if (isIE) { oRemovedHTML = flash.innerHTML; } oRemoved = flash.parentNode.removeChild(flash); } catch(e) { // 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 = flash = null; sm2.enabled = didDCLoaded = didInit = waitingForEI = initPending = didAppend = appendSuccess = disabled = useGlobalHTML5Audio = sm2.swfLoaded = false; sm2.soundIDs = []; sm2.sounds = {}; idCounter = 0; didSetup = false; 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 = []; } // if (!excludeInit) { sm2._wD(sm + ': Rebooting...'); } // // 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() { // 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 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(); setTimeout(function() { if (initPending) { return false; } createMovie(); initMovie(); initPending = true; return true; }, 20); delayWaitForEI(); }; /** * Destroys the SoundManager instance and all SMSound instances. */ this.destruct = function() { sm2._wD(sm + '.destruct()'); sm2.disable(true); }; /** * SMSound() (sound object) constructor * ------------------------------------ * * @param {object} oOptions Sound options (id and url are required attributes) * @return {SMSound} The new SMSound object */ SMSound = function(oOptions) { 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, urlOmitted; lastHTML5State = { // tracks duration + position (time) duration: null, time: null }; this.id = oOptions.id; // legacy this.sID = this.id; this.url = oOptions.url; this.options = mixin(oOptions); // per-play-instance-specific options this.instanceOptions = this.options; // short alias this._iO = this.instanceOptions; // assign property defaults this.pan = this.options.pan; this.volume = this.options.volume; // whether or not this object is using HTML5 this.isHTML5 = false; // internal HTML5 Audio() object reference this._a = null; // for flash 8 special-case createSound() without url, followed by load/play with url case urlOmitted = (this.url ? false : true); /** * SMSound() public methods * ------------------------ */ this.id3 = {}; /** * Writes SMSound object parameters to debug console */ this._debug = function() { // sm2._wD(s.id + ': Merged options:', s.options); // }; /** * Begins loading a sound per its *url*. * * @param {object} oOptions Optional: Sound options * @return {SMSound} The SMSound object */ this.load = function(oOptions) { var oSound = null, instanceOptions; if (oOptions !== _undefined) { s._iO = mixin(oOptions, s.options); } else { oOptions = s.options; s._iO = oOptions; if (lastURL && lastURL !== s.url) { _wDS('manURL'); s._iO.url = s.url; s.url = null; } } if (!s._iO.url) { s._iO.url = s.url; } s._iO.url = parseURL(s._iO.url); // ensure we're in sync s.instanceOptions = s._iO; // local shortcut instanceOptions = s._iO; sm2._wD(s.id + ': load (' + instanceOptions.url + ')'); if (!instanceOptions.url && !s.url) { sm2._wD(s.id + ': load(): url is unassigned. Exiting.', 2); return s; } // if (!s.isHTML5 && fV === 8 && !s.url && !instanceOptions.autoPlay) { // flash 8 load() -> play() won't work before onload has fired. sm2._wD(s.id + ': Flash 8 load() limitation: Wait for onload() before calling play().', 1); } // if (instanceOptions.url === s.url && s.readyState !== 0 && s.readyState !== 2) { _wDS('onURL', 1); // if loaded and an onload() exists, fire immediately. if (s.readyState === 3 && instanceOptions.onload) { // assume success based on truthy duration. wrapCallback(s, function() { instanceOptions.onload.apply(s, [(!!s.duration)]); }); } return s; } // reset a few state properties 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(instanceOptions)) { oSound = s._setup_html5(instanceOptions); if (!oSound._called_load) { s._html5_canplay = false; // TODO: review called_load / html5_canplay logic // if url provided directly to load(), assign it here. if (s.url !== instanceOptions.url) { sm2._wD(_wDS('manURL') + ': ' + instanceOptions.url); s._a.src = instanceOptions.url; // TODO: review / re-apply all relevant options (volume, loop, onposition etc.) // reset position for new URL s.setPosition(0); } // given explicit load call, try to preload. // early HTML5 implementation (non-standard) s._a.autobuffer = 'auto'; // standard property, values: none / metadata / auto // reference: http://msdn.microsoft.com/en-us/library/ie/ff974759%28v=vs.85%29.aspx s._a.preload = 'auto'; s._a._called_load = true; } else { sm2._wD(s.id + ': Ignoring request to load again'); } } else { if (sm2.html5Only) { sm2._wD(s.id + ': No flash support. Exiting.'); return s; } if (s._iO.url && s._iO.url.match(/data\:/i)) { // data: URIs not supported by Flash, either. sm2._wD(s.id + ': data: URIs not supported via Flash. Exiting.'); return s; } try { s.isHTML5 = false; s._iO = policyFix(loopFix(instanceOptions)); // if we have "position", disable auto-play as we'll be seeking to that position at onload(). if (s._iO.autoPlay && (s._iO.position || s._iO.from)) { sm2._wD(s.id + ': Disabling autoPlay because of non-zero offset case'); s._iO.autoPlay = false; } // re-assign local shortcut instanceOptions = s._iO; if (fV === 8) { flash._load(s.id, instanceOptions.url, instanceOptions.stream, instanceOptions.autoPlay, instanceOptions.usePolicyFile); } else { 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 }); } } // after all of this, ensure sound url is up to date. s.url = instanceOptions.url; return s; }; /** * Unloads a sound, canceling any open HTTP requests. * * @return {SMSound} The SMSound object */ this.unload = function() { // 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 (s.readyState !== 0) { sm2._wD(s.id + ': unload()'); if (!s.isHTML5) { if (fV === 8) { flash._unload(s.id, emptyURL); } else { flash._unload(s.id); } } else { stop_html5_timer(); if (s._a) { s._a.pause(); // update empty URL, too lastURL = html5Unload(s._a); } } // reset load/status flags resetProperties(); } return s; }; /** * Unloads and destroys a sound. */ this.destruct = function(_bFromSM) { sm2._wD(s.id + ': Destruct'); if (!s.isHTML5) { // kill sound within Flash // Disable the onfailure handler s._iO.onfailure = null; flash._destroySound(s.id); } else { stop_html5_timer(); if (s._a) { s._a.pause(); html5Unload(s._a); if (!useGlobalHTML5Audio) { remove_html5_events(); } // break obvious circular reference s._a._s = null; s._a = null; } } if (!_bFromSM) { // ensure deletion from controller sm2.destroySound(s.id, true); } }; /** * Begins playing a sound. * * @param {object} oOptions Optional: Sound options * @return {SMSound} The SMSound object */ this.play = function(oOptions, _updatePlayState) { var fN, allowMulti, a, onready, audioClone, onended, oncanplay, startOK = true, exit = null; // fN = s.id + ': play(): '; // // default to true _updatePlayState = (_updatePlayState === _undefined ? true : _updatePlayState); if (!oOptions) { oOptions = {}; } // first, use local URL (if specified) if (s.url) { s._iO.url = s.url; } // mix in any options defined at createSound() s._iO = mixin(s._iO, s.options); // mix in any options specific to this method s._iO = mixin(oOptions, s._iO); s._iO.url = parseURL(s._iO.url); s.instanceOptions = s._iO; // RTMP-only if (!s.isHTML5 && 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 s; } if (html5OK(s._iO)) { s._setup_html5(s._iO); start_html5_timer(); } if (s.playState === 1 && !s.paused) { allowMulti = s._iO.multiShot; if (!allowMulti) { sm2._wD(fN + 'Already playing (one-shot)', 1); if (s.isHTML5) { // go back to original position. s.setPosition(s._iO.position); } exit = s; } else { 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 !== s.url) { // special case for createSound() followed by load() / play() with url; avoid double-load case. if (!s.readyState && !s.isHTML5 && fV === 8 && urlOmitted) { urlOmitted = false; } else { // load using merged options s.load(s._iO); } } if (!s.loaded) { if (s.readyState === 0) { sm2._wD(fN + 'Attempting to load'); // try to get this sound playing ASAP if (!s.isHTML5 && !sm2.html5Only) { // flash: assign directly because setAutoPlay() increments the instanceCount s._iO.autoPlay = true; s.load(s._iO); } else if (s.isHTML5) { // iOS needs this when recycling sounds, loading a new URL on an existing object. s.load(s._iO); } else { sm2._wD(fN + 'Unsupported type. Exiting.'); exit = s; } // HTML5 hack - re-set instanceOptions? s.instanceOptions = s._iO; } else if (s.readyState === 2) { sm2._wD(fN + 'Could not load - exiting', 2); exit = s; } else { sm2._wD(fN + 'Loading - attempting to play...'); } } else { // "play()" sm2._wD(fN.substr(0, fN.lastIndexOf(':'))); } if (exit !== null) { return exit; } 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. 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. * In this case paused is true, but the song hasn't started playing yet. * If we just call resume() the onplay() callback will never be called. * 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 (s.paused && s.position >= 0 && (!s._iO.serverURL || s.position > 0)) { // https://gist.github.com/37b17df75cc4d7a90bf6 sm2._wD(fN + 'Resuming from paused state', 1); s.resume(); } else { s._iO = mixin(oOptions, s._iO); /** * Preload in the event of play() with position under Flash, * or from/to parameters and non-RTMP case */ if (((!s.isHTML5 && s._iO.position !== null && s._iO.position > 0) || (s._iO.from !== null && s._iO.from > 0) || s._iO.to !== null) && s.instanceCount === 0 && s.playState === 0 && !s._iO.serverURL) { onready = function() { // sound "canplay" or onload() // re-apply position/from/to to instance options, and start playback s._iO = mixin(oOptions, s._iO); s.play(s._iO); }; // HTML5 needs to at least have "canplay" fired before seeking. if (s.isHTML5 && !s._html5_canplay) { // this hasn't been loaded yet. load it first, and then do this again. sm2._wD(fN + 'Beginning load for non-zero offset case'); s.load({ // 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)) { // to be safe, preload the whole thing in Flash. sm2._wD(fN + 'Preloading for non-zero offset case'); s.load({ onload: onready }); exit = false; } if (exit !== null) { return exit; } // otherwise, we're ready to go. re-apply local options, and continue s._iO = applyFromTo(); } // sm2._wD(fN + 'Starting to play'); // increment instance counter, where enabled + supported if (!s.instanceCount || s._iO.multiShotEvents || (s.isHTML5 && s._iO.multiShot && !useGlobalHTML5Audio) || (!s.isHTML5 && fV > 8 && !s.getAutoPlay())) { s.instanceCount++; } // if first play and onposition parameters exist, apply them now if (s._iO.onposition && s.playState === 0) { attachOnPosition(s); } s.playState = 1; s.paused = false; s.position = (s._iO.position !== _undefined && !isNaN(s._iO.position) ? s._iO.position : 0); if (!s.isHTML5) { s._iO = policyFix(loopFix(s._iO)); } if (s._iO.onplay && _updatePlayState) { s._iO.onplay.apply(s); onplay_called = true; } s.setVolume(s._iO.volume, true); s.setPan(s._iO.pan, true); if (!s.isHTML5) { startOK = flash._start(s.id, s._iO.loops || 1, (fV === 9 ? s.position : s.position / msecScale), s._iO.multiShot || false); 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 sm2._wD(fN + 'No sound hardware, or 32-sound ceiling hit', 2); if (s._iO.onplayerror) { s._iO.onplayerror.apply(s); } } } else { if (s.instanceCount < 2) { // HTML5 single-instance case start_html5_timer(); a = s._setup_html5(); s.setPosition(s._iO.position); a.play(); } else { // HTML5 multi-shot case sm2._wD(s.id + ': Cloning Audio() for instance #' + s.instanceCount + '...'); audioClone = new Audio(s._iO.url); onended = function() { event.remove(audioClone, 'ended', onended); s._onfinish(s); // cleanup html5Unload(audioClone); audioClone = null; }; oncanplay = function() { event.remove(audioClone, 'canplay', oncanplay); try { audioClone.currentTime = s._iO.position/msecScale; } catch(err) { complain(s.id + ': multiShot play() failed to apply position of ' + (s._iO.position/msecScale)); } 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 { // begin playback at currentTime: 0 audioClone.play(); } } } } return s; }; // just for convenience this.start = this.play; /** * Stops playing a sound (and optionally, all sounds) * * @param {boolean} bAll Optional: Whether to stop all sounds * @return {SMSound} The SMSound object */ this.stop = function(bAll) { var instanceOptions = s._iO, originalPosition; if (s.playState === 1) { sm2._wD(s.id + ': stop()'); s._onbufferchange(0); s._resetOnPosition(0); s.paused = false; if (!s.isHTML5) { s.playState = 0; } // remove onPosition listeners, if any detachOnPosition(); // and "to" position, if set if (instanceOptions.to) { s.clearOnPosition(instanceOptions.to); } if (!s.isHTML5) { flash._stop(s.id, bAll); // hack for netStream: just unload if (instanceOptions.serverURL) { s.unload(); } } else { if (s._a) { originalPosition = s.position; // act like Flash, though s.setPosition(0); // hack: reflect old position for onstop() (also like Flash) s.position = originalPosition; // html5 has no stop() // NOTE: pausing means iOS requires interaction to resume. s._a.pause(); s.playState = 0; // and update UI s._onTimer(); stop_html5_timer(); } } s.instanceCount = 0; s._iO = {}; if (instanceOptions.onstop) { instanceOptions.onstop.apply(s); } } return s; }; /** * Undocumented/internal: Sets autoPlay for RTMP. * * @param {boolean} autoPlay state */ this.setAutoPlay = function(autoPlay) { sm2._wD(s.id + ': Autoplay turned ' + (autoPlay ? 'on' : 'off')); s._iO.autoPlay = 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 (!s.instanceCount && s.readyState === 1) { s.instanceCount++; sm2._wD(s.id + ': Incremented instance count to '+s.instanceCount); } } } }; /** * Undocumented/internal: Returns the autoPlay boolean. * * @return {boolean} The current autoPlay value */ this.getAutoPlay = function() { return s._iO.autoPlay; }; /** * Sets the position of a sound. * * @param {number} nMsecOffset Position (milliseconds) * @return {SMSound} The SMSound object */ this.setPosition = function(nMsecOffset) { if (nMsecOffset === _undefined) { nMsecOffset = 0; } var 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 = (s.isHTML5 ? Math.max(nMsecOffset, 0) : Math.min(s.duration || s._iO.duration, Math.max(nMsecOffset, 0))); s.position = offset; position1K = s.position/msecScale; s._resetOnPosition(s.position); s._iO.position = offset; if (!s.isHTML5) { position = (fV === 9 ? s.position : position1K); if (s.readyState && s.readyState !== 2) { // if paused or not playing, will not resume (by playing) flash._setPosition(s.id, position, (s.paused || !s.playState), s._iO.multiShot); } } else if (s._a) { // Set the position in the canplay handler if the sound is not ready yet 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 */ sm2._wD(s.id + ': setPosition(' + position1K + ')'); try { s._a.currentTime = position1K; if (s.playState === 0 || s.paused) { // allow seek without auto-play/resume s._a.pause(); } } catch(e) { sm2._wD(s.id + ': setPosition(' + position1K + ') failed: ' + e.message, 2); } } } else if (position1K) { // warn on non-zero seek attempts sm2._wD(s.id + ': setPosition(' + position1K + '): Cannot seek yet, sound not ready', 2); return s; } if (s.paused) { // if paused, refresh UI right away by forcing update s._onTimer(true); } } return s; }; /** * Pauses sound playback. * * @return {SMSound} The SMSound object */ this.pause = function(_bCallFlash) { if (s.paused || (s.playState === 0 && s.readyState !== 1)) { return s; } sm2._wD(s.id + ': pause()'); s.paused = true; if (!s.isHTML5) { if (_bCallFlash || _bCallFlash === _undefined) { flash._pause(s.id, s._iO.multiShot); } } else { s._setup_html5().pause(); stop_html5_timer(); } if (s._iO.onpause) { s._iO.onpause.apply(s); } return s; }; /** * Resumes sound playback. * * @return {SMSound} The SMSound object */ /** * When auto-loaded streams pause on buffer full they have a playState of 0. * We need to make sure that the playState is set to 1 when these streams "resume". * When a paused stream is resumed, we need to trigger the onplay() callback if it * hasn't been called already. In this case since the sound is being played for the * first time, I think it's more appropriate to call onplay() rather than onresume(). */ this.resume = function() { var instanceOptions = s._iO; if (!s.paused) { return s; } sm2._wD(s.id + ': resume()'); s.paused = false; s.playState = 1; 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. s.setPosition(s.position); } // flash method is toggle-based (pause/resume) flash._pause(s.id, instanceOptions.multiShot); } else { s._setup_html5().play(); start_html5_timer(); } if (!onplay_called && instanceOptions.onplay) { instanceOptions.onplay.apply(s); onplay_called = true; } else if (instanceOptions.onresume) { instanceOptions.onresume.apply(s); } return s; }; /** * Toggles sound playback. * * @return {SMSound} The SMSound object */ this.togglePause = function() { sm2._wD(s.id + ': togglePause()'); if (s.playState === 0) { s.play({ position: (fV === 9 && !s.isHTML5 ? s.position : s.position / msecScale) }); return s; } if (s.paused) { s.resume(); } else { s.pause(); } return s; }; /** * Sets the panning (L-R) effect. * * @param {number} nPan The pan value (-100 to 100) * @return {SMSound} The SMSound object */ this.setPan = function(nPan, bInstanceOnly) { if (nPan === _undefined) { nPan = 0; } if (bInstanceOnly === _undefined) { bInstanceOnly = false; } if (!s.isHTML5) { flash._setPan(s.id, nPan); } // else { no HTML5 pan? } s._iO.pan = nPan; if (!bInstanceOnly) { s.pan = nPan; s.options.pan = nPan; } return s; }; /** * Sets the volume. * * @param {number} nVol The volume value (0 to 100) * @return {SMSound} The SMSound object */ this.setVolume = function(nVol, _bInstanceOnly) { /** * Note: Setting volume has no effect on iOS "special snowflake" devices. * 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 (nVol === _undefined) { nVol = 100; } if (_bInstanceOnly === _undefined) { _bInstanceOnly = false; } 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 for native HTML5 Audio(): 0-1 s._a.volume = Math.max(0, Math.min(1, nVol/100)); } s._iO.volume = nVol; if (!_bInstanceOnly) { s.volume = nVol; s.options.volume = nVol; } return s; }; /** * Mutes the sound. * * @return {SMSound} The SMSound object */ this.mute = function() { s.muted = true; if (!s.isHTML5) { flash._setVolume(s.id, 0); } else if (s._a) { s._a.muted = true; } return s; }; /** * Unmutes the sound. * * @return {SMSound} The SMSound object */ this.unmute = function() { s.muted = false; var hasIO = (s._iO.volume !== _undefined); if (!s.isHTML5) { flash._setVolume(s.id, hasIO ? s._iO.volume : s.options.volume); } else if (s._a) { s._a.muted = false; } return s; }; /** * Toggles the muted state of a sound. * * @return {SMSound} The SMSound object */ this.toggleMute = function() { return (s.muted ? s.unmute() : s.mute()); }; /** * Registers a callback to be fired when a sound reaches a given position during playback. * * @param {number} nPosition The position to watch for * @param {function} oMethod The relevant callback to fire * @param {object} oScope Optional: The scope to apply the callback to * @return {SMSound} The SMSound object */ this.onPosition = function(nPosition, oMethod, oScope) { // TODO: basic dupe checking? onPositionItems.push({ position: parseInt(nPosition, 10), method: oMethod, scope: (oScope !== _undefined ? oScope : s), fired: false }); return s; }; // legacy/backwards-compability: lower-case method name this.onposition = this.onPosition; /** * Removes registered callback(s) from a sound, by position and/or callback. * * @param {number} nPosition The position to clear callback(s) for * @param {function} oMethod Optional: Identify one callback to be removed when multiple listeners exist for one position * @return {SMSound} The SMSound object */ this.clearOnPosition = function(nPosition, oMethod) { var i; nPosition = parseInt(nPosition, 10); if (isNaN(nPosition)) { // safety check return false; } for (i=0; i < onPositionItems.length; i++) { 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) { // decrement "fired" counter, too onPositionFired--; } onPositionItems.splice(i, 1); } } } }; 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]); // reset j -- onPositionItems.length can be changed in the item callback above... occasionally breaking the loop. j = onPositionItems.length; } } return true; }; this._resetOnPosition = function(nPosition) { // reset "fired" for items interested in this position var i, item, j = onPositionItems.length; if (!j) { return false; } for (i = j - 1; i >= 0; i--) { item = onPositionItems[i]; if (item.fired && nPosition <= item.position) { item.fired = false; onPositionFired--; } } return true; }; /** * SMSound() private internals * -------------------------------- */ applyFromTo = function() { var instanceOptions = s._iO, f = instanceOptions.from, t = instanceOptions.to, start, end; end = function() { // end has been reached. sm2._wD(s.id + ': "To" time of ' + t + ' reached.'); // detach listener s.clearOnPosition(t, end); // stop should clear this, too s.stop(); }; start = function() { sm2._wD(s.id + ': Playing "from" ' + f); // add listener for end if (t !== null && !isNaN(t)) { s.onPosition(t, end); } }; if (f !== null && !isNaN(f)) { // apply to instance options, guaranteeing correct start position. instanceOptions.position = f; // multiShot timing can't be tracked, so prevent that. instanceOptions.multiShot = false; start(); } // return updated instanceOptions including starting position return instanceOptions; }; attachOnPosition = function() { var item, op = s._iO.onposition; // attach onposition things, if any, now. if (op) { for (item in op) { if (op.hasOwnProperty(item)) { s.onPosition(parseInt(item, 10), op[item]); } } } }; detachOnPosition = function() { var item, op = s._iO.onposition; // detach any onposition()-style listeners. if (op) { for (item in op) { if (op.hasOwnProperty(item)) { s.clearOnPosition(parseInt(item, 10)); } } } }; start_html5_timer = function() { if (s.isHTML5) { startTimer(s); } }; stop_html5_timer = function() { if (s.isHTML5) { stopTimer(s); } }; resetProperties = function(retainPosition) { if (!retainPosition) { onPositionItems = []; onPositionFired = 0; } onplay_called = false; 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 s.eqData = []; s.eqData.left = []; s.eqData.right = []; 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 s.readyState = 0; s.muted = false; s.paused = false; s.peakData = { left: 0, right: 0 }; s.waveformData = { left: [], right: [] }; s.playState = 0; s.position = null; s.id3 = {}; }; resetProperties(); /** * Pseudo-private SMSound internals * -------------------------------- */ this._onTimer = function(bForce) { /** * HTML5-only _whileplaying() etc. * called from both HTML5 native events, and polling/interval-based timers * mimics flash and fires only when time/duration change, so as to be polling-friendly */ var duration, isNew = false, time, x = {}; if (s._hasTimer || bForce) { // TODO: May not need to track readyState (1 = loading) if (s._a && (bForce || ((s.playState > 0 || s.readyState === 1) && !s.paused))) { duration = s._get_html5_duration(); if (duration !== lastHTML5State.duration) { lastHTML5State.duration = duration; s.duration = duration; isNew = true; } // TODO: investigate why this goes wack if not set/re-set each time. s.durationEstimate = s.duration; time = (s._a.currentTime * msecScale || 0); if (time !== lastHTML5State.time) { lastHTML5State.time = time; isNew = true; } if (isNew || bForce) { s._whileplaying(time, x, x, x, x); } }/* else { // 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; }*/ return isNew; } }; this._get_html5_duration = function() { 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 = (s._a && s._a.duration ? s._a.duration * msecScale : (instanceOptions && instanceOptions.duration ? instanceOptions.duration : null)), result = (d && !isNaN(d) && d !== Infinity ? d : null); return result; }; this._apply_loop = function(a, nLoops) { /** * boolean instead of "loop", for webkit? - spec says string. http://www.w3.org/TR/html-markup/audio.html#audio.attrs.loop * note that loop is either off or infinite under HTML5, unlike Flash which allows arbitrary loop counts to be specified. */ // if (!a.loop && nLoops > 1) { sm2._wD('Note: Native HTML5 looping is infinite.', 1); } // a.loop = (nLoops > 1 ? 'loop' : ''); }; this._setup_html5 = function(oOptions) { var instanceOptions = mixin(s._iO, oOptions), a = useGlobalHTML5Audio ? globalHTML5Audio : s._a, dURL = decodeURI(instanceOptions.url), 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 */ if (useGlobalHTML5Audio) { if (dURL === decodeURI(lastGlobalHTML5URL)) { // global HTML5 audio: re-use of URL sameURL = true; } } else if (dURL === decodeURI(lastURL)) { // options URL is the same as the "last" URL, and we used (loaded) it sameURL = true; } if (a) { if (a._s) { 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 === decodeURI(lastURL)) { // non-global HTML5 reuse case: same url, ignore request s._apply_loop(a, instanceOptions.loops); return a; } } if (!sameURL) { // don't retain onPosition() stuff with new URLs. if (lastURL) { resetProperties(false); } // assign new HTML5 URL a.src = instanceOptions.url; s.url = instanceOptions.url; lastURL = instanceOptions.url; lastGlobalHTML5URL = instanceOptions.url; a._called_load = false; } } 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()); } // assign local reference a = s._a; a._called_load = false; if (useGlobalHTML5Audio) { globalHTML5Audio = a; } } s.isHTML5 = true; // store a ref on the track s._a = a; // store a ref on the audio a._s = s; add_html5_events(); s._apply_loop(a, instanceOptions.loops); if (instanceOptions.autoLoad || instanceOptions.autoPlay) { s.load(); } else { // early HTML5 implementation (non-standard) a.autobuffer = false; // standard ('none' is also an option.) a.preload = 'auto'; } return a; }; add_html5_events = function() { if (s._a._added_events) { return false; } var f; function add(oEvt, oFn, bCapture) { return s._a ? s._a.addEventListener(oEvt, oFn, bCapture || false) : null; } s._a._added_events = true; for (f in html5_events) { if (html5_events.hasOwnProperty(f)) { add(f, html5_events[f]); } } return true; }; remove_html5_events = function() { // Remove event listeners var f; function remove(oEvt, oFn, bCapture) { return (s._a ? s._a.removeEventListener(oEvt, oFn, bCapture || false) : null); } 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]); } } }; /** * Pseudo-private event internals * ------------------------------ */ this._onload = function(nSuccess) { var fN, // check for duration to prevent false positives from flash 8 when loading from cache. loadOK = !!nSuccess || (!s.isHTML5 && fV === 8 && s.duration); // fN = s.id + ': '; sm2._wD(fN + (loadOK ? 'onload()' : 'Failed to load / invalid sound?' + (!s.duration ? ' Zero-length duration reported.' : ' -') + ' (' + s.url + ')'), (loadOK ? 1 : 2)); if (!loadOK && !s.isHTML5) { if (sm2.sandbox.noRemote === true) { sm2._wD(fN + str('noNet'), 1); } if (sm2.sandbox.noLocal === true) { sm2._wD(fN + str('noLocal'), 1); } } // s.loaded = loadOK; s.readyState = (loadOK ? 3 : 2); s._onbufferchange(0); if (s._iO.onload) { wrapCallback(s, function() { s._iO.onload.apply(s, [loadOK]); }); } return true; }; this._onbufferchange = function(nIsBuffering) { if (s.playState === 0) { // ignore if not playing return false; } if ((nIsBuffering && s.isBuffering) || (!nIsBuffering && !s.isBuffering)) { return false; } s.isBuffering = (nIsBuffering === 1); if (s._iO.onbufferchange) { sm2._wD(s.id + ': Buffer state change: ' + nIsBuffering); s._iO.onbufferchange.apply(s, [nIsBuffering]); } return true; }; /** * 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 (s._iO.onsuspend) { sm2._wD(s.id + ': Playback suspended'); s._iO.onsuspend.apply(s); } return true; }; /** * flash 9/movieStar + RTMP-only method, should fire only once at most * at this point we just recreate failed sounds rather than trying to reconnect */ this._onfailure = function(msg, level, code) { s.failures++; sm2._wD(s.id + ': Failure (' + s.failures + '): ' + msg); if (s._iO.onfailure && s.failures === 1) { s._iO.onfailure(msg, level, code); } else { sm2._wD(s.id + ': Ignoring failure'); } }; /** * flash 9/movieStar + RTMP-only method for unhandled warnings/exceptions from Flash * e.g., RTMP "method missing" warning (non-fatal) for getStreamLength on server */ this._onwarning = function(msg, level, code) { if (s._iO.onwarning) { s._iO.onwarning(msg, level, code); } }; this._onfinish = function() { // store local copy before it gets trashed... var io_onfinish = s._iO.onfinish; s._onbufferchange(0); s._resetOnPosition(0); // reset some state items if (s.instanceCount) { s.instanceCount--; if (!s.instanceCount) { // remove onPosition listeners, if any detachOnPosition(); // reset instance options s.playState = 0; s.paused = false; s.instanceCount = 0; s.instanceOptions = {}; s._iO = {}; stop_html5_timer(); // reset position, too if (s.isHTML5) { s.position = 0; } } if (!s.instanceCount || s._iO.multiShotEvents) { // fire onfinish for last, or every instance if (io_onfinish) { sm2._wD(s.id + ': onfinish()'); wrapCallback(s, function() { io_onfinish.apply(s); }); } } } }; this._whileloading = function(nBytesLoaded, nBytesTotal, nDuration, nBufferLength) { var instanceOptions = s._iO; s.bytesLoaded = nBytesLoaded; s.bytesTotal = nBytesTotal; s.duration = Math.floor(nDuration); s.bufferLength = nBufferLength; if (!s.isHTML5 && !instanceOptions.isMovieStar) { if (instanceOptions.duration) { // use duration from options, if specified and larger. nobody should be specifying duration in options, actually, and it should be retired. s.durationEstimate = (s.duration > instanceOptions.duration) ? s.duration : instanceOptions.duration; } else { s.durationEstimate = parseInt((s.bytesTotal / s.bytesLoaded) * s.duration, 10); } } else { s.durationEstimate = s.duration; } // for flash, reflect sequential-load-style buffering if (!s.isHTML5) { s.buffered = [{ 'start': 0, 'end': s.duration }]; } // allow whileloading to fire even if "load" fired under HTML5, due to HTTP range/partials if ((s.readyState !== 3 || s.isHTML5) && instanceOptions.whileloading) { instanceOptions.whileloading.apply(s); } }; this._whileplaying = function(nPosition, oPeakData, oWaveformDataLeft, oWaveformDataRight, oEQData) { 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. s.position = Math.max(0, nPosition); s._processOnPosition(); if (!s.isHTML5 && fV > 8) { if (instanceOptions.usePeakData && oPeakData !== _undefined && oPeakData) { s.peakData = { left: oPeakData.leftPeak, right: oPeakData.rightPeak }; } if (instanceOptions.useWaveformData && oWaveformDataLeft !== _undefined && oWaveformDataLeft) { s.waveformData = { left: oWaveformDataLeft.split(','), right: oWaveformDataRight.split(',') }; } if (instanceOptions.useEQData) { if (oEQData !== _undefined && oEQData && oEQData.leftEQ) { eqLeft = oEQData.leftEQ.split(','); s.eqData = eqLeft; s.eqData.left = eqLeft; if (oEQData.rightEQ !== _undefined && oEQData.rightEQ) { s.eqData.right = oEQData.rightEQ.split(','); } } } } if (s.playState === 1) { // special case/hack: ensure buffering is false if loading from cache (and not yet started) if (!s.isHTML5 && fV === 8 && !s.position && s.isBuffering) { s._onbufferchange(0); } if (instanceOptions.whileplaying) { // flash may call after actual finish instanceOptions.whileplaying.apply(s); } } return true; }; this._oncaptiondata = function(oData) { /** * internal: flash 9 + NetStream (MovieStar/RTMP-only) feature * * @param {object} oData */ sm2._wD(s.id + ': Caption data received.'); s.captiondata = oData; if (s._iO.oncaptiondata) { s._iO.oncaptiondata.apply(s, [oData]); } }; 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.'); var oData = {}, i, j; for (i = 0, j = oMDProps.length; i < j; i++) { oData[oMDProps[i]] = oMDData[i]; } s.metadata = oData; if (s._iO.onmetadata) { s._iO.onmetadata.call(s, s.metadata); } }; 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.'); var oData = [], i, j; for (i = 0, j = oID3Props.length; i < j; i++) { oData[oID3Props[i]] = oID3Data[i]; } s.id3 = mixin(s.id3, oData); if (s._iO.onid3) { s._iO.onid3.apply(s); } }; // flash/RTMP-only this._onconnect = function(bSuccess) { bSuccess = (bSuccess === 1); sm2._wD(s.id + ': ' + (bSuccess ? 'Connected.' : 'Failed to connect? - ' + s.url), (bSuccess ? 1 : 2)); s.connected = bSuccess; if (bSuccess) { s.failures = 0; if (idCheck(s.id)) { if (s.getAutoPlay()) { // only update the play state if auto playing s.play(_undefined, s.getAutoPlay()); } else if (s._iO.autoLoad) { s.load(); } } 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 (s.playState > 0) { sm2._wD(s.id + ': Data error: ' + sError); if (s._iO.ondataerror) { s._iO.ondataerror.apply(s); } } }; // this._debug(); // }; // SMSound() /** * Private SoundManager internals * ------------------------------ */ getDocument = function() { return (doc.body || doc.getElementsByTagName('div')[0]); }; id = function(sID) { return doc.getElementById(sID); }; mixin = function(oMain, oAdd) { // non-destructive merge var o1 = (oMain || {}), o2, o; // if unspecified, o2 is the default options object o2 = (oAdd === _undefined ? sm2.defaultOptions : oAdd); for (o in o2) { 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]); } } } return o1; }; wrapCallback = function(oSound, callback) { /** * 03/03/2013: Fix for Flash Player 11.6.602.171 + Flash 8 (flashVersion = 8) SWF issue * setTimeout() fix for certain SMSound callbacks like onload() and onfinish(), where subsequent calls like play() and load() fail when Flash Player 11.6.602.171 is installed, and using soundManager with flashVersion = 8 (which is the default). * Not sure of exact cause. Suspect race condition and/or invalid (NaN-style) position argument trickling down to the next JS -> Flash _start() call, in the play() case. * Fix: setTimeout() to yield, plus safer null / NaN checking on position argument provided to Flash. * https://getsatisfaction.com/schillmania/topics/recent_chrome_update_seems_to_have_broken_my_sm2_audio_player */ if (!oSound.isHTML5 && fV === 8) { window.setTimeout(callback, 0); } else { callback(); } }; // additional soundManager properties that soundManager.setup() will accept extraOptions = { 'onready': 1, 'ontimeout': 1, 'defaultOptions': 1, 'flash9Options': 1, 'movieStarOptions': 1 }; assign = function(o, oParent) { /** * recursive assignment of properties, soundManager.setup() helper * allows property assignment based on whitelist */ var i, result = true, hasParent = (oParent !== _undefined), setupOptions = sm2.setupOptions, bonusOptions = extraOptions; // // if soundManager.setup() called, show accepted parameters. if (o === _undefined) { result = []; for (i in setupOptions) { if (setupOptions.hasOwnProperty(i)) { result.push(i); } } for (i in bonusOptions) { if (bonusOptions.hasOwnProperty(i)) { if (typeof sm2[i] === 'object') { result.push(i + ': {...}'); } else if (sm2[i] instanceof Function) { result.push(i + ': function() {...}'); } else { result.push(i); } } } sm2._wD(str('setup', result.join(', '))); return false; } // for (i in o) { 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 || o[i] instanceof RegExp) { // check "allowed" options if (hasParent && bonusOptions[oParent] !== _undefined) { // valid recursive / nested object option, eg., { defaultOptions: { volume: 50 } } sm2[oParent][i] = o[i]; } else if (setupOptions[i] !== _undefined) { // special case: assign to setupOptions object, which soundManager property references sm2.setupOptions[i] = o[i]; // assign directly to soundManager, too sm2[i] = o[i]; } else if (bonusOptions[i] === _undefined) { // invalid or disallowed parameter. complain. complain(str((sm2[i] === _undefined ? 'setupUndef' : 'setupError'), i), 2); result = false; } else { /** * 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 (sm2[i] instanceof Function) { sm2[i].apply(sm2, (o[i] instanceof Array ? o[i] : [o[i]])); } else { // good old-fashioned direct assignment sm2[i] = o[i]; } } } else { // recursion case, eg., { defaultOptions: { ... } } if (bonusOptions[i] === _undefined) { // invalid or disallowed parameter. complain. complain(str((sm2[i] === _undefined ? 'setupUndef' : 'setupError'), i), 2); result = false; } else { // recurse through object return assign(o[i], i); } } } } return result; }; function preferFlashCheck(kind) { // whether flash should play a given type return (sm2.preferFlash && hasFlash && !sm2.ignoreFlash && (sm2.flash[kind] !== _undefined && sm2.flash[kind])); } /** * Internal DOM2-level event helpers * --------------------------------- */ event = (function() { // 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), len = args.length; if (old) { // prefix args[1] = 'on' + args[1]; if (len > 3) { // no capture args.pop(); } } else if (len === 3) { args.push(false); } return args; } 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); } } function add() { apply(getArgs(arguments), 'add'); } function remove() { apply(getArgs(arguments), 'remove'); } return { 'add': add, 'remove': remove }; }()); /** * Internal HTML5 event handling * ----------------------------- */ function html5_event(oFn) { // wrap html5 event handlers so we don't call them on destroyed and/or unloaded sounds return function(e) { var s = this._s, result; if (!s || !s._a) { // if (s && s.id) { sm2._wD(s.id + ': Ignoring ' + e.type); } else { sm2._wD(h5 + 'Ignoring ' + e.type); } // result = null; } else { result = oFn.call(this, e); } return result; }; } html5_events = { // HTML5 event-name-to-handler map abort: html5_event(function() { sm2._wD(this._s.id + ': abort'); }), // enough has loaded to play canplay: html5_event(function() { var s = this._s, position1K; if (s._html5_canplay) { // this event has already fired. ignore. return true; } s._html5_canplay = true; sm2._wD(s.id + ': canplay'); s._onbufferchange(0); // position according to instance options position1K = (s._iO.position !== _undefined && !isNaN(s._iO.position) ? s._iO.position/msecScale : null); // set the position if position was provided before the sound loaded if (this.currentTime !== position1K) { sm2._wD(s.id + ': canplay: Setting position to ' + position1K); try { this.currentTime = position1K; } catch(ee) { sm2._wD(s.id + ': canplay: Setting position of ' + position1K + ' failed: ' + ee.message, 2); } } // hack for HTML5 from/to case if (s._iO._oncanplay) { s._iO._oncanplay(); } }), canplaythrough: html5_event(function() { var s = this._s; if (!s.loaded) { s._onbufferchange(0); s._whileloading(s.bytesLoaded, s.bytesTotal, s._get_html5_duration()); s._onload(true); } }), durationchange: html5_event(function() { // durationchange may fire at various times, probably the safest way to capture accurate/final duration. var s = this._s, duration; duration = s._get_html5_duration(); if (!isNaN(duration) && duration !== s.duration) { sm2._wD(this._s.id + ': durationchange (' + duration + ')' + (s.duration ? ', previously ' + s.duration : '')); s.durationEstimate = s.duration = duration; } }), // TODO: Reserved for potential use /* emptied: html5_event(function() { sm2._wD(this._s.id + ': emptied'); }), */ ended: html5_event(function() { var s = this._s; sm2._wD(s.id + ': ended'); s._onfinish(); }), error: html5_event(function() { sm2._wD(this._s.id + ': HTML5 error, code ' + this.error.code); /** * HTML5 error codes, per W3C * Error 1: Client aborted download at user's request. * Error 2: Network error after load started. * Error 3: Decoding issue. * Error 4: Media (audio file) not supported. * Reference: http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#error-codes */ // call load with error state? this._s._onload(false); }), loadeddata: html5_event(function() { var s = this._s; sm2._wD(s.id + ': loadeddata'); // safari seems to nicely report progress events, eventually totalling 100% if (!s._loaded && !isSafari) { s.duration = s._get_html5_duration(); } }), loadedmetadata: html5_event(function() { sm2._wD(this._s.id + ': loadedmetadata'); }), loadstart: html5_event(function() { sm2._wD(this._s.id + ': loadstart'); // assume buffering at first this._s._onbufferchange(1); }), play: html5_event(function() { // sm2._wD(this._s.id + ': play()'); // once play starts, no buffering this._s._onbufferchange(0); }), playing: html5_event(function() { sm2._wD(this._s.id + ': playing ' + String.fromCharCode(9835)); // once play starts, no buffering this._s._onbufferchange(0); }), 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, 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); // reset the "buffered" (loaded byte ranges) array 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 // HTML5 returns seconds. SM2 API uses msec for setPosition() etc., whether Flash or HTML5. for (i = 0, j = ranges.length; i < j; i++) { s.buffered.push({ 'start': ranges.start(i) * msecScale, 'end': ranges.end(i) * msecScale }); } // use the last value locally buffered = (ranges.end(0) - ranges.start(0)) * msecScale; // linear case, buffer sum; does not account for seeking and HTTP partials / byte ranges loaded = Math.min(1, buffered / (e.target.duration * msecScale)); // if (isProgress && ranges.length > 1) { progStr = []; j = ranges.length; for (i = 0; i < j; i++) { progStr.push((e.target.buffered.start(i) * msecScale) + '-' + (e.target.buffered.end(i) * msecScale)); } sm2._wD(this._s.id + ': progress, timeRanges: ' + progStr.join(', ')); } if (isProgress && !isNaN(loaded)) { sm2._wD(this._s.id + ': progress, ' + Math.floor(loaded * 100) + '% loaded'); } // } if (!isNaN(loaded)) { // TODO: prevent calls with duplicate values. 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); } } }), ratechange: html5_event(function() { sm2._wD(this._s.id + ': ratechange'); }), suspend: html5_event(function(e) { // download paused/stopped, may have finished (eg. onload) var s = this._s; sm2._wD(this._s.id + ': suspend'); html5_events.progress.call(this, e); s._onsuspend(); }), stalled: html5_event(function() { sm2._wD(this._s.id + ': stalled'); }), timeupdate: html5_event(function() { this._s._onTimer(); }), waiting: html5_event(function() { var s = this._s; // see also: seeking sm2._wD(this._s.id + ': waiting'); // playback faster than download rate, etc. s._onbufferchange(1); }) }; html5OK = function(iO) { // playability test based on URL or MIME type var result; if (!iO || (!iO.type && !iO.url && !iO.serverURL)) { // nothing to check result = false; } else if (iO.serverURL || (iO.type && preferFlashCheck(iO.type))) { // RTMP, or preferring flash result = false; } else { // Use type, if specified. Pass data: URIs to HTML5. If HTML5-only mode, no other options, so just give 'er result = ((iO.type ? html5CanPlay({type:iO.type}) : html5CanPlay({url:iO.url}) || sm2.html5Only || iO.url.match(/data\:/i))); } return result; }; html5Unload = function(oAudio) { /** * 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. */ var url; if (oAudio) { // 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) { oAudio._called_load = false; } } if (useGlobalHTML5Audio) { // ensure URL state is trashed, also lastGlobalHTML5URL = null; } return url; }; html5CanPlay = function(o) { /** * Try to find MIME, test and return truthiness * o = { * url: '/path/to/an.mp3', * type: 'audio/mp3' * } */ if (!sm2.useHTML5Audio || !sm2.hasHTML5) { return false; } var url = (o.url || null), mime = (o.type || null), aF = sm2.audioFormats, result, offset, fileExt, item; // account for known cases like audio/mp3 if (mime && sm2.html5[mime] !== _undefined) { return (sm2.html5[mime] && !preferFlashCheck(mime)); } if (!html5Ext) { html5Ext = []; for (item in aF) { if (aF.hasOwnProperty(item)) { html5Ext.push(item); if (aF[item].related) { html5Ext = html5Ext.concat(aF[item].related); } } } html5Ext = new RegExp('\\.('+html5Ext.join('|')+')(\\?.*)?$','i'); } // TODO: Strip URL queries, etc. 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..." fileExt = (offset !== -1 ? mime.substr(0,offset) : mime).substr(6); } } else { // match the raw extension name - "mp3", for example fileExt = fileExt[1]; } if (fileExt && sm2.html5[fileExt] !== _undefined) { // result known result = (sm2.html5[fileExt] && !preferFlashCheck(fileExt)); } else { mime = 'audio/' + fileExt; 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() { /** * Internal: Iterates over audioFormats, determining support eg. audio/mp3, audio/mpeg and so on * assigns results to html5[] and flash[]. */ if (!sm2.useHTML5Audio || !sm2.hasHTML5) { // without HTML5, we need Flash. sm2.html5.usingFlash = true; needsFlash = true; 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 = (Audio !== _undefined ? (isOpera && opera.version() < 10 ? new Audio(null) : new Audio()) : null), item, lookup, support = {}, aF, i; function cp(m) { var canPlay, j, result = false, isOK = false; if (!a || typeof a.canPlayType !== 'function') { return result; } if (m instanceof Array) { // iterate through all mime types, return any successes for (i = 0, j = m.length; i < j; i++) { if (sm2.html5[m[i]] || a.canPlayType(m[i]).match(sm2.html5Test)) { isOK = true; sm2.html5[m[i]] = true; // note flash support, too 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(sm2.html5Test))); } return result; } // test all registered formats + codecs aF = sm2.audioFormats; for (item in aF) { if (aF.hasOwnProperty(item)) { lookup = 'audio/' + item; support[item] = cp(aF[item].type); // write back generic type too, eg. audio/mp3 support[lookup] = support[item]; // assign flash if (item.match(flashMIME)) { sm2.flash[item] = true; sm2.flash[lookup] = true; } else { sm2.flash[item] = false; sm2.flash[lookup] = false; } // assign result to related formats, too if (aF[item] && aF[item].related) { for (i = aF[item].related.length - 1; i >= 0; i--) { // eg. audio/m4a support['audio/' + 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); sm2.html5 = mixin(sm2.html5, support); sm2.html5.usingFlash = featureCheck(); needsFlash = sm2.html5.usingFlash; return true; }; strings = { // notReady: 'Unavailable - wait until onready() has fired.', notOK: 'Audio support is not available.', 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: 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: 'Sound ID "%s" should be a string, starting with a non-numeric character', 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: 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...', fbLoaded: 'Flash loaded', 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.', 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: 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 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. ' + String.fromCharCode(10003), reset: sm + '.reset(): Removing event callbacks', mobileUA: 'Mobile UA detected, preferring HTML5 by default.', globalHTML5: 'Using singleton HTML5 Audio() pattern for this device.', ignoreMobile: 'Ignoring mobile restrictions for this device.' // }; str = function() { // internal string replace helper. // arguments: o [,items to replace] // var args, i, j, o, sstr; // real array, please args = slice.call(arguments); // first argument o = args.shift(); sstr = (strings && strings[o] ? strings[o] : ''); if (sstr && args && args.length) { for (i = 0, j = args.length; i < j; i++) { sstr = sstr.replace('%s', args[i]); } } return sstr; // }; loopFix = function(sOpt) { // flash 8 requires stream = false for looping to work if (fV === 8 && sOpt.loops > 1 && sOpt.stream) { _wDS('as2loop'); sOpt.stream = false; } return sOpt; }; policyFix = function(sOpt, sPre) { if (sOpt && !sOpt.usePolicyFile && (sOpt.onid3 || sOpt.usePeakData || sOpt.useWaveformData || sOpt.useEQData)) { sm2._wD((sPre || '') + str('policy')); sOpt.usePolicyFile = true; } return sOpt; }; complain = function(sMsg) { // if (hasConsole && console.warn !== _undefined) { console.warn(sMsg); } else { sm2._wD(sMsg); } // }; doNothing = function() { return false; }; disableObject = function(o) { var oProp; for (oProp in o) { if (o.hasOwnProperty(oProp) && typeof o[oProp] === 'function') { o[oProp] = doNothing; } } oProp = null; }; failSafely = function(bNoDisable) { // general failure exception handler if (bNoDisable === _undefined) { bNoDisable = false; } if (disabled || bNoDisable) { sm2.disable(bNoDisable); } }; normalizeMovieURL = function(smURL) { var urlParams = null, url; if (smURL) { if (smURL.match(/\.swf(\?.*)?$/i)) { urlParams = smURL.substr(smURL.toLowerCase().lastIndexOf('.swf?') + 4); if (urlParams) { // assume user knows what they're doing return smURL; } } else if (smURL.lastIndexOf('/') !== smURL.length - 1) { // append trailing slash, if needed smURL += '/'; } } url = (smURL && smURL.lastIndexOf('/') !== - 1 ? smURL.substr(0, smURL.lastIndexOf('/') + 1) : './') + sm2.movieURL; if (sm2.noSWFCache) { url += ('?ts=' + new Date().getTime()); } return url; }; setVersionInfo = function() { // short-hand for internal use fV = parseInt(sm2.flashVersion, 10); if (fV !== 8 && fV !== 9) { sm2._wD(str('badFV', fV, defaultFlashVersion)); sm2.flashVersion = fV = defaultFlashVersion; } // debug flash movie, if applicable var isDebug = (sm2.debugMode || sm2.debugFlash ? '_debug.swf' : '.swf'); if (sm2.useHTML5Audio && !sm2.html5Only && sm2.audioFormats.mp4.required && fV < 9) { sm2._wD(str('needfl9')); sm2.flashVersion = fV = 9; } sm2.version = sm2.versionNumber + (sm2.html5Only ? ' (HTML5-only mode)' : (fV === 9 ? ' (AS3/Flash 9)' : ' (AS2/Flash 8)')); // set up default options if (fV > 8) { // +flash 9 base options sm2.defaultOptions = mixin(sm2.defaultOptions, sm2.flash9Options); sm2.features.buffering = true; // +moviestar support sm2.defaultOptions = mixin(sm2.defaultOptions, sm2.movieStarOptions); sm2.filePatterns.flash9 = new RegExp('\\.(mp3|' + netStreamTypes.join('|') + ')(\\?.*)?$', 'i'); sm2.features.movieStar = true; } else { sm2.features.movieStar = false; } // regExp for flash canPlay(), etc. sm2.filePattern = sm2.filePatterns[(fV !== 8 ? 'flash9' : 'flash8')]; // if applicable, use _debug versions of SWFs sm2.movieURL = (fV === 8 ? 'soundmanager2.swf' : 'soundmanager2_flash9.swf').replace('.swf', isDebug); sm2.features.peakData = sm2.features.waveformData = sm2.features.eqData = (fV > 8); }; setPolling = function(bPolling, bHighPerformance) { if (!flash) { return false; } flash._setPolling(bPolling, bHighPerformance); }; initDebug = function() { // starts debug mode, creating output
for UAs without console object // allow force of debug mode via URL // if (sm2.debugURLParam.test(wl)) { sm2.setupOptions.debugMode = sm2.debugMode = true; } if (id(sm2.debugID)) { return false; } var oD, oDebug, oTarget, oToggle, tmp; if (sm2.debugMode && !id(sm2.debugID) && (!hasConsole || !sm2.useConsole || !sm2.consoleOnly)) { oD = doc.createElement('div'); oD.id = sm2.debugID + '-toggle'; oToggle = { 'position': 'fixed', 'bottom': '0px', 'right': '0px', 'width': '1.2em', 'height': '1.2em', 'lineHeight': '1.2em', 'margin': '2px', 'textAlign': 'center', 'border': '1px solid #999', 'cursor': 'pointer', 'background': '#fff', 'color': '#333', 'zIndex': 10001 }; oD.appendChild(doc.createTextNode('-')); oD.onclick = toggleDebug; oD.title = 'Toggle SM2 debug console'; 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 = sm2.debugID; oDebug.style.display = (sm2.debugMode ? 'block' : 'none'); if (sm2.debugMode && !id(oD.id)) { try { oTarget = getDocument(); oTarget.appendChild(oD); } catch(e2) { throw new Error(str('domError') + ' \n' + e2.toString()); } oTarget.appendChild(oDebug); } } oTarget = null; // }; idCheck = this.getSoundById; // _wDS = function(o, errorLevel) { return (!o ? '' : sm2._wD(str(o), errorLevel)); }; toggleDebug = function() { var o = id(sm2.debugID), oT = id(sm2.debugID + '-toggle'); if (!o) { return false; } if (debugOpen) { // minimize oT.innerHTML = '+'; o.style.display = 'none'; } else { oT.innerHTML = '-'; o.style.display = 'block'; } debugOpen = !debugOpen; }; debugTS = function(sEventType, bSuccess, sMessage) { // troubleshooter debug hooks if (window.sm2Debugger !== _undefined) { try { sm2Debugger.handleEvent(sEventType, bSuccess, sMessage); } catch(e) { // oh well return false; } } return true; }; // getSWFCSS = function() { var css = []; if (sm2.debugMode) { css.push(swfCSS.sm2Debug); } if (sm2.debugFlash) { css.push(swfCSS.flashDebug); } if (sm2.useHighPerformance) { css.push(swfCSS.highPerf); } return css.join(' '); }; flashBlockHandler = function() { // *possible* flash block situation. var name = str('fbHandler'), p = sm2.getMoviePercent(), css = swfCSS, error = { type:'FLASHBLOCK' }; if (sm2.html5Only) { // no flash, or unused return false; } if (!sm2.ok()) { if (needsFlash) { // make the movie more visible, so user can fix sm2.oMC.className = getSWFCSS() + ' ' + css.swfDefault + ' ' + (p === null ? css.swfTimedout : css.swfError); sm2._wD(name + ': ' + str('fbTimeout') + (p ? ' (' + str('fbLoaded') + ')' : '')); } sm2.didFlashBlock = true; // fire onready(), complain lightly processOnEvents({ type: 'ontimeout', ignoreInit: true, error: error }); catchError(error); } else { // SM2 loaded OK (or recovered) // if (sm2.didFlashBlock) { sm2._wD(name + ': Unblocked'); } // if (sm2.oMC) { sm2.oMC.className = [getSWFCSS(), css.swfDefault, css.swfLoaded + (sm2.didFlashBlock ? ' ' + css.swfUnblocked : '')].join(' '); } } }; addOnEvent = function(sType, oMethod, oScope) { if (on_queue[sType] === _undefined) { on_queue[sType] = []; } on_queue[sType].push({ 'method': oMethod, 'scope': (oScope || null), 'fired': false }); }; processOnEvents = function(oOptions) { // if unspecified, assume OK/error if (!oOptions) { oOptions = { type: (sm2.ok() ? 'onready' : 'ontimeout') }; } if (!didInit && oOptions && !oOptions.ignoreInit) { // not ready yet. return false; } if (oOptions.type === 'ontimeout' && (sm2.ok() || (disabled && !oOptions.ignoreInit))) { // invalid case return false; } var status = { success: (oOptions && oOptions.ignoreInit ? sm2.ok() : !disabled) }, // queue specified by type, or none srcQueue = (oOptions && oOptions.type ? on_queue[oOptions.type] || [] : []), queue = [], i, j, args = [status], canRetry = (needsFlash && !sm2.ok()); if (oOptions.error) { args[0].error = oOptions.error; } for (i = 0, j = srcQueue.length; i < j; i++) { if (srcQueue[i].fired !== true) { queue.push(srcQueue[i]); } } if (queue.length) { // 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) { // useFlashBlock and SWF timeout case doesn't count here. queue[i].fired = true; } } } return true; }; initUserOnload = function() { window.setTimeout(function() { if (sm2.useFlashBlock) { flashBlockHandler(); } processOnEvents(); // call user-defined "onload", scoped to window if (typeof sm2.onload === 'function') { _wDS('onload', 1); sm2.onload.apply(window); _wDS('onloadOK', 1); } if (sm2.waitForWindowLoad) { event.add(window, 'load', initUserOnload); } }, 1); }; 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 (hasFlash !== _undefined) { // this work has already been done. return hasFlash; } 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 (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 obj = null; } hasPlugin = (!!obj); // cleanup, because it is ActiveX after all obj = null; } hasFlash = hasPlugin; return hasPlugin; }; featureCheck = function() { var flashNeeded, item, 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)\s/i))); if (isSpecial) { // has Audio(), but is broken; let it load links directly. sm2.hasHTML5 = false; // ignore flash case, however sm2.html5Only = true; // hide the SWF, if present if (sm2.oMC) { sm2.oMC.style.display = 'none'; } } else { if (sm2.useHTML5Audio) { if (!sm2.html5 || !sm2.html5.canPlayType) { sm2._wD('SoundManager: No HTML5 Audio() support detected.'); sm2.hasHTML5 = false; } // 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); } // } } if (sm2.useHTML5Audio && sm2.hasHTML5) { // sort out whether flash is optional, required or can be ignored. // innocent until proven guilty. canIgnoreFlash = true; for (item in formats) { if (formats.hasOwnProperty(item)) { if (formats[item].required) { if (!sm2.html5.canPlayType(formats[item].type)) { // 100% HTML5 mode is not possible. canIgnoreFlash = false; flashNeeded = true; } else if (sm2.preferFlash && (sm2.flash[item] || sm2.flash[formats[item].type])) { // flash may be required, or preferred for this format. flashNeeded = true; } } } } } // sanity check... if (sm2.ignoreFlash) { flashNeeded = false; canIgnoreFlash = true; } sm2.html5Only = (sm2.hasHTML5 && sm2.useHTML5Audio && !flashNeeded); return (!sm2.html5Only); }; 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. */ var i, j, urlResult = 0, result; if (url instanceof Array) { // find the first good one for (i = 0, j = url.length; i < j; i++) { if (url[i] instanceof Object) { // MIME check if (sm2.canPlayMIME(url[i].type)) { urlResult = i; break; } } else if (sm2.canPlayURL(url[i])) { // URL string check urlResult = i; break; } } // normalize to string if (url[urlResult].url) { url[urlResult] = url[urlResult].url; } result = url[urlResult]; } else { // single URL case result = url; } return result; }; startTimer = function(oSound) { /** * attach a timer to this sound, and start an interval if needed */ if (!oSound._hasTimer) { oSound._hasTimer = true; if (!mobileHTML5 && sm2.html5PollingInterval) { if (h5IntervalTimer === null && h5TimerCount === 0) { h5IntervalTimer = setInterval(timerExecute, sm2.html5PollingInterval); } h5TimerCount++; } } }; stopTimer = function(oSound) { /** * detach a timer */ if (oSound._hasTimer) { oSound._hasTimer = false; if (!mobileHTML5 && sm2.html5PollingInterval) { // interval will stop itself at next execution. h5TimerCount--; } } }; 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) { // no active timers, stop polling interval. clearInterval(h5IntervalTimer); h5IntervalTimer = null; return false; } // check all HTML5 sounds with timers for (i = sm2.soundIDs.length - 1; i >= 0; i--) { if (sm2.sounds[sm2.soundIDs[i]].isHTML5 && sm2.sounds[sm2.soundIDs[i]]._hasTimer) { sm2.sounds[sm2.soundIDs[i]]._onTimer(); } } }; catchError = function(options) { options = (options !== _undefined ? options : {}); if (typeof sm2.onerror === 'function') { sm2.onerror.apply(window, [{ type: (options.type !== _undefined ? options.type : null) }]); } if (options.fatal !== _undefined && options.fatal) { sm2.disable(); } }; badSafariFix = function() { // special case: "bad" Safari (OS X 10.3 - 10.7) must fall back to flash for MP3/MP4 if (!isBadSafari || !detectFlash()) { // doesn't apply return false; } var aF = sm2.audioFormats, i, item; for (item in aF) { if (aF.hasOwnProperty(item)) { if (item === 'mp3' || item === 'mp4') { 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--) { sm2.html5[aF[item].related[i]] = false; } } } } } }; /** * Pseudo-private flash/ExternalInterface methods * ---------------------------------------------- */ this._setSandboxType = function(sandboxType) { // // Security sandbox according to Flash plugin var sb = sm2.sandbox; sb.type = sandboxType; sb.description = sb.types[(sb.types[sandboxType] !== _undefined?sandboxType : 'unknown')]; if (sb.type === 'localWithFile') { sb.noRemote = true; sb.noLocal = false; _wDS('secNote', 2); } else if (sb.type === 'localWithNetwork') { sb.noRemote = false; sb.noLocal = true; } else if (sb.type === 'localTrusted') { sb.noRemote = false; sb.noLocal = false; } // }; this._externalInterfaceOK = function(swfVersion) { // flash callback confirming flash loaded, EI working etc. // swfVersion: SWF build string if (sm2.swfLoaded) { return false; } var e; debugTS('swf', true); debugTS('flashtojs', true); sm2.swfLoaded = true; tryInitOnFocus = false; if (isBadSafari) { badSafariFix(); } // complain if JS + SWF build/version strings don't match, excluding +DEV builds // if (!swfVersion || swfVersion.replace(/\+dev/i,'') !== sm2.versionNumber.replace(/\+dev/i, '')) { 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); // exit, init will fail with timeout return false; } // // IE needs a larger timeout setTimeout(init, isIE ? 100 : 1); }; /** * Private initialization helpers * ------------------------------ */ createMovie = function(smID, smURL) { if (didAppend && appendSuccess) { // ignore if already succeeded return false; } function initMsg() { // var options = [], title, msg = [], 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) { msg = msg.concat([options.join(delimiter)]); } sm2._wD(title + (msg.length ? delimiter + msg.join(', ') : ''), 1); showSupport(); // } if (sm2.html5Only) { // 100% HTML5 mode setVersionInfo(); initMsg(); sm2.oMC = id(sm2.movieID); init(); // prevent multiple init attempts didAppend = true; appendSuccess = true; return false; } // flash path var remoteURL = (smURL || sm2.url), localURL = (sm2.altURL || remoteURL), swfTitle = 'JS/Flash audio component (SoundManager 2)', oTarget = getDocument(), extraClass = getSWFCSS(), isRTL = null, html = doc.getElementsByTagName('html')[0], oEmbed, oMovie, tmp, movieHTML, oEl, s, x, sClass; isRTL = (html && html.dir && html.dir.match(/rtl/i)); smID = (smID === _undefined ? sm2.id : smID); function param(name, value) { return ''; } // safety check for legacy (change to Flash 9 URL) setVersionInfo(); sm2.url = normalizeMovieURL(overHTTP ? remoteURL : localURL); smURL = sm2.url; sm2.wmode = (!sm2.wmode && sm2.useHighPerformance ? 'transparent' : sm2.wmode); 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 (?) */ messages.push(strings.spcWmode); sm2.wmode = null; } oEmbed = { 'name': smID, 'id': smID, 'src': smURL, 'quality': 'high', 'allowScriptAccess': sm2.allowScriptAccess, 'bgcolor': sm2.bgColor, 'pluginspage': http + 'www.macromedia.com/go/getflashplayer', 'title': swfTitle, 'type': 'application/x-shockwave-flash', 'wmode': sm2.wmode, // http://help.adobe.com/en_US/as3/mobile/WS4bebcd66a74275c36cfb8137124318eebc6-7ffd.html 'hasPriority': 'true' }; if (sm2.debugFlash) { oEmbed.FlashVars = 'debug=1'; } if (!sm2.wmode) { // don't write empty attribute delete oEmbed.wmode; } if (isIE) { // IE is "special". oMovie = doc.createElement('div'); movieHTML = [ '', param('movie', smURL), param('AllowScriptAccess', sm2.allowScriptAccess), param('quality', oEmbed.quality), (sm2.wmode? param('wmode', sm2.wmode): ''), param('bgcolor', sm2.bgColor), param('hasPriority', 'true'), (sm2.debugFlash ? param('FlashVars', oEmbed.FlashVars) : ''), '' ].join(''); } else { oMovie = doc.createElement('embed'); for (tmp in oEmbed) { if (oEmbed.hasOwnProperty(tmp)) { oMovie.setAttribute(tmp, oEmbed[tmp]); } } } initDebug(); extraClass = getSWFCSS(); oTarget = getDocument(); if (oTarget) { sm2.oMC = (id(sm2.movieID) || doc.createElement('div')); if (!sm2.oMC.id) { sm2.oMC.id = sm2.movieID; sm2.oMC.className = swfCSS.swfDefault + ' ' + extraClass; s = null; oEl = null; if (!sm2.useFlashBlock) { if (sm2.useHighPerformance) { // on-screen at all times s = { 'position': 'fixed', 'width': '8px', 'height': '8px', // >= 6px for flash to run fast, >= 8px to start up under Firefox/win32 in some cases. odd? yes. 'bottom': '0px', 'left': '0px', 'overflow': 'hidden' }; } else { // hide off-screen, lower priority s = { 'position': 'absolute', 'width': '6px', 'height': '6px', 'top': '-9999px', 'left': '-9999px' }; if (isRTL) { s.left = Math.abs(parseInt(s.left, 10)) + 'px'; } } } if (isWebkit) { // soundcloud-reported render/crash fix, safari 5 sm2.oMC.style.zIndex = 10000; } if (!sm2.debugFlash) { for (x in s) { if (s.hasOwnProperty(x)) { sm2.oMC.style[x] = s[x]; } } } try { if (!isIE) { sm2.oMC.appendChild(oMovie); } oTarget.appendChild(sm2.oMC); if (isIE) { oEl = sm2.oMC.appendChild(doc.createElement('div')); oEl.className = swfCSS.swfBox; oEl.innerHTML = movieHTML; } appendSuccess = true; } catch(e) { throw new Error(str('domError') + ' \n' + e.toString()); } } else { // SM2 container is already in the document (eg. flashblock use case) 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; } } didAppend = true; initMsg(); // sm2._wD(sm + ': Trying to load ' + smURL + (!overHTTP && sm2.altURL ? ' (alternate URL)' : ''), 1); return true; }; initMovie = function() { if (sm2.html5Only) { createMovie(); return false; } // attempt to get, or create, movie (may already exist) if (flash) { return false; } 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. */ _wDS('noURL'); return false; } // inline markup case flash = sm2.getMovie(sm2.id); if (!flash) { if (!oRemoved) { // try to create createMovie(sm2.id, sm2.url); } else { // try to re-append removed movie after reboot() if (!isIE) { sm2.oMC.appendChild(oRemoved); } else { sm2.oMC.innerHTML = oRemovedHTML; } oRemoved = null; didAppend = true; } flash = sm2.getMovie(sm2.id); } if (typeof sm2.oninitmovie === 'function') { setTimeout(sm2.oninitmovie, 1); } // flushMessages(); // return true; }; delayWaitForEI = function() { 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; if (!sm2.url) { // No SWF url to load (noURL case) - exit for now. Will be retried when url is set. return false; } if (waitingForEI) { return false; } waitingForEI = true; event.remove(window, 'load', delayWaitForEI); if (hasFlash && tryInitOnFocus && !isFocused) { // Safari won't load flash in background tabs, only when focused. _wDS('waitFocus'); return false; } if (!didInit) { p = sm2.getMoviePercent(); if (p > 0 && p < 100) { loadIncomplete = true; } } setTimeout(function() { p = sm2.getMoviePercent(); if (loadIncomplete) { // special case: if movie *partially* loaded, retry until it's 100% before assuming failure. waitingForEI = false; sm2._wD(str('waitSWF')); window.setTimeout(delayWaitForEI, 1); return false; } // 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?)')); } // // give up / time-out, depending if (!didInit && okToDisable) { if (p === null) { // SWF failed to report load progress. Possibly blocked. if (sm2.useFlashBlock || sm2.flashLoadTimeout === 0) { if (sm2.useFlashBlock) { flashBlockHandler(); } _wDS('waitForever'); } else { // no custom flash block handling, but SWF has timed out. Will recover if user unblocks / allows SWF load. if (!sm2.useFlashBlock && canIgnoreFlash) { rebootIntoHTML5(); } else { _wDS('waitForever'); // fire any regular registered ontimeout() listeners. processOnEvents({ type: 'ontimeout', ignoreInit: true, error: { type: 'INIT_FLASHBLOCK' } }); } } } else { // SWF loaded? Shouldn't be a blocking issue, then. if (sm2.flashLoadTimeout === 0) { _wDS('waitForever'); } else { if (!sm2.useFlashBlock && canIgnoreFlash) { rebootIntoHTML5(); } else { failSafely(true); } } } } }, sm2.flashLoadTimeout); }; handleFocus = function() { function cleanup() { event.remove(window, 'focus', handleFocus); } if (isFocused || !tryInitOnFocus) { // already focused, or not special Safari background tab case cleanup(); return true; } okToDisable = true; isFocused = true; _wDS('gotFocus'); // allow init to restart waitingForEI = false; // kick off ExternalInterface timeout, now that the SWF has started delayWaitForEI(); cleanup(); return true; }; flushMessages = function() { // // SM2 pre-init debug messages if (messages.length) { sm2._wD('SoundManager 2: ' + messages.join(' '), 1); messages = []; } // }; showSupport = function() { // flushMessages(); var item, tests = []; if (sm2.useHTML5Audio && sm2.hasHTML5) { for (item in sm2.audioFormats) { if (sm2.audioFormats.hasOwnProperty(item)) { tests.push(item + ' = ' + sm2.html5[item] + (!sm2.html5[item] && needsFlash && sm2.flash[item] ? ' (using flash)' : (sm2.preferFlash && sm2.flash[item] && needsFlash ? ' (preferring flash)' : (!sm2.html5[item] ? ' (' + (sm2.audioFormats[item].required ? 'required, ' : '') + 'and no flash support)' : '')))); } } sm2._wD('SoundManager 2 HTML5 support: ' + tests.join(', '), 1); } // }; initComplete = function(bNoDisable) { if (didInit) { return false; } if (sm2.html5Only) { // all good. _wDS('sm2Loaded', 1); didInit = true; initUserOnload(); debugTS('onload', true); return true; } var wasTimeout = (sm2.useFlashBlock && sm2.flashLoadTimeout && !sm2.getMoviePercent()), result = true, error; if (!wasTimeout) { didInit = true; } error = { type: (!hasFlash && needsFlash ? 'NO_FLASH' : 'INIT_TIMEOUT') }; sm2._wD('SoundManager 2 ' + (disabled ? 'failed to load' : 'loaded') + ' (' + (disabled ? 'Flash security/load error' : 'OK') + ') ' + String.fromCharCode(disabled ? 10006 : 10003), disabled ? 2: 1); 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); result = false; } else { debugTS('onload', true); } if (!disabled) { if (sm2.waitForWindowLoad && !windowLoaded) { _wDS('waitOnload'); event.add(window, 'load', initUserOnload); } else { // if (sm2.waitForWindowLoad && windowLoaded) { _wDS('docLoaded'); } // initUserOnload(); } } return result; }; /** * 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() { var i, o = sm2.setupOptions; for (i in o) { if (o.hasOwnProperty(i)) { // assign local property if not already defined if (sm2[i] === _undefined) { sm2[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 sm2.setupOptions[i] = sm2[i]; } } } }; init = function() { // called after onload() if (didInit) { _wDS('didInit'); return false; } function cleanup() { event.remove(window, 'load', sm2.beginDelayedInit); } if (sm2.html5Only) { if (!didInit) { // we don't need no steenking flash! cleanup(); sm2.enabled = true; initComplete(); } return true; } // flash path initMovie(); try { // attempt to talk to Flash 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, (sm2.flashPollingInterval || (sm2.useHighPerformance ? 10 : 50))); if (!sm2.debugMode) { // stop the SWF from making debug output calls to JS flash._disableDebug(); } sm2.enabled = true; debugTS('jstoflash', true); 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(window, 'unload', doNothing); } } catch(e) { 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(); return false; } initComplete(); // disconnect events cleanup(); return true; }; domContentLoaded = function() { if (didDCLoaded) { return false; } didDCLoaded = true; // assign top-level soundManager properties eg. soundManager.url setProperties(); initDebug(); if (!hasFlash && sm2.hasHTML5) { 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 }); } testHTML5(); if (!hasFlash && needsFlash) { messages.push(strings.needFlash); // TODO: Fatal here vs. timeout approach, etc. // hack: fail sooner. sm2.setup({ 'flashLoadTimeout': 1 }); } if (doc.removeEventListener) { doc.removeEventListener('DOMContentLoaded', domContentLoaded, false); } initMovie(); return true; }; domContentLoadedIE = function() { if (doc.readyState === 'complete') { domContentLoaded(); doc.detachEvent('onreadystatechange', domContentLoadedIE); } return true; }; winOnLoad = function() { // catch edge case of initComplete() firing after window.load() windowLoaded = true; // catch case where DOMContentLoaded has been sent, but we're still in doc.readyState = 'interactive' domContentLoaded(); event.remove(window, 'load', winOnLoad); }; // sniff up-front detectFlash(); // focus and window load, init (primarily flash-driven) event.add(window, 'focus', handleFocus); event.add(window, 'load', delayWaitForEI); event.add(window, 'load', winOnLoad); if (doc.addEventListener) { doc.addEventListener('DOMContentLoaded', domContentLoaded, false); } else if (doc.attachEvent) { 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 }); } } // SoundManager() // SM2_DEFER details: http://www.schillmania.com/projects/soundmanager2/doc/getstarted/#lazy-loading if (window.SM2_DEFER === _undefined || !SM2_DEFER) { soundManager = new SoundManager(); } /** * SoundManager public interfaces * ------------------------------ */ if (typeof module === 'object' && module && typeof module.exports === 'object') { /** * commonJS module */ module.exports.SoundManager = SoundManager; module.exports.soundManager = soundManager; } else if (typeof define === 'function' && define.amd) { /** * AMD - requireJS * basic usage: * require(["/path/to/soundmanager2.js"], function(SoundManager) { * SoundManager.getInstance().setup({ * url: '/swf/', * onready: function() { ... } * }) * }); * * SM2_DEFER usage: * window.SM2_DEFER = true; * require(["/path/to/soundmanager2.js"], function(SoundManager) { * SoundManager.getInstance(function() { * var soundManager = new SoundManager.constructor(); * soundManager.setup({ * url: '/swf/', * ... * }); * ... * soundManager.beginDelayedInit(); * return soundManager; * }) * }); */ define(function() { /** * Retrieve the global instance of SoundManager. * If a global instance does not exist it can be created using a callback. * * @param {Function} smBuilder Optional: Callback used to create a new SoundManager instance * @return {SoundManager} The global SoundManager instance */ function getInstance(smBuilder) { if (!window.soundManager && smBuilder instanceof Function) { var instance = smBuilder(SoundManager); if (instance instanceof SoundManager) { window.soundManager = instance; } } return window.soundManager; } return { constructor: SoundManager, getInstance: getInstance } }); } // standard browser case // constructor window.SoundManager = SoundManager; /** * note: SM2 requires a window global due to Flash, which makes calls to window.soundManager. * Flash may not always be needed, but this is not known until async init and SM2 may even "reboot" into Flash mode. */ // public API, flash callbacks etc. window.soundManager = soundManager; }(window)); SoundManager2-2.97a.20150601/src/000077500000000000000000000000001253275212400157505ustar00rootroot00000000000000SoundManager2-2.97a.20150601/src/SoundManager2.as000066400000000000000000000401441253275212400207450ustar00rootroot00000000000000/** * SoundManager 2: Javascript Sound for the Web * ---------------------------------------------- * http://schillmania.com/projects/soundmanager2/ * * Copyright (c) 2007, Scott Schiller. All rights reserved. * Code licensed under the BSD License: * http://www.schillmania.com/projects/soundmanager2/license.txt * * Flash 8 / ActionScript 2 version * * Compiling AS to Flash 8 SWF using MTASC (free compiler - http://www.mtasc.org/): * mtasc -swf soundmanager2.swf -main -header 16:16:30 SoundManager2.as -version 8 * * ActionScript Sound class reference (Macromedia), documentation download: * http://livedocs.macromedia.com/flash/8/ * Previously-live URL: * http://livedocs.macromedia.com/flash/8/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00002668.html * * *** NOTE ON LOCAL FILE SYSTEM ACCESS *** * * Flash security allows local OR network access, but not both * unless explicitly whitelisted/allowed by the flash player's * security settings. * * To enable in-flash messaging for troubleshooting, pass debug=1 in FlashVars (within object/embed code) * SM2 will do this by default when soundManager.debugFlash = true. * */ import flash.external.ExternalInterface; // woo class SoundManager2 { static var app: SoundManager2; function SoundManager2() { var version = "V2.97a.20150601"; var version_as = "(AS2/Flash 8)"; /** * Cross-domain security options * HTML on foo.com loading .swf hosted on bar.com? Define your "HTML domain" here to allow JS+Flash communication to work. * // allow_xdomain_scripting = true; * // xdomain = "foo.com"; * For all domains (possible security risk?), use xdomain = "*"; which ends up as System.security.allowDomain("*"); * When loading from HTTPS, use System.security.allowInsecureDomain(); * See "allowDomain (security.allowDomain method)" in Flash 8/AS2 liveDocs documentation (AS2 reference -> classes -> security) * download from http://livedocs.macromedia.com/flash/8/ * Related AS3 documentation: http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/system/Security.html#allowDomain%28%29 */ var allow_xdomain_scripting = false; var xdomain = "*"; if (allow_xdomain_scripting && xdomain) { System.security.allowDomain(xdomain); version_as += ' - cross-domain enabled'; } // externalInterface references (for Javascript callbacks) var baseJSController = "soundManager"; var baseJSObject = baseJSController + ".sounds"; // internal objects var sounds = []; // indexed string array var soundObjects = []; // associative Sound() object array var timer = null; var pollingEnabled = false; // polling (timer) flag - disabled by default, enabled by JS->Flash call var debugEnabled = true; // Flash debug output enabled by default, disabled by JS call var flashDebugEnabled = false; // debug output to flash movie, off by default var didSandboxMessage = false; var caughtFatal = false; // for flash text output, debugging etc. var _messages = []; var _messageObj = null; flashDebugEnabled = (_root.debug == 1); // display stuffs Stage.scaleMode = 'noScale'; Stage.align = 'TL'; // context menu item with version info var doNothing = function() {} var sm2Menu:ContextMenu = new ContextMenu(); var sm2MenuItem:ContextMenuItem = new ContextMenuItem('SoundManager ' + version + ' ' + version_as, doNothing); sm2MenuItem.enabled = false; sm2Menu.customItems.push(sm2MenuItem); _root.menu = sm2Menu; var writeDebug = function(s, logLevel) { // if (!debugEnabled) return false; ExternalInterface.call(baseJSController + "['_writeDebug']", "(Flash): " + s, (logLevel || 0)); // } var flashDebug = function(messageText) { // _messages.push(messageText); if (!flashDebugEnabled) { return false; } var sans = new TextFormat(); sans.size = 12; sans.font = "Arial"; // 320x240 if no stage dimensions (happens in IE, apparently 0 before stage resize event fires.) var w = Stage.width?Stage.width:320; var h = Stage.height?Stage.height:240; if (!_messageObj) { _messageObj = _root.createTextField("_messageObj", 0, 0, 0, w, h); _messageObj.x = 0; _messageObj.y = 0; _messageObj.multiline = true; _messageObj.html = true; _messageObj.wordWrap = true; _messageObj.align = 'left'; _messageObj.autoSize = false; } _messageObj.htmlText = _messages.join('\n'); _messageObj.setTextFormat(sans); _messageObj.width = w; _messageObj.height = h; // } var _externalInterfaceTest = function(isFirstCall) { var sandboxType = System.security['sandboxType']; try { if (isFirstCall) { flashDebug('Testing Flash -> JS...') if (!didSandboxMessage && sandboxType != 'remote' && sandboxType != 'localTrusted') { didSandboxMessage = true; flashDebug('
Fatal: Security sandbox error: Got "' + sandboxType + '", expected "remote" or "localTrusted".
Additional security permissions need to be granted.
See flash security settings panel for non-HTTP, eg. file:// use.

http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html

You may also be able to right-click this movie and choose from the menu:
"Global Settings" -> "Advanced" tab -> "Trusted Location Settings"
'); } ExternalInterface.call(baseJSController + "._externalInterfaceOK", version); if (!didSandboxMessage) { flashDebug('Flash -> JS OK'); flashDebug('Waiting for JS -> Flash...'); } } else { // writeDebug('SM2 SWF ' + version + ' ' + version_as, 1); ExternalInterface.call(baseJSController + "._setSandboxType", sandboxType); flashDebug('JS -> Flash OK'); } } catch(e) { flashDebug(e.toString()); if (!caughtFatal) { caughtFatal = true; } return false; } return true; // to verify that a call from JS to here, works. (eg. JS receives "true", thus OK.) } var _disableDebug = function() { // prevent future debug calls from Flash going to client (maybe improve performance) writeDebug('_disableDebug()'); debugEnabled = false; } var checkProgress = function() { var bL = 0; var bT = 0; var nD = 0; var nP = 0; var oSound = null; for (var i = 0, j = sounds.length; i < j; i++) { oSound = soundObjects[sounds[i]]; bL = oSound.getBytesLoaded(); bT = oSound.getBytesTotal(); nD = oSound.duration || 0; // can sometimes be null with short MP3s? Wack. nP = oSound.position; if (bL && bT && bL != oSound.lastValues.bytes) { oSound.lastValues.bytes = bL; ExternalInterface.call(baseJSObject + "['" + oSound.sID + "']._whileloading", bL, bT, nD); } if (typeof nP != 'undefined' && nP != oSound.lastValues.position) { oSound.lastValues.position = nP; ExternalInterface.call(baseJSObject + "['" + oSound.sID + "']._whileplaying", nP); // if position changed, check for near-end } } } var onLoad = function(bSuccess) { checkProgress(); // ensure progress stats are up-to-date // force duration update (doesn't seem to be always accurate) ExternalInterface.call(baseJSObject + "['" + this.sID + "']._whileloading", this.getBytesLoaded(), this.getBytesTotal(), this.duration); ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onload", this.duration > 0 ? 1 : 0); // bSuccess doesn't always seem to work, so check MP3 duration instead. } var onID3 = function() { // --- NOTE: BUGGY? --- // -------------------- // TODO: Investigate holes in ID3 parsing - for some reason, Album will be populated with Date if empty and date is provided. (?) // ID3V1 seem to parse OK, but "holes" / blanks in ID3V2 data seem to get messed up (eg. missing album gets filled with date.) // iTunes issues: onID3 was not called with a test MP3 encoded with iTunes 7.01, and what appeared to be valid ID3V2 data. // May be related to thumbnails for album art included in MP3 file by iTunes. See http://mabblog.com/blog/?p=33 var id3Data = []; var id3Props = []; for (var prop in this.id3) { id3Props.push(prop); id3Data.push(this.id3[prop]); // writeDebug('id3['+prop+']: '+this.id3[prop]); } ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onid3", id3Props, id3Data); // unhook own event handler, prevent second call (can fire twice as data is received - ID3V2 at beginning, ID3V1 at end.) // Therefore if ID3V2 data is received, ID3V1 is ignored. soundObjects[this.sID].onID3 = null; } var registerOnComplete = function(sID) { soundObjects[sID].onSoundComplete = function() { checkProgress(); ExternalInterface.call(baseJSObject + "['" + sID + "']._onfinish"); } } var _setPosition = function(sID, nSecOffset, isPaused, _allowMultiShot) { // note: multiShot is Flash 9-only; retained so JS/Flash function signatures are identical. var s = soundObjects[sID]; // writeDebug('_setPosition()'); s.lastValues.position = s.position; if (s.lastValues.loops > 1 && nSecOffset != 0) { writeDebug('Warning: Looping functionality being disabled due to Flash limitation.', 240); s.lastValues.loops = 1; } s.start(nSecOffset, s.lastValues.nLoops || 1); // start playing at new position if (isPaused) { s.stop(); } } var _load = function(sID, sURL, bStream, bAutoPlay, bCheckPolicyFile) { // writeDebug('_load(): '+sID+', '+sURL+', '+bStream+', '+bAutoPlay+', '+bCheckPolicyFile); if (typeof bAutoPlay == 'undefined') { bAutoPlay = false; } if (typeof bStream == 'undefined') { bStream = true; } if (typeof bCheckPolicyFile == 'undefined') { bCheckPolicyFile = false; } // writeDebug('bStream: '+bStream); // writeDebug('bAutoPlay: '+bAutoPlay); // checkProgress(); var s = soundObjects[sID]; s.onID3 = onID3; s.onLoad = onLoad; s.loaded = true; s.checkPolicyFile = bCheckPolicyFile; s.loadSound(sURL, bStream); if (bAutoPlay != true) { s.stop(); // prevent default auto-play behaviour } else { writeDebug('auto-play allowed'); } registerOnComplete(sID); } var _unload = function(sID, sURL) { // effectively "stop" loading by loading a tiny MP3 var s = soundObjects[sID]; s.onID3 = null; s.onLoad = null; s.loaded = false; // ensure position is reset, if unload fails s.start(0,1); s.stop(); s.loadSound(sURL, true); s.stop(); // prevent auto-play } var _createSound = function(sID, loops, checkPolicyFile) { var s = new Sound(); if (!soundObjects[sID]) { sounds.push(sID); } soundObjects[sID] = s; s.setVolume(100); s.sID = sID; s.paused = false; s.loaded = false; s.checkPolicyFile = checkPolicyFile; s.lastValues = { bytes: 0, position: 0, nLoops: loops||1 }; } var _destroySound = function(sID) { // for the power of garbage collection! .. er, Greyskull! var s = (soundObjects[sID] || null); if (!s) { return false; } for (var i = 0; i < sounds.length; i++) { if (sounds[i] == sID) { sounds.splice(i, 1); break; } } s = null; delete soundObjects[sID]; } var _stop = function(sID, bStopAll) { // stop this particular instance (or "all", based on parameter) if (bStopAll) { _root.stop(); } else { soundObjects[sID].stop(); soundObjects[sID].paused = false; } } var _start = function(sID, nLoops, nMsecOffset, _allowMultiShot) { // note: multiShot is Flash 9-only; retained so JS/Flash function signatures are identical. // writeDebug('_start: ' + sID + ', loops: ' + nLoops + ', nMsecOffset: ' + nMsecOffset); registerOnComplete(); var s = soundObjects[sID]; s.lastValues.paused = false; // reset pause if applicable s.lastValues.nLoops = (nLoops || 1); s.start(nMsecOffset, nLoops); return true; } var _pause = function(sID, _allowMultiShot) { // note: multiShot is Flash 9-only; retained so JS/Flash function signatures are identical. // writeDebug('_pause()'); var s = soundObjects[sID]; if (!s.paused) { // reference current position, stop sound s.paused = true; s.lastValues.position = s.position; // writeDebug('_pause(): position: '+s.lastValues.position); s.stop(); } else { // resume playing from last position // writeDebug('resuming - playing at '+s.lastValues.position+', '+s.lastValues.nLoops+' times'); s.paused = false; s.start(s.lastValues.position / 1000, s.lastValues.nLoops); } } var _setPan = function(sID, nPan) { soundObjects[sID].setPan(nPan); } var _setVolume = function(sID, nVol) { soundObjects[sID].setVolume(nVol); } var _setPolling = function(bPolling, timerInterval) { if (typeof timerInterval === 'undefined') { timerInterval = 50; } pollingEnabled = bPolling; if (timer == null && pollingEnabled) { flashDebug('Enabling polling, ' + timerInterval + ' ms interval'); timer = setInterval(checkProgress, timerInterval); } else if (timer && !pollingEnabled) { flashDebug('Disabling polling'); clearInterval(timer); timer = null; } } var _init = function() { // OK now stuff should be available try { flashDebug('Adding ExternalInterface callbacks...'); ExternalInterface.addCallback('_load', this, _load); ExternalInterface.addCallback('_unload', this, _unload); ExternalInterface.addCallback('_stop', this, _stop); ExternalInterface.addCallback('_start', this, _start); ExternalInterface.addCallback('_pause', this, _pause); ExternalInterface.addCallback('_setPosition', this, _setPosition); ExternalInterface.addCallback('_setPan', this, _setPan); ExternalInterface.addCallback('_setVolume', this, _setVolume); ExternalInterface.addCallback('_setPolling', this, _setPolling); ExternalInterface.addCallback('_externalInterfaceTest', this, _externalInterfaceTest); ExternalInterface.addCallback('_disableDebug', this, _disableDebug); ExternalInterface.addCallback('_createSound', this, _createSound); ExternalInterface.addCallback('_destroySound', this, _destroySound); } catch(e) { flashDebug('Fatal: ExternalInterface error: ' + e.toString()); } // try to talk to JS, do init etc. _externalInterfaceTest(true); // flashDebug('Init OK'); } flashDebug('SM2 SWF ' + version + ' ' + version_as); if (ExternalInterface.available) { flashDebug('ExternalInterface available'); _init(); } else { // d'oh! - may be from a corrupt install, ancient (pre-Netscape 6?) browser etc. flashDebug('Fatal: ExternalInterface (Flash <-> JS) not available'); } } // SoundManager2() // entry point static function main(mc) { app = new SoundManager2(); } }SoundManager2-2.97a.20150601/src/SoundManager2_AS3.as000066400000000000000000001107211253275212400214120ustar00rootroot00000000000000/** * SoundManager 2: Javascript Sound for the Web * ---------------------------------------------- * http://schillmania.com/projects/soundmanager2/ * * Copyright (c) 2007, Scott Schiller. All rights reserved. * Code licensed under the BSD License: * http://www.schillmania.com/projects/soundmanager2/license.txt * * Flash 9 / ActionScript 3 version */ package { import flash.display.Sprite; import flash.events.Event; import flash.events.IOErrorEvent; import flash.events.MouseEvent; import flash.events.SecurityErrorEvent; import flash.events.AsyncErrorEvent; import flash.events.NetStatusEvent; import flash.events.TimerEvent; import flash.external.ExternalInterface; // woo import flash.media.Sound; import flash.media.SoundChannel; import flash.media.SoundMixer; import flash.net.URLLoader; import flash.net.URLRequest; import flash.system.Security; import flash.system.System; import flash.text.TextField; import flash.text.TextFormat; import flash.text.TextFieldAutoSize; import flash.ui.ContextMenu; import flash.ui.ContextMenuItem; import flash.utils.setInterval; import flash.utils.clearInterval; import flash.utils.Dictionary; import flash.utils.Timer; public class SoundManager2_AS3 extends Sprite { public var version:String = "V2.97a.20150601"; public var version_as:String = "(AS3/Flash 9)"; /** * Cross-domain security options * HTML on foo.com loading .swf hosted on bar.com? Define your "HTML domain" here to allow JS+Flash communication to work. * // allow_xdomain_scripting = true; * // xdomain = "foo.com"; * For all domains (possible security risk?), use xdomain = "*"; which ends up as System.security.allowDomain("*"); * When loading from HTTPS, use System.security.allowInsecureDomain(); * See http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/system/Security.html#allowDomain() */ public var allow_xdomain_scripting:Boolean = false; public var xdomain:String = "*"; // externalInterface references (for Javascript callbacks) public var baseJSController:String = "soundManager"; public var baseJSObject:String = baseJSController + ".sounds"; // internal objects public var sounds:Array = []; // indexed string array public var soundObjects: Dictionary = new Dictionary(); // associative Sound() object Dictionary type public var timer: Timer = null; public var pollingEnabled: Boolean = false; // polling (timer) flag - disabled by default, enabled by JS->Flash call public var debugEnabled: Boolean = true; // Flash debug output enabled by default, disabled by JS call public var flashDebugEnabled: Boolean = false; // Flash internal debug output (write to visible SWF in browser) public var loaded: Boolean = false; public var currentObject: SoundManager2_SMSound_AS3 = null; public var paramList:Object = null; public var messages:Array = []; public var textField: TextField = null; public var textStyle: TextFormat = new TextFormat(); public var didSandboxMessage: Boolean = false; public var caughtFatal: Boolean = false; public function SoundManager2_AS3() { if (allow_xdomain_scripting && xdomain) { Security.allowDomain(xdomain); version_as += ' - cross-domain enabled'; } this.paramList = this.root.loaderInfo.parameters; // if (this.paramList['debug'] == 1) { this.flashDebugEnabled = true; } if (this.flashDebugEnabled) { var canvas: Sprite = new Sprite(); canvas.graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight); addChild(canvas); } // flashDebug('SM2 SWF ' + version + ' ' + version_as); // context menu item with version info var sm2Menu:ContextMenu = new ContextMenu(); var sm2MenuItem:ContextMenuItem = new ContextMenuItem('SoundManager ' + version + ' ' + version_as); sm2MenuItem.enabled = false; sm2Menu.customItems.push(sm2MenuItem); contextMenu = sm2Menu; if (ExternalInterface.available) { flashDebug('ExternalInterface available'); try { flashDebug('Adding ExternalInterface callbacks...'); ExternalInterface.addCallback('_load', _load); ExternalInterface.addCallback('_unload', _unload); ExternalInterface.addCallback('_stop', _stop); ExternalInterface.addCallback('_start', _start); ExternalInterface.addCallback('_pause', _pause); ExternalInterface.addCallback('_setPosition', _setPosition); ExternalInterface.addCallback('_setPan', _setPan); ExternalInterface.addCallback('_setVolume', _setVolume); ExternalInterface.addCallback('_setPolling', _setPolling); ExternalInterface.addCallback('_externalInterfaceTest', _externalInterfaceTest); ExternalInterface.addCallback('_disableDebug', _disableDebug); ExternalInterface.addCallback('_getMemoryUse', _getMemoryUse); ExternalInterface.addCallback('_createSound', _createSound); ExternalInterface.addCallback('_destroySound', _destroySound); ExternalInterface.addCallback('_setAutoPlay', _setAutoPlay); } catch(e: Error) { flashDebug('Fatal: ExternalInterface error: ' + e.toString()); } } else { flashDebug('Fatal: ExternalInterface (Flash <-> JS) not available'); }; // call after delay, to be safe (ensure callbacks are registered by the time JS is called below) var timer: Timer = new Timer(20, 0); timer.addEventListener(TimerEvent.TIMER, function() : void { timer.reset(); _externalInterfaceTest(true); // timer.reset(); // flashDebug('Init OK'); }); timer.start(); // delayed, see above // _externalInterfaceTest(true); } // SoundManager2() public function flashDebug (txt:String) : void { // messages.push(txt); if (this.flashDebugEnabled) { var didCreate: Boolean = false; textStyle.font = 'Arial'; textStyle.size = 12; // 320x240 if no stage dimensions (happens in IE, apparently 0 before stage resize event fires.) var w:Number = this.stage.width?this.stage.width:320; var h:Number = this.stage.height?this.stage.height:240; if (textField == null) { didCreate = true; textField = new TextField(); textField.autoSize = TextFieldAutoSize.LEFT; textField.x = 0; textField.y = 0; textField.multiline = true; textField.textColor = 0; textField.wordWrap = true; } textField.htmlText = messages.join('\n'); textField.setTextFormat(textStyle); textField.width = w; textField.height = h; if (didCreate) { this.addChild(textField); } } // } public function _setAutoPlay(sID:String, autoPlay:Boolean) : void { var s: SoundManager2_SMSound_AS3 = soundObjects[sID]; if (s) { s.setAutoPlay(autoPlay); } } // methods // ----------------------------------- public function writeDebug (s:String, logLevel:Number = 0) : Boolean { if (!debugEnabled) { return false; } // ExternalInterface.call(baseJSController + "['_writeDebug']", "(Flash): " + s, null, logLevel); // return true; } public function _externalInterfaceTest(isFirstCall: Boolean) : Boolean { var sandboxType:String = flash.system.Security['sandboxType']; if (!didSandboxMessage && sandboxType != 'localTrusted' && sandboxType != 'remote') { didSandboxMessage = true; flashDebug('
Fatal: Security sandbox error: Got "' + sandboxType + '", expected "remote" or "localTrusted".
Additional security permissions need to be granted.
See flash security settings panel for non-HTTP, eg., file:// use.

http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html

You may also be able to right-click this movie and choose from the menu:
"Global Settings" -> "Advanced" tab -> "Trusted Location Settings"
'); } try { if (isFirstCall == true) { flashDebug('Testing Flash -> JS...'); ExternalInterface.call(baseJSController + "._externalInterfaceOK", version); flashDebug('Flash -> JS OK'); flashDebug('Waiting for JS -> Flash...'); } else { // writeDebug('SM2 SWF ' + version + ' ' + version_as, 1); ExternalInterface.call(baseJSController + "._setSandboxType", sandboxType); flashDebug('JS -> Flash OK'); } } catch(e: Error) { flashDebug('Fatal: Flash <-> JS error: ' + e.toString()); writeDebug('_externalInterfaceTest: Error: ' + e.toString(), 2); if (!caughtFatal) { caughtFatal = true; } return false; } return true; // to verify that a call from JS to here, works. (eg. JS receives "true", thus OK.) } public function _disableDebug() : void { // prevent future debug calls from Flash going to client (maybe improve performance) writeDebug('_disableDebug()'); debugEnabled = false; } public function checkLoadProgress(e: Event) : void { try { var oSound:Object = e.target; var bL: int = oSound.bytesLoaded; var bT: int = oSound.bytesTotal; var nD: int = oSound.length || oSound.duration || 0; var sMethod:String = baseJSObject + "['" + oSound.sID + "']._whileloading"; ExternalInterface.call(sMethod, bL, bT, nD); if (bL && bT && bL != oSound.lastValues.bytes) { oSound.lastValues.bytes = bL; ExternalInterface.call(sMethod, bL, bT, nD); } } catch(e: Error) { writeDebug('checkLoadProgress(): ' + e.toString()); } } public function checkSoundProgress(oSound:SoundManager2_SMSound_AS3, forceCheck:Boolean = false, forceEndCheck:Boolean = false) : void { var bL: int = 0; var bT: int = 0; var nD: int = 0; var nP: int = 0; var bufferLength: int = 0; var lP:Number = 0; var rP:Number = 0; var isBuffering:Object = null; var oSoundChannel: flash.media.SoundChannel = null; var sMethod:String = null; var newPeakData: Boolean = false; var newWaveformData: Boolean = false; var newEQData: Boolean = false; var areSoundsInaccessible: Boolean = SoundMixer.areSoundsInaccessible(); var isPlaying: Boolean = true; // special case for NetStream when ending var hasNew:Boolean = false; var hasNewLoaded:Boolean = false; if (!oSound || !oSound.useEvents || oSound.failed || !oSound.connected) { // edge cases for ignoring: if sounds are destructed within event handlers while checkProgress() is running, may be null return; } sMethod = baseJSObject + "['" + oSound.sID + "']._whileloading"; if (oSound.useNetstream) { // Don't do anything if there is no NetStream object yet if (!oSound.ns) { return; } // stream bufferLength = oSound.ns.bufferLength; bL = oSound.ns.bytesLoaded; bT = oSound.ns.bytesTotal; nD = int(oSound.duration || 0); // can sometimes be null with short MP3s? Wack. nP = oSound.ns.time * 1000; if (oSound.paused) { // special case: paused netStreams don't update if setPosition() is called while they are paused. // instead, return lastValues.position which should reflect setPosition() call. // writeDebug('paused case, setting nP of '+nP+' to -1'); // writeDebug('lastValues: '+oSound.lastValues.position); nP = oSound.lastValues.position; } if (nP >= 0 && nP != oSound.lastValues.position) { oSound.lastValues.position = nP; hasNew = true; } if (nD > oSound.lastValues.duration) { oSound.lastValues.duration = nD; hasNew = true; } if (bL > oSound.lastValues.bytesLoaded) { oSound.lastValues.bytesLoaded = bL; hasNew = true; } if (bT > oSound.lastValues.bytes) { oSound.lastValues.bytes = bT; hasNew = true; } if (bufferLength != oSound.lastValues.bufferLength) { oSound.lastValues.bufferLength = bufferLength; hasNew = true; } // Don't set loaded for streams because bytesLoaded and bytesTotal are always 0 // writeDebug('ns: time/duration, bytesloaded/total: '+nP+'/'+nD+', '+bL+'/'+bT); if (oSound.loaded != true && nD > 0 && bL == bT && bL != 0 && bT != 0) { // non-MP3 has loaded oSound.loaded = true; try { ExternalInterface.call(sMethod, bL, bT, nD, bufferLength); ExternalInterface.call(baseJSObject + "['" + oSound.sID + "']._onload", oSound.duration > 0 ? 1 : 0); } catch(e: Error) { writeDebug('_whileLoading/_onload error: ' + e.toString(), 2); } } else if (oSound.loaded != true && hasNew) { ExternalInterface.call(sMethod, bL, bT, nD, bufferLength); } } else { // MP3 sound oSoundChannel = oSound.soundChannel; bL = oSound.bytesLoaded; bT = oSound.bytesTotal; nD = int(oSound.length || 0); // can sometimes be null with short MP3s? Wack. isBuffering = oSound.isBuffering; if (oSoundChannel) { nP = (oSoundChannel.position || 0); if (oSound.lastValues.loops > 1 && nP > oSound.length) { // round down to nearest loop var playedLoops:Number = Math.floor(nP/oSound.length); nP = nP - (oSound.length*playedLoops); } if (oSound.usePeakData) { lP = int((oSoundChannel.leftPeak) * 1000) / 1000; rP = int((oSoundChannel.rightPeak) * 1000) / 1000; } else { lP = 0; rP = 0; } } else { // stopped, not loaded or feature not used nP = 0; } if (forceEndCheck) { // sound finish case: Ensure position is at end (sound duration), as flash 9 does not always correctly match the two. if (nP < nD) { writeDebug('correcting sound ' + oSound.sID + ' end position ('+nP+') to length: '+ nD, 2); nP = nD; } } if (nP != oSound.lastValues.position && nP !== 0 && !oSound.didFinish) { // once "completed", sound is locked via didFinish so no more position-type events fire. oSound.lastValues.position = nP; hasNew = true; } if (nD > oSound.lastValues.duration) { // original sound duration * number of sound loops oSound.lastValues.duration = nD; hasNew = true; } if (bL > oSound.lastValues.bytesLoaded) { oSound.lastValues.bytesLoaded = bL; hasNew = true; } if (bT > oSound.lastValues.bytes) { oSound.lastValues.bytes = bT; hasNew = true; hasNewLoaded = true; } // loading progress if (hasNewLoaded) { oSound.lastValues.bytes = bL; ExternalInterface.call(sMethod, bL, bT, nD); } } // peak data if (oSoundChannel && oSound.usePeakData) { if (lP != oSound.lastValues.leftPeak) { oSound.lastValues.leftPeak = lP; newPeakData = true; } if (rP != oSound.lastValues.rightPeak) { oSound.lastValues.rightPeak = rP; newPeakData = true; } } var newDataError:Boolean = false; var dataError:String; // special case: Netstream may try to fire whileplaying() after finishing. check that stop hasn't fired. isPlaying = (oSound.didLoad && !oSound.paused && (!oSound.useNetstream || (oSound.useNetstream && oSound.lastNetStatus != "NetStream.Play.Stop"))); // don't update if stream has ended // raw waveform + EQ spectrum data if (isPlaying && (oSoundChannel || oSound.useNetstream)) { if (oSound.useWaveformData) { if (!areSoundsInaccessible && !oSound.handledDataError && !oSound.ignoreDataError) { try { oSound.getWaveformData(); } catch(e: Error) { if (!oSound.handledDataError) { writeDebug('getWaveformData() (waveform data) '+e.toString()); } // oSound.useWaveformData = false; newDataError = true; dataError = e.toString(); } } } if (oSound.useEQData) { if (!areSoundsInaccessible && !oSound.handledDataError && !oSound.ignoreDataError) { try { oSound.getEQData(); } catch(e: Error) { if (!oSound.handledDataError) { writeDebug('computeSpectrum() (EQ data) '+e.toString()); } // oSound.useEQData = false; newDataError = true; dataError = e.toString(); } } } if (oSound.waveformDataArray != oSound.lastValues.waveformDataArray) { oSound.lastValues.waveformDataArray = oSound.waveformDataArray; newWaveformData = true; } if (oSound.eqDataArray != oSound.lastValues.eqDataArray) { oSound.lastValues.eqDataArray = oSound.eqDataArray; newEQData = true; } if (newDataError && !oSound.handledDataError) { sMethod = baseJSObject + "['" + oSound.sID + "']._ondataerror"; ExternalInterface.call(sMethod, 'data unavailable: ' + dataError); oSound.handledDataError = true; } } if (typeof nP != 'undefined' && (hasNew && (oSound.soundChannel || oSound.useNetstream || forceCheck || forceEndCheck))) { // && isPlaying - removed to allow updates while paused, eg. from setPosition() calls. Also be more liberal if we're using netStream. // oSound.lastValues.position = nP; sMethod = baseJSObject + "['" + oSound.sID + "']._whileplaying"; var waveDataLeft:String = (newWaveformData ? oSound.waveformDataArray.slice(0, 256).join(',') : null); var waveDataRight:String = (newWaveformData ? oSound.waveformDataArray.slice(256).join(',') : null); var eqDataLeft:String = (newEQData ? oSound.eqDataArray.slice(0, 256).join(',') : null); var eqDataRight:String = (newEQData ? oSound.eqDataArray.slice(256).join(',') : null); ExternalInterface.call(sMethod, nP, (newPeakData ? { leftPeak: lP, rightPeak: rP } : null), waveDataLeft, waveDataRight, (newEQData ? { leftEQ: eqDataLeft, rightEQ: eqDataRight } : null)); } // check isBuffering if (!oSound.useNetstream && oSound.isBuffering != oSound.lastValues.isBuffering) { // property has changed oSound.lastValues.isBuffering = oSound.isBuffering; sMethod = baseJSObject + "['" + oSound.sID + "']._onbufferchange"; ExternalInterface.call(sMethod, oSound.isBuffering ? 1 : 0); } } public function checkProgress() : void { for (var i: int = 0, j: int = sounds.length; i < j; i++) { checkSoundProgress(soundObjects[sounds[i]]); } } public function onLoadError(oSound:Object) : void { // something went wrong. 404, bad format etc. ExternalInterface.call(baseJSObject + "['" + oSound.sID + "']._onload", 0); } public function onLoad(e: Event) : void { var oSound:Object = e.target; checkSoundProgress(soundObjects[oSound.sID]); // ensure progress stats are up-to-date if (!oSound.useNetstream) { // FLV must also have metadata oSound.loaded = true; // force duration update (doesn't seem to be always accurate) ExternalInterface.call(baseJSObject + "['" + oSound.sID + "']._whileloading", oSound.bytesLoaded, oSound.bytesTotal, oSound.length || oSound.duration); // duration > 0 means a valid sound was loaded. ExternalInterface.call(baseJSObject + "['" + oSound.sID + "']._onload", (oSound.length || oSound.duration ? 1 : 0)); } } public function onID3(e: Event) : void { // --- NOTE: BUGGY (Flash 8 only? Haven't really checked 9 + 10.) --- // TODO: Investigate holes in ID3 parsing - for some reason, Album will be populated with Date if empty and date is provided. (?) // ID3V1 seem to parse OK, but "holes" / blanks in ID3V2 data seem to get messed up (eg. missing album gets filled with date.) // iTunes issues: onID3 was not called with a test MP3 encoded with iTunes 7.01, and what appeared to be valid ID3V2 data. // May be related to thumbnails for album art included in MP3 file by iTunes. See http://mabblog.com/blog/?p=33 try { var oSound:Object = e.target; var id3Data:Array = []; var id3Props:Array = []; for (var prop:String in oSound.id3) { id3Props.push(prop); id3Data.push(oSound.id3[prop]); // writeDebug('id3['+prop+']: '+oSound.id3[prop]); } ExternalInterface.call(baseJSObject + "['" + oSound.sID + "']._onid3", id3Props, id3Data); // unhook own event handler, prevent second call (can fire twice as data is received - ID3V2 at beginning, ID3V1 at end.) // Therefore if ID3V2 data is received, ID3V1 is ignored. // soundObjects[oSound.sID].onID3 = null; } catch(e: Error) { writeDebug('onID3(): Unable to get ID3 info for ' + oSound.sID + '.'); } oSound.removeEventListener(Event.ID3, onID3); } public function registerOnComplete(sID:String) : void { var oSound: SoundManager2_SMSound_AS3 = soundObjects[sID]; if (oSound && oSound.soundChannel) { oSound.didFinish = false; // reset this flag oSound.soundChannel.addEventListener(Event.SOUND_COMPLETE, function() : void { if (oSound) { // force progress check, catching special end-of-sound case where position may not match duration. checkSoundProgress(oSound, true, true); try { oSound.ignoreDataError = true; // workaround: avoid data error handling for this manual step.. // oSound.soundChannel.stop(); oSound.didFinish = true; // "lock" sound, prevent extra whileplaying() position-type updates // call onfinish first (with end position)... ExternalInterface.call(baseJSObject + "['" + sID + "']._onfinish"); // then reset sound so it can be played again. // oSound.start(0, 1); // go back to 0 } catch(e: Error) { writeDebug('Could not set position on ' + sID + ': ' + e.toString()); } oSound.ignoreDataError = false; // ..and reset oSound.handledDataError = false; // reset this flag } else { // safety net ExternalInterface.call(baseJSObject + "['" + sID + "']._onfinish"); } }); } } public function doSecurityError(oSound: SoundManager2_SMSound_AS3, e: SecurityErrorEvent) : void { writeDebug('securityError: ' + e.text); // when this happens, you don't have security rights on the server containing the FLV file // a crossdomain.xml file would fix the problem easily } public function _setPosition(sID:String, nSecOffset:Number, isPaused: Boolean, allowMultiShot: Boolean) : void { var s: SoundManager2_SMSound_AS3 = soundObjects[sID]; if (!s) return void; // writeDebug('_setPosition()'); // stop current channel, start new one. if (s.lastValues) { s.lastValues.position = nSecOffset; // s.soundChannel.position; } if (s.useNetstream) { // Minimize the buffer so playback starts ASAP s.ns.bufferTime = s.bufferTime; writeDebug('setPosition ('+ sID + '): setting buffer to '+s.ns.bufferTime+' secs'); nSecOffset = nSecOffset > 0 ? nSecOffset / 1000 : 0; s.ns.seek(nSecOffset); checkSoundProgress(s); // force UI update } else { if (s.soundChannel) { s.soundChannel.stop(); } writeDebug('setPosition ('+ sID + '): ' + nSecOffset); // +', '+(s.lastValues.loops?s.lastValues.loops:1)); if (s.lastValues.loops > 1 && nSecOffset != 0) { writeDebug('Warning: Looping functionality being disabled due to Flash limitation.'); s.lastValues.loops = 1; } try { s.start(nSecOffset, s.lastValues.loops || 1, allowMultiShot); // start playing at new position } catch(e: Error) { writeDebug('Warning: Could not set position on ' + sID + ': ' + e.toString()); } checkSoundProgress(s); // force UI update try { registerOnComplete(sID); } catch(e: Error) { writeDebug('_setPosition(): Could not register onComplete'); } if (isPaused && s.soundChannel) { // writeDebug('_setPosition: stopping (paused) sound'); // writeDebug('last position: '+s.lastValues.position+' vs '+s.soundChannel.position); s.soundChannel.stop(); } } } public function _load(sID:String, sURL:String, bStream: Boolean, bAutoPlay: Boolean, nLoops: Number, bAutoLoad: Boolean, bCheckPolicyFile: Boolean) : void { // writeDebug('_load()'); if (typeof bAutoPlay == 'undefined') bAutoPlay = false; var s: SoundManager2_SMSound_AS3 = soundObjects[sID]; if (!s) return void; var didRecreate: Boolean = false; if (s.didLoad == true) { // need to recreate sound didRecreate = true; writeDebug('recreating sound ' + sID + ' in order to load ' + sURL); var ns:Object = new Object(); ns.sID = s.sID; ns.loops = nLoops||1; ns.usePeakData = s.usePeakData; ns.useWaveformData = s.useWaveformData; ns.useEQData = s.useEQData; ns.useNetstream = s.useNetstream; ns.bufferTime = s.bufferTime; ns.serverUrl = s.serverUrl; ns.duration = s.duration; ns.checkPolicyFile = s.checkPolicyFile; ns.useEvents = true; _destroySound(s.sID); _createSound(ns.sID, sURL, ns.usePeakData, ns.useWaveformData, ns.useEQData, ns.useNetstream, ns.bufferTime, ns.loops, ns.serverUrl, ns.duration, bAutoPlay, ns.useEvents, bAutoLoad, ns.checkPolicyFile); s = soundObjects[sID]; // writeDebug('Sound object replaced'); } checkSoundProgress(s); if (!s.didLoad) { try { s.addEventListener(Event.ID3, onID3); s.addEventListener(Event.COMPLETE, onLoad); } catch(e: Error) { writeDebug('_load(): could not assign ID3/complete event handlers'); } } // don't try to load if same request already made s.sURL = sURL; try { if (!s.useNetstream) { s.addEventListener(IOErrorEvent.IO_ERROR, function(e: IOErrorEvent) : void { s.doIOError(e); }); } s.loadSound(sURL); } catch(e: Error) { // oh well writeDebug('_load: Error loading ' + sURL + '. Flash error detail: ' + e.toString()); } } public function _unload(sID:String) : void { var s: SoundManager2_SMSound_AS3 = (soundObjects[sID] || null); if (!s) return void; var sURL:String = s.sURL; // save existing sound URL for object recreation try { removeEventListener(Event.ID3, onID3); removeEventListener(Event.COMPLETE, onLoad); } catch(e: Error) { writeDebug('_unload() warn: Could not remove ID3/complete events'); } s.paused = false; if (s.soundChannel) { s.soundChannel.stop(); } try { if (s.didLoad && !s.loaded && !s.useNetstream) { s.close(); // close stream only if still loading? } } catch(e: Error) { // stream may already have closed if sound loaded, etc. writeDebug(sID + '._unload(): Note: Unable to close stream: ' + e.toString()); // oh well } // destroy and recreate Flash sound object, try to reclaim memory // writeDebug('sound._unload(): recreating sound '+sID+' to free memory'); if (s.useNetstream) { // writeDebug('_unload(): closing netStream stuff'); try { s.removeNetstreamEvents(); s.ns.close(); s.nc.close(); // s.nc = null; // s.ns = null; } catch(e: Error) { // oh well writeDebug('_unload(): caught exception during netConnection/netStream close'); } } var ns:Object = new Object(); ns.sID = s.sID; ns.loops = s.loops; ns.usePeakData = s.usePeakData; ns.useWaveformData = s.useWaveformData; ns.useEQData = s.useEQData; ns.useNetstream = s.useNetstream; ns.bufferTime = s.bufferTime; ns.serverUrl = s.serverUrl; ns.duration = s.duration; ns.autoPlay = s.autoPlay; ns.autoLoad = s.autoLoad; ns.checkPolicyFile = s.checkPolicyFile; _destroySound(s.sID); _createSound(ns.sID, sURL, ns.usePeakData, ns.useWaveformData, ns.useEQData, ns.useNetstream, ns.bufferTime, ns.loops, ns.serverUrl, ns.duration, ns.autoPlay, false, ns.autoLoad, ns.checkPolicyFile); // false: don't allow events just yet soundObjects[sID].connected = true; // fake it? writeDebug(s.sID + '.unload(): ok'); } public function _createSound(sID:String, sURL:String, usePeakData: Boolean, useWaveformData: Boolean, useEQData: Boolean, useNetstream: Boolean, bufferTime:Number, loops:Number, serverUrl:String, duration:Number, autoPlay:Boolean, useEvents:Boolean, autoLoad:Boolean, checkPolicyFile:Boolean) : void { var s: SoundManager2_SMSound_AS3 = new SoundManager2_SMSound_AS3(this, sID, sURL, usePeakData, useWaveformData, useEQData, useNetstream, bufferTime, serverUrl, duration, autoPlay, useEvents, autoLoad, checkPolicyFile); if (!soundObjects[sID]) { sounds.push(sID); } soundObjects[sID] = s; this.currentObject = s; s.sID = sID; s.sURL = sURL; s.paused = false; s.loaded = false; s.checkPolicyFile = checkPolicyFile; s.lastValues = { bytes: 0, duration: 0, position: 0, loops: loops||1, leftPeak: 0, rightPeak: 0, bufferLength: 0 }; } public function _destroySound(sID:String) : void { // for the power of garbage collection! .. er, Greyskull! var s: SoundManager2_SMSound_AS3 = (soundObjects[sID] || null); if (!s) return void; // try to unload the sound for (var i: int = 0, j: int = sounds.length; i < j; i++) { if (sounds[i] == sID) { sounds.splice(i, 1); break; } } if (s.soundChannel) { s.soundChannel.stop(); } // if is a movie, remove that as well. if (s.useNetstream) { // s.nc.client = null; try { s.removeNetstreamEvents(); // s.nc.removeEventListener(NetStatusEvent.NET_STATUS, s.doNetStatus); } catch(e: Error) { writeDebug('_destroySound(): Events already removed from netStream/netConnection?'); } if (s.didLoad) { // TODO: figure out if stream is still open first, can't close an already-closed stream. try { s.ns.close(); s.nc.close(); } catch(e: Error) { // oh well writeDebug('_destroySound(): caught exception: '+e.toString()); } } } else if (s.didLoad) { // non-netstream case try { s.close(); // close stream only if still loading? } catch(e: Error) { // oh well } } s = null; soundObjects[sID] = null; delete soundObjects[sID]; } public function _stop(sID:String, bStopAll: Boolean) : void { // stop this particular instance (or "all", based on parameter) if (bStopAll) { SoundMixer.stopAll(); } else { var s: SoundManager2_SMSound_AS3 = soundObjects[sID]; if (!s) return void; if (s.useNetstream && s.ns) { s.ns.pause(); } else if (s.soundChannel) { s.soundChannel.stop(); } s.paused = false; } } public function _start(sID:String, nLoops: int, nMsecOffset: int, allowMultiShot: Boolean) : Boolean { var s: SoundManager2_SMSound_AS3 = soundObjects[sID]; var result: Boolean; if (!s) return true; writeDebug('start (' + sID + '): ' + nMsecOffset + (nLoops > 1 ? ', loops: ' + nLoops : '')); s.lastValues.paused = false; // reset pause if applicable s.lastValues.loops = (nLoops || 1); if (!s.useNetstream) { s.lastValues.position = nMsecOffset; } s.handledDataError = false; // reset this flag try { result = s.start(nMsecOffset, nLoops, allowMultiShot); } catch(e: Error) { writeDebug('Could not start ' + sID + ': ' + e.toString()); } try { registerOnComplete(sID); } catch(e: Error) { writeDebug('_start(): registerOnComplete failed'); } return result; } public function _pause(sID:String, allowMultiShot: Boolean) : void { // writeDebug('_pause(): ' + sID); var s: SoundManager2_SMSound_AS3 = soundObjects[sID]; if (!s) return void; // writeDebug('s.paused: '+s.paused); if (!s.paused) { // reference current position, stop sound s.paused = true; // writeDebug('_pause(): position: '+s.lastValues.position); if (s.useNetstream) { if (s.ns) { s.lastValues.position = s.ns.time*1000; s.ns.pause(); } else if (s.autoPlay) { s.setAutoPlay(false); } } else { if (s.soundChannel) { s.lastValues.position = s.soundChannel.position; s.soundChannel.stop(); } } } else { // resume playing from last position // writeDebug('resuming - playing at '+s.lastValues.position+', '+s.lastValues.loops+' times'); s.paused = false; if (s.useNetstream) { s.ns.resume(); } else { s.start(s.lastValues.position, s.lastValues.loops, allowMultiShot); } try { registerOnComplete(sID); } catch(e: Error) { writeDebug('_pause(): registerOnComplete() failed'); } } } public function _setPan(sID:String, nPan:Number) : void { if (soundObjects[sID]) { soundObjects[sID].setPan(nPan); } } public function _setVolume(sID:String, nVol:Number) : void { // writeDebug('_setVolume: '+nVol); if (soundObjects[sID]) { soundObjects[sID].setVolume(nVol); } } public function _setPolling(bPolling: Boolean = false, nTimerInterval: uint = 50) : void { pollingEnabled = bPolling; if (timer == null && pollingEnabled) { flashDebug('Enabling polling, ' + nTimerInterval + ' ms interval'); timer = new Timer(nTimerInterval, 0); timer.addEventListener(TimerEvent.TIMER, function() : void { checkProgress(); }); // direct reference eg. checkProgress doesn't work? .. odd. timer.start(); } else if (timer && !pollingEnabled) { flashDebug('Disabling polling'); // flash.utils.clearInterval(timer); timer.reset(); } } public function _getMemoryUse() : String { return System.totalMemory.toString(); } // ----------------------------------- // end methods } // package }SoundManager2-2.97a.20150601/src/SoundManager2_SMSound_AS3.as000066400000000000000000000633241253275212400230300ustar00rootroot00000000000000/** * SoundManager 2: Javascript Sound for the Web * ---------------------------------------------- * http://schillmania.com/projects/soundmanager2/ * * Copyright (c) 2007, Scott Schiller. All rights reserved. * Code licensed under the BSD License: * http://www.schillmania.com/projects/soundmanager2/license.txt * * Flash 9 / ActionScript 3 version */ package { import flash.external.*; import flash.events.*; import flash.media.Sound; import flash.media.SoundChannel; import flash.media.SoundLoaderContext; import flash.media.SoundTransform; import flash.media.SoundMixer; import flash.net.URLRequest; import flash.utils.ByteArray; import flash.utils.getTimer; import flash.net.NetConnection; import flash.net.NetStream; import flash.net.Responder; public class SoundManager2_SMSound_AS3 extends Sound { public var sm: SoundManager2_AS3 = null; // externalInterface references (for Javascript callbacks) public var baseJSController: String = "soundManager"; public var baseJSObject: String = baseJSController + ".sounds"; public var soundChannel: SoundChannel = new SoundChannel(); public var urlRequest: URLRequest; public var soundLoaderContext: SoundLoaderContext; public var waveformData: ByteArray = new ByteArray(); public var waveformDataArray: Array = []; public var eqData: ByteArray = new ByteArray(); public var eqDataArray: Array = []; public var usePeakData: Boolean = false; public var useWaveformData: Boolean = false; public var useEQData: Boolean = false; public var sID: String; public var sURL: String; public var didFinish: Boolean; public var loaded: Boolean; public var connected: Boolean; public var failed: Boolean; public var paused: Boolean; public var finished: Boolean; public var duration: Number; public var handledDataError: Boolean = false; public var ignoreDataError: Boolean = false; public var autoPlay: Boolean = false; public var autoLoad: Boolean = false; public var pauseOnBufferFull: Boolean = false; // only applies to RTMP public var loops: Number = 1; public var lastValues: Object = { bytes: 0, position: 0, duration: 0, volume: 100, pan: 0, loops: 1, leftPeak: 0, rightPeak: 0, waveformDataArray: null, eqDataArray: null, isBuffering: null, bufferLength: 0 }; public var didLoad: Boolean = false; public var useEvents: Boolean = false; public var sound: Sound = new Sound(); public var cc: Object; public var nc: NetConnection; public var ns: NetStream = null; public var st: SoundTransform; public var useNetstream: Boolean; public var bufferTime: Number = 3; // previously 0.1 public var lastNetStatus: String = null; public var serverUrl: String = null; public var checkPolicyFile:Boolean = false; public function SoundManager2_SMSound_AS3(oSoundManager: SoundManager2_AS3, sIDArg: String = null, sURLArg: String = null, usePeakData: Boolean = false, useWaveformData: Boolean = false, useEQData: Boolean = false, useNetstreamArg: Boolean = false, netStreamBufferTime: Number = 1, serverUrl: String = null, duration: Number = 0, autoPlay: Boolean = false, useEvents: Boolean = false, autoLoad: Boolean = false, checkPolicyFile: Boolean = false) { this.sm = oSoundManager; this.sID = sIDArg; this.sURL = sURLArg; this.usePeakData = usePeakData; this.useWaveformData = useWaveformData; this.useEQData = useEQData; this.urlRequest = new URLRequest(sURLArg); this.didFinish = false; this.loaded = false; this.connected = false; this.failed = false; this.finished = false; this.soundChannel = null; this.lastNetStatus = null; this.useNetstream = useNetstreamArg; this.serverUrl = serverUrl; this.duration = duration; this.useEvents = useEvents; this.autoLoad = autoLoad; if (netStreamBufferTime) { this.bufferTime = netStreamBufferTime; } this.checkPolicyFile = checkPolicyFile; writeDebug('SoundManager2_SMSound_AS3: Got duration: '+duration+', autoPlay: '+autoPlay); if (this.useNetstream) { // Pause on buffer full if auto-loading an RTMP stream if (this.serverUrl && this.autoLoad) { this.pauseOnBufferFull = true; } this.cc = new Object(); this.nc = new NetConnection(); // Handle FMS bandwidth check callback. // @see onBWDone // @see http://www.adobe.com/devnet/flashmediaserver/articles/dynamic_stream_switching_04.html // @see http://www.johncblandii.com/index.php/2007/12/fms-a-quick-fix-for-missing-onbwdone-onfcsubscribe-etc.html this.nc.client = this; // TODO: security/IO error handling // this.nc.addEventListener(SecurityErrorEvent.SECURITY_ERROR, doSecurityError); nc.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler); if (this.serverUrl != null) { writeDebug('SoundManager2_SMSound_AS3: NetConnection: connecting to server ' + this.serverUrl + '...'); } this.nc.connect(serverUrl); } else { this.connected = true; } } public function rtmpResponder(result:Object):void { // callback from Flash Media Server (RTMP) for 'getStreamLength' server-side method - result should be a floating-point. // http://help.adobe.com/en_US/FlashMediaServer/3.5_Deving/WS5b3ccc516d4fbf351e63e3d11a0773d117-7ffe.html writeDebug('RTMP server getStreamLength() response: ' + result); // we now know the duration. type cast to floating-point - this will update JS-land during whileloading() / whileplaying(). this.duration = Number(result) * 1000; } public function netStatusHandler(event:NetStatusEvent):void { if (this.useEvents) { writeDebug('netStatusHandler: '+event.info.code); } switch (event.info.code) { case "NetConnection.Connect.Success": try { this.ns = new NetStream(this.nc); this.ns.checkPolicyFile = this.checkPolicyFile; // bufferTime reference: http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/net/NetStream.html#bufferTime this.ns.bufferTime = this.bufferTime; // set to 0.1 or higher. 0 is reported to cause playback issues with static files. this.st = new SoundTransform(); this.cc.onMetaData = this.onMetaData; this.cc.setCaption = this.captionHandler; this.ns.client = this.cc; this.ns.receiveAudio(true); this.addNetstreamEvents(); this.connected = true; // RTMP-only if (this.serverUrl && this.useEvents) { var responder:Responder = new Responder(rtmpResponder); // call a method on server to get the length of the stream (like onMetaData, but Flash Media Server-specific) // Red5 and other RTMP servers appear to provide duration via onMetaData event(s) in the stream. // http://help.adobe.com/en_US/FlashMediaServer/3.5_Deving/WS5b3ccc516d4fbf351e63e3d11a0773d117-7ffe.html nc.call('getStreamLength', responder, this.sURL); writeDebug('NetConnection: connected'); writeDebug('firing _onconnect for '+this.sID); ExternalInterface.call(this.sm.baseJSObject + "['" + this.sID + "']._onconnect", 1); } } catch(e: Error) { this.failed = true; writeDebug('netStream error: ' + e.toString()); ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onfailure", 'Connection failed!', event.info.level, event.info.code); } break; case "NetStream.Play.StreamNotFound": this.failed = true; writeDebug("NetConnection: Stream not found!"); ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onfailure", 'Stream not found!', event.info.level, event.info.code); break; // This is triggered when the sound loses the connection with the server. // In some cases one could just try to reconnect to the server and resume playback. // However for streams protected by expiring tokens, I don't think that will work. // // Flash says that this is not an error code, but a status code... // should this call the onFailure handler? case "NetConnection.Connect.Closed": this.failed = true; ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onfailure", 'Connection closed!', event.info.level, event.info.code); writeDebug("NetConnection: Connection closed!"); break; // Couldn't establish a connection with the server. Attempts to connect to the server // can also fail if the permissible number of socket connections on either the client // or the server computer is at its limit. This also happens when the internet // connection is lost. case "NetConnection.Connect.Failed": this.failed = true; writeDebug("NetConnection: Connection failed! Lost internet connection? Try again... Description: " + event.info.description); ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onfailure", 'Connection failed!', event.info.level, event.info.code); break; // A change has occurred to the network status. This could mean that the network // connection is back, or it could mean that it has been lost...just try to resume // playback. // KJV: Can't use this yet because by the time you get your connection back the // song has reached it's maximum retries, so it doesn't retry again. We need // a new _ondisconnect handler. //case "NetConnection.Connect.NetworkChange": // this.failed = true; // writeDebug("NetConnection: Network connection status changed"); // ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onfailure", 'Reconnecting...'); // break; // Consider everything else a warning... default: // this.failed = true; writeDebug("NetConnection: got unhandled code '" + event.info.code + "'. Description: " + event.info.description); ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onwarning", event.info.description, event.info.level, event.info.code); break; } } public function writeDebug (s: String, logLevel: Number = 0) : Boolean { return this.sm.writeDebug (s,logLevel); // defined in main SM object } public function onMetaData(infoObject: Object) : void { var prop:String; if (sm.debugEnabled) { var data:String = new String(); for (prop in infoObject) { data += prop+': '+infoObject[prop]+' \n'; } writeDebug('Metadata: '+data); } this.duration = infoObject.duration * 1000; if (!this.loaded) { // writeDebug('not loaded yet: '+this.ns.bytesLoaded+', '+this.ns.bytesTotal+', '+infoObject.duration*1000); // TODO: investigate loaded/total values // ExternalInterface.call(baseJSObject + "['" + this.sID + "']._whileloading", this.ns.bytesLoaded, this.ns.bytesTotal, infoObject.duration*1000); ExternalInterface.call(baseJSObject + "['" + this.sID + "']._whileloading", this.bytesLoaded, this.bytesTotal, (this.duration || infoObject.duration)) } var metaData:Array = []; var metaDataProps:Array = []; for (prop in infoObject) { metaData.push(prop); metaDataProps.push(infoObject[prop]); } // pass infoObject to _onmetadata, too ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onmetadata", metaData, metaDataProps); writeDebug('waiting for next call...'); // this handler may fire multiple times, eg., when a song changes while playing an RTMP stream. if (!this.serverUrl) { // disconnect for non-RTMP cases, since multiple firings may mess up duration. // this.cc.onMetaData = function(infoObject: Object) : void {} } } public function captionHandler(infoObject: Object) : void { if (sm.debugEnabled) { var data:String = new String(); for (var prop:* in infoObject) { data += prop+': '+infoObject[prop]+' \n'; } writeDebug('Caption: '+data); } // null this out for the duration of this object's existence. // it may be called multiple times. // this.cc.setCaption = function(infoObject: Object) : void {} // writeDebug('Caption\n'+infoObject['dynamicMetadata']); // writeDebug('firing _oncaptiondata for '+this.sID); ExternalInterface.call(this.sm.baseJSObject + "['" + this.sID + "']._oncaptiondata", infoObject['dynamicMetadata']); } public function getWaveformData() : void { // http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/media/SoundMixer.html#computeSpectrum() SoundMixer.computeSpectrum(this.waveformData, false, 0); // sample wave data at 44.1 KHz this.waveformDataArray = []; for (var i: int = 0, j: int = this.waveformData.length / 4; i < j; i++) { // get all 512 values (256 per channel) this.waveformDataArray.push(int(this.waveformData.readFloat() * 1000) / 1000); } } public function getEQData() : void { // http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/media/SoundMixer.html#computeSpectrum() SoundMixer.computeSpectrum(this.eqData, true, 0); // sample EQ data at 44.1 KHz this.eqDataArray = []; for (var i: int = 0, j: int = this.eqData.length / 4; i < j; i++) { // get all 512 values (256 per channel) this.eqDataArray.push(int(this.eqData.readFloat() * 1000) / 1000); } } public function start(nMsecOffset: int, nLoops: int, allowMultiShot:Boolean) : Boolean { this.useEvents = true; if (this.useNetstream) { writeDebug("SMSound::start nMsecOffset "+ nMsecOffset+ ' nLoops '+nLoops + ' current bufferTime '+this.ns.bufferTime+' current bufferLength '+this.ns.bufferLength+ ' this.lastValues.position '+this.lastValues.position); // mark for later Netstream.Play.Stop / sound completion this.finished = false; this.cc.onMetaData = this.onMetaData; // Don't seek if we don't have to because it destroys the buffer var set_position:Boolean = this.lastValues.position != null && this.lastValues.position != nMsecOffset; if (set_position) { // Minimize the buffer so playback starts ASAP this.ns.bufferTime = this.bufferTime; } if (this.paused) { writeDebug('start: resuming from paused state'); this.ns.resume(); // get the sound going again if (!this.didLoad) { this.didLoad = true; } this.paused = false; } else if (!this.didLoad) { writeDebug('start: !didLoad - playing '+this.sURL); this.ns.play(this.sURL); this.pauseOnBufferFull = false; // SAS: playing behaviour overrides buffering behaviour this.didLoad = true; this.paused = false; } else { // previously loaded, perhaps stopped/finished. play again? writeDebug('playing again (not paused, didLoad = true)'); this.pauseOnBufferFull = false; this.ns.play(this.sURL); } // KJV seek after calling play otherwise some streams get a NetStream.Seek.Failed // Should only apply to the !didLoad case, but do it for all for simplicity. // nMsecOffset is in milliseconds for streams but in seconds for progressive // download. if (set_position) { this.ns.seek(this.serverUrl ? nMsecOffset / 1000 : nMsecOffset); this.lastValues.position = nMsecOffset; // https://gist.github.com/1de8a3113cf33d0cff67 } // this.ns.addEventListener(Event.SOUND_COMPLETE, _onfinish); this.applyTransform(); } else { // writeDebug('start: seeking to '+nMsecOffset+', '+nLoops+(nLoops==1?' loop':' loops')); if (!this.soundChannel || allowMultiShot) { this.soundChannel = this.play(nMsecOffset, nLoops); this.addEventListener(Event.SOUND_COMPLETE, _onfinish); this.applyTransform(); } else { // writeDebug('start: was already playing, no-multishot case. Seeking to '+nMsecOffset+', '+nLoops+(nLoops==1?' loop':' loops')); // already playing and no multi-shot allowed, so re-start and set position if (this.soundChannel) { this.soundChannel.stop(); } this.soundChannel = this.play(nMsecOffset, nLoops); // start playing at new position this.addEventListener(Event.SOUND_COMPLETE, _onfinish); this.applyTransform(); } } // if soundChannel is null (and not a netStream), there is no sound card (or 32-channel ceiling has been hit.) // http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/media/Sound.html#play%28%29 return (!this.useNetstream && this.soundChannel === null ? false : true); } private function _onfinish() : void { this.removeEventListener(Event.SOUND_COMPLETE, _onfinish); } public function loadSound(sURL: String) : void { if (this.useNetstream) { this.useEvents = true; if (this.didLoad != true) { this.ns.play(this.sURL); // load streams by playing them if (!this.autoPlay) { this.pauseOnBufferFull = true; } this.paused = false; } // this.addEventListener(Event.SOUND_COMPLETE, _onfinish); this.applyTransform(); } else { try { this.didLoad = true; this.urlRequest = new URLRequest(sURL); this.soundLoaderContext = new SoundLoaderContext(1000, this.checkPolicyFile); // check for policy (crossdomain.xml) file on remote domains - http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/media/SoundLoaderContext.html this.load(this.urlRequest, this.soundLoaderContext); } catch(e: Error) { writeDebug('error during loadSound(): ' + e.toString()); } } } // Set the value of autoPlay public function setAutoPlay(autoPlay: Boolean) : void { if (!this.serverUrl) { this.autoPlay = autoPlay; } else { this.autoPlay = autoPlay; if (this.autoPlay) { this.pauseOnBufferFull = false; } else if (!this.autoPlay) { this.pauseOnBufferFull = true; } } } public function setVolume(nVolume: Number) : void { this.lastValues.volume = nVolume / 100; this.applyTransform(); } public function setPan(nPan: Number) : void { this.lastValues.pan = nPan / 100; this.applyTransform(); } public function applyTransform() : void { var st: SoundTransform = new SoundTransform(this.lastValues.volume, this.lastValues.pan); if (this.useNetstream) { if (this.ns) { this.ns.soundTransform = st; } else { // writeDebug('applyTransform(): Note: No active netStream'); } } else if (this.soundChannel) { this.soundChannel.soundTransform = st; // new SoundTransform(this.lastValues.volume, this.lastValues.pan); } } // Handle FMS bandwidth check callback. // @see http://www.adobe.com/devnet/flashmediaserver/articles/dynamic_stream_switching_04.html // @see http://www.johncblandii.com/index.php/2007/12/fms-a-quick-fix-for-missing-onbwdone-onfcsubscribe-etc.html public function onBWDone() : void { // writeDebug('onBWDone: called and ignored'); } // NetStream client callback. Invoked when the song is complete. public function onPlayStatus(info:Object):void { writeDebug('onPlayStatus called with '+info); switch(info.code) { case "NetStream.Play.Complete": writeDebug('Song has finished!'); break; } } public function doIOError(e: IOErrorEvent) : void { ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onload", 0); // call onload, assume it failed. // there was a connection drop, a loss of internet connection, or something else wrong. 404 error too. } public function doAsyncError(e: AsyncErrorEvent) : void { writeDebug('asyncError: ' + e.text); } public function doNetStatus(e: NetStatusEvent) : void { // Handle failures if (e.info.code == "NetStream.Failed" || e.info.code == "NetStream.Play.FileStructureInvalid" || e.info.code == "NetStream.Play.StreamNotFound") { this.lastNetStatus = e.info.code; writeDebug('netStatusEvent: ' + e.info.code); this.failed = true; ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onfailure", '', e.info.level, e.info.code); return; } writeDebug('netStatusEvent: ' + e.info.code); // KJV we like to see all events // When streaming, Stop is called when buffering stops, not when the stream is actually finished. // @see http://www.actionscript.org/forums/archive/index.php3/t-159194.html if (e.info.code == "NetStream.Play.Stop") { if (!this.finished && (!this.useNetstream || !this.serverUrl)) { // finished playing, and not RTMP writeDebug('calling onfinish for a sound'); // reset the sound? Move back to position 0? this.sm.checkProgress(); this.finished = true; // will be reset via JS callback ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onfinish"); } } else if (e.info.code == "NetStream.Play.Start" || e.info.code == "NetStream.Buffer.Empty" || e.info.code == "NetStream.Buffer.Full") { // RTMP case.. // We wait for the buffer to fill up before pausing the just-loaded song because only if the // buffer is full will the song continue to buffer until the user hits play. if (this.serverUrl && e.info.code == "NetStream.Buffer.Full" && this.pauseOnBufferFull) { this.ns.pause(); this.paused = true; this.pauseOnBufferFull = false; // Call pause in JS. This will call back to us to pause again, but // that should be harmless. writeDebug('Pausing on buffer full'); ExternalInterface.call(baseJSObject + "['" + this.sID + "'].pause", false); } var isNetstreamBuffering: Boolean = (e.info.code == "NetStream.Buffer.Empty" || e.info.code == "NetStream.Play.Start"); // assume buffering when we start playing, eg. initial load. if (isNetstreamBuffering != this.lastValues.isBuffering) { this.lastValues.isBuffering = isNetstreamBuffering; ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onbufferchange", this.lastValues.isBuffering ? 1 : 0); } // We can detect the end of the stream when Play.Stop is called followed by Buffer.Empty. // However, if you pause and let the whole song buffer, Buffer.Flush is called followed by // Buffer.Empty, so handle that case too. // // Ignore this event if we are more than 3 seconds from the end of the song. if (e.info.code == "NetStream.Buffer.Empty" && (this.lastNetStatus == 'NetStream.Play.Stop' || this.lastNetStatus == 'NetStream.Buffer.Flush')) { if (this.duration && (this.ns.time * 1000) < (this.duration - 3000)) { writeDebug('Ignoring Buffer.Empty because this is too early to be the end of the stream! (sID: '+this.sID+', time: '+(this.ns.time*1000)+', duration: '+this.duration+')'); } else if (!this.finished) { this.finished = true; writeDebug('calling onfinish for sound '+this.sID); this.sm.checkProgress(); ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onfinish"); } } else if (e.info.code == "NetStream.Buffer.Empty") { // The buffer is empty. Start from the smallest buffer again. this.ns.bufferTime = this.bufferTime; } } // Remember the last NetStatus event this.lastNetStatus = e.info.code; } // KJV The sound adds some of its own netstatus handlers so we don't need to do it here. public function addNetstreamEvents() : void { ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR, doAsyncError); ns.addEventListener(NetStatusEvent.NET_STATUS, doNetStatus); ns.addEventListener(IOErrorEvent.IO_ERROR, doIOError); } public function removeNetstreamEvents() : void { ns.removeEventListener(AsyncErrorEvent.ASYNC_ERROR, doAsyncError); ns.removeEventListener(NetStatusEvent.NET_STATUS, doNetStatus); ns.addEventListener(NetStatusEvent.NET_STATUS, dummyNetStatusHandler); ns.removeEventListener(IOErrorEvent.IO_ERROR, doIOError); // KJV Stop listening for NetConnection events on the sound nc.removeEventListener(NetStatusEvent.NET_STATUS, netStatusHandler); } // Prevents possible 'Unhandled NetStatusEvent' condition if a sound is being // destroyed and interacted with at the same time. public function dummyNetStatusHandler(e: NetStatusEvent) : void { // Don't do anything } } }SoundManager2-2.97a.20150601/src/make-flash8.bat000066400000000000000000000002321253275212400205350ustar00rootroot00000000000000@REM this builds the soundmanager 2 SWF from source mtasc -swf ../swf/soundmanager2_debug.swf -main -header 16:16:30 SoundManager2.as -version 8 @pause SoundManager2-2.97a.20150601/src/make-flash8.sh000077500000000000000000000001751253275212400204120ustar00rootroot00000000000000#!/bin/bash /Applications/mtasc/mtasc -swf ../swf/soundmanager2_debug.swf -main -header 16:16:30 SoundManager2.as -version 8 SoundManager2-2.97a.20150601/src/make-flash9.bat000066400000000000000000000004531253275212400205430ustar00rootroot00000000000000@REM this builds the soundmanager 2 SWF from source @REM using mxmlc from the Adobe open-source Flex SDK c:\progra~1\flexsdk\bin\mxmlc -debug=true -use-network=false -static-link-runtime-shared-libraries=true -optimize=true -o ../swf/soundmanager2_flash9_debug.swf -file-specs SoundManager2_AS3.as SoundManager2-2.97a.20150601/src/make-flash9.sh000077500000000000000000000002741253275212400204130ustar00rootroot00000000000000#!/bin/bash /Applications/flexsdk/bin/mxmlc -debug=true -static-link-runtime-shared-libraries=true -optimize=true -o ../swf/soundmanager2_flash9_debug.swf -file-specs SoundManager2_AS3.as SoundManager2-2.97a.20150601/swf/000077500000000000000000000000001253275212400157605ustar00rootroot00000000000000SoundManager2-2.97a.20150601/swf/soundmanager2.swf000066400000000000000000000055371253275212400212600ustar00rootroot00000000000000CWSbxڴYs-ۑiaJ-ʑؑEٲdɊ%RNXA@:3ͥM39t^;NLgz{u{ daw{ow29\ y,Vem^ߵ6M'/{n{#|R&DZ[ ̤I C۾ɶD?VtbSJowU3DrC-%4Fw ;@֊pG#fS0b?[ @tKw)x+: w͖Ayg'=C|vx^.]/xh莏PB_\h.F=J 4{F J 񢇑QJRD.Y"LLjmߵH`eF7Յ0LJϟ&¢+,,lHT- ;E0坊(髁܊\aU ީ ٹ.ZH[dpiz-cz' 9Puh9*kĦۏ jGq }Pi;6D FN@pG GW8v"g~;X $B?Ctf>rfAgV-gʦ~Af k=U⬟NhV. Ȝd#5˱)G%-Pa[$. c6 e6q^ގ E(%i|edIz*_Ua!IUANKtC ?(2ZfSr-DeuIeb*N鉚h1sUlgU-sߒ %[dV )SҪʍmiJJ2RLU.Z)Pq3r*ATW1`7UcQI$Ru ABn-]t]Q͵W]//ɾ3˶YZt$^e0I3d~Ǣ Ig?0Zc\e/$ ҽx r2}x-ZXWe'[/,&-VM х?Xʾ{̉bJ'ϚRcK &)ut-΄a,yFRˣ)M9솯E>A OGͺ֪T;ؒw*;5?NUR#> u걫 PxAfR?b ?aDJH!Z&^/W8<krxGwL4jܩyDePyZIW!y88Em$$򉠪U9)*:%A*'#@% `-#t6<@r9٠|q/n%`鴷*O{_nf/?MLh'W;4ZoJ+*'j,b{"k<FM[5۬<%X#G_r[Q&g27`xU<:{^+}@ѨUeڎyHizKx4u9wbKuh =kk v(T iU5sRx}燳ՖbtH 4ׯ('c k18&r) a%!?wI0,KٔudХ{\3YY34]]_FfO7MN%zJo3U~G9"yo## I<|bL!ڱP[3r'?YLdK)6"_gCгR{oGdԋtze OeM<:z _#k&f _5⫎3#>s _+k">k kߵ1|"GcD|O+g182r|qw_@nUT"ސWͱ,7)*mS3K~0h˿@j(EȨwQR}^N,(D?0~ʧ> p_urSoundManager2-2.97a.20150601/swf/soundmanager2_debug.swf000066400000000000000000000063251253275212400224220ustar00rootroot00000000000000CWSxڴYwgfq^M4*&q4I_qjǍ-A4IF3bfdii Zuas6pذ`9l8,8-K½lb}'?88#Xcz}h7Z/ܞcNaY/޲݆aæ|u`bW /t /¹Զ@t͞g`ضu\ߟ0y h„7jxm  nqO4(/C`u,,z^< =oWLˬpGy4zv`ht#xn C-3~ӰŊk pl2UA"^gu{K͞ZС[O9v'gߗ2Pa4mmOľ;59U aZFv&^z6{ Q™Humc[xmaw'{ ޑ8pDeDN'Y}-s\g**-P!dc4?*)C;􎱭#ԓ%tʈ)-rHKcĬ>6gnNFWhZR<9@\[9Wn+r:^Ka<uv}slUmyXz0fcK׷/`k.+iX}+`ݰ{XDpR}ѷIl`&\=,ݸy whꉖN*μsusI,SXW_AS'ˎ9Mx놠Sn8ҹձ$(K,37?0<,&jYgm7[2ekLBzOZ]%POTݵ1k`!s. h%˗ԙRZnp1zI0[ /xkt|"nbt)`i ËA}O6Ls>CXBk(V. :eSD_8zX~:(ƱR (0]ac~ttˇ,hPf{$ o"elL׾Be0X2}|4 = gwBCևŦ !@0A s KߎV9^K?ϟGDb06rܑf(YSŔ#{T3yM #V!/RORr* G !-\_. Ɖ""IC+RuT"ܼdᕾj&5{o67ծfj0h3lڣWv\+ڵj!9}IQR.+jc6'cFPLf:9&_j3 B+Oڼ]a''8NܜvcI.^_b'̾(}/(9F"-0[Lx3ɑ*@심R go3Hu$[86G[QjHpru OI4u&3*aUV:SrHq_Ld݂Q&{3D+kc2ɥ'Jz@GZYf4W? Jik6YĈ{dЛ3ԤV%>i520B,DceVޕb>L_ь[8j4iՠDDV"We+ǛYDHb)w_25 wš;G)tУ$g-y'|HdrHjぺHh@9镍 lfAUIuW'Z ωvJ6LCgJJjqfs""+dNc, Y$"|tY`d&YYAWJ"`m"ˠ4I(9|{^)t++`e-RevsC=nbEhwC]\r絮fwGdWK%F3Aj+CQrp'ˊ)"kDFE¡QHT_D+~><֑ՑTe I~v-RqRKw2?T&%@zհWڦ ȉG.Pht[{[j. WUy[D] хW\gYYJ%bɕY #4g"[L0lT}#qJi{?,(LPnw2HQF!l%&'ˑfT,Υ&nP?e*1g f|9r5jˡ|q@:q6%T/̥MJwވU/MЅ31=$\+ӳu.B%IZʼ##@퐎S=ڏ? O}\tؒĿUF͍qcq1_ɮduw=_3y'!ܑ:8^oDf(&H˲3lZŘ)"\oF^IfKJB?ѡ ?9tn{ÐBеBC}}4ts!JHW@tnky1Y۱8G.mF??4iYHZ-,7Z.D<ާ^hb);GII/DHgwIpR}B*U2+_j?+sKeI(|IBcdek8/_>4