580cf0bd5edea80216e7995db729f9244cdc7a27
[lhc/web/wiklou.git] / js2 / mwEmbed / libEmbedVideo / flowplayerEmbed.js
1 /**
2 * metavid: mv_flashEmbed builds off of flowplayer api (included first in this file)
3 * THIS WILL BE DEPRECIATED SOON
4 */
5
6 /**
7 * flowplayer.js 3.0.0-rc5. The Flowplayer API.
8 *
9 * This file is part of Flowplayer, http://flowplayer.org
10 *
11 * Author: Tero Piirainen, <support@flowplayer.org>
12 * Copyright (c) 2008 Flowplayer Ltd
13 *
14 * Released under the MIT License:
15 * http://www.opensource.org/licenses/mit-license.php
16 *
17 * Version: 3.0.0-rc5 - Thu Nov 20 2008 22:09:49 GMT-0000 (GMT+00:00)
18 */
19 ( function() {
20
21 /*
22 FEATURES
23 --------
24 - handling multiple instances
25 - Flowplayer programming API
26 - Flowplayer event model
27 - player loading / unloading
28 - $f() function
29 - jQuery support
30 */
31
32
33 /*jslint glovar: true, browser: true */
34 /*global flowplayer, $f */
35
36 // {{{ private utility methods
37
38 function log( args ) {
39
40 // write into opera console
41 if ( typeof opera == 'object' ) {
42 opera.postError( "$f.fireEvent: " + args.join( " | " ) );
43
44
45 } else if ( typeof console == 'object' ) {
46 console.log( "$f.fireEvent", [].slice.call( args ) );
47 }
48 }
49
50
51 // thanks: http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone
52 function clone ( obj ) {
53 if ( !obj || typeof obj != 'object' ) { return obj; }
54 var temp = new obj.constructor();
55 for ( var key in obj ) {
56 if ( obj.hasOwnProperty( key ) ) {
57 temp[key] = clone ( obj[key] );
58 }
59 }
60 return temp;
61 }
62
63 // stripped from jQuery, thanks John Resig
64 function each( obj, fn ) {
65 if ( !obj ) { return; }
66
67 var name, i = 0, length = obj.length;
68
69 // object
70 if ( length === undefined ) {
71 for ( name in obj ) {
72 if ( fn.call( obj[name], name, obj[name] ) === false ) { break; }
73 }
74
75 // array
76 } else {
77 for ( var value = obj[0];
78 i < length && fn.call( value, i, value ) !== false; value = obj[++i] ) {
79 }
80 }
81
82 return obj;
83 }
84
85
86 // convenience
87 function el( id ) {
88 return document.getElementById( id );
89 }
90
91
92 // used extensively. a very simple implementation.
93 function extend( to, from, skipFuncs ) {
94 if ( to && from ) {
95 each( from, function( name, value ) {
96 if ( !skipFuncs || typeof value != 'function' ) {
97 to[name] = value;
98 }
99 } );
100 }
101 }
102
103 // var arr = select("elem.className");
104 function select( query ) {
105 var index = query.indexOf( "." );
106 if ( index != - 1 ) {
107 var tag = query.substring( 0, index ) || "*";
108 var klass = query.substring( index + 1, query.length );
109 var els = [];
110 each( document.getElementsByTagName( tag ), function() {
111 if ( this.className && this.className.indexOf( klass ) != - 1 ) {
112 els.push( this );
113 }
114 } );
115 return els;
116 }
117 }
118
119 // fix event inconsistencies across browsers
120 function stopEvent( e ) {
121 e = e || window.event;
122
123 if ( e.preventDefault ) {
124 e.stopPropagation();
125 e.preventDefault();
126
127 } else {
128 e.returnValue = false;
129 e.cancelBubble = true;
130 }
131 return false;
132 }
133
134 // push an event listener into existing array of listeners
135 function bind( to, evt, fn ) {
136 to[evt] = to[evt] || [];
137 to[evt].push( fn );
138 }
139
140
141 // generates an unique id
142 function makeId() {
143 return "_" + ( "" + Math.random() ).substring( 2, 10 );
144 }
145
146 // }}}
147
148
149 // {{{ Clip
150
151 var Clip = function( json, index, player ) {
152
153 // private variables
154 var self = this;
155 var cuepoints = { };
156 var listeners = { };
157 this.index = index;
158
159 // instance variables
160 if ( typeof json == 'string' ) {
161 json = { url:json };
162 }
163
164 extend( this, json, true );
165
166 // event handling
167 each( ( "Begin*,Start,Pause*,Resume*,Seek*,Stop*,Finish*,LastSecond,Update,BufferFull,BufferEmpty,BufferStop" ).split( "," ),
168 function() {
169
170 var evt = "on" + this;
171
172 // before event
173 if ( evt.indexOf( "*" ) != - 1 ) {
174 evt = evt.substring( 0, evt.length - 1 );
175 var before = "onBefore" + evt.substring( 2 );
176
177 self[before] = function( fn ) {
178 bind( listeners, before, fn );
179 return self;
180 };
181 }
182
183 self[evt] = function( fn ) {
184 bind( listeners, evt, fn );
185 return self;
186 };
187
188
189 // set common clip event listeners to player level
190 if ( index == - 1 ) {
191 if ( self[before] ) {
192 player[before] = self[before];
193 }
194 if ( self[evt] ) {
195 player[evt] = self[evt];
196 }
197 }
198
199 } );
200
201 extend( this, {
202
203
204 onCuepoint: function( points, fn ) {
205
206 // embedded cuepoints
207 if ( arguments.length == 1 ) {
208 cuepoints.embedded = [null, points];
209 return self;
210 }
211
212 if ( typeof points == 'number' ) {
213 points = [points];
214 }
215
216 var fnId = makeId();
217 cuepoints[fnId] = [points, fn];
218
219 if ( player.isLoaded() ) {
220 player._api().fp_addCuepoints( points, index, fnId );
221 }
222
223 return self;
224 },
225
226 update: function( json ) {
227 extend( self, json );
228
229 if ( player.isLoaded() ) {
230 player._api().fp_updateClip( json, index );
231 }
232 var conf = player.getConfig();
233 var clip = ( index == - 1 ) ? conf.clip : conf.playlist[index];
234 extend( clip, json, true );
235 },
236
237
238 // internal event for performing clip tasks. should be made private someday
239 _fireEvent: function( evt, arg1, arg2, target ) {
240
241 if ( evt == 'onLoad' ) {
242 each( cuepoints, function( key, val ) {
243 player._api().fp_addCuepoints( val[0], index, key );
244 } );
245 return false;
246 }
247
248 // target clip we are working against
249 if ( index != - 1 ) {
250 target = self;
251 }
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 if ( evt == 'onStart' || evt == 'onUpdate' ) {
261
262 extend( target, arg1 );
263
264 if ( !target.duration ) {
265 target.duration = arg1.metaData.duration;
266 } else {
267 target.fullDuration = arg1.metaData.duration;
268 }
269 }
270
271 var ret = true;
272 each( listeners[evt], function() {
273 ret = this.call( player, target, arg1 );
274 } );
275 return ret;
276 }
277
278 } );
279
280
281 // get cuepoints from config
282 if ( json.onCuepoint ) {
283 self.onCuepoint.apply( self, json.onCuepoint );
284 delete json.onCuepoint;
285 }
286
287 // get other events
288 each( json, function( key, val ) {
289 if ( typeof val == 'function' ) {
290 bind( listeners, key, val );
291 delete json[key];
292 }
293 } );
294
295
296 // setup common clip event callbacks for Player object too (shortcuts)
297 if ( index == - 1 ) {
298 player.onCuepoint = this.onCuepoint;
299 }
300
301 };
302
303 // }}}
304
305
306 // {{{ Plugin
307
308 var Plugin = function( name, json, player, fn ) {
309
310 var listeners = { };
311 var self = this;
312 var hasMethods = false;
313
314 if ( fn ) {
315 extend( listeners, fn );
316 }
317
318 // custom callback functions in configuration
319 each( json, function( key, val ) {
320 if ( typeof val == 'function' ) {
321 listeners[key] = val;
322 delete json[key];
323 }
324 } );
325
326 // core plugin methods
327 extend( this, {
328
329 animate: function( props, speed, fn ) {
330 if ( !props ) {
331 return self;
332 }
333
334 if ( typeof speed == 'function' ) {
335 fn = speed;
336 speed = 500;
337 }
338
339 if ( typeof props == 'string' ) {
340 var key = props;
341 props = { };
342 props[key] = speed;
343 speed = 500;
344 }
345
346 if ( fn ) {
347 var fnId = makeId();
348 listeners[fnId] = fn;
349 }
350
351 if ( speed === undefined ) { speed = 500; }
352 json = player._api().fp_animate( name, props, speed, fnId );
353 return self;
354 },
355
356 css: function( props, val ) {
357 if ( val !== undefined ) {
358 var css = { };
359 css[props] = val;
360 props = css;
361 }
362 try {
363 json = player._api().fp_css( name, props );
364 extend( self, json );
365 return self;
366 } catch ( e ) {
367 js_log( 'flow player could not set css: ' + json );
368 }
369 },
370
371 show: function() {
372 this.display = 'block';
373 player._api().fp_showPlugin( name );
374 return self;
375 },
376
377 hide: function() {
378 this.display = 'none';
379 player._api().fp_hidePlugin( name );
380 return self;
381 },
382
383 toggle: function() {
384 this.display = player._api().fp_togglePlugin( name );
385 return self;
386 },
387
388 fadeTo: function( o, speed, fn ) {
389
390 if ( typeof speed == 'function' ) {
391 fn = speed;
392 speed = 500;
393 }
394
395 if ( fn ) {
396 var fnId = makeId();
397 listeners[fnId] = fn;
398 }
399 this.display = player._api().fp_fadeTo( name, o, speed, fnId );
400 this.opacity = o;
401 return self;
402 },
403
404 fadeIn: function( speed, fn ) {
405 return self.fadeTo( 1, speed, fn );
406 },
407
408 fadeOut: function( speed, fn ) {
409 return self.fadeTo( 0, speed, fn );
410 },
411
412 getName: function() {
413 return name;
414 },
415
416
417 // internal method not meant to be used by clients
418 _fireEvent: function( evt, arg ) {
419
420
421 // update plugins properties & methods
422 if ( evt == 'onUpdate' ) {
423 var json = arg || player._api().fp_getPlugin( name );
424 if ( !json ) { return; }
425
426 extend( self, json );
427 delete self.methods;
428
429 if ( !hasMethods ) {
430 each( json.methods, function() {
431 var method = "" + this;
432
433 self[method] = function() {
434 var a = [].slice.call( arguments );
435 var ret = player._api().fp_invoke( name, method, a );
436 return ret == 'undefined' ? self : ret;
437 };
438 } );
439 hasMethods = true;
440 }
441 }
442
443 // plugin callbacks
444 var fn = listeners[evt];
445
446 if ( fn ) {
447
448 fn.call( self, arg );
449
450 // "one-shot" callback
451 if ( evt.substring( 0, 1 ) == "_" ) {
452 delete listeners[evt];
453 }
454 }
455 }
456
457 } );
458
459 };
460
461
462 // }}}
463
464
465 function Player( wrapper, params, conf ) {
466
467 // private variables (+ arguments)
468 var
469 self = this,
470 api = null,
471 html,
472 commonClip,
473 playlist = [],
474 plugins = { },
475 listeners = { },
476 playerId,
477 apiId,
478 activeIndex,
479 swfHeight,
480 wrapperHeight;
481
482
483 // {{{ public methods
484
485 extend( self, {
486
487 id: function() {
488 return playerId;
489 },
490
491 isLoaded: function() {
492 return ( api !== null );
493 },
494
495 getParent: function() {
496 return wrapper;
497 },
498
499 hide: function( all ) {
500 if ( all ) { wrapper.style.height = "0px"; }
501 if ( api ) { api.style.height = "0px"; }
502 return self;
503 },
504
505 show: function() {
506 wrapper.style.height = wrapperHeight + "px";
507 if ( api ) { api.style.height = swfHeight + "px"; }
508 return self;
509 },
510
511 isHidden: function() {
512 return api && parseInt( api.style.height, 10 ) === 0;
513 },
514
515
516 load: function( fn ) {
517
518 if ( !api && self._fireEvent( "onBeforeLoad" ) !== false ) {
519
520 // unload all instances
521 each( players, function() {
522 this.unload();
523 } );
524
525 html = wrapper.innerHTML;
526 flashembed( wrapper, params, { config: conf } );
527
528 // function argument
529 if ( fn ) {
530 fn.cached = true;
531 bind( listeners, "onLoad", fn );
532 }
533 }
534
535 return self;
536 },
537
538 unload: function() {
539
540 if ( api && html.replace( /\s/g, '' ) !== '' && !api.fp_isFullscreen() &&
541 self._fireEvent( "onBeforeUnload" ) !== false ) {
542 api.fp_close();
543 wrapper.innerHTML = html;
544 self._fireEvent( "onUnload" );
545 api = null;
546 }
547
548 return self;
549 },
550
551 getClip: function( index ) {
552 if ( index === undefined ) {
553 index = activeIndex;
554 }
555 return playlist[index];
556 },
557
558
559 getCommonClip: function() {
560 return commonClip;
561 },
562
563 getPlaylist: function() {
564 return playlist;
565 },
566
567 getPlugin: function( name ) {
568 var plugin = plugins[name];
569
570 // create plugin if nessessary
571 if ( !plugin && self.isLoaded() ) {
572 var json = self._api().fp_getPlugin( name );
573 if ( json ) {
574 plugin = new Plugin( name, json, self );
575 plugins[name] = plugin;
576 }
577 }
578 return plugin;
579 },
580
581 getScreen: function() {
582 return self.getPlugin( "screen" );
583 },
584
585 getControls: function() {
586 return self.getPlugin( "controls" );
587 },
588
589 getConfig: function() {
590 return clone ( conf );
591 },
592
593 getFlashParams: function() {
594 return params;
595 },
596
597 loadPlugin: function( name, url, props, fn ) {
598
599 // properties not supplied
600 if ( typeof props == 'function' ) {
601 fn = props;
602 props = { };
603 }
604
605 // if fn not given, make a fake id so that plugin's onUpdate get's fired
606 var fnId = fn ? makeId() : "_";
607 self._api().fp_loadPlugin( name, url, props, fnId );
608
609 // create new plugin
610 var arg = { };
611 arg[fnId] = fn;
612 var p = new Plugin( name, null, self, arg );
613 plugins[name] = p;
614 return p;
615 },
616
617
618 getState: function() {
619 return api ? api.fp_getState() : - 1;
620 },
621
622 // "lazy" play
623 play: function( clip ) {
624
625 function play() {
626 if ( clip !== undefined ) {
627 self._api().fp_play( clip );
628 } else {
629 if ( typeof self._api().fp_play == 'function' )
630 self._api().fp_play();
631 }
632 }
633
634 if ( api ) {
635 play();
636
637 } else {
638 self.load( function() {
639 play();
640 } );
641 }
642
643 return self;
644 },
645
646 getVersion: function() {
647 var js = "flowplayer.js 3.0.0-rc5";
648 if ( api ) {
649 var ver = api.fp_getVersion();
650 ver.push( js );
651 return ver;
652 }
653 return js;
654 },
655
656 _api: function() {
657 if ( !api ) {
658 throw "Flowplayer " + self.id() + " not loaded. Try moving your call to player's onLoad event";
659 }
660 return api;
661 },
662
663 _dump: function() {
664 console.log( listeners );
665 }
666
667 } );
668
669
670 // event handlers
671 each( ( "Click*,Load*,Unload*,Keypress*,Volume*,Mute*,Unmute*,PlaylistReplace,Fullscreen*,FullscreenExit,Error" ).split( "," ),
672 function() {
673 var name = "on" + this;
674
675 // before event
676 if ( name.indexOf( "*" ) != - 1 ) {
677 name = name.substring( 0, name.length - 1 );
678 var name2 = "onBefore" + name.substring( 2 );
679 self[name2] = function( fn ) {
680 bind( listeners, name2, fn );
681 return self;
682 };
683 }
684
685 // normal event
686 self[name] = function( fn ) {
687 bind( listeners, name, fn );
688 return self;
689 };
690 }
691 );
692
693
694 // core API methods
695 each( ( "pause,resume,mute,unmute,stop,toggle,seek,getStatus,getVolume,setVolume,getTime,isPaused,isPlaying,startBuffering,stopBuffering,isFullscreen,reset" ).split( "," ),
696 function() {
697 var name = this;
698
699 self[name] = function( arg ) {
700 if ( !api ) { return self; }
701 try {
702 var ret = ( arg === undefined ) ? api["fp_" + name]() : api["fp_" + name]( arg );
703 return ret == 'undefined' ? self : ret;
704 } catch ( e ) {
705 js_log( 'flowplayer could not access fp_ ' + name );
706 }
707 };
708 }
709 );
710
711 // }}}
712
713
714 // {{{ public method: _fireEvent
715
716 self._fireEvent = function( evt, arg0, arg1, arg2 ) {
717
718 if ( conf.debug ) {
719 log( arguments );
720 }
721
722 // internal onLoad
723 if ( evt == 'onLoad' && !api ) {
724
725 api = api || el( apiId );
726 swfHeight = api.clientHeight;
727
728 each( playlist, function() {
729 this._fireEvent( "onLoad" );
730 } );
731
732 each( plugins, function( name, p ) {
733 p._fireEvent( "onUpdate" );
734 } );
735
736
737 commonClip._fireEvent( "onLoad" );
738 }
739
740 if ( evt == 'onContextMenu' ) {
741 each( conf.contextMenu[arg0], function( key, fn ) {
742 fn.call( self );
743 } );
744 return;
745 }
746
747 if ( evt == 'onPluginEvent' ) {
748 var name = arg0.name || arg0;
749 var p = plugins[name];
750 if ( p ) {
751 if ( arg0.name ) {
752 p._fireEvent( "onUpdate", arg0 );
753 }
754 p._fireEvent( arg1 );
755 }
756 return;
757 }
758
759 // onPlaylistReplace
760 if ( evt == 'onPlaylistReplace' ) {
761 playlist = [];
762 var index = 0;
763 each( arg0, function() {
764 playlist.push( new Clip( this, index++ ) );
765 } );
766 }
767
768 var ret = true;
769
770 // clip event
771 if ( arg0 === 0 || ( arg0 && arg0 >= 0 ) ) {
772
773 activeIndex = arg0;
774 var clip = playlist[arg0];
775
776 if ( clip ) {
777 ret = clip._fireEvent( evt, arg1, arg2 );
778 }
779
780 if ( !clip || ret !== false ) {
781
782 // clip argument is given for common clip, because it behaves as the target
783 ret = commonClip._fireEvent( evt, arg1, arg2, clip );
784 }
785 }
786
787 // player event
788 var i = 0;
789 each( listeners[evt], function() {
790 ret = this.call( self, arg0 );
791
792 // remove cached entry
793 if ( this.cached ) {
794 listeners[evt].splice( i, 1 );
795 }
796
797 // break loop
798 if ( ret === false ) { return false; }
799 i++;
800
801 } );
802
803 return ret;
804 };
805
806 // }}}
807
808
809 // {{{ init
810
811 function init() {
812
813 if ( $f( wrapper ) ) {
814 return null;
815 }
816
817 wrapperHeight = parseInt( wrapper.style.height ) || wrapper.clientHeight;
818
819 // register this player into global array of instances
820 players.push( self );
821
822
823 // flashembed parameters
824 if ( typeof params == 'string' ) {
825 params = { src: params };
826 }
827
828 // playerId
829 playerId = wrapper.id || "fp" + makeId();
830 apiId = params.id || playerId + "_api";
831 params.id = apiId;
832 conf.playerId = playerId;
833
834
835 // plain url is given as config
836 if ( typeof conf == 'string' ) {
837 conf = { clip: { url:conf } };
838 }
839
840 // common clip is always there
841 conf.clip = conf.clip || { };
842 commonClip = new Clip( conf.clip, - 1, self );
843
844
845 // wrapper href as playlist
846 if ( wrapper.getAttribute( "href" ) ) {
847 conf.playlist = [ { url:wrapper.getAttribute( "href", 2 ) }];
848 }
849
850 // playlist
851 conf.playlist = conf.playlist || [conf.clip];
852
853 var index = 0;
854 each( conf.playlist, function() {
855
856 var clip = this;
857
858 // clip is an array, we don't allow that
859 if ( typeof clip == 'object' && clip.length ) {
860 clip = "" + clip;
861 }
862
863 if ( !clip.url && typeof clip == 'string' ) {
864 clip = { url: clip };
865 }
866
867 // populate common clip properties to each clip
868 extend( clip, conf.clip, true );
869
870 // modify configuration playlist
871 conf.playlist[index] = clip;
872
873 // populate playlist array
874 clip = new Clip( clip, index, self );
875 playlist.push( clip );
876 index++;
877 } );
878
879
880 // event listeners
881 each( conf, function( key, val ) {
882 if ( typeof val == 'function' ) {
883 bind( listeners, key, val );
884 delete conf[key];
885 }
886 } );
887
888
889 // plugins
890 each( conf.plugins, function( name, val ) {
891 if ( val ) {
892 plugins[name] = new Plugin( name, val, self );
893 }
894 } );
895
896
897 // setup controlbar plugin if not explicitly defined
898 if ( !conf.plugins || conf.plugins.controls === undefined ) {
899 plugins.controls = new Plugin( "controls", null, self );
900 }
901
902 // Flowplayer uses black background by default
903 params.bgcolor = params.bgcolor || "#000000";
904
905
906 // setup default settings for express install
907 params.version = params.version || [9, 0];
908 params.expressInstall = 'http://www.flowplayer.org/swf/expressinstall.swf';
909
910
911 // click function
912 function doClick( e ) {
913 if ( self._fireEvent( "onBeforeClick" ) !== false ) {
914 self.load();
915 }
916 return stopEvent( e );
917 }
918
919 // defer loading upon click
920 html = wrapper.innerHTML;
921 if ( html.replace( /\s/g, '' ) !== '' ) {
922
923 if ( wrapper.addEventListener ) {
924 wrapper.addEventListener( "click", doClick, false );
925
926 } else if ( wrapper.attachEvent ) {
927 wrapper.attachEvent( "onclick", doClick );
928 }
929
930 // player is loaded upon page load
931 } else {
932
933 // prevent default action from wrapper (safari problem) loaded
934 if ( wrapper.addEventListener ) {
935 wrapper.addEventListener( "click", stopEvent, false );
936 }
937
938 // load player
939 self.load();
940 }
941
942 }
943
944 // possibly defer initialization until DOM get's loaded
945 if ( typeof wrapper == 'string' ) {
946 flashembed.domReady( function() {
947 var node = el( wrapper );
948
949 if ( !node ) {
950 throw "Flowplayer cannot access element: " + wrapper;
951 } else {
952 wrapper = node;
953 init();
954 }
955 } );
956
957 // we have a DOM element so page is already loaded
958 } else {
959 init();
960 }
961
962
963 // }}}
964
965
966 }
967
968
969 // {{{ flowplayer() & statics
970
971 // container for player instances
972 var players = [];
973
974
975 // this object is returned when multiple player's are requested
976 function Iterator( arr ) {
977
978 this.length = arr.length;
979
980 this.each = function( fn ) {
981 each( arr, fn );
982 };
983
984 this.size = function() {
985 return arr.length;
986 };
987 }
988
989 // these two variables are the only global variables
990 window.flowplayer = window.$f = function() {
991
992 var instance = null;
993 var arg = arguments[0];
994
995
996 // $f()
997 if ( !arguments.length ) {
998 each( players, function() {
999 if ( this.isLoaded() ) {
1000 instance = this;
1001 return false;
1002 }
1003 } );
1004
1005 return instance || players[0];
1006 }
1007
1008 if ( arguments.length == 1 ) {
1009
1010 // $f(index);
1011 if ( typeof arg == 'number' ) {
1012 return players[arg];
1013
1014
1015 // $f(wrapper || 'containerId' || '*');
1016 } else {
1017
1018 // $f("*");
1019 if ( arg == '*' ) {
1020 return new Iterator( players );
1021 }
1022
1023 // $f(wrapper || 'containerId');
1024 each( players, function() {
1025 if ( this.id() == arg.id || this.id() == arg || this.getParent() == arg ) {
1026 instance = this;
1027 return false;
1028 }
1029 } );
1030
1031 return instance;
1032 }
1033 }
1034
1035 // instance builder
1036 if ( arguments.length > 1 ) {
1037
1038 var swf = arguments[1];
1039 var conf = ( arguments.length == 3 ) ? arguments[2] : { };
1040
1041 if ( typeof arg == 'string' ) {
1042
1043 // select arg by classname
1044 if ( arg.indexOf( "." ) != - 1 ) {
1045 var instances = [];
1046
1047 each( select( arg ), function() {
1048 instances.push( new Player( this, clone ( swf ), clone ( conf ) ) );
1049 } );
1050
1051 return new Iterator( instances );
1052
1053 // select node by id
1054 } else {
1055 var node = el( arg );
1056 return new Player( node !== null ? node : arg, swf, conf );
1057 }
1058
1059
1060 // arg is a DOM element
1061 } else if ( arg ) {
1062 return new Player( arg, swf, conf );
1063 }
1064
1065 }
1066
1067 return null;
1068 };
1069
1070 extend( window.$f, {
1071
1072 // called by Flash External Interface
1073 fireEvent: function( id, evt, a0, a1, a2 ) {
1074 var p = $f( id );
1075 return p ? p._fireEvent( evt, a0, a1, a2 ) : null;
1076 },
1077
1078
1079 // create plugins by modifying Player's prototype
1080 addPlugin: function( name, fn ) {
1081 Player.prototype[name] = fn;
1082 return $f;
1083 },
1084
1085 // utility methods for plugin developers
1086 each: each,
1087
1088 extend: extend
1089
1090 } );
1091
1092 // }}}
1093
1094
1095 // {{{ jQuery support
1096
1097 if ( typeof jQuery == 'function' ) {
1098
1099 jQuery.prototype.flowplayer = function( params, conf ) {
1100
1101 // select instances
1102 if ( !arguments.length || typeof arguments[0] == 'number' ) {
1103 var arr = [];
1104 this.each( function() {
1105 var p = $f( this );
1106 if ( p ) {
1107 arr.push( p );
1108 }
1109 } );
1110 return arguments.length ? arr[arguments[0]] : new Iterator( arr );
1111 }
1112
1113 // create flowplayer instances
1114 return this.each( function() {
1115 $f( this, clone ( params ), conf ? clone ( conf ) : { } );
1116 } );
1117
1118 };
1119
1120 }
1121
1122 // }}}
1123
1124
1125 } )();
1126 /**
1127 * flashembed 0.34. Adobe Flash embedding script
1128 *
1129 * http://flowplayer.org/tools/flash-embed.html
1130 *
1131 * Copyright (c) 2008 Tero Piirainen (support@flowplayer.org)
1132 *
1133 * Released under the MIT License:
1134 * http://www.opensource.org/licenses/mit-license.php
1135 *
1136 * >> Basically you can do anything you want but leave this header as is <<
1137 *
1138 * first version 0.01 - 03/11/2008
1139 * version 0.34 - Tue Nov 11 2008 09:09:52 GMT-0000 (GMT+00:00)
1140 */
1141 ( function() {
1142
1143 // {{{ utility functions
1144
1145 var jQ = typeof jQuery == 'function';
1146
1147
1148 // from "Pro JavaScript techniques" by John Resig
1149 function isDomReady() {
1150
1151 if ( domReady.done ) { return false; }
1152
1153 var d = document;
1154 if ( d && d.getElementsByTagName && d.getElementById && d.body ) {
1155 clearInterval( domReady.timer );
1156 domReady.timer = null;
1157
1158 for ( var i = 0; i < domReady.ready.length; i++ ) {
1159 domReady.ready[i].call();
1160 }
1161
1162 domReady.ready = null;
1163 domReady.done = true;
1164 }
1165 }
1166
1167 // if jQuery is present, use it's more effective domReady method
1168 var domReady = jQ ? jQuery : function( f ) {
1169
1170 if ( domReady.done ) {
1171 return f();
1172 }
1173
1174 if ( domReady.timer ) {
1175 domReady.ready.push( f );
1176
1177 } else {
1178 domReady.ready = [f];
1179 domReady.timer = setInterval( isDomReady, 13 );
1180 }
1181 };
1182
1183
1184 // override extend params function
1185 function extend( to, from ) {
1186 if ( from ) {
1187 for ( key in from ) {
1188 if ( from.hasOwnProperty( key ) ) {
1189 to[key] = from[key];
1190 }
1191 }
1192 }
1193
1194 return to;
1195 }
1196
1197
1198 function concatVars( vars ) {
1199 var out = "";
1200
1201 for ( var key in vars ) {
1202 if ( vars[key] ) {
1203 out += [key] + '=' + asString( vars[key] ) + '&';
1204 }
1205 }
1206 return out.substring( 0, out.length - 1 );
1207 }
1208
1209
1210
1211 // JSON.asString() function
1212 function asString( obj ) {
1213
1214 switch ( typeOf( obj ) ) {
1215 case 'string':
1216 obj = obj.replace( new RegExp( '(["\\\\])', 'g' ), '\\$1' );
1217
1218 // flash does not handle %- characters well. transforms "50%" to "50pct" (a dirty hack, I admit)
1219 obj = obj.replace( /^\s?(\d+)%/, "$1pct" );
1220 return '"' + obj + '"';
1221
1222 case 'array':
1223 return '[' + map( obj, function( el ) {
1224 return asString( el );
1225 } ).join( ',' ) + ']';
1226
1227 case 'function':
1228 return '"function()"';
1229
1230 case 'object':
1231 var str = [];
1232 for ( var prop in obj ) {
1233 if ( obj.hasOwnProperty( prop ) ) {
1234 str.push( '"' + prop + '":' + asString( obj[prop] ) );
1235 }
1236 }
1237 return '{' + str.join( ',' ) + '}';
1238 }
1239
1240 // replace ' --> " and remove spaces
1241 return String( obj ).replace( /\s/g, " ").replace( /\'/g, "\"" );
1242 }
1243
1244
1245 // private functions
1246 function typeOf(obj) {
1247 if (obj === null || obj === undefined) { return false; }
1248 var type = typeof obj;
1249 return (type == 'object' && obj.push) ? 'array' : type;
1250 }
1251
1252
1253 // version 9 bugfix: (http://blog.deconcept.com/2006/07/28/swfobject-143-released/)
1254 if (window.attachEvent) {
1255 window.attachEvent("onbeforeunload", function() {
1256 __flash_unloadHandler = function() {};
1257 __flash_savedUnloadHandler = function() {};
1258 });
1259 }
1260
1261 function map(arr, func) {
1262 var newArr = [];
1263 for (var i in arr) {
1264 if (arr.hasOwnProperty(i)) {
1265 newArr[i] = func(arr[i]);
1266 }
1267 }
1268 return newArr;
1269 }
1270
1271 function getEmbedCode(p, c) {
1272 var html = ' < embed type = "application/x-shockwave-flash" ';
1273
1274 if (p.id) { extend(p, {name:p.id}); }
1275
1276 for (var key in p) {
1277 if (p[key] !== null) {
1278 html += key + '="' + p[key] + '"\n\t';
1279 }
1280 }
1281
1282 if (c) {
1283 html += 'flashvars=\'' + concatVars( c ) + '\'';
1284 }
1285
1286 // thanks Tom Price (07/17/2008)
1287 html += '/>';
1288
1289 return html;
1290 }
1291
1292 function getObjectCode( p, c, embeddable ) {
1293
1294 var html = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" ';
1295 html += 'width="' + p.width + '" height="' + p.height + '"';
1296
1297 // force id for IE. otherwise embedded Flash object cannot be returned
1298 if ( !p.id && document.all ) {
1299 p.id = "_" + ( "" + Math.random() ).substring( 5 );
1300 }
1301
1302 if ( p.id ) {
1303 html += ' id="' + p.id + '"';
1304 }
1305
1306 html += '>';
1307
1308 // sometimes ie fails to load flash if it's on cache
1309 if ( document.all ) {
1310 p.src += ( ( p.src.indexOf( "?" ) != - 1 ? "&" : "?" ) + Math.random() );
1311 }
1312
1313 html += '\n\t<param name="movie" value="' + p.src + '" />';
1314
1315 var e = extend( { }, p );
1316 e.id = e.width = e.height = e.src = null;
1317
1318 for ( var k in e ) {
1319 if ( e[k] !== null ) {
1320 html += '\n\t<param name="' + k + '" value="' + e[k] + '" />';
1321 }
1322 }
1323
1324 if ( c ) {
1325 html += '\n\t<param name="flashvars" value=\'' + concatVars( c ) + '\' />';
1326 }
1327
1328 if ( embeddable ) {
1329 html += getEmbedCode( p, c );
1330 }
1331
1332 html += "</object>";
1333
1334 return html;
1335 }
1336
1337 function getFullHTML( p, c ) {
1338 return getObjectCode( p, c, true );
1339 }
1340
1341 function getHTML( p, c ) {
1342 var isNav = navigator.plugins && navigator.mimeTypes && navigator.mimeTypes.length;
1343 return ( isNav ) ? getEmbedCode( p, c ) : getObjectCode( p, c );
1344 }
1345
1346 // }}}
1347
1348
1349 window.flashembed = function( root, userParams, flashvars ) {
1350
1351
1352 // {{{ construction
1353
1354 // setup params
1355 var params = {
1356
1357 // very common params
1358 src: '#',
1359 width: '100%',
1360 height: '100%',
1361
1362 // flashembed specific options
1363 version:null,
1364 onFail:null,
1365 expressInstall:null,
1366 debug: false,
1367
1368 // flashembed defaults
1369 // bgcolor: 'transparent',
1370 allowfullscreen: true,
1371 allowscriptaccess: 'always',
1372 quality: 'high',
1373 type: 'application/x-shockwave-flash',
1374 pluginspage: 'http://www.adobe.com/go/getflashplayer'
1375 };
1376
1377
1378 if ( typeof userParams == 'string' ) {
1379 userParams = { src: userParams };
1380 }
1381
1382 extend( params, userParams );
1383
1384 var version = flashembed.getVersion();
1385 var required = params.version;
1386 var express = params.expressInstall;
1387 var debug = params.debug;
1388
1389
1390 if ( typeof root == 'string' ) {
1391 var el = document.getElementById( root );
1392 if ( el ) {
1393 root = el;
1394 } else {
1395 domReady( function() {
1396 flashembed( root, userParams, flashvars );
1397 } );
1398 return;
1399 }
1400 }
1401
1402 if ( !root ) { return; }
1403
1404
1405 // is supported
1406 if ( !required || flashembed.isSupported( required ) ) {
1407 params.onFail = params.version = params.expressInstall = params.debug = null;
1408
1409 // root.innerHTML may cause broplems: http://domscripting.com/blog/display/99
1410 // thanks to: Ryan Rud
1411 // var tmp = document.createElement("extradiv");
1412 // tmp.innerHTML = getHTML();
1413 // root.appendChild(tmp);
1414
1415 root.innerHTML = getHTML( params, flashvars );
1416
1417 // return our API
1418 return root.firstChild;
1419
1420 // custom fail event
1421 } else if ( params.onFail ) {
1422 var ret = params.onFail.call( params, flashembed.getVersion(), flashvars );
1423 if ( ret === true ) { root.innerHTML = ret; }
1424
1425
1426 // express install
1427 } else if ( required && express && flashembed.isSupported( [6, 65] ) ) {
1428
1429 extend( params, { src: express } );
1430
1431 flashvars = {
1432 MMredirectURL: location.href,
1433 MMplayerType: 'PlugIn',
1434 MMdoctitle: document.title
1435 };
1436
1437 root.innerHTML = getHTML( params, flashvars );
1438
1439 // not supported
1440 } else {
1441
1442 // minor bug fixed here 08.04.2008 (thanks JRodman)
1443
1444 if ( root.innerHTML.replace( /\s/g, '') !== '' ) {
1445 // custom content was supplied
1446
1447 } else {
1448 root.innerHTML =
1449 "<h2>Flash version " + required + " or greater is required</h2>" +
1450 "<h3>" +
1451 ( version[0] > 0 ? "Your version is " + version : "You have no flash plugin installed" ) +
1452 "</h3>" +
1453 "<p>Download latest version from <a href='" + params.pluginspage + "'>here</a></p>";
1454 }
1455 }
1456
1457 return root;
1458
1459 // }}}
1460
1461
1462 };
1463
1464
1465 // {{{ static methods
1466
1467 extend( window.flashembed, {
1468
1469 // returns arr[major, fix]
1470 getVersion: function() {
1471
1472 var version = [0, 0];
1473
1474 if ( navigator.plugins && typeof navigator.plugins["Shockwave Flash"] == "object" ) {
1475 var _d = navigator.plugins["Shockwave Flash"].description;
1476 if ( typeof _d != "undefined" ) {
1477 _d = _d.replace( /^.*\s+(\S+\s+\S+$ )/, "$1" );
1478 var _m = parseInt( _d.replace( /^(.*)\..*$/, "$1" ), 10 );
1479 var _r = / r / .test( _d ) ? parseInt( _d.replace( /^.*r(.*)$/ , "$1" ), 10 ) : 0;
1480 version = [_m, _r];
1481 }
1482
1483 } else if ( window.ActiveXObject ) {
1484
1485 try { // avoid fp 6 crashes
1486 var _a = new ActiveXObject( "ShockwaveFlash.ShockwaveFlash.7" );
1487
1488 } catch ( e ) {
1489 try {
1490 _a = new ActiveXObject( "ShockwaveFlash.ShockwaveFlash.6" );
1491 version = [6, 0];
1492 _a.AllowScriptAccess = "always"; // throws if fp < 6.47
1493
1494 } catch ( ee ) {
1495 if ( version[0] == 6 ) { return; }
1496 }
1497 try {
1498 _a = new ActiveXObject( "ShockwaveFlash.ShockwaveFlash" );
1499 } catch ( eee ) {
1500
1501 }
1502
1503 }
1504
1505 if ( typeof _a == "object" ) {
1506 _d = _a.GetVariable( "$version" ); // bugs in fp 6.21 / 6.23
1507 if ( typeof _d != "undefined" ) {
1508 _d = _d.replace( /^\S+\s+(.*)$/, "$1" ).split( "," );
1509 version = [parseInt( _d[0], 10 ), parseInt( _d[2], 10 )];
1510 }
1511 }
1512 }
1513
1514 return version;
1515 },
1516
1517 isSupported: function( version ) {
1518 var now = flashembed.getVersion();
1519 var ret = ( now[0] > version[0] ) || ( now[0] == version[0] && now[1] >= version[1] );
1520 return ret;
1521 },
1522
1523 domReady: domReady,
1524
1525 // returns a String representation from JSON object
1526 asString: asString,
1527
1528 getHTML: getHTML,
1529
1530 getFullHTML: getFullHTML
1531
1532 } );
1533
1534 // }}}
1535
1536
1537 // setup jquery support
1538 if ( jQ ) {
1539
1540 jQuery.prototype.flashembed = function( params, flashvars ) {
1541 return this.each( function() {
1542 flashembed( this, params, flashvars );
1543 } );
1544 };
1545
1546 }
1547
1548 } )();
1549
1550 /************************************************
1551 ********* mv_embed extension to flowplayer.js ***
1552 ************************************************/
1553 var flowplayerEmbed = {
1554 instanceOf:'flowplayerEmbed',
1555 monitorTimerId : 0,
1556 old_pid:0,
1557 didSeekJump:false,
1558 startedTimedPlayback:false,
1559 didDateStartTimeRestore:false,
1560 supports: {
1561 'play_head' : true,
1562 'pause' : true,
1563 'stop' : true,
1564 'time_display' : true,
1565 'volume_control' : true
1566 },
1567 getEmbedHTML: function () {
1568 setTimeout( 'document.getElementById(\'' + this.id + '\').postEmbedJS()', 150 );
1569 return this.wrapEmebedContainer( this.getEmbedObj() );
1570 },
1571 getEmbedObj:function() {
1572 // give the embed element a unique pid (work around for flowplayer persistence)
1573 if ( this.old_pid != 0 ) {
1574 this.pid = this.pid + '_' + this.old_pid;
1575 }
1576 return '<a ' +
1577 'href="' + this.getSrc() + '" ' +
1578 'style="display:block;width:' + parseInt( this.width ) + 'px;height:' + parseInt( this.height ) + 'px" ' +
1579 'id="' + this.pid + '">' +
1580 '</a>';
1581 },
1582 postEmbedJS: function()
1583 {
1584 var _this = this;
1585 js_log( 'embedFlow: uri:' + this.getSrc() + "\n" + mv_embed_path + 'binPlayers/flowplayer/flowplayer-3.0.1.swf' ) ;
1586 var flowConfig = {
1587 clip: {
1588 url: this.getSrc(),
1589 // when this is false playback does not start until play button is pressed
1590 autoPlay: true
1591 },
1592 plugins: {
1593 controls: {
1594 all: false,
1595 fullscreen: true,
1596 backgroundColor: 'transparent',
1597 backgroundGradient: 'none',
1598 autoHide:'always',
1599 top:'95%',
1600 right:'0px'
1601 }
1602 },
1603 screen: {
1604 opacity : '1.0'
1605 }
1606 };
1607
1608 // if in preview mode set grey and lower volume until "ready"
1609 if ( this.preview_mode ) {
1610 flowConfig.screen.opacity = 0.2;
1611 }
1612
1613 $f( this.pid, mv_embed_path + 'binPlayers/flowplayer/flowplayer-3.0.1.swf', flowConfig );
1614 // get the this.fla value:
1615 this.getFLA();
1616 // set up bindings (for when interacting with the swf causes action:
1617 this.fla.onPause( function() {
1618 _this.parent_pause(); // update the interface
1619 } )
1620 this.fla.onResume( function() {
1621 _this.parent_play(); // update the interface
1622 } );
1623
1624 // start monitor:
1625 this.monitor();
1626 this.old_pid++;
1627 },
1628 /* js hooks/controls */
1629 play: function() {
1630 this.getFLA();
1631 // update play/pause button etc
1632 this.parent_play();
1633 if ( this.fla ) {
1634 this.fla.play();
1635
1636 // on a resume make sure volume and opacity are correct
1637 this.restorePlayer();
1638 setTimeout( '$j(\'#' + this.id + '\').get(0).monitor()', 250 );
1639 }
1640 },
1641 // @@todo support mute
1642 toggleMute: function() {
1643 this.parent_toggleMute();
1644 this.getFLA();
1645 if ( this.fla ) {
1646 if ( this.muted ) {
1647
1648 } else {
1649
1650 }
1651 }
1652 },
1653 // @@ Suport UpDateVolumen
1654 updateVolumen:function( perc ) {
1655 this.getFLA();
1656 if ( this.fla )this.fla.setVolume( perc * 100 );
1657
1658 },
1659 // @@ Get Volumen
1660 getVolumen:function() {
1661 this.getFLA();
1662 if ( this.fla )
1663 return this.fla.getVolume() / 100;
1664 },
1665 fullscreen:function() {
1666 if ( this.fla ) {
1667 this.fla.fullscreen();
1668 } else {
1669 js_log( 'must be playing before you can go fullscreen' );
1670 }
1671 },
1672 pause : function()
1673 {
1674 this.getFLA();
1675 if ( !this.thumbnail_disp ) {
1676 this.parent_pause();
1677 if ( this.fla ) {
1678 js_log( "Flash:Pause: " + this.fla.isPaused() );
1679 if ( this.fla['pause'] ) {
1680 if ( ! this.fla.isPaused() ) {
1681 js_log( 'calling plugin pause' );
1682 this.fla.pause();
1683
1684 // restore volume and opacity
1685 this.restorePlayer();
1686 }
1687 }
1688 }
1689 }
1690 },
1691 monitor : function()
1692 {
1693 var _this = this;
1694 // date time
1695 if ( !this.dateStartTime ) {
1696 var d = new Date();
1697 this.dateStartTime = d.getTime();
1698
1699 } else {
1700 var d = new Date();
1701 if ( !this.didDateStartTimeRestore && this.preview_mode )
1702 this.fla.setVolume( 0 );
1703
1704 if ( ( d.getTime() - this.dateStartTime ) > 6000 && !this.didDateStartTimeRestore ) {
1705 this.restorePlayer();
1706 }
1707 }
1708
1709 var flash_state = this.fla.getStatus();
1710 // update the duration from the clip if its zero or not set:
1711 if ( !this.duration || this.duration == 0 ) {
1712 if ( this.fla.getClip() ) {
1713 this.duration = this.fla.getClip().fullDuration;
1714 js_log( 'set duration via clip value: ' + this.getDuration() );
1715 }
1716 }
1717 // update the duration ntp values:
1718 this.getDuration();
1719
1720 if ( typeof flash_state == 'undefined' ) {
1721 var flash_state = {
1722 "time" : this.fla.getTime()
1723 };
1724 // we are not getting buffered data restore volume and opacity
1725 this.restorePlayer();
1726 } else {
1727 // simplification of buffer state ... should move to support returning time rages like:
1728 // http://www.whatwg.org/specs/web-apps/current-work/#normalized-timeranges-object
1729 this.bufferedPercent = flash_state.bufferEnd / this.getDuration();
1730 }
1731 // set the current Time (based on timeFormat)
1732 if ( this.supportsURLTimeEncoding() ) {
1733 this.currentTime = flash_state.time;
1734 // js_log('set buffer: ' + flash_state.bufferEnd + ' at time: ' + flash_state.time +' of total dur: ' + this.getDuration());
1735 } else {
1736 this.currentTime = flash_state.time + this.start_offset;
1737 // stop buffering if greater than the duration:
1738 if ( flash_state.bufferEnd > this.getDuration() + 5 ) {
1739 // js_log('should stop buffering (does not seem to work)' + flash_state.bufferEnd + ' > dur: ' + this.getDuration() );
1740 this.fla.stopBuffering();
1741 }
1742 }
1743
1744 if ( this.currentTime > npt2seconds( this.start_ntp ) && !this.startedTimedPlayback ) {
1745 var fail = false;
1746 try
1747 {
1748 this.restorePlayer();
1749 }
1750 catch ( err )
1751 {
1752 js_log( 'failed to set values' );
1753 fail = true;
1754 }
1755 if ( !fail )
1756 this.startedTimedPlayback = true;
1757 }
1758
1759 /* to support local seeks */
1760 if ( this.currentTime > 1 && this.seek_time_sec != 0 && !this.supportsURLTimeEncoding() )
1761 {
1762 js_log( 'flashEmbed: _local_ Seeking to ' + this.seek_time_sec );
1763 this.fla.seek( this.seek_time_sec );
1764 this.seek_time_sec = 0;
1765 }
1766
1767 // checks to see if we reached the end of playback:
1768 if ( this.duration && this.startedTimedPlayback &&
1769 ( this.currentTime > ( npt2seconds( this.end_ntp ) + 2 )
1770 ||
1771 ( this.currentTime > ( npt2seconds( this.end_ntp ) - 1 )
1772 && this.prevTime == this.currentTime ) )
1773 ) {
1774 js_log( 'prbally reached end of stream: ' + seconds2npt( this.currentTime ) );
1775 this.onClipDone();
1776 }
1777
1778 // update the status and check timmer via universal parent monitor
1779 this.parent_monitor();
1780
1781
1782 this.prevTime = this.currentTime;
1783 // js_log('cur perc loaded: ' + this.fla.getPercentLoaded() +' cur time : ' + (this.currentTime - npt2seconds(this.start_ntp)) +' / ' +(npt2seconds(this.end_ntp)-npt2seconds(this.start_ntp)));
1784 },
1785 restorePlayer:function() {
1786 if ( !this.fla )
1787 this.getFLA();
1788 if ( this.fla ) {
1789 js_log( 'f:do restorePlayer' );
1790 this.fla.setVolume( 90 );
1791 $f().getPlugin( 'screen' ).css( { 'opacity':'1.0' } );
1792 // set the fallback date restore flag to true:
1793 this.didDateStartTimeRestore = true;
1794 }
1795 },
1796 // get the embed fla object
1797 getFLA : function () {
1798 this.fla = $f( this.pid );
1799 },
1800 stop : function() {
1801 js_log( 'f:flashEmbed:stop' );
1802 this.startedTimedPlayback = false;
1803 if ( this.monitorTimerId != 0 )
1804 {
1805 clearInterval( this.monitorTimerId );
1806 this.monitorTimerId = 0;
1807 }
1808 if ( this.fla )
1809 this.fla.unload();
1810 this.parent_stop();
1811 },
1812 onStop: function() {
1813 js_log( 'f:onStop' );
1814 // stop updates:
1815 if ( this.monitorTimerId != 0 )
1816 {
1817 clearInterval( this.monitorTimerId );
1818 this.monitorTimerId = 0;
1819 }
1820 },
1821 onClipDone: function() {
1822 js_log( 'f:flash:onClipDone' );
1823 if ( ! this.startedTimedPlayback ) {
1824 js_log( 'clip done before timed playback started .. not good. (ignoring) ' );
1825 // keep monitoring:
1826 this.monitor();
1827 } else {
1828 js_log( 'clip done and ' + this.startedTimedPlayback );
1829 // stop the clip if its not stopped already:
1830 this.stop();
1831 this.setStatus( "Clip Done..." );
1832 // run the onClip done action:
1833 this.parent_onClipDone();
1834 }
1835 }
1836 };