Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

add flowplayer js api script, unminified

  • Loading branch information...
commit 1e2ed98505bc1955bd932d92f5955377781ec059 1 parent 7c33f82
Austin Matzko authored April 17, 2011

Showing 1 changed file with 1,571 additions and 0 deletions. Show diff stats Hide diff stats

  1. 1,571  flowplayer-3.2.6.js
1,571  flowplayer-3.2.6.js
... ...
@@ -0,0 +1,1571 @@
  1
+/** 
  2
+ * flowplayer.js 3.2.6. The Flowplayer API
  3
+ * 
  4
+ * Copyright 2009 Flowplayer Oy
  5
+ * 
  6
+ * This file is part of Flowplayer.
  7
+ * 
  8
+ * Flowplayer is free software: you can redistribute it and/or modify
  9
+ * it under the terms of the GNU General Public License as published by
  10
+ * the Free Software Foundation, either version 3 of the License, or
  11
+ * (at your option) any later version.
  12
+ * 
  13
+ * Flowplayer is distributed in the hope that it will be useful,
  14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16
+ * GNU General Public License for more details.
  17
+ * 
  18
+ * You should have received a copy of the GNU General Public License
  19
+ * along with Flowplayer.  If not, see <http://www.gnu.org/licenses/>.
  20
+ * 
  21
+ * Date: 2010-08-25 12:48:46 +0000 (Wed, 25 Aug 2010)
  22
+ * Revision: 575 
  23
+ */
  24
