﻿/** 
* flowplayer.js 3.1.4. The Flowplayer API
* 
* Copyright 2009 Flowplayer Oy
* 
* This file is part of Flowplayer.
* 
* Flowplayer is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* 
* Flowplayer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
* 
* You should have received a copy of the GNU General Public License
* along with Flowplayer.  If not, see <http://www.gnu.org/licenses/>.
* 
* Date: 2009-10-08 04:21:20 +0000 (Thu, 08 Oct 2009)
* Revision: 328 
*/
(function() {

    /* 
    FEATURES 
    --------
    - $f() and flowplayer() functions	
    - handling multiple instances 
    - Flowplayer programming API 
    - Flowplayer event model	
    - player loading / unloading	
    - jQuery support
    */


    /*jslint glovar: true, browser: true */
    /*global flowplayer, $f */

    // {{{ private utility methods

    function log(args) {
        console.log("$f.fireEvent", [].slice.call(args));
    }


    // thanks: http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone
    function clone(obj) {
        if (!obj || typeof obj != 'object') { return obj; }
        var temp = new obj.constructor();
        for (var key in obj) {
            if (obj.hasOwnProperty(key)) {
                temp[key] = clone(obj[key]);
            }
        }
        return temp;
    }

    // stripped from jQuery, thanks John Resig 
    function each(obj, fn) {
        if (!obj) { return; }

        var name, i = 0, length = obj.length;

        // object
        if (length === undefined) {
            for (name in obj) {
                if (fn.call(obj[name], name, obj[name]) === false) { break; }
            }

            // array
        } else {
            for (var value = obj[0];
				i < length && fn.call(value, i, value) !== false; value = obj[++i]) {
            }
        }

        return obj;
    }


    // convenience
    function el(id) {
        return document.getElementById(id);
    }


    // used extensively. a very simple implementation. 
    function extend(to, from, skipFuncs) {
        if (typeof from != 'object') { return to; }

        if (to && from) {
            each(from, function(name, value) {
                if (!skipFuncs || typeof value != 'function') {
                    to[name] = value;
                }
            });
        }

        return to;
    }

    // var arr = select("elem.className"); 
    function select(query) {
        var index = query.indexOf(".");
        if (index != -1) {
            var tag = query.substring(0, index) || "*";
            var klass = query.substring(index + 1, query.length);
            var els = [];
            each(document.getElementsByTagName(tag), function() {
                if (this.className && this.className.indexOf(klass) != -1) {
                    els.push(this);
                }
            });
            return els;
        }
    }

    // fix event inconsistencies across browsers
    function stopEvent(e) {
        e = e || window.event;

        if (e.preventDefault) {
            e.stopPropagation();
            e.preventDefault();

        } else {
            e.returnValue = false;
            e.cancelBubble = true;
        }
        return false;
    }

    // push an event listener into existing array of listeners
    function bind(to, evt, fn) {
        to[evt] = to[evt] || [];
        to[evt].push(fn);
    }


    // generates an unique id
    function makeId() {
        return "_" + ("" + Math.random()).substring(2, 10);
    }

    //}}}	


    // {{{ Clip

    var Clip = function(json, index, player) {

        // private variables
        var self = this;
        var cuepoints = {};
        var listeners = {};
        self.index = index;

        // instance variables
        if (typeof json == 'string') {
            json = { url: json };
        }

        extend(this, json, true);

        // event handling 
        each(("Begin*,Start,Pause*,Resume*,Seek*,Stop*,Finish*,LastSecond,Update,BufferFull,BufferEmpty,BufferStop").split(","),
			function() {

			    var evt = "on" + this;

			    // before event
			    if (evt.indexOf("*") != -1) {
			        evt = evt.substring(0, evt.length - 1);
			        var before = "onBefore" + evt.substring(2);

			        self[before] = function(fn) {
			            bind(listeners, before, fn);
			            return self;
			        };
			    }

			    self[evt] = function(fn) {
			        bind(listeners, evt, fn);
			        return self;
			    };


			    // set common clip event listeners to player level
			    if (index == -1) {
			        if (self[before]) {
			            player[before] = self[before];
			        }
			        if (self[evt]) {
			            player[evt] = self[evt];
			        }
			    }

			});

        extend(this, {

            onCuepoint: function(points, fn) {

                // embedded cuepoints
                if (arguments.length == 1) {
                    cuepoints.embedded = [null, points];
                    return self;
                }

                if (typeof points == 'number') {
                    points = [points];
                }

                var fnId = makeId();
                cuepoints[fnId] = [points, fn];

                if (player.isLoaded()) {
                    player._api().fp_addCuepoints(points, index, fnId);
                }

                return self;
            },

            update: function(json) {
                extend(self, json);

                if (player.isLoaded()) {
                    player._api().fp_updateClip(json, index);
                }
                var conf = player.getConfig();
                var clip = (index == -1) ? conf.clip : conf.playlist[index];
                extend(clip, json, true);
            },


            // internal event for performing clip tasks. should be made private someday
            _fireEvent: function(evt, arg1, arg2, target) {

                if (evt == 'onLoad') {
                    each(cuepoints, function(key, val) {
                        if (val[0]) {
                            player._api().fp_addCuepoints(val[0], index, key);
                        }
                    });
                    return false;
                }

                // target clip we are working against
                target = target || self;

                if (evt == 'onCuepoint') {
                    var fn = cuepoints[arg1];
                    if (fn) {
                        return fn[1].call(player, target, arg2);
                    }
                }

                // 1. clip properties, 2-3. metadata, 4. updates, 5. resumes from nested clip
                if (arg1 && "onBeforeBegin,onMetaData,onStart,onUpdate,onResume".indexOf(evt) != -1) {
                    // update clip properties
                    extend(target, arg1);

                    if (arg1.metaData) {
                        if (!target.duration) {
                            target.duration = arg1.metaData.duration;
                        } else {
                            target.fullDuration = arg1.metaData.duration;
                        }
                    }
                }


                var ret = true;
                each(listeners[evt], function() {
                    ret = this.call(player, target, arg1, arg2);
                });
                return ret;
            }

        });


        // get cuepoints from config
        if (json.onCuepoint) {
            var arg = json.onCuepoint;
            self.onCuepoint.apply(self, typeof arg == 'function' ? [arg] : arg);
            delete json.onCuepoint;
        }

        // get other events
        each(json, function(key, val) {

            if (typeof val == 'function') {
                bind(listeners, key, val);
                delete json[key];
            }

        });


        // setup common clip event callbacks for Player object too (shortcuts)
        if (index == -1) {
            player.onCuepoint = this.onCuepoint;
        }

    };

    //}}}


    // {{{ Plugin

    var Plugin = function(name, json, player, fn) {

        var listeners = {};
        var self = this;
        var hasMethods = false;

        if (fn) {
            extend(listeners, fn);
        }

        // custom callback functions in configuration
        each(json, function(key, val) {
            if (typeof val == 'function') {
                listeners[key] = val;
                delete json[key];
            }
        });

        // core plugin methods		
        extend(this, {

            // speed and fn are optional
            animate: function(props, speed, fn) {
                if (!props) {
                    return self;
                }

                if (typeof speed == 'function') {
                    fn = speed;
                    speed = 500;
                }

                if (typeof props == 'string') {
                    var key = props;
                    props = {};
                    props[key] = speed;
                    speed = 500;
                }

                if (fn) {
                    var fnId = makeId();
                    listeners[fnId] = fn;
                }

                if (speed === undefined) { speed = 500; }
                json = player._api().fp_animate(name, props, speed, fnId);
                return self;
            },

            css: function(props, val) {
                if (val !== undefined) {
                    var css = {};
                    css[props] = val;
                    props = css;
                }
                json = player._api().fp_css(name, props);
                extend(self, json);
                return self;
            },

            show: function() {
                this.display = 'block';
                player._api().fp_showPlugin(name);
                return self;
            },

            hide: function() {
                this.display = 'none';
                player._api().fp_hidePlugin(name);
                return self;
            },

            // toggle between visible / hidden state
            toggle: function() {
                this.display = player._api().fp_togglePlugin(name);
                return self;
            },

            fadeTo: function(o, speed, fn) {

                if (typeof speed == 'function') {
                    fn = speed;
                    speed = 500;
                }

                if (fn) {
                    var fnId = makeId();
                    listeners[fnId] = fn;
                }
                this.display = player._api().fp_fadeTo(name, o, speed, fnId);
                this.opacity = o;
                return self;
            },

            fadeIn: function(speed, fn) {
                return self.fadeTo(1, speed, fn);
            },

            fadeOut: function(speed, fn) {
                return self.fadeTo(0, speed, fn);
            },

            getName: function() {
                return name;
            },

            getPlayer: function() {
                return player;
            },

            // internal method. should be made private some day
            _fireEvent: function(evt, arg, arg2) {

                // update plugins properties & methods
                if (evt == 'onUpdate') {
                    var json = player._api().fp_getPlugin(name);
                    if (!json) { return; }

                    extend(self, json);
                    delete self.methods;

                    if (!hasMethods) {
                        each(json.methods, function() {
                            var method = "" + this;

                            self[method] = function() {
                                var a = [].slice.call(arguments);
                                var ret = player._api().fp_invoke(name, method, a);
                                return ret === 'undefined' || ret === undefined ? self : ret;
                            };
                        });
                        hasMethods = true;
                    }
                }

                // plugin callbacks
                var fn = listeners[evt];

                if (fn) {
                    fn.apply(self, arg);

                    // "one-shot" callback
                    if (evt.substring(0, 1) == "_") {
                        delete listeners[evt];
                    }
                }
            }

        });

    };


    //}}}


    function Player(wrapper, params, conf) {

        // private variables (+ arguments)
        var 
		self = this,
		api = null,
		html,
		commonClip,
		playlist = [],
		plugins = {},
		listeners = {},
		playerId,
		apiId,

        // n'th player on the page
		playerIndex,

        // active clip's index number
		activeIndex,

		swfHeight,
		wrapperHeight;


        // {{{ public methods 

        extend(self, {

            id: function() {
                return playerId;
            },

            isLoaded: function() {
                return (api !== null);
            },

            getParent: function() {
                return wrapper;
            },

            hide: function(all) {
                if (all) { wrapper.style.height = "0px"; }
                if (api) { api.style.height = "0px"; }
                return self;
            },

            show: function() {
                wrapper.style.height = wrapperHeight + "px";
                if (api) { api.style.height = swfHeight + "px"; }
                return self;
            },

            isHidden: function() {
                return api && parseInt(api.style.height, 10) === 0;
            },


            load: function(fn) {

                if (!api && self._fireEvent("onBeforeLoad") !== false) {

                    // unload all instances
                    each(players, function() {
                        this.unload();
                    });

                    html = wrapper.innerHTML;

                    // do not use splash as alternate content for flashembed
                    if (html && !flashembed.isSupported(params.version)) {
                        wrapper.innerHTML = "";
                    }

                    // install Flash object inside given container
                    flashembed(wrapper, params, { config: conf });

                    // onLoad listener given as argument
                    if (fn) {
                        fn.cached = true;
                        bind(listeners, "onLoad", fn);
                    }
                }

                return self;
            },

            unload: function() {

                // unload only if in splash state
                if (html.replace(/\s/g, '') !== '') {

                    if (self._fireEvent("onBeforeUnload") === false) {
                        return self;
                    }

                    // try closing
                    try {
                        if (api) {
                            api.fp_close();

                            // fire unload only when API is present
                            self._fireEvent("onUnload");
                        }
                    } catch (error) { }

                    api = null;
                    wrapper.innerHTML = html;
                }

                return self;

            },

            getClip: function(index) {
                if (index === undefined) {
                    index = activeIndex;
                }
                return playlist[index];
            },


            getCommonClip: function() {
                return commonClip;
            },

            getPlaylist: function() {
                return playlist;
            },

            getPlugin: function(name) {
                var plugin = plugins[name];

                // create plugin if nessessary
                if (!plugin && self.isLoaded()) {
                    var json = self._api().fp_getPlugin(name);
                    if (json) {
                        plugin = new Plugin(name, json, self);
                        plugins[name] = plugin;
                    }
                }
                return plugin;
            },

            getScreen: function() {
                return self.getPlugin("screen");
            },

            getControls: function() {
                return self.getPlugin("controls");
            },

            getConfig: function(copy) {
                return copy ? clone(conf) : conf;
            },

            getFlashParams: function() {
                return params;
            },

            loadPlugin: function(name, url, props, fn) {

                // properties not supplied			
                if (typeof props == 'function') {
                    fn = props;
                    props = {};
                }

                // if fn not given, make a fake id so that plugin's onUpdate get's fired
                var fnId = fn ? makeId() : "_";
                self._api().fp_loadPlugin(name, url, props, fnId);

                // create new plugin
                var arg = {};
                arg[fnId] = fn;
                var p = new Plugin(name, null, self, arg);
                plugins[name] = p;
                return p;
            },


            getState: function() {
                return api ? api.fp_getState() : -1;
            },

            // "lazy" play
            play: function(clip, instream) {

                function play() {
                    if (clip !== undefined) {
                        self._api().fp_play(clip, instream);
                    } else {
                        self._api().fp_play();
                    }
                }

                if (api) {
                    play();

                } else {
                    self.load(function() {
                        play();
                    });
                }

                return self;
            },

            getVersion: function() {
                var js = "flowplayer.js 3.1.4";
                if (api) {
                    var ver = api.fp_getVersion();
                    ver.push(js);
                    return ver;
                }
                return js;
            },

            _api: function() {
                if (!api) {
                    throw "Flowplayer " + self.id() + " not loaded when calling an API method";
                }
                return api;
            },

            setClip: function(clip) {
                self.setPlaylist([clip]);
                return self;
            },

            getIndex: function() {
                return playerIndex;
            }

        });


        // event handlers
        each(("Click*,Load*,Unload*,Keypress*,Volume*,Mute*,Unmute*,PlaylistReplace,ClipAdd,Fullscreen*,FullscreenExit,Error,MouseOver,MouseOut").split(","),
		function() {
		    var name = "on" + this;

		    // before event
		    if (name.indexOf("*") != -1) {
		        name = name.substring(0, name.length - 1);
		        var name2 = "onBefore" + name.substring(2);
		        self[name2] = function(fn) {
		            bind(listeners, name2, fn);
		            return self;
		        };
		    }

		    // normal event
		    self[name] = function(fn) {
		        bind(listeners, name, fn);
		        return self;
		    };
		}
	);


        // core API methods
        each(("pause,resume,mute,unmute,stop,toggle,seek,getStatus,getVolume,setVolume,getTime,isPaused,isPlaying,startBuffering,stopBuffering,isFullscreen,toggleFullscreen,reset,close,setPlaylist,addClip,playFeed").split(","),
		function() {
		    var name = this;

		    self[name] = function(a1, a2) {
		        if (!api) { return self; }
		        var ret = null;

		        // two arguments
		        if (a1 !== undefined && a2 !== undefined) {
		            ret = api["fp_" + name](a1, a2);

		        } else {
		            ret = (a1 === undefined) ? api["fp_" + name]() : api["fp_" + name](a1);
		        }

		        return ret === 'undefined' || ret === undefined ? self : ret;
		    };
		}
	);

        //}}}


        // {{{ public method: _fireEvent

        self._fireEvent = function(a) {

            if (typeof a == 'string') { a = [a]; }

            var evt = a[0], arg0 = a[1], arg1 = a[2], arg2 = a[3], i = 0;

            if (conf.debug) { log(a); }

            // internal onLoad
            if (!api && evt == 'onLoad' && arg0 == 'player') {

                api = api || el(apiId);
                swfHeight = api.clientHeight;

                each(playlist, function() {
                    this._fireEvent("onLoad");
                });

                each(plugins, function(name, p) {
                    p._fireEvent("onUpdate");
                });

                commonClip._fireEvent("onLoad");
            }

            // other onLoad events are skipped
            if (evt == 'onLoad' && arg0 != 'player') { return; }


            // "normalize" error handling
            if (evt == 'onError') {
                if (typeof arg0 == 'string' || (typeof arg0 == 'number' && typeof arg1 == 'number')) {
                    arg0 = arg1;
                    arg1 = arg2;
                }
            }


            if (evt == 'onContextMenu') {
                each(conf.contextMenu[arg0], function(key, fn) {
                    fn.call(self);
                });
                return;
            }

            if (evt == 'onPluginEvent') {
                var name = arg0.name || arg0;
                var p = plugins[name];

                if (p) {
                    p._fireEvent("onUpdate", arg0);
                    p._fireEvent(arg1, a.slice(3));
                }
                return;
            }

            // replace whole playlist
            if (evt == 'onPlaylistReplace') {
                playlist = [];
                var index = 0;
                each(arg0, function() {
                    playlist.push(new Clip(this, index++, self));
                });
            }

            // insert new clip to the playlist. arg0 = clip, arg1 = index 
            if (evt == 'onClipAdd') {

                // instream clip additions are ignored at this point
                if (arg0.isInStream) { return; }

                // add new clip into playlist			
                arg0 = new Clip(arg0, arg1, self);
                playlist.splice(arg1, 0, arg0);

                // increment index variable for the rest of the clips on playlist 
                for (i = arg1 + 1; i < playlist.length; i++) {
                    playlist[i].index++;
                }
            }


            var ret = true;

            // clip event
            if (typeof arg0 == 'number' && arg0 < playlist.length) {

                activeIndex = arg0;
                var clip = playlist[arg0];

                if (clip) {
                    ret = clip._fireEvent(evt, arg1, arg2);
                }

                if (!clip || ret !== false) {

                    // clip argument is given for common clip, because it behaves as the target
                    ret = commonClip._fireEvent(evt, arg1, arg2, clip);
                }
            }


            // trigger player event
            each(listeners[evt], function() {
                ret = this.call(self, arg0, arg1);

                // remove cached entry
                if (this.cached) {
                    listeners[evt].splice(i, 1);
                }

                // break loop
                if (ret === false) { return false; }
                i++;

            });

            return ret;
        };

        //}}}


        // {{{ init

        function init() {

            // replace previous installation 
            if ($f(wrapper)) {
                $f(wrapper).getParent().innerHTML = "";
                playerIndex = $f(wrapper).getIndex();
                players[playerIndex] = self;

                // register this player into global array of instances
            } else {
                players.push(self);
                playerIndex = players.length - 1;
            }

            wrapperHeight = parseInt(wrapper.style.height, 10) || wrapper.clientHeight;

            // flashembed parameters
            if (typeof params == 'string') {
                params = { src: params };
            }

            // playerId	
            playerId = wrapper.id || "fp" + makeId();
            apiId = params.id || playerId + "_api";
            params.id = apiId;
            conf.playerId = playerId;


            // plain url is given as config
            if (typeof conf == 'string') {
                conf = { clip: { url: conf} };
            }

            if (typeof conf.clip == 'string') {
                conf.clip = { url: conf.clip };
            }

            // common clip is always there
            conf.clip = conf.clip || {};


            // wrapper href as common clip's url
            if (wrapper.getAttribute("href", 2) && !conf.clip.url) {
                conf.clip.url = wrapper.getAttribute("href", 2);
            }

            commonClip = new Clip(conf.clip, -1, self);

            // playlist
            conf.playlist = conf.playlist || [conf.clip];

            var index = 0;

            each(conf.playlist, function() {

                var clip = this;

                /* sometimes clip is given as array. this is not accepted. */
                if (typeof clip == 'object' && clip.length) {
                    clip = { url: "" + clip };
                }

                // populate common clip properties to each clip
                each(conf.clip, function(key, val) {
                    if (val !== undefined && clip[key] === undefined && typeof val != 'function') {
                        clip[key] = val;
                    }
                });

                // modify playlist in configuration
                conf.playlist[index] = clip;

                // populate playlist array
                clip = new Clip(clip, index, self);
                playlist.push(clip);
                index++;
            });

            // event listeners
            each(conf, function(key, val) {
                if (typeof val == 'function') {

                    // common clip event
                    if (commonClip[key]) {
                        commonClip[key](val);

                        // player event
                    } else {
                        bind(listeners, key, val);
                    }

                    // no need to supply for the Flash component
                    delete conf[key];
                }
            });


            // plugins
            each(conf.plugins, function(name, val) {
                if (val) {
                    plugins[name] = new Plugin(name, val, self);
                }
            });


            // setup controlbar plugin if not explicitly defined
            if (!conf.plugins || conf.plugins.controls === undefined) {
                plugins.controls = new Plugin("controls", null, self);
            }

            // setup canvas as plugin
            plugins.canvas = new Plugin("canvas", null, self);


            // Flowplayer uses black background by default
            params.bgcolor = params.bgcolor || "#000000";


            // setup default settings for express install
            params.version = params.version || [9, 0];
            params.expressInstall = 'http://www.flowplayer.org/swf/expressinstall.swf';


            // click function
            function doClick(e) {
                if (!self.isLoaded() && self._fireEvent("onBeforeClick") !== false) {
                    self.load();
                }
                return stopEvent(e);
            }

            // defer loading upon click
            html = wrapper.innerHTML;
            if (html.replace(/\s/g, '') !== '') {

                if (wrapper.addEventListener) {
                    wrapper.addEventListener("click", doClick, false);

                } else if (wrapper.attachEvent) {
                    wrapper.attachEvent("onclick", doClick);
                }

                // player is loaded upon page load 
            } else {

                // prevent default action from wrapper. (fixes safari problems)
                if (wrapper.addEventListener) {
                    wrapper.addEventListener("click", stopEvent, false);
                }

                // load player
                self.load();
            }
        }

        // possibly defer initialization until DOM get's loaded
        if (typeof wrapper == 'string') {
            flashembed.domReady(function() {
                var node = el(wrapper);

                if (!node) {
                    throw "Flowplayer cannot access element: " + wrapper;
                } else {
                    wrapper = node;
                    init();
                }
            });

            // we have a DOM element so page is already loaded
        } else {
            init();
        }


        //}}}


    }


    // {{{ flowplayer() & statics 

    // container for player instances
    var players = [];


    // this object is returned when multiple player's are requested 
    function Iterator(arr) {

        this.length = arr.length;

        this.each = function(fn) {
            each(arr, fn);
        };

        this.size = function() {
            return arr.length;
        };
    }

    // these two variables are the only global variables
    window.flowplayer = window.$f = function() {

        var instance = null;
        var arg = arguments[0];

        // $f()
        if (!arguments.length) {
            each(players, function() {
                if (this.isLoaded()) {
                    instance = this;
                    return false;
                }
            });

            return instance || players[0];
        }

        if (arguments.length == 1) {

            // $f(index);
            if (typeof arg == 'number') {
                return players[arg];


                // $f(wrapper || 'containerId' || '*');
            } else {

                // $f("*");
                if (arg == '*') {
                    return new Iterator(players);
                }

                // $f(wrapper || 'containerId');
                each(players, function() {
                    if (this.id() == arg.id || this.id() == arg || this.getParent() == arg) {
                        instance = this;
                        return false;
                    }
                });

                return instance;
            }
        }

        // instance builder 
        if (arguments.length > 1) {

            var swf = arguments[1];
            var conf = (arguments.length == 3) ? arguments[2] : {};

            if (typeof arg == 'string') {

                // select arg by classname
                if (arg.indexOf(".") != -1) {
                    var instances = [];

                    each(select(arg), function() {
                        instances.push(new Player(this, clone(swf), clone(conf)));
                    });

                    return new Iterator(instances);

                    // select node by id
                } else {
                    var node = el(arg);
                    return new Player(node !== null ? node : arg, swf, conf);
                }


                // arg is a DOM element
            } else if (arg) {
                return new Player(arg, swf, conf);
            }

        }

        return null;
    };

    extend(window.$f, {

        // called by Flash External Interface 		
        fireEvent: function() {
            var a = [].slice.call(arguments);
            var p = $f(a[0]);
            return p ? p._fireEvent(a.slice(1)) : null;
        },


        // create plugins by modifying Player's prototype
        addPlugin: function(name, fn) {
            Player.prototype[name] = fn;
            return $f;
        },

        // utility methods for plugin developers
        each: each,

        extend: extend

    });


    /* sometimes IE leaves sockets open (href="javascript:..." links break this)
    if (document.all) {
    window.onbeforeunload = function(e) { 
    $f("*").each(function() {
    if (this.isLoaded()) {
    this.close();	
    }
    });
    };	
    }
    */


    //}}}


    //{{{ jQuery support

    if (typeof jQuery == 'function') {

        jQuery.prototype.flowplayer = function(params, conf) {

            // select instances
            if (!arguments.length || typeof arguments[0] == 'number') {
                var arr = [];
                this.each(function() {
                    var p = $f(this);
                    if (p) {
                        arr.push(p);
                    }
                });
                return arguments.length ? arr[arguments[0]] : new Iterator(arr);
            }

            // create flowplayer instances
            return this.each(function() {
                $f(this, clone(params), conf ? clone(conf) : {});
            });

        };

    }

    //}}}


})();
/**
* tools.flashembed 1.0.4 - The future of Flash embedding.
* 
* Copyright (c) 2009 Tero Piirainen
* http://flowplayer.org/tools/flash-embed.html
*
* Dual licensed under MIT and GPL 2+ licenses
* http://www.opensource.org/licenses
*
* Launch  : March 2008
* Date: ${date}
* Revision: ${revision} 
*/
(function() {

    //{{{ utility functions 

    var jQ = typeof jQuery == 'function';

    var options = {

        // very common opts
        width: '100%',
        height: '100%',

        // flashembed defaults
        allowfullscreen: true,
        allowscriptaccess: 'always',
        quality: 'high',

        // flashembed specific options
        version: null,
        onFail: null,
        expressInstall: null,
        w3c: false,
        cachebusting: false
    };

    if (jQ) {

        // tools version number
        jQuery.tools = jQuery.tools || {};

        jQuery.tools.flashembed = {
            version: '1.0.4',
            conf: options
        };
    }


    // from "Pro JavaScript techniques" by John Resig
    function isDomReady() {

        if (domReady.done) { return false; }

        var d = document;
        if (d && d.getElementsByTagName && d.getElementById && d.body) {
            clearInterval(domReady.timer);
            domReady.timer = null;

            for (var i = 0; i < domReady.ready.length; i++) {
                domReady.ready[i].call();
            }

            domReady.ready = null;
            domReady.done = true;
        }
    }

    // if jQuery is present, use it's more effective domReady method
    var domReady = jQ ? jQuery : function(f) {

        if (domReady.done) {
            return f();
        }

        if (domReady.timer) {
            domReady.ready.push(f);

        } else {
            domReady.ready = [f];
            domReady.timer = setInterval(isDomReady, 13);
        }
    };


    // override extend opts function 
    function extend(to, from) {
        if (from) {
            for (key in from) {
                if (from.hasOwnProperty(key)) {
                    to[key] = from[key];
                }
            }
        }

        return to;
    }


    // JSON.asString() function
    function asString(obj) {

        switch (typeOf(obj)) {
            case 'string':
                obj = obj.replace(new RegExp('(["\\\\])', 'g'), '\\$1');

                // flash does not handle %- characters well. transforms "50%" to "50pct" (a dirty hack, I admit)
                obj = obj.replace(/^\s?(\d+)%/, "$1pct");
                return '"' + obj + '"';

            case 'array':
                return '[' + map(obj, function(el) {
                    return asString(el);
                }).join(',') + ']';

            case 'function':
                return '"function()"';

            case 'object':
                var str = [];
                for (var prop in obj) {
                    if (obj.hasOwnProperty(prop)) {
                        str.push('"' + prop + '":' + asString(obj[prop]));
                    }
                }
                return '{' + str.join(',') + '}';
        }

        // replace ' --> "  and remove spaces
        return String(obj).replace(/\s/g, " ").replace(/\'/g, "\"");
    }


    // private functions
    function typeOf(obj) {
        if (obj === null || obj === undefined) { return false; }
        var type = typeof obj;
        return (type == 'object' && obj.push) ? 'array' : type;
    }


    // version 9 bugfix: (http://blog.deconcept.com/2006/07/28/swfobject-143-released/)
    if (window.attachEvent) {
        window.attachEvent("onbeforeunload", function() {
            __flash_unloadHandler = function() { };
            __flash_savedUnloadHandler = function() { };
        });
    }

    function map(arr, func) {
        var newArr = [];
        for (var i in arr) {
            if (arr.hasOwnProperty(i)) {
                newArr[i] = func(arr[i]);
            }
        }
        return newArr;
    }

    function getHTML(p, c) {

        var e = extend({}, p);
        var ie = document.all;
        var html = '<object width="' + e.width + '" height="' + e.height + '"';

        // force id for IE or Flash API cannot be returned
        if (ie && !e.id) {
            e.id = "_" + ("" + Math.random()).substring(9);
        }

        if (e.id) {
            html += ' id="' + e.id + '"';
        }

        // prevent possible caching problems
        if (e.cachebusting) {
            e.src += ((e.src.indexOf("?") != -1 ? "&" : "?") + Math.random());
        }

        if (e.w3c || !ie) {
            html += ' data="' + e.src + '" type="application/x-shockwave-flash"';
        } else {
            html += ' classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"';
        }

        html += '>';

        if (e.w3c || ie) {
            html += '<param name="movie" value="' + e.src + '" />';
        }

        // parameters
        e.width = e.height = e.id = e.w3c = e.src = null;

        for (var k in e) {
            if (e[k] !== null) {
                html += '<param name="' + k + '" value="' + e[k] + '" />';
            }
        }

        // flashvars
        var vars = "";

        if (c) {
            for (var key in c) {
                if (c[key] !== null) {
                    vars += key + '=' + (typeof c[key] == 'object' ? asString(c[key]) : c[key]) + '&';
                }
            }
            vars = vars.substring(0, vars.length - 1);
            html += '<param name="flashvars" value=\'' + vars + '\' />';
        }

        html += "</object>";

        return html;

    }

    //}}}


    function Flash(root, opts, flashvars) {

        var version = flashembed.getVersion();

        // API methods for callback
        extend(this, {

            getContainer: function() {
                return root;
            },

            getConf: function() {
                return opts;
            },

            getVersion: function() {
                return version;
            },

            getFlashvars: function() {
                return flashvars;
            },

            getApi: function() {
                return root.firstChild;
            },

            getHTML: function() {
                return getHTML(opts, flashvars);
            }

        });

        // variables	
        var required = opts.version;
        var express = opts.expressInstall;


        // everything ok -> generate OBJECT tag 
        var ok = !required || flashembed.isSupported(required);

        if (ok) {
            opts.onFail = opts.version = opts.expressInstall = null;
            root.innerHTML = getHTML(opts, flashvars);

            // fail #1. express install
        } else if (required && express && flashembed.isSupported([6, 65])) {

            extend(opts, { src: express });

            flashvars = {
                MMredirectURL: location.href,
                MMplayerType: 'PlugIn',
                MMdoctitle: document.title
            };

            root.innerHTML = getHTML(opts, flashvars);

            // fail #2. 
        } else {

            // fail #2.1 custom content inside container
            if (root.innerHTML.replace(/\s/g, '') !== '') {
                // minor bug fixed here 08.04.2008 (thanks JRodman)			

                // fail #2.2 default content
            } else {
                root.innerHTML =
				"<h2>Flash version " + required + " or greater is required</h2>" +
				"<h3>" +
					(version[0] > 0 ? "Your version is " + version : "You have no flash plugin installed") +
				"</h3>" +

				(root.tagName == 'A' ? "<p>Click here to download latest version</p>" :
					"<p>Download latest version from <a href='http://www.adobe.com/go/getflashplayer'>here</a></p>");

                if (root.tagName == 'A') {
                    root.onclick = function() {
                        location.href = 'http://www.adobe.com/go/getflashplayer';
                    };
                }
            }
        }

        // onFail
        if (!ok && opts.onFail) {
            var ret = opts.onFail.call(this);
            if (typeof ret == 'string') { root.innerHTML = ret; }
        }

        // http://flowplayer.org/forum/8/18186#post-18593
        if (document.all) {
            window[opts.id] = document.getElementById(opts.id);
        }

    }

    window.flashembed = function(root, conf, flashvars) {

        //{{{ construction

        // root must be found / loaded	
        if (typeof root == 'string') {
            var el = document.getElementById(root);
            if (el) {
                root = el;
            } else {
                domReady(function() {
                    flashembed(root, conf, flashvars);
                });
                return;
            }
        }

        // not found
        if (!root) { return; }

        if (typeof conf == 'string') {
            conf = { src: conf };
        }

        var opts = extend({}, options);
        extend(opts, conf);

        return new Flash(root, opts, flashvars);

        //}}}


    };


    //{{{ static methods

    extend(window.flashembed, {

        // returns arr[major, fix]
        getVersion: function() {

            var version = [0, 0];

            if (navigator.plugins && typeof navigator.plugins["Shockwave Flash"] == "object") {
                var _d = navigator.plugins["Shockwave Flash"].description;
                if (typeof _d != "undefined") {
                    _d = _d.replace(/^.*\s+(\S+\s+\S+$)/, "$1");
                    var _m = parseInt(_d.replace(/^(.*)\..*$/, "$1"), 10);
                    var _r = /r/.test(_d) ? parseInt(_d.replace(/^.*r(.*)$/, "$1"), 10) : 0;
                    version = [_m, _r];
                }

            } else if (window.ActiveXObject) {

                try { // avoid fp 6 crashes
                    var _a = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");

                } catch (e) {

                    try {
                        _a = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");
                        version = [6, 0];
                        _a.AllowScriptAccess = "always"; // throws if fp < 6.47 

                    } catch (ee) {
                        if (version[0] == 6) { return version; }
                    }
                    try {
                        _a = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
                    } catch (eee) {

                    }

                }

                if (typeof _a == "object") {
                    _d = _a.GetVariable("$version"); // bugs in fp 6.21 / 6.23
                    if (typeof _d != "undefined") {
                        _d = _d.replace(/^\S+\s+(.*)$/, "$1").split(",");
                        version = [parseInt(_d[0], 10), parseInt(_d[2], 10)];
                    }
                }
            }

            return version;
        },

        isSupported: function(version) {
            var now = flashembed.getVersion();
            var ret = (now[0] > version[0]) || (now[0] == version[0] && now[1] >= version[1]);
            return ret;
        },

        domReady: domReady,

        // returns a String representation from JSON object 
        asString: asString,


        getHTML: getHTML

    });

    //}}}


    // setup jquery support
    if (jQ) {

        jQuery.fn.flashembed = function(conf, flashvars) {

            var el = null;

            this.each(function() {
                el = flashembed(this, conf, flashvars);
            });

            return conf.api === false ? this : el;
        };

    }

})();

