/* 
 * flowplayer.js 3.2.0. The Flowplayer API
 * 
 * Copyright 2010 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-20 17:15:38 +0000 (Thu, 20 May 2010)
 * Revision: 480 
 */
(function(){function g(o){console.log("$f.fireEvent",[].slice.call(o))}function k(q){if(!q||typeof q!="object"){return q}var o=new q.constructor();for(var p in q){if(q.hasOwnProperty(p)){o[p]=k(q[p])}}return o}function m(t,q){if(!t){return}var o,p=0,r=t.length;if(r===undefined){for(o in t){if(q.call(t[o],o,t[o])===false){break}}}else{for(var s=t[0];p<r&&q.call(s,p,s)!==false;s=t[++p]){}}return t}function c(o){return document.getElementById(o)}function i(q,p,o){if(typeof p!="object"){return q}if(q&&p){m(p,function(r,s){if(!o||typeof s!="function"){q[r]=s}})}return q}function n(s){var q=s.indexOf(".");if(q!=-1){var p=s.slice(0,q)||"*";var o=s.slice(q+1,s.length);var r=[];m(document.getElementsByTagName(p),function(){if(this.className&&this.className.indexOf(o)!=-1){r.push(this)}});return r}}function f(o){o=o||window.event;if(o.preventDefault){o.stopPropagation();o.preventDefault()}else{o.returnValue=false;o.cancelBubble=true}return false}function j(q,o,p){q[o]=q[o]||[];q[o].push(p)}function e(){return"_"+(""+Math.random()).slice(2,10)}var h=function(t,r,s){var q=this,p={},u={};q.index=r;if(typeof t=="string"){t={url:t}}i(this,t,true);m(("Begin*,Start,Pause*,Resume*,Seek*,Stop*,Finish*,LastSecond,Update,BufferFull,BufferEmpty,BufferStop").split(","),function(){var v="on"+this;if(v.indexOf("*")!=-1){v=v.slice(0,v.length-1);var w="onBefore"+v.slice(2);q[w]=function(x){j(u,w,x);return q}}q[v]=function(x){j(u,v,x);return q};if(r==-1){if(q[w]){s[w]=q[w]}if(q[v]){s[v]=q[v]}}});i(this,{onCuepoint:function(x,w){if(arguments.length==1){p.embedded=[null,x];return q}if(typeof x=="number"){x=[x]}var v=e();p[v]=[x,w];if(s.isLoaded()){s._api().fp_addCuepoints(x,r,v)}return q},update:function(w){i(q,w);if(s.isLoaded()){s._api().fp_updateClip(w,r)}var v=s.getConfig();var x=(r==-1)?v.clip:v.playlist[r];i(x,w,true)},_fireEvent:function(v,y,w,A){if(v=="onLoad"){m(p,function(B,C){if(C[0]){s._api().fp_addCuepoints(C[0],r,B)}});return false}A=A||q;if(v=="onCuepoint"){var z=p[y];if(z){return z[1].call(s,A,w)}}if(y&&"onBeforeBegin,onMetaData,onStart,onUpdate,onResume".indexOf(v)!=-1){i(A,y);if(y.metaData){if(!A.duration){A.duration=y.metaData.duration}else{A.fullDuration=y.metaData.duration}}}var x=true;m(u[v],function(){x=this.call(s,A,y,w)});return x}});if(t.onCuepoint){var o=t.onCuepoint;q.onCuepoint.apply(q,typeof o=="function"?[o]:o);delete t.onCuepoint}m(t,function(v,w){if(typeof w=="function"){j(u,v,w);delete t[v]}});if(r==-1){s.onCuepoint=this.onCuepoint}};var l=function(p,r,q,t){var o=this,s={},u=false;if(t){i(s,t)}m(r,function(v,w){if(typeof w=="function"){s[v]=w;delete r[v]}});i(this,{animate:function(y,z,x){if(!y){return o}if(typeof z=="function"){x=z;z=500}if(typeof y=="string"){var w=y;y={};y[w]=z;z=500}if(x){var v=e();s[v]=x}if(z===undefined){z=500}r=q._api().fp_animate(p,y,z,v);return o},css:function(w,x){if(x!==undefined){var v={};v[w]=x;w=v}r=q._api().fp_css(p,w);i(o,r);return o},show:function(){this.display="block";q._api().fp_showPlugin(p);return o},hide:function(){this.display="none";q._api().fp_hidePlugin(p);return o},toggle:function(){this.display=q._api().fp_togglePlugin(p);return o},fadeTo:function(y,x,w){if(typeof x=="function"){w=x;x=500}if(w){var v=e();s[v]=w}this.display=q._api().fp_fadeTo(p,y,x,v);this.opacity=y;return o},fadeIn:function(w,v){return o.fadeTo(1,w,v)},fadeOut:function(w,v){return o.fadeTo(0,w,v)},getName:function(){return p},getPlayer:function(){return q},_fireEvent:function(w,v,x){if(w=="onUpdate"){var z=q._api().fp_getPlugin(p);if(!z){return}i(o,z);delete o.methods;if(!u){m(z.methods,function(){var B=""+this;o[B]=function(){var C=[].slice.call(arguments);var D=q._api().fp_invoke(p,B,C);return D==="undefined"||D===undefined?o:D}});u=true}}var A=s[w];if(A){var y=A.apply(o,v);if(w.slice(0,1)=="_"){delete s[w]}return y}return o}})};function b(q,G,t){var w=this,v=null,D=false,u,s,F=[],y={},x={},E,r,p,C,o,A;i(w,{id:function(){return E},isLoaded:function(){return(v!==null&&v.fp_play!=undefined&&!D)},getParent:function(){return q},hide:function(H){if(H){q.style.height="0px"}if(w.isLoaded()){v.style.height="0px"}return w},show:function(){q.style.height=A+"px";if(w.isLoaded()){v.style.height=o+"px"}return w},isHidden:function(){return w.isLoaded()&&parseInt(v.style.height,10)===0},load:function(J){if(!w.isLoaded()&&w._fireEvent("onBeforeLoad")!==false){var H=function(){u=q.innerHTML;if(u&&!flashembed.isSupported(G.version)){q.innerHTML=""}flashembed(q,G,{config:t});if(J){J.cached=true;j(x,"onLoad",J)}};var I=0;m(a,function(){this.unload(function(K){if(++I==a.length){H()}})})}return w},unload:function(J){if(this.isFullscreen()&&/WebKit/i.test(navigator.userAgent)){if(J){J(false)}return w}if(u.replace(/\s/g,"")!==""){if(w._fireEvent("onBeforeUnload")===false){if(J){J(false)}return w}D=true;try{if(v){v.fp_close();w._fireEvent("onUnload")}}catch(H){}var I=function(){v=null;q.innerHTML=u;D=false;if(J){J(true)}};setTimeout(I,50)}else{if(J){J(false)}}return w},getClip:function(H){if(H===undefined){H=C}return F[H]},getCommonClip:function(){return s},getPlaylist:function(){return F},getPlugin:function(H){var J=y[H];if(!J&&w.isLoaded()){var I=w._api().fp_getPlugin(H);if(I){J=new l(H,I,w);y[H]=J}}return J},getScreen:function(){return w.getPlugin("screen")},getControls:function(){return w.getPlugin("controls")._fireEvent("onUpdate")},getLogo:function(){try{return w.getPlugin("logo")._fireEvent("onUpdate")}catch(H){}},getPlay:function(){return w.getPlugin("play")._fireEvent("onUpdate")},getConfig:function(H){return H?k(t):t},getFlashParams:function(){return G},loadPlugin:function(K,J,M,L){if(typeof M=="function"){L=M;M={}}var I=L?e():"_";w._api().fp_loadPlugin(K,J,M,I);var H={};H[I]=L;var N=new l(K,null,w,H);y[K]=N;return N},getState:function(){return w.isLoaded()?v.fp_getState():-1},play:function(I,H){var J=function(){if(I!==undefined){w._api().fp_play(I,H)}else{w._api().fp_play()}};if(w.isLoaded()){J()}else{if(D){setTimeout(function(){w.play(I,H)},50)}else{w.load(function(){J()})}}return w},getVersion:function(){var I="flowplayer.js 3.2.0";if(w.isLoaded()){var H=v.fp_getVersion();H.push(I);return H}return I},_api:function(){if(!w.isLoaded()){throw"Flowplayer "+w.id()+" not loaded when calling an API method"}return v},setClip:function(H){w.setPlaylist([H]);return w},getIndex:function(){return p}});m(("Click*,Load*,Unload*,Keypress*,Volume*,Mute*,Unmute*,PlaylistReplace,ClipAdd,Fullscreen*,FullscreenExit,Error,MouseOver,MouseOut").split(","),function(){var H="on"+this;if(H.indexOf("*")!=-1){H=H.slice(0,H.length-1);var I="onBefore"+H.slice(2);w[I]=function(J){j(x,I,J);return w}}w[H]=function(J){j(x,H,J);return w}});m(("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 H=this;w[H]=function(J,I){if(!w.isLoaded()){return w}var K=null;if(J!==undefined&&I!==undefined){K=v["fp_"+H](J,I)}else{K=(J===undefined)?v["fp_"+H]():v["fp_"+H](J)}return K==="undefined"||K===undefined?w:K}});w._fireEvent=function(Q){if(typeof Q=="string"){Q=[Q]}var R=Q[0],O=Q[1],M=Q[2],L=Q[3],K=0;if(t.debug){g(Q)}if(!w.isLoaded()&&R=="onLoad"&&O=="player"){v=v||c(r);o=v.clientHeight;m(F,function(){this._fireEvent("onLoad")});m(y,function(S,T){T._fireEvent("onUpdate")});s._fireEvent("onLoad")}if(R=="onLoad"&&O!="player"){return}if(R=="onError"){if(typeof O=="string"||(typeof O=="number"&&typeof M=="number")){O=M;M=L}}if(R=="onContextMenu"){m(t.contextMenu[O],function(S,T){T.call(w)});return}if(R=="onPluginEvent"||R=="onBeforePluginEvent"){var H=O.name||O;var I=y[H];if(I){I._fireEvent("onUpdate",O);return I._fireEvent(M,Q.slice(3))}return}if(R=="onPlaylistReplace"){F=[];var N=0;m(O,function(){F.push(new h(this,N++,w))})}if(R=="onClipAdd"){if(O.isInStream){return}O=new h(O,M,w);F.splice(M,0,O);for(K=M+1;K<F.length;K++){F[K].index++}}var P=true;if(typeof O=="number"&&O<F.length){C=O;var J=F[O];if(J){P=J._fireEvent(R,M,L)}if(!J||P!==false){P=s._fireEvent(R,M,L,J)}}m(x[R],function(){P=this.call(w,O,M);if(this.cached){x[R].splice(K,1)}if(P===false){return false}K++});return P};function B(){if($f(q)){$f(q).getParent().innerHTML="";p=$f(q).getIndex();a[p]=w}else{a.push(w);p=a.length-1}A=parseInt(q.style.height,10)||q.clientHeight;E=q.id||"fp"+e();r=G.id||E+"_api";G.id=r;t.playerId=E;if(typeof t=="string"){t={clip:{url:t}}}if(typeof t.clip=="string"){t.clip={url:t.clip}}t.clip=t.clip||{};if(q.getAttribute("href",2)&&!t.clip.url){t.clip.url=q.getAttribute("href",2)}s=new h(t.clip,-1,w);t.playlist=t.playlist||[t.clip];var H=0;m(t.playlist,function(){var J=this;if(typeof J=="object"&&J.length){J={url:""+J}}m(t.clip,function(K,L){if(L!==undefined&&J[K]===undefined&&typeof L!="function"){J[K]=L}});t.playlist[H]=J;J=new h(J,H,w);F.push(J);H++});m(t,function(J,K){if(typeof K=="function"){if(s[J]){s[J](K)}else{j(x,J,K)}delete t[J]}});m(t.plugins,function(J,K){if(K){y[J]=new l(J,K,w)}});if(!t.plugins||t.plugins.controls===undefined){y.controls=new l("controls",null,w)}y.canvas=new l("canvas",null,w);function I(J){if(!w.isLoaded()&&w._fireEvent("onBeforeClick")!==false){w.load()}return f(J)}u=q.innerHTML;if(u.replace(/\s/g,"")!==""){if(q.addEventListener){q.addEventListener("click",I,false)}else{if(q.attachEvent){q.attachEvent("onclick",I)}}}else{if(q.addEventListener){q.addEventListener("click",f,false)}w.load()}}if(typeof q=="string"){var z=c(q);if(!z){throw"Flowplayer cannot access element: "+q}else{q=z;B()}}else{B()}}var a=[];function d(o){this.length=o.length;this.each=function(p){m(o,p)};this.size=function(){return o.length}}window.flowplayer=window.$f=function(){var p=null;var o=arguments[0];if(!arguments.length){m(a,function(){if(this.isLoaded()){p=this;return false}});return p||a[0]}if(arguments.length==1){if(typeof o=="number"){return a[o]}else{if(o=="*"){return new d(a)}m(a,function(){if(this.id()==o.id||this.id()==o||this.getParent()==o){p=this;return false}});return p}}if(arguments.length>1){var t=arguments[1],q=(arguments.length==3)?arguments[2]:{};if(typeof t=="string"){t={src:t}}t=i({bgcolor:"#000000",version:[9,0],expressInstall:"http://static.flowplayer.org/swf/expressinstall.swf",cachebusting:true},t);if(typeof o=="string"){if(o.indexOf(".")!=-1){var s=[];m(n(o),function(){s.push(new b(this,k(t),k(q)))});return new d(s)}else{var r=c(o);return new b(r!==null?r:o,t,q)}}else{if(o){return new b(o,t,q)}}}return null};i(window.$f,{fireEvent:function(){var o=[].slice.call(arguments);var q=$f(o[0]);return q?q._fireEvent(o.slice(1)):null},addPlugin:function(o,p){b.prototype[o]=p;return $f},each:m,extend:i});if(typeof jQuery=="function"){jQuery.fn.flowplayer=function(q,p){if(!arguments.length||typeof arguments[0]=="number"){var o=[];this.each(function(){var r=$f(this);if(r){o.push(r)}});return arguments.length?o[arguments[0]]:new d(o)}return this.each(function(){$f(this,k(q),p?k(p):{})})}}})();(function(){var h=document.all,j="http://www.adobe.com/go/getflashplayer",c=typeof jQuery=="function",e=/(\d+)[^\d]+(\d+)[^\d]*(\d*)/,b={width:"100%",height:"100%",id:"_"+(""+Math.random()).slice(9),allowfullscreen:true,allowscriptaccess:"always",quality:"high",version:[3,0],onFail:null,expressInstall:null,w3c:false,cachebusting:false};if(window.attachEvent){window.attachEvent("onbeforeunload",function(){__flash_unloadHandler=function(){};__flash_savedUnloadHandler=function(){}})}function i(l,f){if(f){for(key in f){if(f.hasOwnProperty(key)){l[key]=f[key]}}}return l}function a(f,n){var m=[];for(var l in f){if(f.hasOwnProperty(l)){m[l]=n(f[l])}}return m}window.flashembed=function(f,m,l){if(typeof f=="string"){f=document.getElementById(f.replace("#",""))}if(!f){return}if(typeof m=="string"){m={src:m}}return new d(f,i(i({},b),m),l)};var g=i(window.flashembed,{conf:b,getVersion:function(){var f;try{f=navigator.plugins["Shockwave Flash"].description.slice(16)}catch(n){try{var l=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");f=l&&l.GetVariable("$version")}catch(m){}}f=e.exec(f);return f?[f[1],f[3]]:[0,0]},asString:function(l){if(l===null||l===undefined){return null}var f=typeof l;if(f=="object"&&l.push){f="array"}switch(f){case"string":l=l.replace(new RegExp('(["\\\\])',"g"),"\\$1");l=l.replace(/^\s?(\d+\.?\d+)%/,"$1pct");return'"'+l+'"';case"array":return"["+a(l,function(o){return g.asString(o)}).join(",")+"]";case"function":return'"function()"';case"object":var m=[];for(var n in l){if(l.hasOwnProperty(n)){m.push('"'+n+'":'+g.asString(l[n]))}}return"{"+m.join(",")+"}"}return String(l).replace(/\s/g," ").replace(/\'/g,'"')},getHTML:function(o,l){o=i({},o);var n='<object width="'+o.width+'" height="'+o.height+'" id="'+o.id+'" name="'+o.id+'"';if(o.cachebusting){o.src+=((o.src.indexOf("?")!=-1?"&":"?")+Math.random())}if(o.w3c||!h){n+=' data="'+o.src+'" type="application/x-shockwave-flash"'}else{n+=' classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'}n+=">";if(o.w3c||h){n+='<param name="movie" value="'+o.src+'" />'}o.width=o.height=o.id=o.w3c=o.src=null;o.onFail=o.version=o.expressInstall=null;for(var m in o){if(o[m]){n+='<param name="'+m+'" value="'+o[m]+'" />'}}var p="";if(l){for(var f in l){if(l[f]){var q=l[f];p+=f+"="+(/function|object/.test(typeof q)?g.asString(q):q)+"&"}}p=p.slice(0,-1);n+='<param name="flashvars" value=\''+p+"' />"}n+="</object>";return n},isSupported:function(f){return k[0]>f[0]||k[0]==f[0]&&k[1]>=f[1]}});var k=g.getVersion();function d(f,n,m){if(g.isSupported(n.version)){f.innerHTML=g.getHTML(n,m)}else{if(n.expressInstall&&g.isSupported([6,65])){f.innerHTML=g.getHTML(i(n,{src:n.expressInstall}),{MMredirectURL:location.href,MMplayerType:"PlugIn",MMdoctitle:document.title})}else{if(!f.innerHTML.replace(/\s/g,"")){f.innerHTML="<h2>Flash version "+n.version+" or greater is required</h2><h3>"+(k[0]>0?"Your version is "+k:"You have no flash plugin installed")+"</h3>"+(f.tagName=="A"?"<p>Click here to download latest version</p>":"<p>Download latest version from <a href='"+j+"'>here</a></p>");if(f.tagName=="A"){f.onclick=function(){location.href=j}}}if(n.onFail){var l=n.onFail.call(this);if(typeof l=="string"){f.innerHTML=l}}}}if(h){window[n.id]=document.getElementById(n.id)}i(this,{getRoot:function(){return f},getOptions:function(){return n},getConf:function(){return m},getApi:function(){return f.firstChild}})}if(c){jQuery.tools=jQuery.tools||{version:"3.2.0"};jQuery.tools.flashembed={conf:b};jQuery.fn.flashembed=function(l,f){return this.each(function(){$(this).data("flashembed",flashembed(this,l,f))})}}})();
$f.addPlugin("ipad", function(options) {
	var STATE_UNLOADED = -1;
	var STATE_LOADED    = 0;
	var STATE_UNSTARTED = 1;
	var STATE_BUFFERING = 2;
	var STATE_PLAYING   = 3;
	var STATE_PAUSED    = 4;
	var STATE_ENDED     = 5;

	var self = this;

	var currentVolume = 1;
	var onStartFired = false;
	var stopping = false;
	var playAfterSeek = false;

	var activeIndex = 0;
	var activePlaylist = [];
	var clipDefaults = {
		accelerated: 	false,		// unused
		autoBuffering: 	false,
		autoPlay: 		true,
		baseUrl: 		null,
		bufferLength: 	3,			// unused
		connectionProvider: null,	// unused
		cuepointMultiplier: 1000,
		cuepoints: [],
		controls: {},				// unused
		duration: 0,				// not yet implemented
		extension: '',
		fadeInSpeed: 1000,			// not yet implemented
		fadeOutSpeed: 1000,			// not yet implemented
		image: false,				// unused
		linkUrl: null,				// not yet implemented
		linkWindow: '_self',		// not yet implemented
		live: false,				// unused
		metaData: {},
		originalUrl: null,
		position: 0,				// unused
		playlist: [],				// unused
		provider: 'http',
		scaling: 'scale',			// not yet implemented
		seekableOnBegin: false,		// unused
		start: 0,					// not yet implemented
		url: null,
		urlResolvers: []			// unused
	};

	var currentState = STATE_UNLOADED;
	var previousState= STATE_UNLOADED;

	var isiDevice = /iPad|iPhone|iPod/i.test(navigator.userAgent);

	var video = null;

	function extend(to, from, includeFuncs) {
		if (from) {
			for (key in from) {
				if (key) {
					if ( from[key] && typeof from[key] == "function" && ! includeFuncs )
						continue;
					if ( from[key] && typeof from[key] == "object" && from[key].length == undefined) {
						var cp = {};
						extend(cp, from[key]);
						to[key] = cp;
					} else {
						to[key] = from[key];
					}
				}
			}
		}
		return to;
	}

	var opts = {
		simulateiDevice: false,
		controlsSizeRatio: 1.5,
		controls: true,
		debug: false,
		validExtensions: /mov|m4v|mp4|avi/gi
	};

	extend(opts, options);

	// some util funcs
	function log() {
		if ( opts.debug ) {
			if ( isiDevice ) {
				var str = [].splice.call(arguments,0).join(', ');
				console.log.apply(console, [str]);
			} else {
				console.log.apply(console, arguments);
			}
		}

	}

	function stateDescription(state) {
		switch(state) {
			case -1: return "UNLOADED";
			case  0: return "LOADED";
			case  1: return "UNSTARTED";
			case  2: return "BUFFERING";
			case  3: return "PLAYING";
			case  4: return "PAUSED";
			case  5: return "ENDED";
		}
		return "UNKOWN";
	}

	function actionAllowed(eventName) {
		var ret = $f.fireEvent(self.id(), "onBefore"+eventName, activeIndex);
		return ret !== false;
	}

	function stopEvent(e) {
		e.stopPropagation();
		e.preventDefault();
		return false;
	}

	function setState(state, force) {
		if ( currentState == STATE_UNLOADED && ! force )
			return;

		previousState = currentState;
		currentState = state;

		stopPlayTimeTracker();
		if ( state == STATE_PLAYING )
			startPlayTimeTracker();

		log(stateDescription(state));
	}

	function resetState() {
		video.fp_stop();

		onStartFired = false;
		stopping 	 = false;
		playAfterSeek= false;
		// call twice so previous state is unstarted too
		setState(STATE_UNSTARTED);
		setState(STATE_UNSTARTED);
	}

	/* PLAY TIME TRACKING */
	var _playTimeTracker = null;

	function startPlayTimeTracker() {
		if ( _playTimeTracker )
			return;

		console.log("starting tracker");
		_playTimeTracker = setInterval(onTimeTracked, 100);
		onTimeTracked();
	}

	function stopPlayTimeTracker() {
		clearInterval(_playTimeTracker);
		_playTimeTracker = null;
	}

	function onTimeTracked() {
		// cue points handling
		var currentTime = Math.floor(video.fp_getTime() * 10) * 100;
		var duration    = Math.floor(video.duration * 10) * 100;
		var fireTime	= (new Date()).time;
		// find nearest cuepoint and fire it. Should be moved to avoid closure compilation each time
		function fireCuePointsIfNeeded(time, cues) {
			time = time >= 0 ? time : duration - Math.abs(time);
			for ( var i = 0; i < cues.length; i++ ) {
				// if cue point fired in the future, reset it
				if ( cues[i].lastTimeFired > fireTime ) {
					cues[i].lastTimeFired = -1;
				} else if ( cues[i].lastTimeFired + 500 > fireTime ) {	// cuepoint was fired less that 500 ms ago, don't do anything
					continue;
				} else {
					if ( time == currentTime || // we got the right tick
						(currentTime - 500 < time && currentTime > time) ) {	// we missed one
						cues[i].lastTimeFired = fireTime;
						$f.fireEvent(self.id(), 'onCuepoint', activeIndex, cues[i].fnId, cues[i].parameters);
					}
				}
			}
		}
		$f.each(self.getCommonClip()._cuepoints	, 		fireCuePointsIfNeeded);
		$f.each(activePlaylist[activeIndex]._cuepoints, fireCuePointsIfNeeded);
	}

	function replay() {
		resetState();
		playAfterSeek = true;
		video.fp_seek(0);
	}

	function scaleVideo(clip) {

	}

	// internal func, maps flowplayer's API
	function addAPI() {


		function fixClip(clip) {
			var extendedClip = {};
			extend(extendedClip, clipDefaults);
			extend(extendedClip, self.getCommonClip());
			extend(extendedClip, clip);

			if ( extendedClip.ipadUrl )
                url = decodeURIComponent(extendedClip.ipadUrl);
			else if ( extendedClip.url )
				url = extendedClip.url;

			if ( url && url.indexOf('://') == -1 && extendedClip.baseUrl )
				url = extendedClip.baseUrl + '/' + url;

			extendedClip.originalUrl = extendedClip.url;
			extendedClip.completeUrl = url;
			extendedClip.extension = extendedClip.completeUrl.substr(extendedClip.completeUrl.lastIndexOf('.'));
			extendedClip.type = 'video';

			// remove this
			delete extendedClip.index;
			log("fixed clip", extendedClip);

			return extendedClip;
		}

		video.fp_play = function(clip, inStream, /* private one, handy for playlists */ forcePlay) {
			var url = null;
			var autoBuffering 	 = true;
			var autoPlay 		 = true;

			log("Calling play() " + clip, clip);

			if ( inStream ) {
				log("ERROR: inStream clips not yet supported");
				return;
			}

			// we got a param :
			// array, index, clip obj, url
			if ( clip !== undefined ) {

				// simply change the index
				if ( typeof clip == "number" ) {
					if ( activeIndex >= activePlaylist.length )
						return;

					activeIndex = clip;
					clip = activePlaylist[activeIndex];
				} else {
					// String
					if ( typeof clip == "string" ) {
						clip = {
							url: clip
						};
					}

					// replace playlist
					video.fp_setPlaylist(clip.length !== undefined ? clip : [clip]);
				}


				if ( ! opts.validExtensions.test(activePlaylist[activeIndex].extension)) {
					if ( activePlaylist.length > 1 && activeIndex < (activePlaylist.length - 1) ) {
						// not the last clip in the playlist
						log("Not last clip in the playlist, moving to next one");
						video.fp_play(++activeIndex, false, true);
					}
					return;
				}

				clip = activePlaylist[activeIndex];
				url = clip.completeUrl;

				if ( clip.autoBuffering !== undefined && clip.autoBuffering === false )
					autoBuffering = false;

				if ( clip.autoPlay === undefined || clip.autoPlay === true || forcePlay === true ) {
					autoBuffering = true;
					autoPlay = true;
				} else {
					autoPlay = false;
				}
			} else {
				log("clip was not given, simply calling video.play, if not already buffering");

				// clip was not given, simply calling play
				if ( currentState != STATE_BUFFERING )
					video.play();

				return;
			}

			log("about to play "+ url, autoBuffering, autoPlay);

			// we have a new clip to play
			resetState();

			if ( url ) {
				log("Changing SRC attribute"+ url);
				video.setAttribute('src', url);
			}


			//return;

			// autoBuffering is true or we just called play
			if ( autoBuffering ) {
				if ( ! actionAllowed('Begin') )
					return false;

				$f.fireEvent(self.id(), 'onBegin', activeIndex);

				log("calling video.load()");
				video.load();
			}

			// auto
			if ( autoPlay ) {
				log("calling video.play()");
				video.play();
			}
		}

		video.fp_pause = function() {
			log("pause called");

			if ( ! actionAllowed('Pause') )
				return false;

			video.pause();
		};

		video.fp_resume = function() {
			log("resume called");

			if ( ! actionAllowed('Resume') )
				return false;

			video.play();
		};

		video.fp_stop = function() {
			log("stop called");

			if ( ! actionAllowed('Stop') )
				return false;

			stopping = true;
			video.pause();
			try {
				video.currentTime = 0;
			} catch(ignored) {}
		};

		video.fp_seek = function(position) {
			log("seek called "+ position);

			if ( ! actionAllowed('Seek') )
				return false;

			var seconds = 0;
			var position = position + "";
			if ( position.charAt(position.length-1) == '%' ) {
				var percentage = parseInt(position.substr(0, position.length-1)) / 100;
				var duration = video.duration;

				seconds = duration * percentage;
			} else {
				seconds = position;
			}

			try {
				video.currentTime = seconds;
			} catch(e) {
				log("Wrong seek time");
			}
		};

		video.fp_getTime = function() {
		//  log("getTime called");
			return video.currentTime;
		};

		video.fp_mute = function() {
			log("mute called");

			if ( ! actionAllowed('Mute') )
				return false;

			currentVolume = video.volume;
			video.volume = 0;
		};

		video.fp_unmute = function() {
			if ( ! actionAllowed('Unmute') )
				return false;

			video.volume = currentVolume;
		};

		video.fp_getVolume = function() {
			return video.volume * 100;
		};

		video.fp_setVolume = function(volume) {
			if ( ! actionAllowed('Volume') )
				return false;

			video.volume = volume / 100;
		};

		video.fp_toggle = function() {
			log('toggle called');
			if ( self.getState() == STATE_ENDED ) {
				replay();
				return;
			}

			if ( video.paused )
				video.fp_play();
			else
				video.fp_pause();
		};

		video.fp_isPaused = function() {
			return video.paused;
		};

		video.fp_isPlaying = function() {
			return ! video.paused;
		};

		video.fp_getPlugin = function(name) {
			if ( name == 'canvas' || name == 'controls' ) {
				var config = self.getConfig();
				//log("looking for config for "+ name, config);

				return config['plugins'] && config['plugins'][name] ? config['plugins'][name] : null;
			}
			log("ERROR: no support for "+ name +" plugin on iDevices");
			return null;
		};
		/*
		video.fp_css = function(name, css) {
			if ( self.plugins[name] && self.plugins[name]._api &&
				 self.plugins[name]['_api'] && self.plugins[name]['_api']['css'] &&
				 self.plugins[name]['_api']['css'] instanceof Function )
				return self.plugins[name]['_api']['css']();

			return self;
		}*/

		video.fp_close = function() {
			setState(STATE_UNLOADED);

			video.parentNode.removeChild(video);
			video = null;
		};

		video.fp_getStatus = function() {
			var bufferStart = 0;
			var bufferEnd   = 0;

			try {
				bufferStart = video.buffered.start();
				bufferEnd   = video.buffered.end();
			} catch(ignored) {}

			return {
				bufferStart: bufferStart,
				bufferEnd:  bufferEnd,
				state: currentState,
				time: video.fp_getTime(),
				muted: video.muted,
				volume: video.fp_getVolume()
			};
		};

		video.fp_getState = function() {
			return currentState;
		};

		video.fp_startBuffering = function() {
			if ( currentState == STATE_UNSTARTED )
				video.load();
		};

		video.fp_setPlaylist = function(playlist) {
			log("Setting playlist");
			activeIndex = 0;
			for ( var i = 0; i < playlist.length; i++ )
				playlist[i] = fixClip(playlist[i]);

			activePlaylist = playlist;

			// keep flowplayer.js in sync
			$f.fireEvent(self.id(), 'onPlaylistReplace', playlist);
		};

		video.fp_addClip = function(clip, index) {
			clip = fixClip(clip);
			activePlaylist.splice(index, 0, clip);

			// keep flowplayer.js in sync
			$f.fireEvent(self.id(), 'onClipAdd', clip, index);
		};

		video.fp_updateClip = function(clip, index) {
			extend(activePlaylist[index], clip);
			return activePlaylist[index];
		};

		video.fp_getVersion = function() {
			return '3.2.3';
		}

		video.fp_isFullscreen = function() {
			return false; //video.webkitDisplayingFullscreen;
		}

		video.fp_toggleFullscreen = function() {
			if ( video.fp_isFullscreen() )
				video.webkitExitFullscreen();
			else
				video.webkitEnterFullscreen();
		}

		video.fp_addCuepoints = function(points, index, fnId) {
			var clip = index == -1 ? self.getCommonClip() : activePlaylist[index];
			clip._cuepoints = clip._cuepoints || {};
			points = points instanceof Array ? points : [points];
			for ( var i = 0; i < points.length; i++ ) {
				var time = typeof points[i] == "object" ? (points[i]['time'] || null) : points[i];
				if ( time == null ) continue;

				time = Math.floor(time / 100) * 100;

				var parameters = time;
				if ( typeof points[i] == "object" ) {
					parameters = extend({}, points[i], false);
					if ( parameters['time'] != undefined ) delete parameters['time'];
					if ( parameters['parameters'] != undefined ) {
						extend(parameters, parameters['parameters'], false);
						delete parameters['parameters'];
					}
				}

				clip._cuepoints[time] = clip._cuepoints[time] || [];
				clip._cuepoints[time].push({fnId: fnId, lastTimeFired: -1, parameters: parameters});
			}
		}

		// install all other core API with dummy function
		// core API methods
		$f.each(("toggleFullscreen,stopBuffering,reset,playFeed,setKeyboardShortcutsEnabled,isKeyboardShortcutsEnabled,css,animate,showPlugin,hidePlugin,togglePlugin,fadeTo,invoke,loadPlugin").split(","),
			function() {
				var name = this;

				video["fp_"+name] = function() {
				log("ERROR: unsupported API on iDevices "+ name);
					return false;
				};
			}
		);
	}

	// Internal func, maps Flowplayer's events
	function addListeners() {

		/* CLIP EVENTS MAPPING */

		var events = [	'abort',
						'canplay',
						'canplaythrough',
						'durationchange',
						'emptied',
						'ended',
						'error',
						'loadeddata',
						'loadedmetadata',
						'loadstart',
						'pause',
						'play',
						'playing',
						'progress',
						'ratechange',
						'seeked',
						'seeking',
						'stalled',
						'suspend',
					//	'timeupdate',
						'volumechange',
						'waiting'];
		var eventsLogger = function(e) {
			log("Got event "+ e.type, e);
		}

		for ( var i = 0; i < events.length; i++ )
			video.addEventListener(events[i], eventsLogger, false);



		var onBufferEmpty = function(e) {
			log("got onBufferEmpty event "+e.type)
			setState(STATE_BUFFERING);
			$f.fireEvent(self.id(), 'onBufferEmpty', activeIndex);
		};
		video.addEventListener('emptied', onBufferEmpty, false);
		video.addEventListener('waiting', onBufferEmpty, false);

		var onBufferFull = function(e) {
			if ( previousState == STATE_UNSTARTED || previousState == STATE_BUFFERING )	{
				// wait for play event, nothing to do

			} else {
				log("Restoring old state "+ stateDescription(previousState));
				setState(previousState);
			}
			$f.fireEvent(self.id(), 'onBufferFull', activeIndex);
		};
		video.addEventListener('canplay', onBufferFull, false);
		video.addEventListener('canplaythrough', onBufferFull, false);

		var onMetaData = function(e) {
			// update clip
			video.fp_updateClip({duration: video.duration, metaData: {duration: video.duration}}, activeIndex);
			activePlaylist[activeIndex].duration = video.duration;

			$f.fireEvent(self.id(), 'onMetaData', activeIndex, activePlaylist[activeIndex]);
		};
		video.addEventListener('loadedmetadata', onMetaData, false);
		video.addEventListener('durationchange', onMetaData, false);

		var onStart = function(e) {
			if ( currentState == STATE_PAUSED ) {
				if ( ! actionAllowed('Resume') ) {
					// user initiated resume
					log("Resume disallowed, pausing");
					video.fp_pause();
					return stopEvent(e);
				}

				$f.fireEvent(self.id(), 'onResume', activeIndex);
			}

			setState(STATE_PLAYING);

			if ( ! onStartFired ) {
				onStartFired = true;
				$f.fireEvent(self.id(), 'onStart', activeIndex);
			}
		};
		video.addEventListener('playing', onStart, false);

		var onFinish = function(e) {
			if ( ! actionAllowed('Finish') ) {
				if ( activePlaylist.length == 1 ) {
					//In the case of a single clip, the player will start from the beginning of the clip.
					log("Active playlist only has one clip, onBeforeFinish returned false. Replaying");
					replay();
				} else if ( activeIndex != (activePlaylist.length -1) ) {
					// In the case of an ordinary clip in a playlist, the "Play again" button will appear.
					// oops, we don't have any play again button yet :)
					// simply go to the beginning of the video
					log("Not the last clip in the playlist, but onBeforeFinish returned false. Returning to the beginning of current clip");
					video.fp_seek(0);
				} else {
					//In the case of the final clip in a playlist, the player will start from the beginning of the playlist.
					log("Last clip in playlist, but onBeforeFinish returned false, start again from the beginning");
					video.fp_play(0);
				}

				return stopEvent(e);
			}	// action was canceled

			setState(STATE_ENDED);
			$f.fireEvent(self.id(), 'onFinish', activeIndex);

			if ( activePlaylist.length > 1 && activeIndex < (activePlaylist.length - 1) ) {
				// not the last clip in the playlist
				log("Not last clip in the playlist, moving to next one");
				video.fp_play(++activeIndex, false, true);
			}

		};
		video.addEventListener('ended', onFinish, false);

		var onError = function(e) {
			setState(STATE_LOADED, true);
			$f.fireEvent(self.id(), 'onError', activeIndex, 201);
			if ( opts.onFail && opts.onFail instanceof Function )
				opts.onFail.apply(self, []);
		};
		video.addEventListener('error', onError, false);

		var onPause = function(e) {
			log("got pause event from player" + self.id());
			if ( stopping )
				return;

			if ( currentState == STATE_BUFFERING && previousState == STATE_UNSTARTED ) {
				log("forcing play");
				setTimeout(function() { video.play(); }, 0);
				return;// stopEvent(e);
			}

			if ( ! actionAllowed('Pause') ) {
				// user initiated pause
				video.fp_resume();
				return stopEvent(e);
			}

			setState(STATE_PAUSED);
			$f.fireEvent(self.id(), 'onPause', activeIndex);
		}
		video.addEventListener('pause', onPause, false);

		var onSeek = function(e) {
			$f.fireEvent(self.id(), 'onBeforeSeek', activeIndex);
		};
		video.addEventListener('seeking', onSeek, false);

		var onSeekDone = function(e) {
			if ( stopping ) {
				stopping = false;
				$f.fireEvent(self.id(), 'onStop', activeIndex);
			}
			else
				$f.fireEvent(self.id(), 'onSeek', activeIndex);

			log("seek done, currentState", stateDescription(currentState));

			if ( playAfterSeek ) {
				playAfterSeek = false;
				video.fp_play();
			} else if ( currentState != STATE_PLAYING )
				video.fp_pause();
		};
		video.addEventListener('seeked', onSeekDone, false);

		/* PLAYER EVENTS MAPPING */

		var onVolumeChange = function(e) {
			// add onBeforeQwe here
			$f.fireEvent(self.id(), 'onVolume', video.fp_getVolume());
		};
		video.addEventListener('volumechange', onVolumeChange, false);


	}

	// this is called only on iDevices
	function onPlayerLoaded() {
		video.fp_play(0);
		//installControlbar();
	}


	function installControlbar() {
		// if we're on an iDevice, try to load the js controlbar if needed
		/*
		if ( self['controls'] == undefined )
			return;	// js controlbar not loaded

		var controlsConf = {};
		if ( self.getConfig() && self.getConfig()['plugins'] && self.getConfig()['plugins']['controls'] )
			controlsConf = self.getConfig()['plugins']['controls'];

		var controlsRoot = document.createElement('div');

		// dynamically load js, css file according to swf url ?

		// something more smart here

		controlsRoot.style.position = "absolute";
		controlsRoot.style.bottom = 0;
		self.getParent().children[0].appendChild(controlsRoot);

		self.controls(controlsRoot, {heightRatio: opts.controlsSizeRatio  }, controlsConf);
		*/
	}




	// Here we are getting serious. If we're on an iDevice, we don't care about Flash embed.
	// replace it by ours so we can install a video html5 tag instead when FP's init will be called.
	if ( isiDevice || opts.simulateiDevice ) {

		if ( ! window.flashembed.__replaced ) {

			var realFlashembed = window.flashembed;
			window.flashembed = function(root, opts, conf) {
				// DON'T, I mean, DON'T use self here as we are in a global func

				if (typeof root == 'string') {
					root = document.getElementById(root.replace("#", ""));
				}

				// not found
				if (!root) { return; }

				var style = window.getComputedStyle(root, null);
				var width = parseInt(style.width);
				var height= parseInt(style.height);

				// clearing root
				while(root.firstChild)
					root.removeChild(root.firstChild);

				var container = document.createElement('div');
				var api = document.createElement('video');
				container.appendChild(api);
				root.appendChild(container);

				//var hasBuiltinControls = conf.config['plugins'] == undefined || (conf.config['plugins'] && conf.config['plugins']['controls'] && conf.config['plugins']['controls'] != null
				//						&& self['controls'] == undefined);	// we make a careful use of "self", as we're looking in the prototype

				// styling  container
				container.style.height = height+'px';
				container.style.width  = width+'px';
				container.style.display= 'block';
				container.style.position = 'relative';
				container.style.background = '-webkit-gradient(linear, left top, left bottom, from(rgba(0, 0, 0, 0.5)), to(rgba(0, 0, 0, 0.7)))';
				container.style.cursor = 'default';
				container.style.webkitUserDrag = 'none';

				// styling video tag
				api.style.height = '100%';
				api.style.width  = '100%';
				api.style.display= 'block';
				api.id = opts.id;
				api.name = opts.id;
				api.style.cursor = 'pointer';
				api.style.webkitUserDrag = 'none';

				api.type="video/mp4";
			//	if ( hasBuiltinControls )
			//		api.controls="controls";

				api.playerConfig = conf.config;

				// tell the player we are ready and go back to player's closure
				$f.fireEvent(conf.config.playerId, 'onLoad', 'player');

				//api.fp_play(conf.config.playlist);
			};

			flashembed.getVersion = realFlashembed.getVersion;
			flashembed.asString = realFlashembed.asString;
			flashembed.isSupported = function() {return true;}
			flashembed.__replaced = true;
		}


		// hack so we get the onload event before everybody and we can set the api
		var __fireEvent = self._fireEvent;
		// only on iDevice, of course

		self._fireEvent = function(a) {
			if ( a[0] == 'onLoad' && a[1] == 'player' ) {
				video = self.getParent().querySelector('video');

				if ( opts.controls )
					video.controls="controls";

				addAPI();
				addListeners();

				setState(STATE_LOADED, true);

				// set up first clip
				video.fp_setPlaylist(video.playerConfig.playlist);

				// we are loaded
				onPlayerLoaded();

				__fireEvent.apply(self, [a]);
			}


			var shouldFireEvent = currentState != STATE_UNLOADED;
			if ( currentState == STATE_UNLOADED && typeof a == 'string' )
				shouldFireEvent = true;

			if ( shouldFireEvent )
				return __fireEvent.apply(self, [a]);
		}

		// please, don't ask me why, but if you call video.clientHeight while the video is buffering
		// it will be stuck buffering
		self._swfHeight = function() {
			return parseInt(video.style.height);
		}

		self.hasiPadSupport = function() {
			return true;
		}
	} // end of iDevice test


	// some chaining
	return self;
});