+(function() {
  25
+ 
  26
+/* 
  27
+	FEATURES 
  28
+	--------
  29
+	- $f() and flowplayer() functions	
  30
+	- handling multiple instances 
  31
+	- Flowplayer programming API 
  32
+	- Flowplayer event model	
  33
+	- player loading / unloading	
  34
+	- jQuery support
  35
+*/ 
  36
+ 
  37
+
  38
+/*jslint glovar: true, browser: true */
  39
+/*global flowplayer, $f */
  40
+
  41
+// {{{ private utility methods
  42
+	
  43
+	function log(args) {
  44
+		console.log("$f.fireEvent", [].slice.call(args));	
  45
+	}
  46
+
  47
+		
  48
+	// thanks: http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone
  49
+	function clone(obj) {	
  50
+		if (!obj || typeof obj != 'object') { return obj; }		
  51
+		var temp = new obj.constructor();	
  52
+		for (var key in obj) {	
  53
+			if (obj.hasOwnProperty(key)) {
  54
+				temp[key] = clone(obj[key]);
  55
+			}
  56
+		}		
  57
+		return temp;
  58
+	}
  59
+
  60
+	// stripped from jQuery, thanks John Resig 
  61
+	function each(obj, fn) {
  62
+		if (!obj) { return; }
  63
+		
  64
+		var name, i = 0, length = obj.length;
  65
+	
  66
+		// object
  67
+		if (length === undefined) {
  68
+			for (name in obj) {
  69
+				if (fn.call(obj[name], name, obj[name]) === false) { break; }
  70
+			}
  71
+			
  72
+		// array
  73
+		} else {
  74
+			for (var value = obj[0];
  75
+				i < length && fn.call( value, i, value ) !== false; value = obj[++i]) {				
  76
+			}
  77
+		}
  78
+	
  79
+		return obj;
  80
+	}
  81
+
  82
+	
  83
+	// convenience
  84
+	function el(id) {
  85
+		return document.getElementById(id); 	
  86
+	}	
  87
+
  88
+	
  89
+	// used extensively. a very simple implementation. 
  90
+	function extend(to, from, skipFuncs) {
  91
+		if (typeof from != 'object') { return to; }
  92
+		
  93
+		if (to && from) {			
  94
+			each(from, function(name, value) {
  95
+				if (!skipFuncs || typeof value != 'function') {
  96
+					to[name] = value;		
  97
+				}
  98
+			});
  99
+		}
  100
+		
  101
+		return to;
  102
+	}
  103
+	
  104
+	// var arr = select("elem.className"); 
  105
+	function select(query) {
  106
+		var index = query.indexOf("."); 
  107
+		if (index != -1) {
  108
+			var tag = query.slice(0, index) || "*";
  109
+			var klass = query.slice(index + 1, query.length);
  110
+			var els = [];
  111
+			each(document.getElementsByTagName(tag), function() {
  112
+				if (this.className && this.className.indexOf(klass) != -1) {
  113
+					els.push(this);		
  114
+				}
  115
+			});
  116
+			return els;
  117
+		}
  118
+	}
  119
+	
  120
+	// fix event inconsistencies across browsers
  121
+	function stopEvent(e) {
  122
+		e = e || window.event;
  123
+		
  124
+		if (e.preventDefault) {
  125
+			e.stopPropagation();
  126
+			e.preventDefault();
  127
+			
  128
+		} else {
  129
+			e.returnValue = false;	
  130
+			e.cancelBubble = true;
  131
+		} 
  132
+		return false;
  133
+	}
  134
+
  135
+	// push an event listener into existing array of listeners
  136
+	function bind(to, evt, fn) {
  137
+		to[evt] = to[evt] || [];
  138
+		to[evt].push(fn);		
  139
+	}
  140
+	
  141
+	
  142
+	// generates an unique id
  143
+   function makeId() {
  144
+      return "_" + ("" + Math.random()).slice(2, 10);   
  145
+   }
  146
+	
  147
+//}}}	
  148
+	
  149
+
  150
+// {{{ Clip
  151
+
  152
+	var Clip = function(json, index, player) {
  153
+		
  154
+		// private variables
  155
+		var self = this,
  156
+			 cuepoints = {},
  157
+			 listeners = {};
  158
+			 
  159
+		self.index = index;
  160
+		
  161
+		// instance variables
  162
+		if (typeof json == 'string') {
  163
+			json = {url:json};	
  164
+		}
  165
+	
  166
+		extend(this, json, true);	
  167
+		
  168
+		// event handling 
  169
+		each(("Begin*,Start,Pause*,Resume*,Seek*,Stop*,Finish*,LastSecond,Update,BufferFull,BufferEmpty,BufferStop").split(","),
  170
+			function() {
  171
+			
  172
+			var evt = "on" + this;
  173
+				
  174
+			// before event
  175
+			if (evt.indexOf("*") != -1) {
  176
+				evt = evt.slice(0, evt.length -1); 
  177
+				var before = "onBefore" + evt.slice(2); 
  178
+				
  179
+				self[before] = function(fn) {
  180
+					bind(listeners, before, fn);
  181
+					return self;
  182
+				};				
  183
+			}  
  184
+			
  185
+			self[evt] = function(fn) {
  186
+				bind(listeners, evt, fn);
  187
+				return self;
  188
+			};
  189
+			
  190
+			
  191
+			// set common clip event listeners to player level
  192
+			if (index == -1) {
  193
+				if (self[before]) {
  194
+					player[before] = self[before];		
  195
+				}				
  196
+				if (self[evt])  {
  197
+					player[evt] = self[evt];		
  198
+				}
  199
+			}
  200
+			
  201
+		});			  
  202
+		
  203
+		extend(this, { 
  204
+			 
  205
+			onCuepoint: function(points, fn) {    
  206
+				
  207
+				// embedded cuepoints
  208
+				if (arguments.length == 1) {
  209
+					cuepoints.embedded = [null, points];
  210
+					return self;
  211
+				}
  212
+				
  213
+				if (typeof points == 'number') {
  214
+					points = [points];	
  215
+				}
  216
+				
  217
+				var fnId = makeId();  
  218
+				cuepoints[fnId] = [points, fn]; 
  219
+				
  220
+				if (player.isLoaded()) {
  221
+					player._api().fp_addCuepoints(points, index, fnId);	
  222
+				}  
  223
+				
  224
+				return self;
  225
+			},
  226
+			
  227
+			update: function(json) {
  228
+				extend(self, json);
  229
+
  230
+				if (player.isLoaded()) {
  231
+					player._api().fp_updateClip(json, index);	
  232
+				}
  233
+				var conf = player.getConfig(); 
  234
+				var clip = (index == -1) ? conf.clip : conf.playlist[index];
  235
+				extend(clip, json, true);
  236
+			},
  237
+			
  238
+			
  239
+			// internal event for performing clip tasks. should be made private someday
  240
+			_fireEvent: function(evt, arg1, arg2, target) { 
  241
+				if (evt == 'onLoad') { 
  242
+					each(cuepoints, function(key, val) {
  243
+						if (val[0]) {
  244
+							player._api().fp_addCuepoints(val[0], index, key); 		
  245
+						}
  246
+					}); 
  247
+					return false;
  248
+				}
  249
+				
  250
+				// target clip we are working against
  251
+				target = target || self;	
  252
+				
  253
+				if (evt == 'onCuepoint') {
  254
+					var fn = cuepoints[arg1];
  255
+					if (fn) {
  256
+						return fn[1].call(player, target, arg2);
  257
+					}
  258
+				}  
  259
+
  260
+				// 1. clip properties, 2-3. metadata, 4. updates, 5. resumes from nested clip
  261
+				if (arg1 && "onBeforeBegin,onMetaData,onStart,onUpdate,onResume".indexOf(evt) != -1) {					
  262
+					// update clip properties
  263
+					extend(target, arg1);					
  264
+					
  265
+					if (arg1.metaData) {
  266
+						if (!target.duration) {
  267
+							target.duration = arg1.metaData.duration; 	
  268
+						} else {
  269
+							target.fullDuration = arg1.metaData.duration;	
  270
+						}  					
  271
+					}
  272
+				}  				
  273
+				
  274
+
  275
+				var ret = true;
  276
+				each(listeners[evt], function() {
  277
+					ret = this.call(player, target, arg1, arg2);		
  278
+				}); 
  279
+				return ret;				
  280
+			}			
  281
+			
  282
+		});
  283
+		
  284
+		
  285
+		// get cuepoints from config
  286
+		if (json.onCuepoint) {
  287
+			var arg = json.onCuepoint;
  288
+			self.onCuepoint.apply(self, typeof arg == 'function' ? [arg] : arg);
  289
+			delete json.onCuepoint;
  290
+		} 
  291
+		
  292
+		// get other events
  293
+		each(json, function(key, val) {
  294
+			
  295
+			if (typeof val == 'function') {
  296
+				bind(listeners, key, val);
  297
+				delete json[key];
  298
+			}
  299
+			
  300
+		});
  301
+
  302
+		
  303
+		// setup common clip event callbacks for Player object too (shortcuts)
  304
+		if (index == -1) {
  305
+			player.onCuepoint = this.onCuepoint;	
  306
+		}
  307
+
  308
+	};
  309
+
  310
+//}}}
  311
+
  312
+
  313
+// {{{ Plugin
  314
+		
  315
+	var Plugin = function(name, json, player, fn) {
  316
+	
  317
+		var self = this,
  318
+			 listeners = {},
  319
+			 hasMethods = false;
  320
+	
  321
+		if (fn) {
  322
+			extend(listeners, fn);	
  323
+		}   
  324
+		
  325
+		// custom callback functions in configuration
  326
+		each(json, function(key, val) {
  327
+			if (typeof val == 'function') {
  328
+				listeners[key] = val;
  329
+				delete json[key];	
  330
+			}
  331
+		});  
  332
+		
  333
+		// core plugin methods		
  334
+		extend(this, {
  335
+  
  336
+			// speed and fn are optional
  337
+			animate: function(props, speed, fn) { 
  338
+				if (!props) {
  339
+					return self;	
  340
+				}
  341
+				
  342
+				if (typeof speed == 'function') { 
  343
+					fn = speed; 
  344
+					speed = 500;
  345
+				}
  346
+				
  347
+				if (typeof props == 'string') {
  348
+					var key = props;
  349
+					props = {};
  350
+					props[key] = speed;
  351
+					speed = 500; 
  352
+				}
  353
+				
  354
+				if (fn) {
  355
+					var fnId = makeId();
  356
+					listeners[fnId] = fn;
  357
+				}
  358
+		
  359
+				if (speed === undefined) { speed = 500; }
  360
+				json = player._api().fp_animate(name, props, speed, fnId);	
  361
+				return self;
  362
+			},
  363
+			
  364
+			css: function(props, val) {
  365
+				if (val !== undefined) {
  366
+					var css = {};
  367
+					css[props] = val;
  368
+					props = css;					
  369
+				}
  370
+				json = player._api().fp_css(name, props);
  371
+				extend(self, json);
  372
+				return self;
  373
+			},
  374
+			
  375
+			show: function() {
  376
+				this.display = 'block';
  377
+				player._api().fp_showPlugin(name);
  378
+				return self;
  379
+			},
  380
+			
  381
+			hide: function() {
  382
+				this.display = 'none';
  383
+				player._api().fp_hidePlugin(name);
  384
+				return self;
  385
+			},
  386
+			
  387
+			// toggle between visible / hidden state
  388
+			toggle: function() {
  389
+				this.display = player._api().fp_togglePlugin(name);
  390
+				return self;
  391
+			},			
  392
+			
  393
+			fadeTo: function(o, speed, fn) {
  394
+				
  395
+				if (typeof speed == 'function') { 
  396
+					fn = speed; 
  397
+					speed = 500;
  398
+				}
  399
+				
  400
+				if (fn) {
  401
+					var fnId = makeId();
  402
+					listeners[fnId] = fn;
  403
+				}				
  404
+				this.display = player._api().fp_fadeTo(name, o, speed, fnId);
  405
+				this.opacity = o;
  406
+				return self;
  407
+			},
  408
+			
  409
+			fadeIn: function(speed, fn) { 
  410
+				return self.fadeTo(1, speed, fn);				
  411
+			},
  412
+	
  413
+			fadeOut: function(speed, fn) {
  414
+				return self.fadeTo(0, speed, fn);	
  415
+			},
  416
+			
  417
+			getName: function() {
  418
+				return name;	
  419
+			},
  420
+			
  421
+			getPlayer: function() {
  422
+				return player;	
  423
+			},
  424
+			
  425
+			// internal method. should be made private some day
  426
+         _fireEvent: function(evt, arg, arg2) {
  427
+				
  428
+            // update plugins properties & methods
  429
+            if (evt == 'onUpdate') {
  430
+               var json = player._api().fp_getPlugin(name); 
  431
+					if (!json) { return;	}					
  432
+					
  433
+               extend(self, json);
  434
+               delete self.methods;
  435
+					
  436
+               if (!hasMethods) {
  437
+                  each(json.methods, function() {
  438
+                     var method = "" + this;       
  439
+							
  440
+                     self[method] = function() {
  441
+                        var a = [].slice.call(arguments);
  442
+                        var ret = player._api().fp_invoke(name, method, a); 
  443
+                        return ret === 'undefined' || ret === undefined ? self : ret;
  444
+                     };
  445
+                  });
  446
+                  hasMethods = true;         
  447
+               }
  448
+            }
  449
+            
  450
+            // plugin callbacks
  451
+            var fn = listeners[evt];
  452
+
  453
+			if (fn) {
  454
+				var ret = fn.apply(self, arg);
  455
+					
  456
+				// "one-shot" callback
  457
+				if (evt.slice(0, 1) == "_") {
  458
+					delete listeners[evt];  
  459
+				} 
  460
+				
  461
+				return ret;
  462
+            }
  463
+            
  464
+            return self;
  465
+         }
  466
+			
  467
+		});
  468
+
  469
+	};
  470
+
  471
+
  472
+//}}}
  473
+
  474
+
  475
+function Player(wrapper, params, conf) {   
  476
+	
  477
+	// private variables (+ arguments)
  478
+	var self = this, 
  479
+		api = null, 
  480
+		isUnloading = false,
  481
+		html, 
  482
+		commonClip, 
  483
+		playlist = [], 
  484
+		plugins = {},
  485
+		listeners = {},
  486
+		playerId,
  487
+		apiId,
  488
+		
  489
+		// n'th player on the page
  490
+		playerIndex,
  491
+		
  492
+		// active clip's index number
  493
+		activeIndex,
  494
+		
  495
+		swfHeight,
  496
+		wrapperHeight;	
  497
+
  498
+  
  499
+// {{{ public methods 
  500
+	
  501
+	extend(self, {
  502
+			
  503
+		id: function() {
  504
+			return playerId;	
  505
+		}, 
  506
+		
  507
+		isLoaded: function() {
  508
+			return (api !== null && api.fp_play !== undefined && !isUnloading);	
  509
+		},
  510
+		
  511
+		getParent: function() {
  512
+			return wrapper;	
  513
+		},
  514
+		
  515
+		hide: function(all) {
  516
+			if (all) { wrapper.style.height = "0px"; }
  517
+			if (self.isLoaded()) { api.style.height = "0px"; } 
  518
+			return self;
  519
+		},
  520
+
  521
+		show: function() {
  522
+			wrapper.style.height = wrapperHeight + "px";
  523
+			if (self.isLoaded()) { api.style.height = swfHeight + "px"; }
  524
+			return self;
  525
+		}, 
  526
+					
  527
+		isHidden: function() {
  528
+			return self.isLoaded() && parseInt(api.style.height, 10) === 0;
  529
+		},
  530
+		
  531
+		load: function(fn) { 
  532
+			if (!self.isLoaded() && self._fireEvent("onBeforeLoad") !== false) {
  533
+				var onPlayersUnloaded = function() {
  534
+					html = wrapper.innerHTML;				
  535
+				
  536
+					// do not use splash as alternate content for flashembed
  537
+					if (html && !flashembed.isSupported(params.version)) {
  538
+						wrapper.innerHTML = "";					
  539
+					}				  
  540
+					
  541
+					// onLoad listener given as argument
  542
+					if (fn) {
  543
+						fn.cached = true;
  544
+						bind(listeners, "onLoad", fn);	
  545
+					}
  546
+					
  547
+					// install Flash object inside given container
  548
+					flashembed(wrapper, params, {config: conf});
  549
+				};
  550
+				
  551
+				
  552
+				// unload all instances
  553
+				var unloadedPlayersNb = 0;
  554
+				each(players, function()  {
  555
+					this.unload(function(wasUnloaded) {
  556
+						if ( ++unloadedPlayersNb == players.length ) {
  557
+							onPlayersUnloaded();
  558
+						}
  559
+					});		
  560
+				});
  561
+			}
  562
+			
  563
+			return self;	
  564
+		},
  565
+		
  566
+		unload: function(fn) {
  567
+			
  568
+			
  569
+			// if we are fullscreen on safari, we can't unload as it would crash the PluginHost, sorry
  570
+			if (this.isFullscreen() && /WebKit/i.test(navigator.userAgent)) {
  571
+				if ( fn ) { fn(false); }
  572
+				return self;
  573
+			}
  574
+			
  575
+			
  576
+			// unload only if in splash state
  577
+			if (html.replace(/\s/g,'') !== '') {
  578
+				
  579
+				if (self._fireEvent("onBeforeUnload") === false) {
  580
+					if ( fn ) { fn(false); }
  581
+					return self;
  582
+				}	
  583
+				
  584
+				isUnloading = true;
  585
+				// try closing
  586
+				try {
  587
+					if (api) { 
  588
+						api.fp_close();
  589
+						
  590
+						// fire unload only when API is present
  591
+						self._fireEvent("onUnload");
  592
+					}				
  593
+				} catch (error) {}				
  594
+				
  595
+				var clean = function() {
  596
+					api = null;				
  597
+					wrapper.innerHTML = html;
  598
+					isUnloading = false;
  599
+					
  600
+					if ( fn ) { fn(true); }
  601
+				};
  602
+				
  603
+				setTimeout(clean, 50);			
  604
+			} 
  605
+			else if ( fn ) { fn(false); }
  606
+			
  607
+			return self;
  608
+		
  609
+		},
  610
+		
  611
+		getClip: function(index) {
  612
+			if (index === undefined) {
  613
+				index = activeIndex;	
  614
+			}
  615
+			return playlist[index];
  616
+		},
  617
+		
  618
+		
  619
+		getCommonClip: function() {
  620
+			return commonClip;	
  621
+		},		
  622
+		
  623
+		getPlaylist: function() {
  624
+			return playlist; 
  625
+		},
  626
+		
  627
+      getPlugin: function(name) {  
  628
+         var plugin = plugins[name];
  629
+         
  630
+			// create plugin if nessessary
  631
+         if (!plugin && self.isLoaded()) {
  632
+				var json = self._api().fp_getPlugin(name);
  633
+				if (json) {
  634
+					plugin = new Plugin(name, json, self);
  635
+					plugins[name] = plugin;  						
  636
+				} 
  637
+         }        
  638
+         return plugin; 
  639
+      },
  640
+		
  641
+		getScreen: function() { 
  642
+			return self.getPlugin("screen");
  643
+		}, 
  644
+		
  645
+		getControls: function() { 
  646
+			return self.getPlugin("controls")._fireEvent("onUpdate");
  647
+		}, 
  648
+		
  649
+		// 3.2
  650
+		getLogo: function() {
  651
+			try {
  652
+				return self.getPlugin("logo")._fireEvent("onUpdate");
  653
+			} catch (ignored) {}
  654
+		},
  655
+		
  656
+		// 3.2
  657
+		getPlay: function() {
  658
+			return self.getPlugin("play")._fireEvent("onUpdate");
  659
+		},
  660
+		
  661
+
  662
+		getConfig: function(copy) { 
  663
+			return copy ? clone(conf) : conf;
  664
+		},
  665
+		
  666
+		getFlashParams: function() { 
  667
+			return params;
  668
+		},		
  669
+		
  670
+		loadPlugin: function(name, url, props, fn) { 
  671
+
  672
+			// properties not supplied			
  673
+			if (typeof props == 'function') { 
  674
+				fn = props; 
  675
+				props = {};
  676
+			} 
  677
+			
  678
+			// if fn not given, make a fake id so that plugin's onUpdate get's fired
  679
+			var fnId = fn ? makeId() : "_"; 
  680
+			self._api().fp_loadPlugin(name, url, props, fnId); 
  681
+			
  682
+			// create new plugin
  683
+			var arg = {};
  684
+			arg[fnId] = fn;
  685
+			var p = new Plugin(name, null, self, arg);
  686
+			plugins[name] = p;
  687
+			return p;			
  688
+		},
  689
+		
  690
+		
  691
+		getState: function() {
  692
+			return self.isLoaded() ? api.fp_getState() : -1;
  693
+		},
  694
+		
  695
+		// "lazy" play
  696
+		play: function(clip, instream) {
  697
+			
  698
+			var p = function() {
  699
+				if (clip !== undefined) {
  700
+					self._api().fp_play(clip, instream);
  701
+				} else {
  702
+					self._api().fp_play();	
  703
+				}
  704
+			};
  705
+			
  706
+			if (self.isLoaded()) {
  707
+				p();	
  708
+			} else if ( isUnloading ) {
  709
+				setTimeout(function() { 
  710
+					self.play(clip, instream); 
  711
+				}, 50);
  712
+				
  713
+			} else {
  714
+				self.load(function() { 
  715
+					p();
  716
+				});
  717
+			}
  718
+			
  719
+			return self;
  720
+		},
  721
+		
  722
+		getVersion: function() {
  723
+			var js = "flowplayer.js 3.2.6";
  724
+			if (self.isLoaded()) {
  725
+				var ver = api.fp_getVersion();
  726
+				ver.push(js);
  727
+				return ver;
  728
+			}
  729
+			return js; 
  730
+		},
  731
+		
  732
+		_api: function() {
  733
+			if (!self.isLoaded()) {
  734
+				throw "Flowplayer " +self.id()+ " not loaded when calling an API method";
  735
+			}
  736
+			return api;				
  737
+		},
  738
+		
  739
+		setClip: function(clip) {
  740
+			self.setPlaylist([clip]);
  741
+			return self;
  742
+		},
  743
+		
  744
+		getIndex: function() {
  745
+			return playerIndex;	
  746
+		},
  747
+		
  748
+		_swfHeight: function() {
  749
+			return api.clientHeight;
  750
+		}
  751
+		
  752
+	}); 
  753
+	
  754
+	
  755
+	// event handlers
  756
+	each(("Click*,Load*,Unload*,Keypress*,Volume*,Mute*,Unmute*,PlaylistReplace,ClipAdd,Fullscreen*,FullscreenExit,Error,MouseOver,MouseOut").split(","),
  757
+		function() {		 
  758
+			var name = "on" + this;
  759
+			
  760
+			// before event
  761
+			if (name.indexOf("*") != -1) {
  762
+				name = name.slice(0, name.length -1); 
  763
+				var name2 = "onBefore" + name.slice(2);
  764
+				self[name2] = function(fn) {
  765
+					bind(listeners, name2, fn);	
  766
+					return self;
  767
+				};						
  768
+			}
  769
+			
  770
+			// normal event
  771
+			self[name] = function(fn) {
  772
+				bind(listeners, name, fn);	
  773
+				return self;
  774
+			};			 
  775
+		}
  776
+	); 
  777
+	
  778
+	
  779
+	// core API methods
  780
+	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(","),		
  781
+		function() {		 
  782
+			var name = this;
  783
+			
  784
+			self[name] = function(a1, a2) {
  785
+				if (!self.isLoaded()) { return self; }
  786
+				var ret = null;
  787
+				
  788
+				// two arguments
  789
+				if (a1 !== undefined && a2 !== undefined) { 
  790
+					ret = api["fp_" + name](a1, a2);
  791
+					
  792
+				} else { 
  793
+					ret = (a1 === undefined) ? api["fp_" + name]() : api["fp_" + name](a1);
  794
+					
  795
+				}
  796
+							
  797
+				return ret === 'undefined' || ret === undefined ? self : ret;
  798
+			};			 
  799
+		}
  800
+	); 		
  801
+	
  802
+//}}}
  803
+
  804
+
  805
+// {{{ public method: _fireEvent
  806
+		
  807
+	self._fireEvent = function(a) {		
  808
+		
  809
+		if (typeof a == 'string') { a = [a]; }
  810
+		
  811
+		var evt = a[0], arg0 = a[1], arg1 = a[2], arg2 = a[3], i = 0;  		
  812
+		if (conf.debug) { log(a); }				
  813
+		
  814
+		// internal onLoad
  815
+		if (!self.isLoaded() && evt == 'onLoad' && arg0 == 'player') {						
  816
+			
  817
+			api = api || el(apiId); 
  818
+			swfHeight = self._swfHeight();
  819
+			
  820
+			each(playlist, function() {
  821
+				this._fireEvent("onLoad");		
  822
+			});
  823
+			
  824
+			each(plugins, function(name, p) {
  825
+				p._fireEvent("onUpdate");		
  826
+			}); 
  827
+			
  828
+			commonClip._fireEvent("onLoad");  
  829
+		}
  830
+		
  831
+		// other onLoad events are skipped
  832
+		if (evt == 'onLoad' && arg0 != 'player') { return; }
  833
+		
  834
+		
  835
+		// "normalize" error handling
  836
+		if (evt == 'onError') { 
  837
+			if (typeof arg0 == 'string' || (typeof arg0 == 'number' && typeof arg1 == 'number'))  {
  838
+				arg0 = arg1;
  839
+				arg1 = arg2;
  840
+			}			 
  841
+		}
  842
+		
  843
+		
  844
+      if (evt == 'onContextMenu') {
  845
+         each(conf.contextMenu[arg0], function(key, fn)  {
  846
+            fn.call(self);
  847
+         });
  848
+         return;
  849
+      }
  850
+
  851
+		if (evt == 'onPluginEvent' || evt == 'onBeforePluginEvent') { 
  852
+			var name = arg0.name || arg0;
  853
+			var p = plugins[name];
  854
+
  855
+			if (p) {
  856
+				p._fireEvent("onUpdate", arg0);
  857
+				return p._fireEvent(arg1, a.slice(3));		
  858
+			}
  859
+			return;
  860
+		}		
  861
+
  862
+		// replace whole playlist
  863
+		if (evt == 'onPlaylistReplace') {
  864
+			playlist = [];
  865
+			var index = 0;
  866
+			each(arg0, function() {
  867
+				playlist.push(new Clip(this, index++, self));
  868
+			});		
  869
+		}
  870
+		
  871
+		// insert new clip to the playlist. arg0 = clip, arg1 = index 
  872
+		if (evt == 'onClipAdd') {
  873
+			
  874
+			// instream clip additions are ignored at this point
  875
+			if (arg0.isInStream) { return; }
  876
+			
  877
+			// add new clip into playlist			
  878
+			arg0 = new Clip(arg0, arg1, self);
  879
+			playlist.splice(arg1, 0, arg0);
  880
+			
  881
+			// increment index variable for the rest of the clips on playlist 
  882
+			for (i = arg1 + 1; i < playlist.length; i++) {
  883
+				playlist[i].index++;	
  884
+			}
  885
+		}
  886
+		
  887
+		
  888
+		var ret = true;
  889
+		
  890
+		// clip event
  891
+		if (typeof arg0 == 'number' && arg0 < playlist.length) {
  892
+			
  893
+			activeIndex = arg0;
  894
+			var clip = playlist[arg0];			
  895
+			
  896
+			if (clip) {
  897
+				ret = clip._fireEvent(evt, arg1, arg2);	
  898
+			} 
  899
+			
  900
+			if (!clip || ret !== false) {
  901
+				// clip argument is given for common clip, because it behaves as the target
  902
+				ret = commonClip._fireEvent(evt, arg1, arg2, clip);	
  903
+			}  
  904
+		}
  905
+		
  906
+		
  907
+		// trigger player event
  908
+		each(listeners[evt], function() {
  909
+			ret = this.call(self, arg0, arg1);		
  910
+			
  911
+			// remove cached entry
  912
+			if (this.cached) {
  913
+				listeners[evt].splice(i, 1);	
  914
+			}
  915
+			
  916
+			// break loop
  917
+			if (ret === false) { return false;	 }
  918
+			i++;
  919
+			
  920
+		}); 
  921
+
  922
+		return ret;
  923
+	};
  924
+
  925
+//}}}
  926
+ 
  927
+
  928
+// {{{ init
  929
+	
  930
+   function init() {
  931
+		// replace previous installation 
  932
+		if ($f(wrapper)) {
  933
+			$f(wrapper).getParent().innerHTML = ""; 
  934
+			playerIndex = $f(wrapper).getIndex();
  935
+			players[playerIndex] = self;
  936
+			
  937
+		// register this player into global array of instances
  938
+		} else {
  939
+			players.push(self);
  940
+			playerIndex = players.length -1;
  941
+		}
  942
+		
  943
+		wrapperHeight = parseInt(wrapper.style.height, 10) || wrapper.clientHeight;     
  944
+		
  945
+		// playerId	
  946
+		playerId = wrapper.id || "fp" + makeId();
  947
+		apiId = params.id || playerId + "_api";
  948
+		params.id = apiId;
  949
+		conf.playerId = playerId;
  950
+		
  951
+
  952
+		// plain url is given as config
  953
+		if (typeof conf == 'string') {
  954
+			conf = {clip:{url:conf}};	
  955
+		} 
  956
+		
  957
+		if (typeof conf.clip == 'string') {
  958
+			conf.clip = {url: conf.clip};	
  959
+		}
  960
+		
  961
+		// common clip is always there
  962
+		conf.clip = conf.clip || {};  
  963
+		
  964
+		
  965
+		// wrapper href as common clip's url
  966
+		if (wrapper.getAttribute("href", 2) && !conf.clip.url) { 
  967
+			conf.clip.url = wrapper.getAttribute("href", 2);			
  968
+		} 
  969
+		
  970
+		commonClip = new Clip(conf.clip, -1, self); 
  971
+		
  972
+		// playlist
  973
+		conf.playlist = conf.playlist || [conf.clip]; 
  974
+		
  975
+		var index = 0;
  976
+		
  977
+		each(conf.playlist, function() {
  978
+			
  979
+			var clip = this;			
  980
+			
  981
+			/* sometimes clip is given as array. this is not accepted. */
  982
+			if (typeof clip == 'object' && clip.length) {
  983
+				clip = {url: "" + clip};	
  984
+			}			
  985
+			
  986
+			// populate common clip properties to each clip
  987
+			each(conf.clip, function(key, val) {
  988
+				if (val !== undefined && clip[key] === undefined && typeof val != 'function') {
  989
+					clip[key] = val;	
  990
+				}
  991
+			});	
  992
+			
  993
+			// modify playlist in configuration
  994
+			conf.playlist[index] = clip;			
  995
+			
  996
+			// populate playlist array
  997
+			clip = new Clip(clip, index, self);
  998
+			playlist.push(clip);						
  999
+			index++;			
  1000
+		});				
  1001
+		
  1002
+		// event listeners
  1003
+		each(conf, function(key, val) {
  1004
+			if (typeof val == 'function') {
  1005
+				
  1006
+				// common clip event
  1007
+				if (commonClip[key]) {
  1008
+					commonClip[key](val);
  1009
+					
  1010
+				// player event
  1011
+				} else {
  1012
+					bind(listeners, key, val);	
  1013
+				}				
  1014
+				
  1015
+				// no need to supply for the Flash component
  1016
+				delete conf[key];	
  1017
+			}
  1018
+		});		 
  1019
+		
  1020
+		
  1021
+		// plugins
  1022
+		each(conf.plugins, function(name, val) {
  1023
+			if (val) {
  1024
+				plugins[name] = new Plugin(name, val, self);
  1025
+			}
  1026
+		});
  1027
+		
  1028
+		
  1029
+		// setup controlbar plugin if not explicitly defined
  1030
+		if (!conf.plugins || conf.plugins.controls === undefined) {
  1031
+			plugins.controls = new Plugin("controls", null, self);	
  1032
+		}
  1033
+		
  1034
+		// setup canvas as plugin
  1035
+		plugins.canvas = new Plugin("canvas", null, self);		
  1036
+		
  1037
+		html = wrapper.innerHTML;
  1038
+		
  1039
+		// click function
  1040
+		function doClick(e) { 
  1041
+			
  1042
+			// ipad/iPhone --> follow the link if plugin not installed
  1043
+			var hasiPadSupport = self.hasiPadSupport && self.hasiPadSupport();
  1044
+			if (/iPad|iPhone|iPod/i.test(navigator.userAgent) && !/.flv$/i.test(playlist[0].url) && ! hasiPadSupport ) {
  1045
+				return true;	
  1046
+			}
  1047
+			
  1048
+			if (!self.isLoaded() && self._fireEvent("onBeforeClick") !== false) {
  1049
+				self.load();		
  1050
+			} 
  1051
+			return stopEvent(e);					
  1052
+		}
  1053
+		
  1054
+		function installPlayer() {
  1055
+			// defer loading upon click
  1056
+			if (html.replace(/\s/g, '') !== '') {	 
  1057
+
  1058
+				if (wrapper.addEventListener) {
  1059
+					wrapper.addEventListener("click", doClick, false);	
  1060
+
  1061
+				} else if (wrapper.attachEvent) {
  1062
+					wrapper.attachEvent("onclick", doClick);	
  1063
+				}
  1064
+
  1065
+			// player is loaded upon page load 
  1066
+			} else {
  1067
+
  1068
+				// prevent default action from wrapper. (fixes safari problems)
  1069
+				if (wrapper.addEventListener) {
  1070
+					wrapper.addEventListener("click", stopEvent, false);	
  1071
+				}
  1072
+				// load player
  1073
+				self.load();
  1074
+			}
  1075
+		}
  1076
+		
  1077
+		// now that the player is initialized, wait for the plugin chain to finish
  1078
+		// before actually changing the dom
  1079
+		setTimeout(installPlayer, 0);
  1080
+	}
  1081
+
  1082
+	// possibly defer initialization until DOM get's loaded
  1083
+	if (typeof wrapper == 'string') { 
  1084
+		var node = el(wrapper); 		
  1085
+		if (!node) { throw "Flowplayer cannot access element: " + wrapper; }
  1086
+		wrapper = node; 
  1087
+		init();
  1088
+		
  1089
+	// we have a DOM element so page is already loaded
  1090
+	} else {		
  1091
+		init();
  1092
+	}
  1093
+	
  1094
+	
  1095
+//}}}
  1096
+
  1097
+
  1098
+}
  1099
+
  1100
+
  1101
+// {{{ flowplayer() & statics 
  1102
+
  1103
+// container for player instances
  1104
+var players = [];
  1105
+
  1106
+
  1107
+// this object is returned when multiple player's are requested 
  1108
+function Iterator(arr) {
  1109
+	
  1110
+	this.length = arr.length;
  1111
+	
  1112
+	this.each = function(fn)  {
  1113
+		each(arr, fn);	
  1114
+	};
  1115
+	
  1116
+	this.size = function() {
  1117
+		return arr.length;	
  1118
+	};	
  1119
+}
  1120
+
  1121
+// these two variables are the only global variables
  1122
+window.flowplayer = window.$f = function() {
  1123
+	var instance = null;
  1124
+	var arg = arguments[0];	
  1125
+	
  1126
+	// $f()
  1127
+	if (!arguments.length) {
  1128
+		each(players, function() {
  1129
+			if (this.isLoaded())  {
  1130
+				instance = this;	
  1131
+				return false;
  1132
+			}
  1133
+		});
  1134
+		
  1135
+		return instance || players[0];
  1136
+	} 
  1137
+	
  1138
+	if (arguments.length == 1) {
  1139
+		
  1140
+		// $f(index);
  1141
+		if (typeof arg == 'number') { 
  1142
+			return players[arg];	
  1143
+	
  1144
+			
  1145
+		// $f(wrapper || 'containerId' || '*');
  1146
+		} else {
  1147
+			
  1148
+			// $f("*");
  1149
+			if (arg == '*') {
  1150
+				return new Iterator(players);	
  1151
+			}
  1152
+			
  1153
+			// $f(wrapper || 'containerId');
  1154
+			each(players, function() {
  1155
+				if (this.id() == arg.id || this.id() == arg || this.getParent() == arg)  {
  1156
+					instance = this;	
  1157
+					return false;
  1158
+				}
  1159
+			});
  1160
+			
  1161
+			return instance;					
  1162
+		}
  1163
+	} 			
  1164
+
  1165
+	// instance builder 
  1166
+	if (arguments.length > 1) {		
  1167
+
  1168
+		// flashembed parameters
  1169
+		var params = arguments[1],
  1170
+			 conf = (arguments.length == 3) ? arguments[2] : {};
  1171
+			 		
  1172
+		
  1173
+		if (typeof params == 'string') {
  1174
+			params = {src: params};	
  1175
+		} 
  1176
+		
  1177
+		params = extend({
  1178
+			bgcolor: "#000000",
  1179
+			version: [9, 0],
  1180
+			expressInstall: "http://static.flowplayer.org/swf/expressinstall.swf",
  1181
+			cachebusting: false
  1182
+			
  1183
+		}, params);		
  1184
+		
  1185
+		if (typeof arg == 'string') {
  1186
+			
  1187
+			// select arg by classname
  1188
+			if (arg.indexOf(".") != -1) {
  1189
+				var instances = [];
  1190
+				
  1191
+				each(select(arg), function() { 
  1192
+					instances.push(new Player(this, clone(params), clone(conf))); 		
  1193
+				});	
  1194
+				
  1195
+				return new Iterator(instances);
  1196
+				
  1197
+			// select node by id
  1198
+			} else {		
  1199
+				var node = el(arg);
  1200
+				return new Player(node !== null ? node : arg, params, conf);  	
  1201
+			} 
  1202
+