2
* flowplayer.js 3.2.4. The Flowplayer API
4
* Copyright 2009 Flowplayer Oy
6
* This file is part of Flowplayer.
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.
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.
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/>.
21
* Date: 2010-08-25 12:48:46 +0000 (Wed, 25 Aug 2010)
29
- $f() and flowplayer() functions
30
- handling multiple instances
31
- Flowplayer programming API
32
- Flowplayer event model
33
- player loading / unloading
38
/*jslint glovar: true, browser: true */
39
/*global flowplayer, $f */
41
// {{{ private utility methods
44
console.log("$f.fireEvent", [].slice.call(args));
48
// thanks: http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone
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]);
60
// stripped from jQuery, thanks John Resig
61
function each(obj, fn) {
64
var name, i = 0, length = obj.length;
67
if (length === undefined) {
69
if (fn.call(obj[name], name, obj[name]) === false) { break; }
74
for (var value = obj[0];
75
i < length && fn.call( value, i, value ) !== false; value = obj[++i]) {
85
return document.getElementById(id);
89
// used extensively. a very simple implementation.
90
function extend(to, from, skipFuncs) {
91
if (typeof from != 'object') { return to; }
94
each(from, function(name, value) {
95
if (!skipFuncs || typeof value != 'function') {
104
// var arr = select("elem.className");
105
function select(query) {
106
var index = query.indexOf(".");
108
var tag = query.slice(0, index) || "*";
109
var klass = query.slice(index + 1, query.length);
111
each(document.getElementsByTagName(tag), function() {
112
if (this.className && this.className.indexOf(klass) != -1) {
120
// fix event inconsistencies across browsers
121
function stopEvent(e) {
122
e = e || window.event;
124
if (e.preventDefault) {
129
e.returnValue = false;
130
e.cancelBubble = true;
135
// push an event listener into existing array of listeners
136
function bind(to, evt, fn) {
137
to[evt] = to[evt] || [];
142
// generates an unique id
144
return "_" + ("" + Math.random()).slice(2, 10);
152
var Clip = function(json, index, player) {
161
// instance variables
162
if (typeof json == 'string') {
166
extend(this, json, true);
169
each(("Begin*,Start,Pause*,Resume*,Seek*,Stop*,Finish*,LastSecond,Update,BufferFull,BufferEmpty,BufferStop").split(","),
172
var evt = "on" + this;
175
if (evt.indexOf("*") != -1) {
176
evt = evt.slice(0, evt.length -1);
177
var before = "onBefore" + evt.slice(2);
179
self[before] = function(fn) {
180
bind(listeners, before, fn);
185
self[evt] = function(fn) {
186
bind(listeners, evt, fn);
191
// set common clip event listeners to player level
194
player[before] = self[before];
197
player[evt] = self[evt];
205
onCuepoint: function(points, fn) {
207
// embedded cuepoints
208
if (arguments.length == 1) {
209
cuepoints.embedded = [null, points];
213
if (typeof points == 'number') {
218
cuepoints[fnId] = [points, fn];
220
if (player.isLoaded()) {
221
player._api().fp_addCuepoints(points, index, fnId);
227
update: function(json) {
230
if (player.isLoaded()) {
231
player._api().fp_updateClip(json, index);
233
var conf = player.getConfig();
234
var clip = (index == -1) ? conf.clip : conf.playlist[index];
235
extend(clip, json, true);
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) {
244
player._api().fp_addCuepoints(val[0], index, key);
250
// target clip we are working against
251
target = target || self;
253
if (evt == 'onCuepoint') {
254
var fn = cuepoints[arg1];
256
return fn[1].call(player, target, arg2);
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);
266
if (!target.duration) {
267
target.duration = arg1.metaData.duration;
269
target.fullDuration = arg1.metaData.duration;
276
each(listeners[evt], function() {
277
ret = this.call(player, target, arg1, arg2);
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;
293
each(json, function(key, val) {
295
if (typeof val == 'function') {
296
bind(listeners, key, val);
303
// setup common clip event callbacks for Player object too (shortcuts)
305
player.onCuepoint = this.onCuepoint;
315
var Plugin = function(name, json, player, fn) {
322
extend(listeners, fn);
325
// custom callback functions in configuration
326
each(json, function(key, val) {
327
if (typeof val == 'function') {
328
listeners[key] = val;
333
// core plugin methods
336
// speed and fn are optional
337
animate: function(props, speed, fn) {
342
if (typeof speed == 'function') {
347
if (typeof props == 'string') {
356
listeners[fnId] = fn;
359
if (speed === undefined) { speed = 500; }
360
json = player._api().fp_animate(name, props, speed, fnId);
364
css: function(props, val) {
365
if (val !== undefined) {
370
json = player._api().fp_css(name, props);
376
this.display = 'block';
377
player._api().fp_showPlugin(name);
382
this.display = 'none';
383
player._api().fp_hidePlugin(name);
387
// toggle between visible / hidden state
389
this.display = player._api().fp_togglePlugin(name);
393
fadeTo: function(o, speed, fn) {
395
if (typeof speed == 'function') {
402
listeners[fnId] = fn;
404
this.display = player._api().fp_fadeTo(name, o, speed, fnId);
409
fadeIn: function(speed, fn) {
410
return self.fadeTo(1, speed, fn);
413
fadeOut: function(speed, fn) {
414
return self.fadeTo(0, speed, fn);
417
getName: function() {
421
getPlayer: function() {
425
// internal method. should be made private some day
426
_fireEvent: function(evt, arg, arg2) {
428
// update plugins properties & methods
429
if (evt == 'onUpdate') {
430
var json = player._api().fp_getPlugin(name);
431
if (!json) { return; }
437
each(json.methods, function() {
438
var method = "" + this;
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;
451
var fn = listeners[evt];
454
var ret = fn.apply(self, arg);
456
// "one-shot" callback
457
if (evt.slice(0, 1) == "_") {
458
delete listeners[evt];
475
function Player(wrapper, params, conf) {
477
// private variables (+ arguments)
489
// n'th player on the page
492
// active clip's index number
499
// {{{ public methods
507
isLoaded: function() {
508
return (api !== null && api.fp_play !== undefined && !isUnloading);
511
getParent: function() {
515
hide: function(all) {
516
if (all) { wrapper.style.height = "0px"; }
517
if (self.isLoaded()) { api.style.height = "0px"; }
522
wrapper.style.height = wrapperHeight + "px";
523
if (self.isLoaded()) { api.style.height = swfHeight + "px"; }
527
isHidden: function() {
528
return self.isLoaded() && parseInt(api.style.height, 10) === 0;
532
if (!self.isLoaded() && self._fireEvent("onBeforeLoad") !== false) {
533
var onPlayersUnloaded = function() {
534
html = wrapper.innerHTML;
536
// do not use splash as alternate content for flashembed
537
if (html && !flashembed.isSupported(params.version)) {
538
wrapper.innerHTML = "";
541
// onLoad listener given as argument
544
bind(listeners, "onLoad", fn);
547
// install Flash object inside given container
548
flashembed(wrapper, params, {config: conf});
552
// unload all instances
553
var unloadedPlayersNb = 0;
554
each(players, function() {
555
this.unload(function(wasUnloaded) {
556
if ( ++unloadedPlayersNb == players.length ) {
566
unload: function(fn) {
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); }
576
// unload only if in splash state
577
if (html.replace(/\s/g,'') !== '') {
579
if (self._fireEvent("onBeforeUnload") === false) {
580
if ( fn ) { fn(false); }
590
// fire unload only when API is present
591
self._fireEvent("onUnload");
595
var clean = function() {
597
wrapper.innerHTML = html;
600
if ( fn ) { fn(true); }
603
setTimeout(clean, 50);
605
else if ( fn ) { fn(false); }
611
getClip: function(index) {
612
if (index === undefined) {
615
return playlist[index];
619
getCommonClip: function() {
623
getPlaylist: function() {
627
getPlugin: function(name) {
628
var plugin = plugins[name];
630
// create plugin if nessessary
631
if (!plugin && self.isLoaded()) {
632
var json = self._api().fp_getPlugin(name);
634
plugin = new Plugin(name, json, self);
635
plugins[name] = plugin;
641
getScreen: function() {
642
return self.getPlugin("screen");
645
getControls: function() {
646
return self.getPlugin("controls")._fireEvent("onUpdate");
650
getLogo: function() {
652
return self.getPlugin("logo")._fireEvent("onUpdate");
657
getPlay: function() {
658
return self.getPlugin("play")._fireEvent("onUpdate");
662
getConfig: function(copy) {
663
return copy ? clone(conf) : conf;
666
getFlashParams: function() {
670
loadPlugin: function(name, url, props, fn) {
672
// properties not supplied
673
if (typeof props == 'function') {
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);
685
var p = new Plugin(name, null, self, arg);
691
getState: function() {
692
return self.isLoaded() ? api.fp_getState() : -1;
696
play: function(clip, instream) {
699
if (clip !== undefined) {
700
self._api().fp_play(clip, instream);
702
self._api().fp_play();
706
if (self.isLoaded()) {
708
} else if ( isUnloading ) {
709
setTimeout(function() {
710
self.play(clip, instream);
714
self.load(function() {
722
getVersion: function() {
723
var js = "flowplayer.js 3.2.4";
724
if (self.isLoaded()) {
725
var ver = api.fp_getVersion();
733
if (!self.isLoaded()) {
734
throw "Flowplayer " +self.id()+ " not loaded when calling an API method";
739
setClip: function(clip) {
740
self.setPlaylist([clip]);
744
getIndex: function() {
748
_swfHeight: function() {
749
return api.clientHeight;
756
each(("Click*,Load*,Unload*,Keypress*,Volume*,Mute*,Unmute*,PlaylistReplace,ClipAdd,Fullscreen*,FullscreenExit,Error,MouseOver,MouseOut").split(","),
758
var name = "on" + this;
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);
771
self[name] = function(fn) {
772
bind(listeners, name, fn);
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(","),
784
self[name] = function(a1, a2) {
785
if (!self.isLoaded()) { return self; }
789
if (a1 !== undefined && a2 !== undefined) {
790
ret = api["fp_" + name](a1, a2);
793
ret = (a1 === undefined) ? api["fp_" + name]() : api["fp_" + name](a1);
797
return ret === 'undefined' || ret === undefined ? self : ret;
805
// {{{ public method: _fireEvent
807
self._fireEvent = function(a) {
809
if (typeof a == 'string') { a = [a]; }
811
var evt = a[0], arg0 = a[1], arg1 = a[2], arg2 = a[3], i = 0;
812
if (conf.debug) { log(a); }
815
if (!self.isLoaded() && evt == 'onLoad' && arg0 == 'player') {
817
api = api || el(apiId);
818
swfHeight = self._swfHeight();
820
each(playlist, function() {
821
this._fireEvent("onLoad");
824
each(plugins, function(name, p) {
825
p._fireEvent("onUpdate");
828
commonClip._fireEvent("onLoad");
831
// other onLoad events are skipped
832
if (evt == 'onLoad' && arg0 != 'player') { return; }
835
// "normalize" error handling
836
if (evt == 'onError') {
837
if (typeof arg0 == 'string' || (typeof arg0 == 'number' && typeof arg1 == 'number')) {
844
if (evt == 'onContextMenu') {
845
each(conf.contextMenu[arg0], function(key, fn) {
851
if (evt == 'onPluginEvent' || evt == 'onBeforePluginEvent') {
852
var name = arg0.name || arg0;
853
var p = plugins[name];
856
p._fireEvent("onUpdate", arg0);
857
return p._fireEvent(arg1, a.slice(3));
862
// replace whole playlist
863
if (evt == 'onPlaylistReplace') {
866
each(arg0, function() {
867
playlist.push(new Clip(this, index++, self));
871
// insert new clip to the playlist. arg0 = clip, arg1 = index
872
if (evt == 'onClipAdd') {
874
// instream clip additions are ignored at this point
875
if (arg0.isInStream) { return; }
877
// add new clip into playlist
878
arg0 = new Clip(arg0, arg1, self);
879
playlist.splice(arg1, 0, arg0);
881
// increment index variable for the rest of the clips on playlist
882
for (i = arg1 + 1; i < playlist.length; i++) {
891
if (typeof arg0 == 'number' && arg0 < playlist.length) {
894
var clip = playlist[arg0];
897
ret = clip._fireEvent(evt, arg1, arg2);
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);
907
// trigger player event
908
each(listeners[evt], function() {
909
ret = this.call(self, arg0, arg1);
911
// remove cached entry
913
listeners[evt].splice(i, 1);
917
if (ret === false) { return false; }
931
// replace previous installation
933
$f(wrapper).getParent().innerHTML = "";
934
playerIndex = $f(wrapper).getIndex();
935
players[playerIndex] = self;
937
// register this player into global array of instances
940
playerIndex = players.length -1;
943
wrapperHeight = parseInt(wrapper.style.height, 10) || wrapper.clientHeight;
946
playerId = wrapper.id || "fp" + makeId();
947
apiId = params.id || playerId + "_api";
949
conf.playerId = playerId;
952
// plain url is given as config
953
if (typeof conf == 'string') {
954
conf = {clip:{url:conf}};
957
if (typeof conf.clip == 'string') {
958
conf.clip = {url: conf.clip};
961
// common clip is always there
962
conf.clip = conf.clip || {};
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);
970
commonClip = new Clip(conf.clip, -1, self);
973
conf.playlist = conf.playlist || [conf.clip];
977
each(conf.playlist, function() {
981
/* sometimes clip is given as array. this is not accepted. */
982
if (typeof clip == 'object' && clip.length) {
983
clip = {url: "" + clip};
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') {
993
// modify playlist in configuration
994
conf.playlist[index] = clip;
996
// populate playlist array
997
clip = new Clip(clip, index, self);
1003
each(conf, function(key, val) {
1004
if (typeof val == 'function') {
1006
// common clip event
1007
if (commonClip[key]) {
1008
commonClip[key](val);
1012
bind(listeners, key, val);
1015
// no need to supply for the Flash component
1022
each(conf.plugins, function(name, val) {
1024
plugins[name] = new Plugin(name, val, self);
1029
// setup controlbar plugin if not explicitly defined
1030
if (!conf.plugins || conf.plugins.controls === undefined) {
1031
plugins.controls = new Plugin("controls", null, self);
1034
// setup canvas as plugin
1035
plugins.canvas = new Plugin("canvas", null, self);
1037
html = wrapper.innerHTML;
1040
function doClick(e) {
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 ) {
1048
if (!self.isLoaded() && self._fireEvent("onBeforeClick") !== false) {
1051
return stopEvent(e);
1054
function installPlayer() {
1055
// defer loading upon click
1056
if (html.replace(/\s/g, '') !== '') {
1058
if (wrapper.addEventListener) {
1059
wrapper.addEventListener("click", doClick, false);
1061
} else if (wrapper.attachEvent) {
1062
wrapper.attachEvent("onclick", doClick);
1065
// player is loaded upon page load
1068
// prevent default action from wrapper. (fixes safari problems)
1069
if (wrapper.addEventListener) {
1070
wrapper.addEventListener("click", stopEvent, false);
1077
// now that the player is initialized, wait for the plugin chain to finish
1078
// before actually changing the dom
1079
setTimeout(installPlayer, 0);
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; }
1089
// we have a DOM element so page is already loaded
1101
// {{{ flowplayer() & statics
1103
// container for player instances
1107
// this object is returned when multiple player's are requested
1108
function Iterator(arr) {
1110
this.length = arr.length;
1112
this.each = function(fn) {
1116
this.size = function() {
1121
// these two variables are the only global variables
1122
window.flowplayer = window.$f = function() {
1123
var instance = null;
1124
var arg = arguments[0];
1127
if (!arguments.length) {
1128
each(players, function() {
1129
if (this.isLoaded()) {
1135
return instance || players[0];
1138
if (arguments.length == 1) {
1141
if (typeof arg == 'number') {
1142
return players[arg];
1145
// $f(wrapper || 'containerId' || '*');
1150
return new Iterator(players);
1153
// $f(wrapper || 'containerId');
1154
each(players, function() {
1155
if (this.id() == arg.id || this.id() == arg || this.getParent() == arg) {
1166
if (arguments.length > 1) {
1168
// flashembed parameters
1169
var params = arguments[1],
1170
conf = (arguments.length == 3) ? arguments[2] : {};
1173
if (typeof params == 'string') {
1174
params = {src: params};
1180
expressInstall: "http://static.flowplayer.org/swf/expressinstall.swf",
1185
if (typeof arg == 'string') {
1187
// select arg by classname
1188
if (arg.indexOf(".") != -1) {
1191
each(select(arg), function() {
1192
instances.push(new Player(this, clone(params), clone(conf)));
1195
return new Iterator(instances);
1197
// select node by id
1200
return new Player(node !== null ? node : arg, params, conf);
1204
// arg is a DOM element
1206
return new Player(arg, params, conf);
1216
// called by Flash External Interface
1217
fireEvent: function() {
1218
var a = [].slice.call(arguments);
1220
return p ? p._fireEvent(a.slice(1)) : null;
1224
// create plugins by modifying Player's prototype
1225
addPlugin: function(name, fn) {
1226
Player.prototype[name] = fn;
1230
// utility methods for plugin developers
1240
//{{{ jQuery support
1242
if (typeof jQuery == 'function') {
1244
jQuery.fn.flowplayer = function(params, conf) {
1247
if (!arguments.length || typeof arguments[0] == 'number') {
1249
this.each(function() {
1255
return arguments.length ? arr[arguments[0]] : new Iterator(arr);
1258
// create flowplayer instances
1259
return this.each(function() {
1260
$f(this, clone(params), conf ? clone(conf) : {});
1273
* jQuery Tools 3.2.4 / Flashembed - New wave Flash embedding
1275
* NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.
1277
* http://flowplayer.org/tools/toolbox/flashembed.html
1279
* Since : March 2008
1284
var IE = document.all,
1285
URL = 'http://www.adobe.com/go/getflashplayer',
1286
JQUERY = typeof jQuery == 'function',
1287
RE = /(\d+)[^\d]+(\d+)[^\d]*(\d*)/,
1292
id: "_" + ("" + Math.random()).slice(9),
1294
// flashembed defaults
1295
allowfullscreen: true,
1296
allowscriptaccess: 'always',
1299
// flashembed specific options
1302
expressInstall: null,
1307
// version 9 bugfix: (http://blog.deconcept.com/2006/07/28/swfobject-143-released/)
1308
if (window.attachEvent) {
1309
window.attachEvent("onbeforeunload", function() {
1310
__flash_unloadHandler = function() {};
1311
__flash_savedUnloadHandler = function() {};
1316
function extend(to, from) {
1318
for (var key in from) {
1319
if (from.hasOwnProperty(key)) {
1320
to[key] = from[key];
1327
// used by asString method
1328
function map(arr, func) {
1330
for (var i in arr) {
1331
if (arr.hasOwnProperty(i)) {
1332
newArr[i] = func(arr[i]);
1338
window.flashembed = function(root, opts, conf) {
1340
// root must be found / loaded
1341
if (typeof root == 'string') {
1342
root = document.getElementById(root.replace("#", ""));
1346
if (!root) { return; }
1348
if (typeof opts == 'string') {
1352
return new Flash(root, extend(extend({}, GLOBAL_OPTS), opts), conf);
1355
// flashembed "static" API
1356
var f = extend(window.flashembed, {
1360
getVersion: function() {
1364
ver = navigator.plugins["Shockwave Flash"].description.slice(16);
1368
fo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");
1369
ver = fo && fo.GetVariable("$version");
1373
fo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");
1374
ver = fo && fo.GetVariable("$version");
1380
return ver ? [ver[1], ver[3]] : [0, 0];
1383
asString: function(obj) {
1385
if (obj === null || obj === undefined) { return null; }
1386
var type = typeof obj;
1387
if (type == 'object' && obj.push) { type = 'array'; }
1392
obj = obj.replace(new RegExp('(["\\\\])', 'g'), '\\$1');
1394
// flash does not handle %- characters well. transforms "50%" to "50pct" (a dirty hack, I admit)
1395
obj = obj.replace(/^\s?(\d+\.?\d+)%/, "$1pct");
1396
return '"' +obj+ '"';
1399
return '['+ map(obj, function(el) {
1400
return f.asString(el);
1404
return '"function()"';
1408
for (var prop in obj) {
1409
if (obj.hasOwnProperty(prop)) {
1410
str.push('"'+prop+'":'+ f.asString(obj[prop]));
1413
return '{'+str.join(',')+'}';
1416
// replace ' --> " and remove spaces
1417
return String(obj).replace(/\s/g, " ").replace(/\'/g, "\"");
1420
getHTML: function(opts, conf) {
1422
opts = extend({}, opts);
1424
/******* OBJECT tag and it's attributes *******/
1425
var html = '<object width="' + opts.width +
1426
'" height="' + opts.height +
1427
'" id="' + opts.id +
1428
'" name="' + opts.id + '"';
1430
if (opts.cachebusting) {
1431
opts.src += ((opts.src.indexOf("?") != -1 ? "&" : "?") + Math.random());
1434
if (opts.w3c || !IE) {
1435
html += ' data="' +opts.src+ '" type="application/x-shockwave-flash"';
1437
html += ' classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"';
1442
/******* nested PARAM tags *******/
1443
if (opts.w3c || IE) {
1444
html += '<param name="movie" value="' +opts.src+ '" />';
1447
// not allowed params
1448
opts.width = opts.height = opts.id = opts.w3c = opts.src = null;
1449
opts.onFail = opts.version = opts.expressInstall = null;
1451
for (var key in opts) {
1453
html += '<param name="'+ key +'" value="'+ opts[key] +'" />';
1457
/******* FLASHVARS *******/
1461
for (var k in conf) {
1464
vars += k +'='+ (/function|object/.test(typeof val) ? f.asString(val) : val) + '&';
1467
vars = vars.slice(0, -1);
1468
html += '<param name="flashvars" value=\'' + vars + '\' />';
1471
html += "</object>";
1476
isSupported: function(ver) {
1477
return VERSION[0] > ver[0] || VERSION[0] == ver[0] && VERSION[1] >= ver[1];
1482
var VERSION = f.getVersion();
1484
function Flash(root, opts, conf) {
1487
if (f.isSupported(opts.version)) {
1488
root.innerHTML = f.getHTML(opts, conf);
1491
} else if (opts.expressInstall && f.isSupported([6, 65])) {
1492
root.innerHTML = f.getHTML(extend(opts, {src: opts.expressInstall}), {
1493
MMredirectURL: location.href,
1494
MMplayerType: 'PlugIn',
1495
MMdoctitle: document.title
1500
// fail #2.1 custom content inside container
1501
if (!root.innerHTML.replace(/\s/g, '')) {
1503
"<h2>Flash version " + opts.version + " or greater is required</h2>" +
1505
(VERSION[0] > 0 ? "Your version is " + VERSION : "You have no flash plugin installed") +
1508
(root.tagName == 'A' ? "<p>Click here to download latest version</p>" :
1509
"<p>Download latest version from <a href='" + URL + "'>here</a></p>");
1511
if (root.tagName == 'A') {
1512
root.onclick = function() {
1513
location.href = URL;
1520
var ret = opts.onFail.call(this);
1521
if (typeof ret == 'string') { root.innerHTML = ret; }
1525
// http://flowplayer.org/forum/8/18186#post-18593
1527
window[opts.id] = document.getElementById(opts.id);
1530
// API methods for callback
1533
getRoot: function() {
1537
getOptions: function() {
1542
getConf: function() {
1546
getApi: function() {
1547
return root.firstChild;
1553
// setup jquery support
1556
// tools version number
1557
jQuery.tools = jQuery.tools || {version: '3.2.4'};
1559
jQuery.tools.flashembed = {
1563
jQuery.fn.flashembed = function(opts, conf) {
1564
return this.each(function() {
1565
$(this).data("flashembed", flashembed(this, opts, conf));