/** 
/* 
***********************************************************************
*********** THIS FILE IS MODIFIED FROM THE ORIGINAL VERSION ***********
***********************************************************************
*
* 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,
		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 && !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) {

        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(("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) {
		    if (!self.isLoaded()) { return self; }
		    var ret = null;

		    // two arguments
		    if (a1 !== undefined && a2 !== undefined) {
		      ret = api["fp_" + name](a1, a2);

		    } else {
		      if (api["fp_" + name] != undefined) {
		        try {
		          ret = (a1 === undefined) ? api["fp_" + name]() : api["fp_" + name](a1);
		        } catch (error) {
              // API error
		        }
		      }
		    }

		    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 = ['0', '0', '0', '0'];
        }
      }

      ver = RE.exec(ver);
      return [ver[1], ver[3]];
    },

    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));
      });
    };
  }

})();


