Browse files

Initial commit.

Still not ready for a prime time realese, however the code is
starting to get big, and I'd like to save it on GitHub for
backup purposes at least.
  • Loading branch information...
0 parents commit 9774b819787a4f026c38a6a1090b422a1fab3074 @TooTallNate committed Mar 15, 2010
Showing with 894 additions and 0 deletions.
  1. +2 −0 .gitignore
  2. +87 −0 HTMLMediaElement.htc
  3. BIN HtmlAudio.swf
  4. +494 −0 HtmlMedia.js
  5. +104 −0 README.md
  6. +4 −0 build.hxml
  7. +13 −0 index.html
  8. BIN sounds/ping.mp3
  9. BIN sounds/theme.mp3
  10. +95 −0 src/HTMLAudioElement.hx
  11. +91 −0 src/HtmlAudio.hx
  12. +4 −0 swfobject.js
2 .gitignore
@@ -0,0 +1,2 @@
+._*
+
87 HTMLMediaElement.htc
@@ -0,0 +1,87 @@
+/* The MIT License
+ *
+ * Copyright (c) 2010 Nathan Rajlich
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+<PUBLIC:COMPONENT NAME="HTMLMediaElement">
+
+ <PUBLIC:PROPERTY NAME="readyState" GET="readyStateGet" />
+
+ <PUBLIC:EVENT NAME="onloadstart" ID="onloadstart" />
+ <PUBLIC:EVENT NAME="onprogress" ID="onprogress" />
+ <PUBLIC:EVENT NAME="onsuspend" ID="onsuspend" />
+ <PUBLIC:EVENT NAME="onabort" ID="onabort" />
+ <PUBLIC:EVENT NAME="onerror" ID="onerror" />
+ <PUBLIC:EVENT NAME="onemptied" ID="onemptied" />
+ <PUBLIC:EVENT NAME="onstalled" ID="onstalled" />
+ <PUBLIC:EVENT NAME="onplay" ID="onplay" />
+ <PUBLIC:EVENT NAME="onpause" ID="onpause" />
+ <PUBLIC:EVENT NAME="onloadedmetadata" ID="onloadedmetadata" />
+ <PUBLIC:EVENT NAME="onloadeddata" ID="onloadeddata" />
+ <PUBLIC:EVENT NAME="onwaiting" ID="onwaiting" />
+ <PUBLIC:EVENT NAME="onplaying" ID="onplaying" />
+ <PUBLIC:EVENT NAME="oncanplay" ID="oncanplay" />
+ <PUBLIC:EVENT NAME="oncanplaythrough" ID="oncanplaythrough" />
+ <PUBLIC:EVENT NAME="onseeking" ID="onseeking" />
+ <PUBLIC:EVENT NAME="onseeked" ID="onseeked" />
+ <PUBLIC:EVENT NAME="ontimeupdate" ID="ontimeupdate" />
+ <PUBLIC:EVENT NAME="onended" ID="onended" />
+ <PUBLIC:EVENT NAME="onratechange" ID="onratechange" />
+ <PUBLIC:EVENT NAME="ondurationchange" ID="ondurationchange" />
+ <PUBLIC:EVENT NAME="onvolumechange" ID="onvolumechange" />
+
+ <PUBLIC:METHOD NAME="__fireMediaEvent" />
+ <SCRIPT LANGUAGE="JScript" >
+ var ev, events = {
+ "loadstart":onloadstart,
+ "progress":onprogress,
+ "suspend":onsuspend,
+ "abort":onabort,
+ "error":onerror,
+ "emptied":onemptied,
+ "stalled":onstalled,
+ "play":onplay,
+ "pause":onpause,
+ "loadedmetadata":onloadedmetadata,
+ "loadeddata":onloadeddata,
+ "waiting":onwaiting,
+ "playing":onplaying,
+ "canplay":oncanplay,
+ "canplaythrough":oncanplaythrough,
+ "seeking":onseeking,
+ "seeked":onseeked,
+ "timeupdate":ontimeupdate,
+ "ended":onended,
+ "ratechange":onratechange,
+ "durationchange":ondurationchange,
+ "volumechange":onvolumechange
+ };
+ function readyStateGet() {
+ return this.__readyState;
+ }
+ function __fireMediaEvent(eventName)
+ {
+ if (eventName in events) {
+ ev = createEventObject();
+ events[eventName].fire(ev);
+ }
+ }
+ </SCRIPT>
+</PUBLIC:COMPONENT>
BIN HtmlAudio.swf
Binary file not shown.
494 HtmlMedia.js
@@ -0,0 +1,494 @@
+/* The MIT License
+ *
+ * Copyright (c) 2010 Nathan Rajlich
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+(function() {
+ // Feel free to change the paths to appropriate for your environment:
+ var AUDIO_SWF_PATH = "HtmlAudio.swf",
+ VIDEO_SWF_PATH = "HtmlVideo.swf",
+ IE_HTC_PATH = "HTMLMediaElement.htc",
+
+ // Dont modify below here though!
+ REGEXP_FILENAME_MP3 = /\.mp3(\?.*)?$/i,
+ REGEXP_MIMETYPE_MP3 = /^audio\/(?:x-)?(?:mp(?:eg|3))\s*;?/i,
+ documentHead = document.getElementsByTagName("head")[0],
+ isIE = !!document.attachEvent && !(Object.prototype.toString.call(window.opera) == '[object Opera]');
+
+ function nativeCheckComplete(canPlayNativeMp3, error) {
+ if (!canPlayNativeMp3) {
+
+ if (isIE) {
+ // IE behaves strangely for html tags it doesn't recognize
+ // like audio, video, and source. The workaround is to create
+ // said node via JavaScript before the HTML parser finds them
+ // in your HTML code.
+ document.createElement("audio");
+ document.createElement("video");
+ document.createElement("source");
+ }
+
+ // http://dev.w3.org/html5/spec/video.html#timeranges
+ function TimeRanges() {
+ this.length = 0;
+ this.starts = [];
+ this.ends = [];
+ }
+ TimeRanges.prototype = {
+ start: function(index) {
+ return this.starts[index];
+ },
+ end: function(index) {
+ return this.ends[index];
+ },
+ add: function(start, end) {
+ this.starts.push(start);
+ this.ends.push(end);
+ this.length++;
+ }
+ };
+ if (!window.TimeRanges) window.TimeRanges = TimeRanges;
+
+ // http://dev.w3.org/html5/spec/video.html#mediaerror
+ function MediaError(code) {
+ this.code = code;
+ }
+ MediaError.prototype = {
+ code: -1,
+ MEDIA_ERR_ABORTED: 1,
+ MEDIA_ERR_NETWORK: 2,
+ MEDIA_ERR_DECODE: 3,
+ MEDIA_ERR_SRC_NOT_SUPPORTED: 4
+ };
+ if (!window.MediaError) window.MediaError = MediaError;
+
+ // Browsers other than IE will use the W3C events model to fire media events
+ function fireMediaEvent(eventName) {
+ var ev = document.createEvent("Events");
+ ev.initEvent(eventName, true, true);
+ this.dispatchEvent(ev);
+ }
+
+
+ // There's two cases for HTMLMediaElement: the user agent natively
+ // implements it or it doesn't. If it does, then we need to augment the
+ // functions in the prototype to use our wrapper if required.
+ if (!window.HTMLMediaElement) {
+
+ if (isIE) {
+ // For IE to fire custom events, they must be defined in an HTC file,
+ // and set as a CSS behavior on the element. Adding this essentially
+ // adds the internal '__fireMediaEvent' to <audio> and <video> nodes.
+ var style = document.createElement("style");
+ style.type = "text/css";
+ style.styleSheet.cssText = "audio, video { behavior:url("+IE_HTC_PATH+"); }";
+ documentHead.appendChild(style);
+ }
+
+
+
+ // Accepts a HTMLElement with a settable 'src' property. Returns
+ // the element if it resolves relative paths to absolute of
+ // the current document, null otherwise.
+ function resolvesSrc(element) {
+ element.src="";
+ return element.src.indexOf(window.location.protocol) === 0 ? element : null;
+ }
+ // We send the absolute path to the fallback player, and need some
+ // native browser way of resolving relative URLs. Check a <script> first,
+ // since it won't make an HTTP request until it's placed in the DOM,
+ // some old browsers won't do that, so fall back to an <img>, which WILL
+ // fire an HTTP request when it's 'src' is set (bad).
+ var RELATIVE_URL_RESOLVER = resolvesSrc(document.createElement("script")) || new Image();
+
+
+
+ // IE doesn't support __defineSetter__, but we will use 'onpropertychange'
+ // instead. The event fires every time ANY prop on the element gets changed.
+ function iePropertyChange() {
+ if (event.propertyName.indexOf('.') >= 0) return;
+ //console.log(event.propertyName + " changed to: " + this[event.propertyName]);
+ var prop = event.propertyName, propSetter = this["__" + prop + "Set"];
+ if (propSetter) propSetter.call(this, this[prop]);
+ }
+ function cloneNode(deep) {
+ var clone = this.__cloneNode(deep), nodeName = this.nodeName.toLowerCase();
+ if (nodeName === "audio") return new HTMLAudioElement(clone);
+ if (nodeName === "video") return new HTMLVideoElement(clone);
+ //return clone; Should never happen
+ }
+ function HTMLMediaElement(element) {
+ if (element) {
+ for (var i in this) {
+ try {
+ element[i] = this[i];
+ } catch(ex) {
+ //console.log("Error setting '" + i + "' on <audio> node");
+ }
+ }
+ // The cloneNode function needs to call the appropriate
+ // HTMLMediaElement constructor as well.
+ if (!element.__cloneNode) element.__cloneNode = element.cloneNode;
+ element.cloneNode = cloneNode;
+
+ if (!isIE) {
+ // Browser's other than IE need to get the internally
+ // used '__fireMediaEvent' here. IE gets it from the HTC file
+ element.__fireMediaEvent = fireMediaEvent;
+ } else {
+ element.style.behavior = "url("+IE_HTC_PATH+")";
+ }
+ this.__initGettersSetters(element);
+ }
+ }
+ HTMLMediaElement.prototype = {
+ // error state
+ //readonly attribute MediaError error;
+ error: null,
+
+ // network state
+ // attribute DOMString src;
+ src: "",
+ __src: "",
+ //readonly attribute DOMString currentSrc;
+ currentSrc: "",
+ NETWORK_EMPTY: 0,
+ NETWORK_IDLE: 1,
+ NETWORK_LOADING: 2,
+ NETWORK_LOADED: 3,
+ NETWORK_NO_SOURCE: 4,
+ //readonly attribute unsigned short networkState;
+ networkState: 0,
+ __networkState: 0,
+ // attribute DOMString preload;
+ preload: null,
+ //readonly attribute TimeRanges buffered;
+ buffered: null,
+ //void load();
+ load: function() {
+ // Overridden by HTMLAudioElement & HTMLVideoElement
+ },
+ //DOMString canPlayType(in DOMString type);
+ canPlayType: function() {
+ return "";
+ },
+
+ // ready state
+ HAVE_NOTHING: 0,
+ HAVE_METADATA: 1,
+ HAVE_CURRENT_DATA: 2,
+ HAVE_FUTURE_DATA: 3,
+ HAVE_ENOUGH_DATA: 4,
+ //readonly attribute unsigned short readyState;
+ readyState: 0,
+ __readyState: 0,
+ //readonly attribute boolean seeking;
+ seeking: false,
+
+ // playback state
+ // attribute float currentTime;
+ currentTime: 0.0,
+ __currentTime: 0.0,
+ //readonly attribute float startTime;
+ startTime: 0.0,
+ //readonly attribute float duration;
+ duration: NaN,
+ //readonly attribute boolean paused;
+ paused: true,
+ // attribute float defaultPlaybackRate;
+ defaultPlaybackRate: 1.0,
+ // attribute float playbackRate;
+ playbackRate: 1.0,
+ //readonly attribute TimeRanges played;
+ played: null,
+ //readonly attribute TimeRanges seekable;
+ seekable: null,
+ //readonly attribute boolean ended;
+ ended: false,
+ // attribute boolean autoplay;
+ autoplay: false,
+ // attribute boolean loop;
+ loop: false,
+ //void play();
+ play: function() {
+
+ },
+ //void pause();
+ pause: function() {
+
+ },
+
+ // controls
+ // attribute boolean controls;
+ controls: false,
+ // attribute float volume;
+ volume: 1.0,
+ __volume: 1.0,
+ // attribute boolean muted;
+ muted: false,
+ __muted: false,
+
+ __initGettersSetters: function(element) {
+ if (isIE) {
+ // 'onpropertychange' won't fire unless the element
+ // is somewhere in the DOM, append to <head> first.
+ documentHead.appendChild(element);
+ //element.attachEvent("onpropertychange", iePropertyChange); //Wrong scope, less obtrusive
+ element.onpropertychange = iePropertyChange;
+ } else {
+ element.__defineGetter__("currentTime", this.__currentTimeGet);
+ element.__defineGetter__("muted", this.__mutedGet);
+ element.__defineGetter__("src", this.__srcGet);
+ element.__defineGetter__("volume", this.__volumeGet);
+
+ element.__defineSetter__("currentTime", this.__currentTimeSet);
+ element.__defineSetter__("muted", this.__mutedSet);
+ element.__defineSetter__("src", this.__srcSet);
+ element.__defineSetter__("volume", this.__volumeSet);
+ }
+ },
+
+ __currentTimeGet: function() { return this.__currentTime; },
+ __mutedGet: function() { return this.__muted; },
+ __srcGet: function() { return this.__src; },
+ __volumeGet: function() { return this.__volume; },
+
+ __currentTimeSet: function(curTime) {
+ this.__currentTime = curTime;
+ },
+ __mutedSet: function(muted) {
+ if (typeof(muted) !== 'boolean') {
+ this.muted = this.__muted;
+ } else if (this.__muted !== muted) {
+ // TODO: Send mute message to fallback
+ this.__muted = muted;
+ this.__fireMediaEvent("volumechange");
+ }
+ },
+ __srcSet: function(src) {
+ //console.log("src: " + src);
+ this.__src = src;
+ if (src !== "" && src.indexOf(':') < 0) {
+ RELATIVE_URL_RESOLVER.src = src;
+ this.src = RELATIVE_URL_RESOLVER.src;
+ }
+ },
+ __volumeSet: function(vol) {
+ if (vol > 1 || vol < 0) {
+ this.volume = this.__volume;
+ throw new Error("INDEX_SIZE_ERROR: DOM Exception 1");
+ } else if (this.__volume !== vol) {
+ //console.log("volume: " + vol);
+ if (this.__fallbackId != undefined) {
+ HTMLAudioElement.__swf.__setVolume(this.__fallbackId, vol);
+ }
+ this.__volume = vol;
+ this.__fireMediaEvent("volumechange");
+ }
+ },
+
+ toString: function() {
+ return "[object HTMLMediaElement]";
+ }
+ };
+ window.HTMLMediaElement = HTMLMediaElement;
+ }
+
+
+
+
+
+
+
+
+
+ // If the user agent has HTMLAudioElement defined in the window,
+ // then we must augment some prototypes, and overwrite a few
+ // functions to transparently use the Flash fallback when needed.
+ if (window.HTMLAudioElement) {
+ var nativeLoad = HTMLAudioElement.prototype.load;
+ HTMLAudioElement.prototype.load = function() {
+ console.log("calling overriden HTMLAudioElement#load");
+ nativeLoad.apply(this, arguments);
+ }
+ } else {
+ // User agent hasn't implemented HTMLAudioElement, we must
+ // implement our own with regular JS, following the spec.
+ function HTMLAudioElement() {
+ HTMLMediaElement.apply(this, arguments);
+ return arguments[0];
+ }
+ HTMLAudioElement.prototype = new HTMLMediaElement;
+ HTMLAudioElement.prototype.toString = function() {
+ return "[object HTMLAudioElement]";
+ };
+ HTMLAudioElement.prototype.load = function() {
+ this.currentSrc = this.src;
+ if (!this.__fallbackId) {
+ this.__fallbackId = HTMLAudioElement.__swf.__createSound(this.src, this.volume);
+ if (!HTMLAudioElement.__swfSounds) HTMLAudioElement.__swfSounds = [];
+ HTMLAudioElement.__swfSounds[this.fallbackId] = this;
+ } else {
+ HTMLAudioElement.__swf.__load(this.src);
+ }
+ };
+ HTMLAudioElement.prototype.play = function() {
+ if (this.networkState === this.NETWORK_EMPTY) {
+ // Invoke 'resource selection algorithm'
+ }
+ if (this.ended && this.playbackRate >= 0) {
+ this.currentTime = this.startTime;
+ }
+ if (this.paused === true) {
+ this.paused = false;
+ this.__fireMediaEvent("play");
+ // TODO Finish
+ }
+ HTMLAudioElement.__swf.__play(this.__fallbackId);
+ }
+ HTMLAudioElement.prototype.pause = function() {
+ HTMLAudioElement.__swf.__pause(this.__fallbackId);
+ }
+ window.HTMLAudioElement = HTMLAudioElement;
+
+ var nativeCreateElement = document.createElement;
+ document.createElement = function() {
+ var ele = nativeCreateElement.apply(document, arguments);
+ if (ele.nodeName.toLowerCase() === "audio") return new HTMLAudioElement(ele);
+ return ele;
+ };
+ }
+ // We want to augment both the native and our custom 'canPlayType'
+ // functions to check the argument for valid MP3 mime-types.
+ var origCanPlayType = HTMLAudioElement.prototype.canPlayType;
+ function canPlayType() {
+ if (REGEXP_MIMETYPE_MP3.test(arguments[0])) return "probably";
+ return origCanPlayType.apply(this, arguments);
+ }
+ HTMLAudioElement.prototype.canPlayType = canPlayType;
+
+
+
+
+
+ // The HTMLAudioElement has a convience constructor Audio. It must
+ // be implemented from scratch, or wrapped to integrate with the
+ // fallback mechanism when needed.
+ if (window.Audio && (new Audio("")) instanceof window.HTMLAudioElement) {
+ var nativeAudio = window.Audio;
+ function Audio() {
+ console.log("Calling 'Audio' constructor");
+ var a, src = arguments[0];
+ if (src) a = new nativeAudio(a);
+ else a = new nativeAudio();
+ //HTMLAudioElement.__integrateFallback(a);
+ return a;
+ }
+ window.Audio = Audio;
+ } else {
+ function Audio() {
+ if (!(this instanceof arguments.callee)) {
+ var message = "DOM object constructor cannot be called as a function.";
+ if (window.TypeError) throw new TypeError(message);
+ else throw new Error(message);
+ }
+ var a = document.createElement("audio");
+ a.preload = "auto";
+ if (arguments[0]) {
+ a.src = arguments[0];
+ }
+ return a;
+ }
+ Audio.prototype = HTMLAudioElement.prototype;
+ window.Audio = Audio;
+ }
+
+
+
+
+
+ // Embed the fallback SWF into the page
+ function embedSwf() {
+ console.log("embedding SWF at: " + AUDIO_SWF_PATH);
+ var container = document.createElement("div"), id = "HtmlAudio";
+ container.id = id;
+ document.body.appendChild(container);
+ swfobject.embedSWF(AUDIO_SWF_PATH, id, 400, 400, "10");
+ }
+
+ function swfLoaded() {
+ console.log("swf embedded, called from EI");
+ }
+ HTMLAudioElement.__swfLoaded = swfLoaded;
+
+ function fixHtmlTags() {
+ var audioNodes = document.getElementsByTagName("audio"), i;
+ console.log(audioNodes.length + " <audio> nodes in the document.");
+
+ }
+
+ swfobject.addDomLoadEvent(function() {
+ console.log("DOMContentLoaded");
+ embedSwf();
+ fixHtmlTags();
+ });
+ } else {
+ /* If we get here, then the user agent natively supports both
+ * the HTML5 Audio API, as well as MP3 decoding support, so this
+ * script can bail and do nothing!
+ */
+ }
+ }
+
+ /* From http://gist.github.com/253174
+ *
+ * Detect if the browser can play MP3 audio using native HTML5 Audio.
+ * Invokes the callack function with first parameter is the boolean success
+ * value; if that value is false, a second error parameter is passed. This error
+ * is either HTMLMediaError or some other DOMException or Error object.
+ * Note the callback is likely to be invoked asynchronously!
+ * @param {function(boolean, Object|undefined)} callback
+ **/
+ (function(callback){
+ try {
+ var audio = new Audio();
+ // Shortcut which doesn't work in Chrome (always returns ""); pass through
+ // if "maybe" to do asynchronous check by loading MP3 data: URI
+ if(audio.canPlayType('audio/mpeg') == "probably")
+ callback(true);
+
+ // If this event fires, then MP3s can be played
+ audio.addEventListener('canplaythrough', function(e) {
+ callback(true);
+ }, false);
+
+ // If this is fired, then client can't play MP3s
+ audio.addEventListener('error', function(e){
+ callback(false, this.error)
+ }, false);
+
+ // Smallest base64-encoded MP3 I could come up with (<0.000001 seconds long)
+ audio.src = "data:audio/mpeg;base64,/+MYxAAAAANIAAAAAExBTUUzLjk4LjIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
+ audio.load();
+ } catch(e) {
+ callback(false, e);
+ }
+ })(nativeCheckComplete);
+})();
104 README.md
@@ -0,0 +1,104 @@
+HtmlMediaFallback
+=================
+
+Presenting the first and only "proper"
+[HTML5 <audio> and <video>](http://www.w3.org/TR/html5/video.html) fallback
+implementation: `HtmlMediaFallback`
+
+There are a few open-source HTML5 Video and Audio fallbacks that depend on
+Flash out there, but in-short, all were too heavy-weight AND not complete
+enough for my needs.
+
+`HtmlMediaFallback` was created with the term "progressive enhancement" in
+mind. The sad truth is that HTML5 isn't even finalized yet. None of the
+current browser's implementations have a uniform codec support, and Internet
+Explorer doesn't support it at all! This script aims to provide an
+implementation of the HTML5 media API, with a uniform, cross-browser codec
+support via Flash, upgrading the browser's current implementation if one exists.
+
+The `HtmlMediaFallback` script should:
+
+ * If no HTML5 media support is detected, implement the interfaces related to
+ the <audio> and <video> element (HTMLMediaElement, HTMLAudioElement, etc.)
+ via standard JavaScript, and provide MP3 playback support via the Flash
+ fallback.
+
+ * If some level of HTML5 media is supported, but MP3 playback is not
+ (Firefox 3.5+, Opera 10.5), then `HtmlAudio` should augment the native
+ implementation to support MP3 playback via the Flash fallback transparently.
+
+ * If HTML5 media is supported AND MP3 playback is natively supported
+ (Google Chrome), then this library should detect so and do nothing.
+
+Ultimately, this gives the web developer access to the HTML5 Audio and Video
+API on all browsers with Flash installed (~98%), with **AT LEAST** the codecs
+supported by the Flash fallback guaranteed to work, plus any other codecs that
+the web browser's implementation decides to implement.
+
+Supported Browsers
+------------------
+
+I'll put this section first simply because I know that's what you're dying
+to drool about:
+
+ * Internet Explorer 6+
+ * Mozilla Firefox 2+
+ * Opera 9+ (though abandoning their weird old Audio object)
+ * Testing for more needed!!
+
+Supported Codecs
+----------------
+
+This library provides guaranteed support for MP3 files via the Flash fallback
+for the `<audio>` tag.
+
+If the current browser has any others codecs supported via its HTML5 media
+implementation, then those are obviously still supported as well.
+
+How to Use
+----------
+
+`HtmlMediaFallback` was designed to be a drop-in enhancement, with little
+or no configuration required by the developer. Here's a minimal HTML file,
+with guaranteed HTML5 media support via `HtmlMediaFallback`:
+
+ <!DOCTYPE html>
+ <html>
+ <head>
+ <script type="text/javascript" src="swfobject.js"></script>
+ <script type="text/javascript" src="HtmlMedia.js"></script>
+ </head>
+ <body>
+ <audio src="theme.mp3" autoplay></audio>
+ </body>
+ </html>
+
+You essentially need to include SWFObject 2+ and the `HtmlMedia.js` file.
+That's it! Optionally you can set the path to the SWF files. Once the
+`HtmlMedia.js` file finishes loading you have access to the entire HTML5
+audio and video API on your HTML page **AND** via JavaScript.
+
+Try things like:
+
+ document.createElement("audio").canPlayType("audio/mpeg");
+ //--> "probably"
+
+or
+
+ var a = new Audio("theme.mp3");
+ a.play();
+ // load and listen to some music :)
+ a.volume = a.volume / 2;
+ // cut the volume in half
+ setTimeout("a.pause()", 1000);
+ // pause the sound in 1 second
+
+You now have full access to the [HTML5 <audio> and <video>](http://www.w3.org/TR/html5/video.html)
+API on your webpage, with a guaranteed codec set, and an implementation for
+old/non-supporting browsers.
+
+License
+-------
+
+I've decided to license this enhancement library under the MIT license. See
+the `LICENSE` file for legal text.
4 build.hxml
@@ -0,0 +1,4 @@
+-cp src
+-main HtmlAudio
+-swf-version 10
+-swf9 HtmlAudio.swf
13 index.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML5 &lt;audio&gt; and &lt;video&gt; Flash Fallback</title>
+
+ <script type="text/javascript" src="swfobject.js"></script>
+ <script type="text/javascript" src="HtmlMedia.js"></script>
+ <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/prototype/1.6.1.0/prototype.js"></script>
+</head>
+<body>
+ <audio src="sounds/ping.mp3" autoplay loop></audio>
+</body>
+</html>
BIN sounds/ping.mp3
Binary file not shown.
BIN sounds/theme.mp3
Binary file not shown.
95 src/HTMLAudioElement.hx
@@ -0,0 +1,95 @@
+/* The MIT License
+ *
+ * Copyright (c) 2010 Nathan Rajlich
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+import flash.external.ExternalInterface;
+import flash.media.Sound;
+import flash.media.SoundChannel;
+import flash.media.SoundTransform;
+import flash.net.URLRequest;
+
+class HTMLAudioElement {
+ private var sound : Sound;
+ private var channel : SoundChannel;
+ private var volume : Float;
+ private var lastPosition : Float;
+
+ public function new(src:String, volume:Float) {
+ this.volume = volume;
+ this.lastPosition = 0;
+ this.sound = new Sound();
+ this.sound.addEventListener("complete", soundComplete);
+ this.sound.addEventListener("id3", soundId3);
+ this.sound.addEventListener("ioError", soundIoError);
+ this.sound.addEventListener("open", soundOpen);
+ this.sound.addEventListener("progress", soundProgress);
+ this.load(src);
+ }
+
+ public function setVolume(vol: Float) {
+ this.volume = vol;
+ if (this.channel != null) {
+ this.channel.soundTransform = new SoundTransform(vol, 0);
+ }
+ }
+
+ public function load(src:String) {
+ this.sound.load(new URLRequest(src));
+ }
+
+ public function play() {
+ this.channel = this.sound.play(this.lastPosition, 0, new SoundTransform(this.volume, 0));
+ }
+
+ public function pause() {
+ if (this.channel != null) {
+ this.lastPosition = this.channel.position;
+ this.channel.stop();
+ }
+ }
+
+
+ /////////////////// Event Handlers ///////////////////
+ private static function soundComplete(e) {
+ ExternalInterface.call("console.log", e.target + " complete");
+ ExternalInterface.call("console.log", e);
+ }
+
+ private static function soundId3(e) {
+ ExternalInterface.call("console.log", e.target + " id3");
+ ExternalInterface.call("console.log", e);
+ }
+
+ private static function soundIoError(e) {
+ ExternalInterface.call("console.log", e.target + " ioError");
+ ExternalInterface.call("console.log", e);
+ }
+
+ private static function soundOpen(e) {
+ ExternalInterface.call("console.log", e.target + " open");
+ ExternalInterface.call("console.log", e);
+ }
+
+ private static function soundProgress(e) {
+ //ExternalInterface.call("console.log", e.target + " progress");
+ //ExternalInterface.call("console.log", e);
+ }
+}
91 src/HtmlAudio.hx
@@ -0,0 +1,91 @@
+/* The MIT License
+ *
+ * Copyright (c) 2010 Nathan Rajlich
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+import flash.events.Event;
+import flash.events.IOErrorEvent;
+import flash.events.SecurityErrorEvent;
+import flash.events.ProgressEvent;
+import flash.external.ExternalInterface;
+
+class HtmlAudio {
+ public static var sounds:Array<HTMLAudioElement> = new Array();
+
+ public static function IS_AUDIO_BRIDGE() {
+ return true;
+ }
+
+ /* Called from JavaScript; HTMLAudioElement#load.
+ * Should create a new HTMLAudioElement AS object, and begin loading
+ * the specified resource. Creating an HTMLAudioElement in Flash is
+ * deferred until HTMLAudioElement#load is called by the webpage.
+ *
+ * @return The array index of the new HTMLAudioElement, which should be
+ * matched in the HTMLAudioElement.__swfSounds Array in JavaScript.
+ */
+ public static function createSound(src:String, volume:Float) {
+ var a : HTMLAudioElement = new HTMLAudioElement(src, volume);
+ sounds.push(a);
+ return sounds.length-1; // Return the index of the new Sound in the array
+ }
+
+ // ExternalInterface functions available to JavaScript
+ public static function load(index:Int, src:String) {
+ var sound:HTMLAudioElement = sounds[index];
+ sound.load(src);
+ }
+
+ public static function play(index:Int) {
+ var sound:HTMLAudioElement = sounds[index];
+ sound.play();
+ }
+
+ public static function pause(index:Int) {
+ var sound:HTMLAudioElement = sounds[index];
+ sound.pause();
+ }
+
+ public static function setVolume(index:Int, volume:Float) {
+ var sound:HTMLAudioElement = sounds[index];
+ sound.setVolume(volume);
+ }
+
+ // Sound Event Handlers
+
+
+ public static function main() {
+ ExternalInterface.addCallback("IS_AUDIO_BRIDGE", IS_AUDIO_BRIDGE);
+ ExternalInterface.addCallback("__createSound", createSound);
+ ExternalInterface.addCallback("__load", load);
+ ExternalInterface.addCallback("__play", play);
+ ExternalInterface.addCallback("__pause", pause);
+ ExternalInterface.addCallback("__setVolume", setVolume);
+ ExternalInterface.call([
+ "(function(){",
+ "var f = function(tag){",
+ "var elems = document.getElementsByTagName(tag);",
+ "for (var i=0; i<elems.length; i++) if (elems[i].IS_AUDIO_BRIDGE) return elems[i];",
+ "};",
+ "HTMLAudioElement.__swf = f('embed') || f('object');",
+ "})" ].join('') );
+ ExternalInterface.call("HTMLAudioElement.__swfLoaded");
+ }
+}
4 swfobject.js
@@ -0,0 +1,4 @@
+/* SWFObject v2.2 <http://code.google.com/p/swfobject/>
+ is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
+*/
+var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y<X;Y++){U[Y]()}}function K(X){if(J){X()}else{U[U.length]=X}}function s(Y){if(typeof O.addEventListener!=D){O.addEventListener("load",Y,false)}else{if(typeof j.addEventListener!=D){j.addEventListener("load",Y,false)}else{if(typeof O.attachEvent!=D){i(O,"onload",Y)}else{if(typeof O.onload=="function"){var X=O.onload;O.onload=function(){X();Y()}}else{O.onload=Y}}}}}function h(){if(T){V()}else{H()}}function V(){var X=j.getElementsByTagName("body")[0];var aa=C(r);aa.setAttribute("type",q);var Z=X.appendChild(aa);if(Z){var Y=0;(function(){if(typeof Z.GetVariable!=D){var ab=Z.GetVariable("$version");if(ab){ab=ab.split(" ")[1].split(",");M.pv=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}else{if(Y<10){Y++;setTimeout(arguments.callee,10);return}}X.removeChild(aa);Z=null;H()})()}else{H()}}function H(){var ag=o.length;if(ag>0){for(var af=0;af<ag;af++){var Y=o[af].id;var ab=o[af].callbackFn;var aa={success:false,id:Y};if(M.pv[0]>0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad<ac;ad++){if(X[ad].getAttribute("name").toLowerCase()!="movie"){ah[X[ad].getAttribute("name")]=X[ad].getAttribute("value")}}P(ai,ah,Y,ab)}else{p(ae);if(ab){ab(aa)}}}}}else{w(Y,true);if(ab){var Z=z(Y);if(Z&&typeof Z.SetVariable!=D){aa.success=true;aa.ref=Z}ab(aa)}}}}}function z(aa){var X=null;var Y=c(aa);if(Y&&Y.nodeName=="OBJECT"){if(typeof Y.SetVariable!=D){X=Y}else{var Z=Y.getElementsByTagName(r)[0];if(Z){X=Z}}}return X}function A(){return !a&&F("6.0.65")&&(M.win||M.mac)&&!(M.wk&&M.wk<312)}function P(aa,ab,X,Z){a=true;E=Z||null;B={success:false,id:X};var ae=c(X);if(ae){if(ae.nodeName=="OBJECT"){l=g(ae);Q=null}else{l=ae;Q=X}aa.id=R;if(typeof aa.width==D||(!/%$/.test(aa.width)&&parseInt(aa.width,10)<310)){aa.width="310"}if(typeof aa.height==D||(!/%$/.test(aa.height)&&parseInt(aa.height,10)<137)){aa.height="137"}j.title=j.title.slice(0,47)+" - Flash Player Installation";var ad=M.ie&&M.win?"ActiveX":"PlugIn",ac="MMredirectURL="+O.location.toString().replace(/&/g,"%26")+"&MMplayerType="+ad+"&MMdoctitle="+j.title;if(typeof ab.flashvars!=D){ab.flashvars+="&"+ac}else{ab.flashvars=ac}if(M.ie&&M.win&&ae.readyState!=4){var Y=C("div");X+="SWFObjectNew";Y.setAttribute("id",X);ae.parentNode.insertBefore(Y,ae);ae.style.display="none";(function(){if(ae.readyState==4){ae.parentNode.removeChild(ae)}else{setTimeout(arguments.callee,10)}})()}u(aa,ab,X)}}function p(Y){if(M.ie&&M.win&&Y.readyState!=4){var X=C("div");Y.parentNode.insertBefore(X,Y);X.parentNode.replaceChild(g(Y),X);Y.style.display="none";(function(){if(Y.readyState==4){Y.parentNode.removeChild(Y)}else{setTimeout(arguments.callee,10)}})()}else{Y.parentNode.replaceChild(g(Y),Y)}}function g(ab){var aa=C("div");if(M.win&&M.ie){aa.innerHTML=ab.innerHTML}else{var Y=ab.getElementsByTagName(r)[0];if(Y){var ad=Y.childNodes;if(ad){var X=ad.length;for(var Z=0;Z<X;Z++){if(!(ad[Z].nodeType==1&&ad[Z].nodeName=="PARAM")&&!(ad[Z].nodeType==8)){aa.appendChild(ad[Z].cloneNode(true))}}}}}return aa}function u(ai,ag,Y){var X,aa=c(Y);if(M.wk&&M.wk<312){return X}if(aa){if(typeof ai.id==D){ai.id=Y}if(M.ie&&M.win){var ah="";for(var ae in ai){if(ai[ae]!=Object.prototype[ae]){if(ae.toLowerCase()=="data"){ag.movie=ai[ae]}else{if(ae.toLowerCase()=="styleclass"){ah+=' class="'+ai[ae]+'"'}else{if(ae.toLowerCase()!="classid"){ah+=" "+ae+'="'+ai[ae]+'"'}}}}}var af="";for(var ad in ag){if(ag[ad]!=Object.prototype[ad]){af+='<param name="'+ad+'" value="'+ag[ad]+'" />'}}aa.outerHTML='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'+ah+">"+af+"</object>";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab<ac;ab++){I[ab][0].detachEvent(I[ab][1],I[ab][2])}var Z=N.length;for(var aa=0;aa<Z;aa++){y(N[aa])}for(var Y in M){M[Y]=null}M=null;for(var X in swfobject){swfobject[X]=null}swfobject=null})}}();return{registerObject:function(ab,X,aa,Z){if(M.w3&&ab&&X){var Y={};Y.id=ab;Y.swfVersion=X;Y.expressInstall=aa;Y.callbackFn=Z;o[o.length]=Y;w(ab,false)}else{if(Z){Z({success:false,id:ab})}}},getObjectById:function(X){if(M.w3){return z(X)}},embedSWF:function(ab,ah,ae,ag,Y,aa,Z,ad,af,ac){var X={success:false,id:ah};if(M.w3&&!(M.wk&&M.wk<312)&&ab&&ah&&ae&&ag&&Y){w(ah,false);K(function(){ae+="";ag+="";var aj={};if(af&&typeof af===r){for(var al in af){aj[al]=af[al]}}aj.data=ab;aj.width=ae;aj.height=ag;var am={};if(ad&&typeof ad===r){for(var ak in ad){am[ak]=ad[ak]}}if(Z&&typeof Z===r){for(var ai in Z){if(typeof am.flashvars!=D){am.flashvars+="&"+ai+"="+Z[ai]}else{am.flashvars=ai+"="+Z[ai]}}}if(F(Y)){var an=u(aj,am,ah);if(aj.id==ah){w(ah,true)}X.success=true;X.ref=an}else{if(aa&&A()){aj.data=aa;P(aj,am,ah,ac);return}else{w(ah,true)}}if(ac){ac(X)}})}else{if(ac){ac(X)}}},switchOffAutoHideShow:function(){m=false},ua:M,getFlashPlayerVersion:function(){return{major:M.pv[0],minor:M.pv[1],release:M.pv[2]}},hasFlashPlayerVersion:F,createSWF:function(Z,Y,X){if(M.w3){return u(Z,Y,X)}else{return undefined}},showExpressInstall:function(Z,aa,X,Y){if(M.w3&&A()){P(Z,aa,X,Y)}},removeSWF:function(X){if(M.w3){y(X)}},createCSS:function(aa,Z,Y,X){if(M.w3){v(aa,Z,Y,X)}},addDomLoadEvent:K,addLoadEvent:s,getQueryParamValue:function(aa){var Z=j.location.search||j.location.hash;if(Z){if(/\?/.test(Z)){Z=Z.split("?")[1]}if(aa==null){return L(Z)}var Y=Z.split("&");for(var X=0;X<Y.length;X++){if(Y[X].substring(0,Y[X].indexOf("="))==aa){return L(Y[X].substring((Y[X].indexOf("=")+1)))}}}return""},expressInstallCallback:function(){if(a){var X=c(R);if(X&&l){X.parentNode.replaceChild(l,X);if(Q){w(Q,true);if(M.ie&&M.win){l.style.display="block"}}if(E){E(B)}}a=false}}}}();

0 comments on commit 9774b81

Please sign in to comment.