/** 
 * flowplayer.js 3.2.0. 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: 2010-05-03 20:23:59 +0000 (Mon, 03 May 2010)
 * Revision: 468 
 */
(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.slice(0, index) || "*";
            var klass = query.slice(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()).slice(2, 10);   
   }
    
//}}}   
    

// {{{ Clip

    var Clip = function(json, index, player) {
        
        // private variables
        var self = this,
             cuepoints = {},
             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.slice(0, evt.length -1); 
                var before = "onBefore" + evt.slice(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 self = this,
             listeners = {},
             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.slice(0, 1) == "_") {
                        delete listeners[evt];  
                    } 
            }
            
            return self;
         }
            
        });

    };


//}}}


function Player(wrapper, params, conf) {   

    
    // private variables (+ arguments)
    var self = this, 
        api = null, 
        isUnloading = false,
        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 && ! isUnloading); 
        },
        
        getParent: function() {
            return wrapper; 
        },
        
        hide: function(all) {
            if (all) { wrapper.style.height = "0px"; }
            if (self.isLoaded()) { api.style.height = "0px"; } 
            return self;
        },

        show: function() {
            wrapper.style.height = wrapperHeight + "px";
            if (self.isLoaded()) { api.style.height = swfHeight + "px"; }
            return self;
        }, 
                    
        isHidden: function() {
            return self.isLoaded() && parseInt(api.style.height, 10) === 0;
        },
        
        load: function(fn) { 

            if (!self.isLoaded() && self._fireEvent("onBeforeLoad") !== false) {
                
                var onPlayersUnloaded = function() {
                    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);  
                    }
                };
                
                
                // unload all instances
                var unloadedPlayersNb = 0;
                each(players, function()  {
                    this.unload(function(wasUnloaded) {
                        if ( ++unloadedPlayersNb == players.length ) {
                            onPlayersUnloaded();
                        }
                    });     
                });
            }
            
            return self;    
        },
        
        unload: function(fn) {
            
            
            // if we are fullscreen on safari, we can't unload as it would crash the PluginHost, sorry
            if (this.isFullscreen() && /WebKit/i.test(navigator.userAgent)) {
                if ( fn ) { fn(false); }
                return self;
            }
            
            
            // unload only if in splash state
            if (html.replace(/\s/g,'') !== '') {
                
                if (self._fireEvent("onBeforeUnload") === false) {
                    if ( fn ) { fn(false); }
                    return self;
                }   
                
                isUnloading = true;
                // try closing
                try {
                    if (api) { 
                        api.fp_close();
                        
                        // fire unload only when API is present
                        self._fireEvent("onUnload");
                    }               
                } catch (error) {}              
                
                var clean = function() {
                    api = null;             
                    wrapper.innerHTML = html;
                    isUnloading = false;
                    
                    if ( fn ) { fn(true); }
                };
                
                setTimeout(clean, 50);          
            } 
            else if ( fn ) { fn(false); }
            
            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")._fireEvent("onUpdate");
        }, 
        
        // 3.2
        getLogo: function() {
            try {
                return self.getPlugin("logo")._fireEvent("onUpdate");
            } catch (ignored) {}
        },
        
        // 3.2
        getPlay: function() {
            return self.getPlugin("play")._fireEvent("onUpdate");
        },
        

        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 self.isLoaded() ? api.fp_getState() : -1;
        },
        
        // "lazy" play
        play: function(clip, instream) {
            
            //added for html5
            globalClip = clip
            globalInstream = instream
            
            var p = function() {
                if (clip !== undefined) {
                    self._api().fp_play(clip, instream);
                } else {
                    self._api().fp_play();  
                }
            };
            
            if (self.isLoaded()) {
                p();    
            } else if ( isUnloading ) {
                setTimeout(function() { 
                    self.play(clip, instream); 
                }, 50);
            
            } else {
                self.load(function() { 
                    p();
                });
            }
            
            return self;
        },
        
        getVersion: function() {
            var js = "flowplayer.js 3.2.0";
            if (self.isLoaded()) {
                var ver = api.fp_getVersion();
                ver.push(js);
                return ver;
            }
            return js; 
        },
        
        _api: function() {
            if (!self.isLoaded()) {
                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(("Play*,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.slice(0, name.length -1); 
                var name2 = "onBefore" + name.slice(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,setKeyboardShortcutsEnabled,isKeyboardShortcutsEnabled").split(","),      
        function() {         
            var name = this;
            
            self[name] = function(a1, a2) {
                //modified for html5
                if (!self.isLoaded()) { 
                    if (typeof(html5_active) == "boolean" && html5_active) {
                            if (name in {"pause":'', "resume":'', "stop":''}) {
                                self._fireEvent(["onBefore" + name.slice(0,1).toUpperCase() + name.slice(1), globalClip]);
                            } else if (name in {"mute":'', "unmute":''}) {
                                self._fireEvent("onBefore" + name.slice(0,1).toUpperCase() + name.slice(1)); 
                            }// else if (name in {"setPlaylist":''}) {
                            //  self._fireEvent("onBeforePlaylistReplace");
                            //}
                        };
                    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 (!self.isLoaded() && 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;     
        
        // 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);      
        
        // 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') { 
        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) {     

        // flashembed parameters
        var params = arguments[1],
             conf = (arguments.length == 3) ? arguments[2] : {};
                    
        
        if (typeof params == 'string') {
            params = {src: params}; 
        } 
        
        params = extend({
            bgcolor: "#000000",
            version: [9, 0],
            expressInstall: "http://static.flowplayer.org/swf/expressinstall.swf",
            cachebusting: true
            
        }, params);     
        
        if (typeof arg == 'string') {
            
            // select arg by classname
            if (arg.indexOf(".") != -1) {
                var instances = [];
                
                each(select(arg), function() { 
                    instances.push(new Player(this, clone(params), clone(conf)));       
                }); 
                
                return new Iterator(instances);
                
            // select node by id
            } else {        
                var node = el(arg);
                return new Player(node !== null ? node : arg, params, conf);    
            } 
            
            
        // arg is a DOM element
        } else if (arg) {
            return new Player(arg, params, 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
    
});

    
//}}}


//{{{ jQuery support

if (typeof jQuery == 'function') {
    
    jQuery.fn.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) : {});   
        }); 
        
    };
    
}

//}}}


})();
/**
 * @license 
 * jQuery Tools 3.2.0 / Flashembed - New wave Flash embedding
 * 
 * NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.
 * 
 * http://flowplayer.org/tools/toolbox/flashembed.html
 *
 * Since : March 2008
 * Date  : @DATE 
 */ 
(function() {
        
    var IE = document.all,
         URL = 'http://www.adobe.com/go/getflashplayer',
         JQUERY = typeof jQuery == 'function', 
         RE = /(\d+)[^\d]+(\d+)[^\d]*(\d*)/,
         GLOBAL_OPTS = { 
            // very common opts
            width: '100%',
            height: '100%',     
            id: "_" + ("" + Math.random()).slice(9),
            
            // flashembed defaults
            allowfullscreen: true,
            allowscriptaccess: 'always',
            quality: 'high',    
            
            // flashembed specific options
            version: [3, 0],
            onFail: null,
            expressInstall: null, 
            w3c: false,
            cachebusting: false                  
    };
    
    // 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() {};
        });
    }
    
    // simple extend
    function extend(to, from) {
        if (from) {
            for (key in from) {
                if (from.hasOwnProperty(key)) {
                    to[key] = from[key];
                }
            }
        } 
        return to;
    }   

    // used by asString method  
    function map(arr, func) {
        var newArr = []; 
        for (var i in arr) {
            if (arr.hasOwnProperty(i)) {
                newArr[i] = func(arr[i]);
            }
        }
        return newArr;
    }

    window.flashembed = function(root, opts, conf) {
    
        // root must be found / loaded  
        if (typeof root == 'string') {
            root = document.getElementById(root.replace("#", ""));
        }
        
        // not found
        if (!root) { return; }
        
        if (typeof opts == 'string') {
            opts = {src: opts}; 
        }

        return new Flash(root, extend(extend({}, GLOBAL_OPTS), opts), conf); 
    };  
    
    // flashembed "static" API
    var f = extend(window.flashembed, {
        
        conf: GLOBAL_OPTS,
    
        getVersion: function()  {
            var ver;
            
            try {
                ver = navigator.plugins["Shockwave Flash"].description.slice(16); 
            } catch(e) {
                
                try  {
                    var fo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");
                    ver = fo && fo.GetVariable("$version");  
                } catch(err) {
                        
                } 
            }
            
            ver = RE.exec(ver);
            //modified for html5
            if (ver) {
                return [ver[1], ver[3]];
            } else {
                return [0, 0]
            }
        },
        
        asString: function(obj) { 

            if (obj === null || obj === undefined) { return null; }
            var type = typeof obj;
            if (type == 'object' && obj.push) { type = 'array'; }
            
            switch (type){  
                
                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+\.?\d+)%/, "$1pct");
                    return '"' +obj+ '"';
                    
                case 'array':
                    return '['+ map(obj, function(el) {
                        return f.asString(el);
                    }).join(',') +']'; 
                    
                case 'function':
                    return '"function()"';
                    
                case 'object':
                    var str = [];
                    for (var prop in obj) {
                        if (obj.hasOwnProperty(prop)) {
                            str.push('"'+prop+'":'+ f.asString(obj[prop]));
                        }
                    }
                    return '{'+str.join(',')+'}';
            }
            
            // replace ' --> "  and remove spaces
            return String(obj).replace(/\s/g, " ").replace(/\'/g, "\"");
        },
        
        getHTML: function(opts, conf) {

            opts = extend({}, opts);
            
            /******* OBJECT tag and it's attributes *******/
            var html = '<object width="' + opts.width + 
                '" height="' + opts.height + 
                '" id="' + opts.id + '"' + 
                '" name="' + opts.id + '"';
            
            if (opts.cachebusting) {
                opts.src += ((opts.src.indexOf("?") != -1 ? "&" : "?") + Math.random());        
            }           
            
            if (opts.w3c || !IE) {
                html += ' data="' +opts.src+ '" type="application/x-shockwave-flash"';      
            } else {
                html += ' classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"';    
            }
            
            html += '>'; 
            
            /******* nested PARAM tags *******/
            if (opts.w3c || IE) {
                html += '<param name="movie" value="' +opts.src+ '" />';    
            } 
            
            // not allowed params
            opts.width = opts.height = opts.id = opts.w3c = opts.src = null;
            opts.onFail = opts.version = opts.expressInstall = null;
            
            for (var key in opts) {
                if (opts[key]) {
                    html += '<param name="'+ key +'" value="'+ opts[key] +'" />';
                }
            }   
        
            /******* FLASHVARS *******/
            var vars = "";
            
            if (conf) {
                for (var k in conf) { 
                    if (conf[k]) {
                        var val = conf[k]; 
                        vars += k +'='+ (/function|object/.test(typeof val) ? f.asString(val) : val) + '&';
                    }
                }
                vars = vars.slice(0, -1);
                html += '<param name="flashvars" value=\'' + vars + '\' />';
            }
            
            html += "</object>";    
            
            return html;                
        },
        
        isSupported: function(ver) {
            return VERSION[0] > ver[0] || VERSION[0] == ver[0] && VERSION[1] >= ver[1];         
        }       
        
    });
    
    var VERSION = f.getVersion(); 
    
    function Flash(root, opts, conf) {  
                                                    
        // version is ok
        if (f.isSupported(opts.version)) {
            root.innerHTML = f.getHTML(opts, conf);
            
        // express install
        } else if (opts.expressInstall && f.isSupported([6, 65])) {
            root.innerHTML = f.getHTML(extend(opts, {src: opts.expressInstall}), {
                MMredirectURL: location.href,
                MMplayerType: 'PlugIn',
                MMdoctitle: document.title
            }); 
            
        } else {
            
            // fail #2.1 custom content inside container
            if (!root.innerHTML.replace(/\s/g, '')) {
                root.innerHTML = 
                    "<h2>Flash version " + opts.version + " 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='" + URL + "'>here</a></p>");
                    
                if (root.tagName == 'A') {  
                    root.onclick = function() {
                        location.href = URL;
                    };
                }               
            }
            
            // onFail
            if (opts.onFail) {
                var ret = opts.onFail.call(this);
                if (typeof ret == 'string') { root.innerHTML = ret; }   
            }           
        }
        
        // http://flowplayer.org/forum/8/18186#post-18593
        if (IE) {
            window[opts.id] = document.getElementById(opts.id);
        } 
        
        // API methods for callback
        extend(this, {
                
            getRoot: function() {
                return root;    
            },
            
            getOptions: function() {
                return opts;    
            },

            
            getConf: function() {
                return conf;    
            }, 
            
            getApi: function() {
                return root.firstChild; 
            }
            
        }); 
    }
    
    // setup jquery support
    if (JQUERY) {
        
        // tools version number
        jQuery.tools = jQuery.tools || {version: '3.2.0'};
        
        jQuery.tools.flashembed = {  
            conf: GLOBAL_OPTS
        };  
        
        jQuery.fn.flashembed = function(opts, conf) {       
            return this.each(function() { 
                $(this).data("flashembed", flashembed(this, opts, conf));
            });
        }; 
    } 
    
})();


