Bug 20489 Configure illegal file characters https://bugzilla.wikimedia.org/show_bug...
[mediawiki.git] / js2 / mwEmbed / libEmbedVideo / embedVideo.js
blob5c6394565809d19fa5786bde73bab13046efbbb4
1 /*  the base video control JSON object with default attributes
2 *       for supported attribute details see README
3 */
5 loadGM({
6         "mwe-loading_plugin" : "loading plugin <blink>...<\/blink>",
7         "mwe-select_playback" : "Set playback preference",
8         "mwe-link_back" : "Link back",
9         "mwe-error_load_lib" : "Error: mv_embed was unable to load required JavaScript libraries.\nInsert script via DOM has failed. Please try reloading this page.",
10         "mwe-error_swap_vid" : "Error: mv_embed was unable to swap the video tag for the mv_embed interface",
11         "mwe-add_to_end_of_sequence" : "Add to end of sequence",
12         "mwe-missing_video_stream" : "The video file for this stream is missing",
13         "mwe-play_clip" : "Play clip",
14         "mwe-pause_clip" : "Pause clip",
15         "mwe-volume_control" : "Volume control",
16         "mwe-player_options" : "Player options",
17         "mwe-closed_captions" : "Close captions",
18         "mwe-player_fullscreen" : "Fullscreen",
19         "mwe-next_clip_msg" : "Play next clip",
20         "mwe-prev_clip_msg" : "Play previous clip",
21         "mwe-current_clip_msg" : "Continue playing this clip",
22         "mwe-seek_to" : "Seek to",
23         "mwe-download_segment" : "Download selection:",
24         "mwe-download_full" : "Download full video file:",
25         "mwe-download_right_click" : "To download, right click and select <i>Save target as...<\/i>",
26         "mwe-download_clip" : "Download video",
27         "mwe-download_text" : "Download text (<a style=\"color:white\" title=\"cmml\" href=\"http:\/\/wiki.xiph.org\/index.php\/CMML\">CMML<\/a> xml):",
28         "mwe-download" : "Download",
29         "mwe-share" : "Share",
30         "mwe-credits" : "Credits",
31         "mwe-clip_linkback" : "Clip source page",
32         "mwe-chose_player" : "Choose Video Player",
33         "mwe-share_this_video" : "Share this video",
34         "mwe-video_credits" : "Video credits",
35         "mwe-menu_btn" : "Menu",
36         "mwe-close_btn" : "Close",
37         "mwe-ogg-player-vlc-mozilla" : "VLC plugin",
38         "mwe-ogg-player-videoElement" : "Native Ogg video support",
39         "mwe-ogg-player-vlc-activex" : "VLC ActiveX",
40         "mwe-ogg-player-oggPlugin" : "Generic Ogg plugin",
41         "mwe-ogg-player-quicktime-mozilla" : "Quicktime plugin",
42         "mwe-ogg-player-quicktime-activex" : "Quicktime ActiveX",
43         "mwe-ogg-player-cortado" : "Java Cortado",
44         "mwe-ogg-player-flowplayer" : "Flowplayer",
45         "mwe-ogg-player-selected" : "(selected)",
46         "mwe-ogg-player-omtkplayer" : "OMTK Flash Vorbis",
47         "mwe-generic_missing_plugin" : "You browser does not appear to support the following playback type: <b>$1<\/b><br \/>Visit the <a href=\"http:\/\/commons.wikimedia.org\/wiki\/Commons:Media_help\">Playback Methods<\/a> page to download a player.<br \/>",
48         "mwe-for_best_experience" : "For a better video playback experience we recommend:<br \/><b><a href=\"http:\/\/www.mozilla.com\/en-US\/firefox\/upgrade.html?from=mwEmbed\">Firefox 3.5<\/a>.<\/b>",
49         "mwe-do_not_warn_again" : "Dismiss for now.",
50         "mwe-playerselect" : "Players",
51         "mwe-read_before_embed" : "Please <a href=\"http:\/\/mediawiki.org\/wiki\/Security_Notes_on_Remote_Embedding\" target=\"_new\">Read This<\/a> before embeding!",
52         "mwe-embed_site_or_blog" : "Embed on your site or blog", 
53         "mwe_related_videos" : "Related Videos"
54 });
56 //set the globals:
57 var commons_api_url =  'http://commons.wikimedia.org/w/api.php';
59 var default_video_attributes = {
60         "id":null,
61         "class":null,
62         "style":null,
63         "name":null,
64         "innerHTML":null,
65         "width":"320",
66         "height":"240",
68         //video attributes:
69         "src":null,
70         "autoplay":false,
71         "start":0,
72         "end":null,
73         "controls":true,
74         "muted":false,
75         "wikiTitleKey":null,
76         
77         //roe url (for xml based metadata)
78         "roe":null,
79         //if roe includes metadata tracks we can expose a link to metadata
80         "show_meta_link":true,
82         //default state attributes per html5 spec:
83         //http://www.whatwg.org/specs/web-apps/current-work/#video)
84         "paused":true,
85         "readyState":0,  //http://www.whatwg.org/specs/web-apps/current-work/#readystate
86         "currentTime":0, //current playback position (should be updated by plugin)
87         "duration":null,   //media duration (read from file or the temporal url)
88         "networkState":0,
90         "startOffset":null, //if serving an ogg_chop segment use this to offset the presentation time 
92         //custom attributes for mv_embed:
93         "play_button":true,     
94         "thumbnail":null,
95         "linkback":null,
96         "embed_link":true,
97         "download_link":true,
98         "type":null      //the content type of the media 
101  * the base source attibute checks
102  */
103 var mv_default_source_attr= new Array(
104         'id',
105         'src',
106         'title',
107         'URLTimeEncoding', //boolean if we support temporal url requests on the source media
108         'startOffset',
109         'durationHint',
110         'start',
111         'end',  
112         'default',
113         'lang'
115 //set the dismissNativeWarn flag: 
116 _global['dismissNativeWarn'] = false;
118 * Converts all occurrences of <video> tag into video object
120 function mv_video_embed(swap_done_callback, force_id){
121         mvEmbed.init( swap_done_callback, force_id );
123 mvEmbed = {     
124         //flist stores the set of functions to run after the video has been swaped in. 
125         flist:new Array(),
126         init:function( swap_done_callback, force_id ){
127                 
128                 if(swap_done_callback)
129                         mvEmbed.flist.push( swap_done_callback );
130                         
131                 //get mv_embed location if it has not been set
132                 js_log('mv_embed ' + MV_EMBED_VERSION);                         
133                 
134                 var loadPlaylistLib=false;                                              
135                 
136                 var eAction = function(this_elm){
137                         js_log( "Do SWAP: " + $j(this_elm).attr("id") + ' tag: '+ this_elm.tagName.toLowerCase() );
138                                         
139                         if( $j(this_elm).attr("id") == '' ){
140                                 $j(this_elm).attr("id", 'v'+ global_player_list.length);
141                         }                       
142                         //stre a global reference to the id     
143                    global_player_list.push( $j(this_elm).attr("id") );
144                    //if video doSwap
145                    switch( this_elm.tagName.toLowerCase()){
146                            case 'video':
147                                     var videoInterface = new embedVideo(this_elm);       
148                                         mvEmbed.swapEmbedVideoElement( this_elm, videoInterface );
149                            break;
150                            case 'audio':
151                                    var videoInterface = new embedVideo(this_elm);        
152                                    videoInterface.type ='audio';
153                                    mvEmbed.swapEmbedVideoElement( this_elm, videoInterface );
154                            break;
155                            case 'playlist':
156                                    loadPlaylistLib=true;
157                            break;
158                    }            
159                 }
160                 
161                 if( force_id == null && force_id != '' ){
162                         var j_selector = 'video,audio,playlist';                                
163                 }else{
164                         var j_selector = '#' + force_id;
165                 }
166                 //process selected elements: 
167                 //ie8 does not play well with the jQuery video,audio,playlist selector use native: 
168                 if($j.browser.msie && $j.browser.version >= 8){
169                         jtags = j_selector.split(',');                          
170                         for( var i=0; i < jtags.length; i++){                           
171                                 $j( document.getElementsByTagName( jtags[i] )).each(function(){
172                                         eAction(this);
173                                 });
174                         }                               
175                 }else{                  
176                         $j( j_selector ).each(function(){
177                                 eAction(this);
178                         });     
179                 }                               
180                 if(loadPlaylistLib){            
181                         mvJsLoader.doLoad([ 
182                                 'mvPlayList',
183                                 '$j.ui',        //include dialog for pop-ing up thigns
184                                 '$j.ui.dialog'  
185                         ], function(){
186                                 $j('playlist').each(function(){                                                                                                                                                                                                                                                                 
187                                         //create new playlist interface:
188                                         var plObj = new mvPlayList( this );
189                                         mvEmbed.swapEmbedVideoElement(this, plObj);
190                                         var added_height = plObj.pl_layout.title_bar_height + plObj.pl_layout.control_height;
191                                         //move into a blocking display container with height + controls + title height: 
192                                         $j('#'+plObj.id).wrap('<div style="display:block;height:' + (plObj.height + added_height) + 'px;"></div>');                                                                             
193                                 });
194                         });
195                 }          
196                 this.checkClipsReady();
197         },
198         /*
199         * swapEmbedVideoElement
200         * takes a video element as input and swaps it out with
201         * an embed video interface based on the video_elements attributes
202         */
203         swapEmbedVideoElement:function(video_element, videoInterface){
204                 js_log('do swap ' + videoInterface.id + ' for ' + video_element);
205                 embed_video = document.createElement('div');
206                 //make sure our div has a hight/width set:
207                         
208                 $j(embed_video).css({
209                         'width':videoInterface.width,
210                         'height':videoInterface.height
211                 }).html( mv_get_loading_img() );                
212                 //inherit the video interface
213                 for(var method in videoInterface){ //for in loop oky in Element context 
214                         if(method!='readyState'){ //readyState crashes IE
215                                 if(method=='style'){
216                                                 embed_video.setAttribute('style', videoInterface[method]);
217                                 }else if(method=='class'){
218                                         if( $j.browser.msie )
219                                                 embed_video.setAttribute("className", videoInterface['class']);
220                                         else
221                                                 embed_video.setAttribute("class", videoInterface['class']);
222                                 }else{
223                                         //normal inherit:
224                                         embed_video[method]=videoInterface[method];
225                                 }
226                         }
227                         //string -> boolean:
228                         if(embed_video[method]=="false")embed_video[method]=false;
229                         if(embed_video[method]=="true")embed_video[method]=true;
230                 }       
231                 ///js_log('did vI style');  
232                 //now swap out the video element for the embed_video obj:         
233                 $j(video_element).after(embed_video).remove();  
234                 //js_log('did swap');             
235                 $j('#'+embed_video.id).get(0).on_dom_swap();    
236                           
237                 // now that "embed_video" is stable, do more initialization (if we are ready)
238                 if($j('#'+embed_video.id).get(0).loading_external_data == false && 
239                            $j('#'+embed_video.id).get(0).init_with_sources_loadedDone == false){
240                         //load and set ready state since source are available: 
241                         $j('#'+embed_video.id).get(0).init_with_sources_loaded();
242                 }
243                 
244                 js_log('done with child: ' + embed_video.id + ' len:' + global_player_list.length);
245                 return true;
246         },
247         //this should not be needed.
248         checkClipsReady : function(){
249                 //js_log('checkClipsReady');
250                 var is_ready=true;        
251                   for(var i=0; i < global_player_list.length; i++){
252                           if( $j('#'+global_player_list[i]).length !=0){
253                                   var cur_vid =  $j('#'+global_player_list[i]).get(0);            
254                                 is_ready = ( cur_vid.ready_to_play ) ? is_ready : false;
255                                 if( !is_ready && cur_vid.load_error ){ 
256                                         is_ready=true;
257                                         $j(cur_vid).html( cur_vid.load_error );
258                                 }
259                         }
260                 }
261                 if( is_ready ){
262                         mvEmbed.allClipsReady = true;
263                         // run queued functions 
264                         //js_log('run queded functions:' + mvEmbed.flist[0]);
265                         mvEmbed.runFlist();
266                 }else{                           
267                          setTimeout( 'mvEmbed.checkClipsReady()', 25 );
268                  }                        
269         },
270         runFlist:function(){
271                 while (this.flist.length){                              
272                         this.flist.shift()();
273                 }
274         }
278   * mediaSource class represents a source for a media element.
279   * @param {String} type MIME type of the source.
280   * @param {String} uri URI of the source.
281   * @constructor
282   */
283 function mediaSource(element)
285         this.init(element);
289 mediaSource.prototype =
291         /** MIME type of the source. */
292         mime_type:null,
293         /** URI of the source. */
294         uri:null,
295         /** Title of the source. */
296         title:null,
297         /** True if the source has been marked as the default. */
298         marked_default:false,
299         /** True if the source supports url specification of offset and duration */
300         URLTimeEncoding:false,
301         /** Start offset of the requested segment */
302         start_offset:null,
303         /** Duration of the requested segment (0 if not known) */
304         duration:0,
305         is_playable:null,
306         upddate_interval:null,
308         id:null,
309         start_ntp:null,
310         end_ntp:null,
312         init : function(element)
313         {
314                 //js_log('adding mediaSource: ' + element);                             
315                 this.src = $j(element).attr('src');
316                 this.marked_default = false;
317                 if ( element.tagName.toLowerCase() == 'video')
318                         this.marked_default = true;             
319                 
320                 //set default URLTimeEncoding if we have a time  url:
321                 //not ideal way to discover if content is on an oggz_chop server. 
322                 //should check some other way. 
323                 var pUrl = parseUri ( this.src );
324                 if(typeof pUrl['queryKey']['t'] != 'undefined'){
325                         this['URLTimeEncoding']=true;                   
326                 }                               
327                 for(var i=0; i < mv_default_source_attr.length; i++){ //array loop:
328                         var attr = mv_default_source_attr[ i ];                 
329                         if( $j(element).attr( attr ) ) {
330                                 this[ attr ] =  $j(element).attr( attr );
331                         }
332                 }                               
333                 //update duration from hit if present: 
334                 if(this.durationHint)
335                         this.duration = this.durationHint;              
336                                         
337                         
338                 if ( $j(element).attr('type'))
339                         this.mime_type = $j(element).attr('type');
340                 else if ($j(element).attr('content-type'))
341                         this.mime_type = $j(element).attr('content-type');
342                 else                    
343                         this.mime_type = this.detectType(this.src);
344                 
345                 //set the title if unset:
346                 if( !this.title )
347                         this.title = this.mime_type;
349                 this.parseURLDuration();
350         },
351         updateSource:function(element){
352                 //for now just update the title: 
353                 if ($j(element).attr("title"))
354                         this.title = $j(element).attr("title");         
355         },
356         /** updates the src time and start & end
357          *  @param {String} start_time in NTP format
358          *  @param {String} end_time in NTP format
359          */
360         updateSrcTime:function (start_ntp, end_ntp){
361                 //js_log("f:updateSrcTime: "+ start_ntp+'/'+ end_ntp + ' from org: ' + this.start_ntp+ '/'+this.end_ntp);
362                 //js_log("pre uri:" + this.src);
363                 //if we have time we can use:
364                 if( this.URLTimeEncoding ){
365                         //make sure its a valid start time / end time (else set default) 
366                         if( !npt2seconds(start_ntp) ) 
367                                 start_ntp = this.start_ntp;
368                                 
369                         if( !npt2seconds(end_ntp) )
370                                 end_ntp = this.end_ntp;
371                                                                                   
372                         this.src = getURLParamReplace(this.src, { 't': start_ntp +'/'+ end_ntp } );                
373                         
374                         //update the duration
375                         this.parseURLDuration();
376                 }                
377         },
378         setDuration:function (duration)
379         {
380                 this.duration = duration;
381                 if(!this.end_ntp){
382                         this.end_ntp = seconds2npt( this.start_offset + duration);
383                 }
384         },
385         /** MIME type accessor function.
386                 @return the MIME type of the source.
387                 @type String
388         */
389         getMIMEType : function()
390         {
391                 return this.mime_type;
392         },
393         /** URI accessor function.
394          *       @param int seek_time_sec (used to adjust the URI for url based seeks) 
395                 @return the URI of the source.
396                 @type String
397         */
398         getURI : function( seek_time_sec )
399         {                               
400            if( !seek_time_sec || !this.URLTimeEncoding ){                       
401                            return this.src;                                               
402            }                            
403            if(!this.end_ntp){
404                   var endvar = '';
405            }else{
406                   var endvar = '/'+ this.end_ntp;
407            }            
408            return getURLParamReplace(this.src,  { 't': seconds2npt( seek_time_sec )+endvar } ); ;
409         },
410         /** Title accessor function.
411                 @return the title of the source.
412                 @type String
413         */
414         getTitle : function()
415         {
416                 return this.title;
417         },
418         /** Index accessor function.
419                 @return the source's index within the enclosing mediaElement container.
420                 @type Integer
421         */
422         getIndex : function()
423         {
424                 return this.index;
425         },
426         /*
427          * function getDuration in milliseconds
428          * special case derive duration from request url
429          * supports media_url?t=ntp_start/ntp_end url request format
430          */
431         parseURLDuration : function(){                    
432                 //check if we have a URLTimeEncoding: 
433                 if( this.URLTimeEncoding ){               
434                         var annoURL = parseUri( this.src );
435                         if( annoURL.queryKey['t'] ){
436                                 var times = annoURL.queryKey['t'].split('/');
437                                 this.start_ntp = times[0];
438                                 this.end_ntp = times[1];
439                                 this.start_offset = npt2seconds( this.start_ntp );
440                                 this.duration = npt2seconds( this.end_ntp ) - this.start_offset;
441                         }else{
442                                 //look for this info as attributes
443                                 if(this.startOffset){
444                                         this.start_offset = this.startOffset;
445                                         this.start_ntp = seconds2npt( this.startOffset);                                        
446                                 } 
447                                 if(this.duration){
448                                         this.end_ntp = seconds2npt( parseInt(this.duration) + parseInt(this.start_offset) );
449                                 }                               
450                         }                                               
451                 }
452                 //else nothing to parse just keep whatever info we already have
453                 
454                 //js_log('f:parseURLDuration() for:' + this.src  + ' d:' + this.duration);
455         },
456         /** Attempts to detect the type of a media file based on the URI.
457                 @param {String} uri URI of the media file.
458                 @returns The guessed MIME type of the file.
459                 @type String
460         */
461         detectType:function(uri)
462         {
463                 //@@todo if media is on the same server as the javascript or we have mv_proxy configured
464                 //we can issue a HEAD request and read the mime type of the media...
465                 // (this will detect media mime type independently of the url name)
466                 //http://www.jibbering.com/2002/4/httprequest.html (this should be done by extending jquery's ajax objects)
467                 var end_inx =  (uri.indexOf('?')!=-1)? uri.indexOf('?') : uri.length;
468                 var no_param_uri = uri.substr(0, end_inx);
469                 switch( no_param_uri.substr(no_param_uri.lastIndexOf('.'),4).toLowerCase() ){
470                         case '.flv':return 'video/x-flv';break;
471                         case '.ogg': case '.ogv': return 'video/ogg';break;
472                         case '.oga': return 'audio/ogg'; break;
473                         case '.anx':return 'video/ogg';break;
474                 }
475         }
478 /** A media element corresponding to a <video> element.
479         It is implemented as a collection of mediaSource objects.  The media sources
480         will be initialized from the <video> element, its child <source> elements,
481         and/or the ROE file referenced by the <video> element.
482         @param {element} video_element <video> element used for initialization.
483         @constructor
485 function mediaElement(video_element)
487         this.init(video_element);
490 mediaElement.prototype =
492         /** The array of mediaSource elements. */
493         sources:null,   
494         addedROEData:false,     
495         /** Selected mediaSource element. */
496         selected_source:null,
497         thumbnail:null,
498         linkback:null,  
500         /** @private */
501         init:function( video_element )
502         {
503                 var _this = this;
504                 js_log('Initializing mediaElement...' );
505                 this.sources = new Array();
506                 this.thumbnail = mv_default_thumb_url;
507                 // Process the source element:
508                 if($j(video_element).attr("src"))
509                         this.tryAddSource(video_element);                         
510                 
511                 if($j(video_element).attr('thumbnail'))
512                         this.thumbnail = $j(video_element).attr('thumbnail');
513                         
514                 if($j(video_element).attr('poster'))
515                         this.thumbnail = $j(video_element).attr('poster');
516                 
517                 if($j(video_element).attr('wikiTitleKey'))
518                         this.wikiTitleKey=$j(video_element).attr('wikiTitleKey');
519                         
520                 // Process all inner <source> elements  
521                 //js_log("inner source count: " + video_element.getElementsByTagName('source').length );
522                 
523                 $j(video_element).find('source,text').each(function(inx, inner_source){                 
524                         _this.tryAddSource( inner_source );
525                 });                                               
526         },
527         /** Updates the time request for all sources that have a standard time request argument (ie &t=start_time/end_time)
528          */
529         updateSourceTimes:function(start_ntp, end_ntp){
530                 var _this = this;
531                 $j.each(this.sources, function(inx, mediaSource){
532                         mediaSource.updateSrcTime(start_ntp, end_ntp);
533                 });
534         },
535         /*timed Text check*/
536         timedTextSources:function(){
537                 for(var i=0; i < this.sources.length; i++){                     
538                         if(     this.sources[i].mime_type == 'text/cmml' ||
539                                 this.sources[i].mime_type == 'text/x-srt')
540                                         return true;                                                            
541                 };
542                 return false;
543         },
544         /** Returns the array of mediaSources of this element.
545                 \returns {Array} Array of mediaSource elements.
546         */
547         getSources:function( mime_filter )
548         {
549                 if(!mime_filter)
550                         return this.sources;
551                 //apply mime filter: 
552                    var source_set = new Array();
553                 for(var i=0; i < this.sources.length ; i++){
554                         if( this.sources[i].mime_type.indexOf( mime_filter ) != -1 )
555                                 source_set.push( this.sources[i] );
556                 }
557                 return source_set;
558         },
559         getSourceById:function( source_id ){            
560                 for(var i=0; i < this.sources.length ; i++){
561                         if( this.sources[i].id ==  source_id)
562                                 return this.sources[i];
563                 }
564                 return null;
565         },
566         /** Selects a particular source for playback.
567         */
568         selectSource:function(index)
569         {
570                 js_log('f:selectSource:'+index);
571                 var playable_sources = this.getPlayableSources();
572                 for(var i=0; i < playable_sources.length; i++){
573                         if( i==index ){
574                                 this.selected_source = playable_sources[i];
575                                 //update the user selected format: 
576                                 embedTypes.players.userSelectFormat( playable_sources[i].mime_type );
577                                 break;
578                         }
579                 }               
580         },
581         /** selects the default source via cookie preference, default marked, or by id order
582          * */
583         autoSelectSource:function(){ 
584                 js_log('f:autoSelectSource:');  
585                 //@@todo read user preference for source                
586                 // Select the default source
587                 var playable_sources = this.getPlayableSources();                                                       
588                 var flash_flag=ogg_flag=false;            
589                 //debugger;
590                 for(var source=0; source < playable_sources.length; source++){
591                         var mime_type =playable_sources[source].mime_type;
592                         if( playable_sources[source].marked_default ){
593                                 js_log('set via marked default: ' + playable_sources[source].marked_default);
594                                 this.selected_source = playable_sources[source];                                
595                                 return true;
596                         }
597                         //set via user-preference
598                         if(embedTypes.players.preference['format_prefrence'] == mime_type){
599                                  js_log('set via preference: '+playable_sources[source].mime_type);
600                                  this.selected_source = playable_sources[source];
601                                  return true; 
602                         }                                                                                                                       
603                 }          
604                 //set Ogg via player support            
605                 for(var source=0; source < playable_sources.length; source++){
606                         js_log('f:autoSelectSource:' + playable_sources[source].mime_type);
607                         var mime_type =playable_sources[source].mime_type;                                      
608                            //set source via player                               
609                         if(mime_type=='video/ogg' || mime_type=='ogg/video' || mime_type=='video/annodex' || mime_type=='application/ogg'){                             
610                                 for(var i=0; i < embedTypes.players.players.length; i++){ //for in loop on object oky
611                                         var player = embedTypes.players.players[i];                                     
612                                         if(player.library=='vlc' || player.library=='native'){
613                                                 js_log('set via ogg via order');                                                
614                                                 this.selected_source = playable_sources[source];        
615                                                 return true;
616                                         }                                       
617                                 }
618                         }
619                 }
620                 //set basic flash
621                 for(var source=0; source < playable_sources.length; source++){  
622                         var mime_type =playable_sources[source].mime_type;                                                                              
623                         if( mime_type=='video/x-flv' ){
624                                 js_log('set via by player preference normal flash')
625                                 this.selected_source = playable_sources[source];
626                                 return true;
627                         }                                               
628                 }
629                 //set h264 flash 
630                 for(var source=0; source < playable_sources.length; source++){  
631                         var mime_type =playable_sources[source].mime_type;                                                                              
632                         if( mime_type=='video/h264' ){
633                                 js_log('set via playable_sources preference h264 flash')
634                                 this.selected_source = playable_sources[source];
635                                 return true;
636                         }                          
637                 }               
638                 //select first source           
639                 if (!this.selected_source)
640                 {
641                         js_log('set via first source:' + playable_sources[0]);
642                         this.selected_source = playable_sources[0];
643                         return true;
644                 }
645         },
646         /** Returns the thumbnail URL for the media element.
647                 \returns {String} thumbnail URL
648         */
649         getThumbnailURL:function()
650         {
651                 return this.thumbnail;
652         },
653         /** Checks whether there is a stream of a specified MIME type.
654                 @param {String} mime_type MIME type to check.
655                 @type {BooleanPrimitive}.
656         */
657         hasStreamOfMIMEType:function(mime_type)
658         {
659                 for(source in this.sources)
660                 {
661                         if(this.sources[source].getMIMEType() == mime_type)
662                                 return true;
663                 }
664                 return false;
665         },
666         isPlayableType:function(mime_type)
667         {
668                 if( embedTypes.players.defaultPlayer( mime_type ) ){
669                         return true;
670                 }else{
671                         return false;
672                 }
673                 //if(this.selected_player){
674                 //return mime_type=='video/ogg' || mime_type=='ogg/video' || mime_type=='video/annodex' || mime_type=='video/x-flv';
675         },
676         /** Adds a single mediaSource using the provided element if
677                 the element has a 'src' attribute.              
678                 @param element {element} <video>, <source> or <mediaSource> element.
679         */
680         tryAddSource:function(element)
681         {
682                 js_log('f:tryAddSource:'+ $j(element).attr("src"));             
683                 if (! $j(element).attr("src")){
684                         //js_log("element has no src");
685                         return false;
686                 }
687                 var new_src = $j(element).attr('src');
688                 //make sure an existing element with the same src does not already exist:                
689                 for( var i=0; i < this.sources.length; i++ ){                           
690                         if(this.sources[i].src == new_src){
691                                 //js_log('checking existing: '+this.sources[i].getURI() + ' != '+ new_src);      
692                                 //can't add it all but try to update any additional attr: 
693                                 this.sources[i].updateSource(element);
694                                 return false;
695                         }
696                 }
697                 var source = new mediaSource( element );                
698                 this.sources.push(source);              
699                 //alert('pushed source to stack'+ source + 'sl:'+this.sources.length);
700         },
701         getPlayableSources: function(){          
702                  var playable_sources= new Array();
703                  for(var i=0; i < this.sources.length; i++){                                                               
704                          if( this.isPlayableType( this.sources[i].mime_type ) ){                                 
705                                  playable_sources.push( this.sources[i] );
706                          }else{
707                                  js_log("type "+ this.sources[i].mime_type + 'is not playable');
708                          }
709                  };              
710                  return playable_sources;
711         },
712         /* Imports media sources from ROE data.
713          *   @param roe_data ROE data.
714         */
715         addROE:function(roe_data){              
716                 js_log('f:addROE');
717                 this.addedROEData=true;
718                 var _this = this;               
719                 if( typeof roe_data == 'string' )
720                 {
721                         var parser=new DOMParser();
722                         js_log('ROE data:' + roe_data);
723                         roe_data=parser.parseFromString(roe_data,"text/xml");
724                 }
725                 if( roe_data ){
726                         $j.each(roe_data.getElementsByTagName('mediaSource'), function(inx, source){
727                                 _this.tryAddSource(source);
728                         });
729                         //set the thumbnail:
730                         $j.each(roe_data.getElementsByTagName('img'), function(inx, n){
731                                 if($j(n).attr("id")=="stream_thumb"){
732                                         js_log('roe:set thumb to '+$j(n).attr("src"));
733                                         _this['thumbnail'] =$j(n).attr("src");
734                                 }
735                         });
736                         //set the linkback:
737                         $j.each(roe_data.getElementsByTagName('link'), function(inx, n){
738                                 if($j(n).attr('id')=='html_linkback'){
739                                         js_log('roe:set linkback to '+$j(n).attr("href"));
740                                         _this['linkback'] = $j(n).attr('href');
741                                 }
742                         });
743                 }else{
744                         js_log('ROE data empty.');
745                 }                               
746         }
750 /** base embedVideo object
751         @param element <video> tag used for initialization.
752         @constructor
754 var embedVideo = function(element) {
755         return this.init(element);
758 embedVideo.prototype = {
759         /** The mediaElement object containing all mediaSource objects */
760         media_element:null,
761         preview_mode:false,                
762         ready_to_play:false, //should use html5 ready state
763         load_error:false, //used to set error in case of error
764         loading_external_data:false,
765         thumbnail_updating:false,
766         thumbnail_disp:true,
767         init_with_sources_loadedDone:false,
768         inDOM:false,
769         //for onClip done stuff: 
770         anno_data_cache:null,
771         seek_time_sec:0,
772         base_seeker_slider_offset:null,
773         onClipDone_disp:false,
774         supports:{},    
775         //for seek thumb updates:
776         cur_thumb_seek_time:0,
777         thumb_seek_interval:null,
778         
779         seeking:false,
780         //set the buffered percent:     
781         bufferedPercent:0,      
782         //utility functions for property values:
783         hx : function ( s ) {
784                 if ( typeof s != 'String' ) {
785                         s = s.toString();
786                 }
787                 return s.replace( /&/g, '&amp;' )
788                         . replace( /</g, '&lt;' )
789                         . replace( />/g, '&gt;' );
790         },
791         hq : function ( s ) {
792                 return '"' + this.hx( s ) + '"';
793         },
794         playerPixelWidth : function()
795         {
796                 var player = $j('#mv_embedded_player_'+this.id).get(0);
797                 if(typeof player!='undefined' && player['offsetWidth'])
798                         return player.offsetWidth;
799                 else
800                         return parseInt(this.width);
801         },
802         playerPixelHeight : function()
803         {
804                 var player = $j('#mv_embedded_player_'+this.id).get(0);
805                 if(typeof player!='undefined' && player['offsetHeight'])
806                         return player.offsetHeight;
807                 else
808                         return parseInt(this.height);
809         },
810         init: function(element){                
811                 //this.element_pointer = element;
813                 //inherit all the default video_attributes
814                 for(var attr in default_video_attributes){ //for in loop oky on user object
815                         if(element.getAttribute(attr)){
816                                 this[attr]=element.getAttribute(attr);
817                                 //js_log('attr:' + attr + ' val: ' + element.getAttribute(attr) +'(set by elm)');
818                         }else{
819                                 this[attr]=default_video_attributes[attr];
820                                 //js_log('attr:' + attr + ' val: ' + video_attributes[attr] +" "+ 'elm_val:' + element.getAttribute(attr) + "\n (set by attr)");
821                         }
822                 }
823                 //make sure startOffset is cast as an int                  
824                 if( this.startOffset && this.startOffset.split(':').length >= 2)
825                         this.startOffset = npt2seconds(this.startOffset);
826                 //make sure offset is in float: 
827                 this.startOffset = parseFloat(this.startOffset);
828                  
829                 if( this.duration && this.duration.split(':').length >= 2)
830                         this.duration = npt2seconds( this.duration );    
831                    //make sure duration is in float:  
832                 this.duration = parseFloat(this.duration);  
833                 js_log("duration is: " +  this.duration);
834                 //if style is set override width and height
835                 var dwh = mwConfig['video_size'].split('x');
836                 this.width = element.style.width ? element.style.width : dwh[0];
837                 this.height = element.style.height ? element.style.height : dwh[1];
838                 //set the plugin id
839                 this.pid = 'pid_' + this.id;
841                 //grab any innerHTML and set it to missing_plugin_html
842                 //@@todo we should strip source tags instead of checking and skipping
843                 if(element.innerHTML!='' && element.getElementsByTagName('source').length==0){
844                         js_log('innerHTML: ' + element.innerHTML);
845                         this.user_missing_plugin_html=element.innerHTML;
846                 }                                 
847                 // load all of the specified sources
848                 this.media_element = new mediaElement(element);                                                  
849         },
850         on_dom_swap: function(){
851                 js_log('f:on_dom_swap');                                
852                 // Process the provided ROE file... if we don't yet have sources
853                 if(this.roe && this.media_element.sources.length==0 ){
854                         js_log('loading external data');
855                         this.loading_external_data=true;
856                         var _this = this;                                       
857                         do_request(this.roe, function(data)
858                         {                                                       
859                                 //continue                                         
860                                 _this.media_element.addROE( data );                                                                       
861                                 js_log('added_roe::' + _this.media_element.sources.length);                                                        
862                                                                                                            
863                                 js_log('set loading_external_data=false');       
864                                 _this.loading_external_data=false;                                                         
865                                 
866                                 _this.init_with_sources_loaded();               
867                         });
868                 }
869         },
870         init_with_sources_loaded : function()
871         {       
872                 js_log('f:init_with_sources_loaded');
873                 //set flag that we have run this function:
874                 this.init_with_sources_loadedDone=true;                          
875                 //autoseletct the source
876                 this.media_element.autoSelectSource();          
877                 //auto select player based on prefrence or default order
878                 if( !this.media_element.selected_source )
879                 {
880                         //check for parent clip: 
881                         if( typeof this.pc != 'undefined' ){                    
882                                 js_log('no sources, type:' +this.type + ' check for html');     
883                                 //debugger;                     
884                                 //do load player if just displaying innerHTML: 
885                                 if( this.pc.type == 'text/html' ){
886                                         this.selected_player = embedTypes.players.defaultPlayer( 'text/html' );
887                                         js_log('set selected player:'+ this.selected_player.mime_type); 
888                                 }
889                         }
890                 }else{          
891                         this.selected_player = embedTypes.players.defaultPlayer( this.media_element.selected_source.mime_type );
892                 }                                                 
893                 if( this.selected_player ){
894                         js_log('selected ' + this.selected_player.getName());
895                         js_log("PLAYBACK TYPE: "+this.selected_player.library);
896                         this.thumbnail_disp = true;             
897                         this.inheritEmbedObj();            
898                 }else{                                                   
899                         //no source's playable
900                         var missing_type ='';
901                         var or ='';  
902                         for( var i=0; i < this.media_element.sources.length; i++){
903                                 missing_type+= or + this.media_element.sources[i].mime_type;
904                                 or=' or ';
905                         }                       
906                         if( this.pc )
907                                 var missing_type = this.pc.type;                                                                                
908                            js_log('no player found for given source type ' + missing_type);
909                            this.load_error= this.getPluginMissingHTML(missing_type);                                                                               
910                 }               
911         },
912         inheritEmbedObj:function(){             
913                 js_log("inheritEmbedObj:duration is: " +  this.duration);  
914                 //@@note: tricky cuz direct overwrite is not so ideal.. since the extended object is already tied to the dom
915                 //clear out any non-base embedObj stuff:
916                 if(this.instanceOf){
917                         eval('tmpObj = '+this.instanceOf);
918                         for(var i in tmpObj){ //for in loop oky for object  
919                                 if(this['parent_'+i]){
920                                         this[i]=this['parent_'+i];
921                                 }else{
922                                         this[i]=null;
923                                 }
924                         }
925                 }                                 
926                 //set up the new embedObj
927                 js_log('f: inheritEmbedObj: embedding with ' + this.selected_player.library);
928                 var _this = this;               
929                 this.selected_player.load( function()
930                 {
931                          js_log("selected_player::load:duration is: " +  _this.duration);  
932                         //js_log('inheriting '+_this.selected_player.library +'Embed to ' + _this.id + ' ' + $j('#'+_this.id).length);
933                         //var _this = $j('#'+_this.id).get(0);
934                         //js_log( 'type of ' + _this.selected_player.library +'Embed + ' +
935                         //              eval('typeof '+_this.selected_player.library +'Embed')); 
936                         eval('embedObj = ' +_this.selected_player.library +'Embed;');
937                         for(var method in embedObj){ //for in loop oky for object  
938                                 //parent method preservation for local overwritten methods
939                                 if(_this[method])
940                                         _this['parent_' + method] = _this[method];
941                                 _this[method]=embedObj[method];
942                         }
943                         js_log('TYPEOF_ppause: ' + typeof _this['parent_pause']);
944                         
945                         if(_this.inheritEmbedOverride){
946                                 _this.inheritEmbedOverride();
947                         }
948                         //update controls if possible
949                         if(!_this.loading_external_data)
950                                 _this.refreshControlsHTML();                                                                                            
951                         
952                         //js_log("READY TO PLAY:"+_this.id);                    
953                         _this.ready_to_play=true;                               
954                         _this.getDuration();
955                         _this.getHTML();
956                 });
957         },
958         selectPlayer:function(player)
959         {
960                 var _this = this;
961                 if(this.selected_player.id != player.id){
962                         this.selected_player = player;
963                         this.inheritEmbedObj();
964                 }                       
965         },
966         doNativeWarningCheck:function(){                        
967                 if( $j.cookie('dismissNativeWarn') && $j.cookie('dismissNativeWarn')===true){
968                         return false;
969                 }else{                  
970                         //see if we have native support for ogg: 
971                         var supporting_players = embedTypes.players.getMIMETypePlayers( 'video/ogg' );          
972                         for(var i=0; i < supporting_players.length; i++){
973                                 if(supporting_players[i].id == 'videoElement'){
974                                         return false;
975                                 }                       
976                         }       
977                         //see if we are using mv_embed without a ogg source in which case no point in promoting firefox :P                      
978                         if(this.media_element && this.media_element.sources){
979                                 var foundOgg = false;
980                                 var playable_sources = this.media_element.getPlayableSources();
981                                 for(var sInx=0; sInx < playable_sources.length; sInx++){  
982                                         var mime_type = playable_sources[sInx].mime_type;
983                                         if( mime_type=='video/ogg' ){
984                                                 //they  have flash / h.264 fallback no need to push firefox :( 
985                                                 foundOgg = true;
986                                         }
987                                 }
988                                 //no ogg no point in download firefox
989                                 if(!foundOgg)
990                                         return false;
991                                                                                 
992                         }
993                 }
994                 return true;    
995         },
996         getTimeReq:function(){
997                 var default_time_req = '0:00:00/' + seconds2npt(this.getDuration());
998                 if(!this.media_element)
999                         return default_time_req;
1000                 if(!this.media_element.selected_source)
1001                         return default_time_req;                
1002                 if(!this.media_element.selected_source.end_ntp)
1003                         return default_time_req;                
1004                 return this.media_element.selected_source.start_ntp+'/'+this.media_element.selected_source.end_ntp;
1005         },      
1006         getDuration:function(){                                                 
1007                 //update some local pointers for the selected source:   
1008                 if(this.media_element && this.media_element.selected_source && this.media_element.selected_source.duration){                                                                     
1009                         this.duration = this.media_element.selected_source.duration;                                            
1010                         this.start_offset = this.media_element.selected_source.start_offset;
1011                         this.start_ntp = this.media_element.selected_source.start_ntp;
1012                         this.end_ntp = this.media_element.selected_source.end_ntp;                                      
1013                 }
1014                 //update start end_ntp if duration !=0 (set from plugin) 
1015                 if(!this.start_ntp)
1016                         this.start_ntp = '0:0:0';
1017                 if(!this.end_ntp && this.duration)
1018                         this.end_ntp = seconds2npt( this.duration );            
1019                 //return the duration
1020                 return this.duration;
1021         },
1022         /*
1023          * wrapEmebedContainer
1024          * wraps the embed code into a container to better support playlist function
1025          *  (where embed element is swapped for next clip
1026          *  (where plugin method does not support playlsits) 
1027          */
1028         wrapEmebedContainer:function(embed_code){
1029                 //check if parent clip is set( ie we are in a playlist so name the embed container by playlistID)
1030                 var id = (this.pc!=null)?this.pc.pp.id:this.id;
1031                 return '<div id="mv_ebct_'+id+'" style="width:'+this.width+'px;height:'+this.height+'px;">' + 
1032                                         embed_code + 
1033                                 '</div>';
1034         },      
1035         getEmbedHTML : function(){
1036                 //return this.wrapEmebedContainer( this.getEmbedObj() );
1037                 return 'function getEmbedHTML should be overitten by embedLib ';
1038         },
1039         //do seek function (should be overwritten by implementing embedLibs)
1040         // first check if seek can be done on locally downloaded content. 
1041         doSeek : function( perc ){              
1042                 if( this.supportsURLTimeEncoding() ){                   
1043                         //make sure this.seek_time_sec is up-to-date:
1044                         this.seek_time_sec = npt2seconds( this.start_ntp ) + parseFloat( perc * this.getDuration() );
1045                         js_log('updated seek_time_sec: ' + seconds2npt ( this.seek_time_sec) );
1046                         this.stop();                                    
1047                         this.didSeekJump=true;
1048                         //update the slider
1049                         this.setSliderValue( perc ); 
1050                 }               
1051                 //do play in 100ms (give things time to clear) 
1052                 setTimeout('$j(\'#' + this.id + '\').get(0).play()',100);
1053         },
1054         /*
1055          * seeks to the requested time and issues a callback when ready 
1056          * (should be overwitten by client that supports frame serving)
1057          */     
1058         setCurrentTime:function( time, callback){
1059                 js_log('error: base embed setCurrentTime can not frame serve (overide via plugin)');
1060         },
1061         addPresTimeOffset:function(){
1062            //add in the offset:         
1063            if(this.seek_time_sec && this.seek_time_sec!=0){
1064                         this.currentTime+=this.seek_time_sec;
1065            }else if(this.start_offset && this.start_offset!=0){    
1066                    this.currentTime = parseFloat(this.currentTime) + parseFloat(this.start_offset);                      
1067            }               
1068         },
1069         doEmbedHTML:function()
1070         {
1071                 js_log('f:doEmbedHTML');
1072                 js_log('thum disp:'+this.thumbnail_disp);
1073                 var _this = this;
1074                 this.closeDisplayedHTML();
1076 //              if(!this.selected_player){
1077 //                      return this.getPluginMissingHTML();             
1078                 //Set "loading" here
1079                 $j('#mv_embedded_player_'+_this.id).html(''+
1080                         '<div style="color:black;width:'+this.width+'px;height:'+this.height+'px;">' + 
1081                                 gM('mwe-loading_plugin') + 
1082                         '</div>'                                        
1083                 );
1084                 // schedule embedding
1085                 this.selected_player.load(function()
1086                 {
1087                         js_log('performing embed for ' + _this.id);                     
1088                         var embed_code = _this.getEmbedHTML();                  
1089                         //js_log('shopuld embed:' + embed_code);
1090                         $j('#mv_embedded_player_'+_this.id).html(embed_code);   
1091                 });
1092         },
1093         mvVideoAudioSearch:function(){
1094                 var _this = this;                   
1095                 js_log('switch video Relational'  );            
1096                 var reqObj = {
1097                         'action'        :       'query',                         
1098                         'titles'        :       this.wikiTitleKey,
1099                     'generator' :       'categories'
1100                 };                              
1101                 var req_categories= new Array();                                                
1102             do_api_req( {
1103                         'data'  : reqObj, 
1104                         'url'   : commons_api_url
1105             },  function(data){ 
1106                         req_categories = Array();
1107                         if(data.query && data.query.pages){
1108                                 for(var pageid in  data.query.pages){
1109                                         if(data.query.pages[pageid].title)
1110                                                 req_categories.push(data.query.pages[pageid].title);
1111                                 }                                               
1112                         }
1113                         _this.getRelatedFromCat( req_categories );
1114                 });             
1115         },
1116         getRelatedFromCat:function(catAry){
1117                 js_log('getRelatedFromCat');
1118                 var _this = this;
1119                 for ( var i= 0 ; i <= catAry.length ;i++ ){
1120                         if(!catAry[i])
1121                                 continue;                                               
1122                         var reqObj = {
1123                                 'action'        : 'query',                       
1124                                 'generator'     : 'categorymembers'  ,
1125                                 'gcmtitle'      : catAry[i],
1126                                 'prop'          : 'imageinfo',
1127                                 'iiprop'        : 'url',
1128                                 'iiurlwidth': '80'      
1129                         };
1130                         do_api_req({
1131                                 'data':reqObj, 
1132                                 'url': commons_api_url
1133                         },  function(data){                         
1134                             //empty the videos:                     
1135                             $j('#dc_'+ _this.id + ' .related_vids ul').html(' ');
1136                                                                    
1137                                         for(var j in data.query.pages){         
1138                                                 //setup poster default:                                         
1139                                                 var local_poster ="http://upload.wikimedia.org/wikipedia/commons/7/79/Wiki-commons.png";
1140                                                 //make sure it exists: 
1141                                                 var page = data.query.pages[j];
1142                                                 if( j > 0 && page && page['imageinfo'] ){
1143                                                         if(     page['imageinfo'][0].thumburl ){
1144                                                                 local_poster = page['imageinfo'][0].thumburl;
1145                                                         }                                                       
1146                                                         var descriptionurl = page['imageinfo'][0].descriptionurl;                                                               
1147                                                         var title_str = page.title.replace( /File:|.ogv$|.oga$|.ogg$/gi, "" ); 
1148                                                         //only link to other videos:                                                            
1149                                                         if ( descriptionurl.match( /\.ogg$|\.ogv$|\.oga$/gi ) != null) {                
1150                                                                 var liout = '<li>' +
1151                                                                                 '<a href="' + descriptionurl + '" >' +
1152                                                                                         '<img src="' + local_poster + '">' +
1153                                                                                 '</a>' +                                                                                
1154                                                                                         ' <a title="' + title_str + '" target="_blank" ' +
1155                                                                                                 'href="'+ descriptionurl +'">' + title_str + '</a>' +                                                   
1156                                                                         '</li>';                                                
1157                                                                 $j('#dc_'+ _this.id + ' .related_vids ul').append(liout) ;                                                              
1158                                                         }
1159                                                  }
1160                                         };
1161                                         //js_log( 'content: ' + $j('#dc_'+ _this.id + ' .related_vids ul').html() );
1162                                 }); //end do_api_req
1163                 };
1164         },
1165         onClipDone:function(){
1166                         js_log('base:onClipDone');
1167                         //stop the clip (load the thumbnail etc) 
1168                         this.stop();
1169                         this.seek_time_sec = 0;
1170                         this.setSliderValue(0);
1171                         var _this = this;
1172                         
1173                         if(this.width < 300){
1174                                 return ;
1175                         }
1176                         this.onClipDone_disp=true;
1177                         this.thumbnail_disp=true;
1178                         //make sure we are not in preview mode( no end clip actions in preview mode) 
1179                         if( this.preview_mode )
1180                                 return ;
1181                                 
1182                         $j('#img_thumb_'+this.id).css('zindex',1);
1183                         $j('#big_play_link_'+this.id).hide();
1184         
1185                         //add black background
1186                         $j('#dc_'+this.id).append('<div id="black_back_'+this.id+'" ' +
1187                                                 'style="z-index:-2;position:absolute;background:#000;' +
1188                                                 'top:0px;left:0px;width:'+parseInt(this.width)+'px;' +
1189                                                 'height:'+parseInt(this.height)+'px;">' +
1190                                         '</div>');      
1191         
1192                         if( this.wikiTitleKey){
1193                                 $j('#dc_'+this.id).append(
1194                                 '<div class="related_vids" >' +
1195                                    '<h1>' + gM('mwe_related_videos') + '</h1>'+
1196                                         '<ul>' +
1197                                         '</ul>' +
1198                                 '</div>');      
1199                                 $j('#img_thumb_' + this.id).fadeOut("fast");
1200                                 $j('#dc_'+ _this.id + ' .related_vids ul').html( gM('mwe-loading_txt') );
1201                                 this.mvVideoAudioSearch();
1202                         }else{
1203                                 //add the liks_info_div black back 
1204                                 $j('#dc_'+this.id).append('<div id="liks_info_'+this.id+'" ' +
1205                                                 'style="width:' +parseInt(parseInt(this.width)/2)+'px;'+                
1206                                                 'height:'+ parseInt(parseInt(this.height)) +'px;'+
1207                                                 'position:absolute;top:10px;overflow:auto'+                             
1208                                                 'width: '+parseInt( ((parseInt(this.width)/2)-15) ) + 'px;'+
1209                                                 'left:'+ parseInt( ((parseInt(this.width)/2)+15) ) +'px;">'+                                    
1210                                         '</div>'                                                                        
1211                            );
1212                            //start animation (make thumb small in upper left add in div for "loading"                   
1213                                 $j('#img_thumb_'+this.id).animate({                             
1214                                                 width:parseInt(parseInt(_this.width)/2),
1215                                                 height:parseInt(parseInt(_this.height)/2),
1216                                                 top:20,
1217                                                 left:10
1218                                         },
1219                                         1000, 
1220                                         function(){
1221                                                 //animation done.. add "loading" to div if empty                
1222                                                 if($j('#liks_info_'+_this.id).html()==''){
1223                                                         $j('#liks_info_'+_this.id).html(gM('mwe-loading_txt'));
1224                                                 }               
1225                                         }
1226                                 )                                  
1227                                 //now load roe if run the showNextPrevLinks
1228                                 if(this.roe && this.media_element.addedROEData==false){
1229                                         do_request(this.roe, function(data)
1230                                         {                                                                                                                          
1231                                                 _this.media_element.addROE(data);
1232                                                 _this.getNextPrevLinks();
1233                                         });     
1234                                 }else{
1235                                         this.getNextPrevLinks();
1236                                 }
1237                         }                               
1238                 },
1239         //@@todo we should merge getNextPrevLinks with textInterface .. there is repeated code between them. 
1240         getNextPrevLinks:function(){
1241                 js_log('f:getNextPrevLinks');
1242                 var anno_track_url = null;
1243                 var _this = this; 
1244                 //check for annoative track
1245                 $j.each(this.media_element.sources, function(inx, n){                   
1246                         if(n.mime_type=='text/cmml'){
1247                                 if( n.id == 'Anno_en'){
1248                                         anno_track_url = n.src;
1249                                 }
1250                         }
1251                 });
1252                 if( anno_track_url ){
1253                         js_log('found annotative track:'+ anno_track_url);
1254                         //zero out seconds (should improve cache hit rate and generally expands metadata search)
1255                         //@@todo this could be repalced with a regExp
1256                         var annoURL = parseUri(anno_track_url);
1257                         var times = annoURL.queryKey['t'].split('/');                     
1258                         var stime_parts = times[0].split(':');   
1259                         var etime_parts = times[1].split(':');                                           
1260                         //zero out the hour:
1261                         var new_start = stime_parts[0]+':'+'0:0';
1262                         //zero out the end sec
1263                         var new_end   = (etime_parts[0]== stime_parts[0])? (etime_parts[0]+1)+':0:0' :etime_parts[0]+':0:0';
1264                                          
1265                         var etime_parts = times[1].split(':');
1266                         
1267                         var new_anno_track_url = annoURL.protocol +'://'+ annoURL.host + annoURL.path +'?';
1268                         $j.each(annoURL.queryKey, function(i, val){
1269                                 new_anno_track_url +=(i=='t')?'t='+new_start+'/'+new_end +'&' :
1270                                                                                  i+'='+ val+'&';
1271                         });
1272                         var request_key = new_start+'/'+new_end;
1273                         //check the anno_data cache: 
1274                         //@@todo search cache see if current is in range.  
1275                         if(this.anno_data_cache){
1276                                 js_log('anno data found in cache: '+request_key);
1277                                 this.showNextPrevLinks();
1278                         }else{                                                          
1279                                 do_request(new_anno_track_url, function(cmml_data){
1280                                         js_log('raw response: '+ cmml_data);
1281                                         if(typeof cmml_data == 'string')
1282                                         {
1283                                                 var parser=new DOMParser();
1284                                                 js_log('Parse CMML data:' + cmml_data);
1285                                                 cmml_data=parser.parseFromString(cmml_data,"text/xml");
1286                                         }
1287                                         //init anno_data_cache
1288                                         if(!_this.anno_data_cache)
1289                                                 _this.anno_data_cache={};                                       
1290                                         //grab all metadata and put it into the anno_data_cache:                                         
1291                                         $j.each(cmml_data.getElementsByTagName('clip'), function(inx, clip){
1292                                                 _this.anno_data_cache[ $j(clip).attr("id") ]={
1293                                                                 'start_time_sec':npt2seconds($j(clip).attr("start").replace('npt:','')),
1294                                                                 'end_time_sec':npt2seconds($j(clip).attr("end").replace('npt:','')),
1295                                                                 'time_req':$j(clip).attr("start").replace('npt:','')+'/'+$j(clip).attr("end").replace('npt:','')
1296                                                         };
1297                                                 //grab all its meta
1298                                                 _this.anno_data_cache[ $j(clip).attr("id") ]['meta']={};
1299                                                 $j.each(clip.getElementsByTagName('meta'),function(imx, meta){                                                  
1300                                                         //js_log('adding meta: '+ $j(meta).attr("name")+ ' = '+ $j(meta).attr("content"));
1301                                                         _this.anno_data_cache[$j(clip).attr("id")]['meta'][$j(meta).attr("name")]=$j(meta).attr("content");
1302                                                 });
1303                                         });
1304                                         _this.showNextPrevLinks();                                      
1305                                 });
1306                         }
1307                 }else{
1308                         js_log('no annotative track found');
1309                         $j('#liks_info_'+this.id).html('no metadata found for related links');
1310                 }
1311                 //query current request time +|- 60s to get prev next speech links. 
1312         },
1313         showNextPrevLinks:function(){
1314                 //js_log('f:showNextPrevLinks');
1315                 //int requested links: 
1316                 var link = {
1317                         'prev':'',
1318                         'current':'',
1319                         'next':''
1320                 }               
1321                 var curTime = this.getTimeReq().split('/');
1322                 
1323                 var s_sec = npt2seconds(curTime[0]);
1324                 var e_sec = npt2seconds(curTime[1]); 
1325                 js_log('showNextPrevLinks: req time: '+ s_sec + ' to ' + e_sec);
1326                 //now we have all the data in anno_data_cache
1327                 var current_done=false;
1328                 for(var clip_id in this.anno_data_cache){  //for in loop oky for object
1329                          var clip =  this.anno_data_cache[clip_id];
1330                          //js_log('on clip:'+ clip_id);
1331                          //set prev_link (if cur_link is still empty)
1332                         if( s_sec > clip.end_time_sec){
1333                                 link.prev = clip_id;
1334                                 js_log('showNextPrevLinks: ' + s_sec + ' < ' + clip.end_time_sec + ' set prev');
1335                         }
1336                                 
1337                         if(e_sec==clip.end_time_sec && s_sec== clip.start_time_sec)
1338                                 current_done = true;
1339                         //current clip is not done:
1340                         if(  e_sec < clip.end_time_sec  && link.current=='' && !current_done){
1341                                 link.current = clip_id;
1342                                 js_log('showNextPrevLinks: ' + e_sec + ' < ' + clip.end_time_sec + ' set current'); 
1343                         }
1344                         
1345                         //set end clip (first clip where start time is > end_time of req
1346                         if( e_sec <  clip.start_time_sec && link.next==''){
1347                                 link.next = clip_id;
1348                                 js_log('showNextPrevLinks: '+  e_sec + ' < '+ clip.start_time_sec + ' && ' + link.next );
1349                         }
1350                 }   
1351                 var html='';   
1352                 if(link.prev=='' && link.current=='' && link.next==''){
1353                         html='<p><a href="'+this.media_element.linkbackgetMsg+'">clip page</a>';
1354                 }else{          
1355                         for(var link_type in link){
1356                                 var link_id = link[link_type];                  
1357                                 if(link_id!=''){
1358                                         var clip = this.anno_data_cache[link_id];                               
1359                                         var title_msg='';
1360                                         for(var j in clip['meta']){
1361                                                 title_msg+=j.replace(/_/g,' ') +': ' +clip['meta'][j].replace(/_/g,' ') +" <br>";
1362                                         }               
1363                                         var time_req =   clip.time_req;
1364                                         if(link_type=='current') //if current start from end of current clip play to end of current meta:                                
1365                                                 time_req = curTime[1]+ '/' + seconds2npt( clip.end_time_sec );
1366                                         
1367                                         //do special linkbacks for metavid content: 
1368                                         var regTimeCheck = new RegExp(/[0-9]+:[0-9]+:[0-9]+\/[0-9]+:[0-9]+:[0-9]+/);                            
1369                                         html+='<p><a  ';
1370                                         if( regTimeCheck.test( this.media_element.linkback ) ){
1371                                                 html+=' href="'+ this.media_element.linkback.replace(regTimeCheck,time_req) +'" '; 
1372                                         }else{
1373                                                 html+=' href="#" onClick="$j(\'#'+this.id+'\').get(0).playByTimeReq(\''+ 
1374                                                                 time_req + '\'); return false; "';                              
1375                                         }
1376                                         html+=' title="' + title_msg + '">' + 
1377                                                  gM('mwe-' + link_type+'_clip_msg') +                             
1378                                         '</a><br><span style="font-size:small">'+ title_msg +'<span></p>';
1379                                 }                                                       
1380                         }
1381                 }       
1382                 //js_og("should set html:"+ html);
1383                 $j('#liks_info_'+this.id).html(html);
1384         },
1385         playByTimeReq: function(time_req){
1386                 js_log('f:playByTimeReq: '+time_req );
1387                 this.stop();
1388                 this.updateVideoTimeReq(time_req);
1389                 this.play();            
1390         },
1391         doThumbnailHTML:function()
1392         {         
1393                 var _this = this;
1394                 js_log('f:doThumbnailHTML'+ this.thumbnail_disp);               
1395                 this.closeDisplayedHTML();         
1396                 $j( '#mv_embedded_player_' + this.id ).html( this.getThumbnailHTML() );
1397                 this.paused = true;             
1398                 this.thumbnail_disp = true;
1399         },
1400         refreshControlsHTML:function(){
1401                 js_log('refreshing controls HTML');
1402                 if($j('#mv_embedded_controls_'+this.id).length==0)
1403                 {
1404                         js_log('#mv_embedded_controls_'+this.id + ' not present, returning');
1405                         return;
1406                 }else{
1407                         $j('#mv_embedded_controls_'+this.id).html( this.getControlsHTML() );
1408                         ctrlBuilder.addControlHooks(this);                                              
1409                 }               
1410         },   
1411         getControlsHTML:function()
1412         {                       
1413                 return ctrlBuilder.getControls( this );
1414         },      
1415         getHTML : function (){          
1416                 //@@todo check if we have sources avaliable     
1417                 js_log('embedVideo:getHTML : ' + this.id  + ' resource type: ' + this.type);                    
1418                 var _this = this;                                
1419                 var html_code = '';             
1420                 html_code = '<div id="videoPlayer_'+this.id+'" style="width:'+this.width+'px;position:relative;" class="videoPlayer">';         
1421                 html_code += '<div style="width:'+parseInt(this.width)+'px;height:'+parseInt(this.height)+'px;"  id="mv_embedded_player_'+this.id+'">' +
1422                                                 this.getThumbnailHTML() + 
1423                                         '</div>';                                                                                       
1424                 //js_log("mvEmbed:controls "+ typeof this.controls);                                                                    
1425                 if( this.controls )
1426                 {
1427                         js_log("f:getHTML:AddControls");
1428                         html_code +='<div id="mv_embedded_controls_' + this.id + '" class="ui-widget ui-corner-bottom ui-state-default controls" >';
1429                         html_code += this.getControlsHTML();       
1430                         html_code +='</div>';     
1431                         //block out some space by encapulating the top level div 
1432                         $j(this).wrap('<div style="width:'+parseInt(this.width)+'px;height:'
1433                                         +(parseInt(this.height)+ctrlBuilder.height)+'px"></div>');                                      
1434                 }
1435                 html_code += '</div>'; //videoPlayer div close          
1436                 //js_log('should set: '+this.id);
1437                 $j(this).html( html_code );                                     
1438                 //add hooks once Controls are in DOM
1439                 ctrlBuilder.addControlHooks(this);              
1440                                                   
1441                 //js_log('set this to: ' + $j(this).html() );   
1442                 //alert('stop');
1443                 //if auto play==true directly embed the plugin
1444                 if(this.autoplay)
1445                 {
1446                         js_log('activating autoplay');
1447                         this.play();
1448                 }
1449         },
1450         /*
1451         * get missing plugin html (check for user included code)
1452         */
1453         getPluginMissingHTML : function(missing_type){
1454                 //keep the box width hight:
1455                 var out = '<div style="width:'+this.width+'px;height:'+this.height+'px">';
1456                 if(this.user_missing_plugin_html){
1457                   out+= this.user_missing_plugin_html;
1458                 }else{
1459                   if(!missing_type)
1460                         missing_type='';
1461                   out+= gM('mwe-generic_missing_plugin', missing_type) + ' or <a title="'+gM('mwe-download_clip')+'" href="'+this.src +'">'+gM('mwe-download_clip')+'</a>';
1462                 }
1463                 return out + '</div>';
1464         },
1465         updateVideoTimeReq:function(time_req){
1466                 js_log('f:updateVideoTimeReq');
1467                 var time_parts =time_req.split('/');
1468                 this.updateVideoTime(time_parts[0], time_parts[1]);
1469         },
1470         //update video time
1471         updateVideoTime:function(start_ntp, end_ntp){                                   
1472                 //update media
1473                 this.media_element.updateSourceTimes( start_ntp, end_ntp );
1474                 //update mv_time
1475                 this.setStatus(start_ntp+'/'+end_ntp);
1476                 //reset slider
1477                 this.setSliderValue(0);
1478                 //reset seek_offset:
1479                 if(this.media_element.selected_source.URLTimeEncoding )
1480                         this.seek_time_sec=0;
1481                 else
1482                         this.seek_time_sec=npt2seconds(start_ntp);
1483         },              
1484         //@@todo overwite by embed library if we can render frames natavily 
1485         renderTimelineThumbnail:function( options ){
1486                 var my_thumb_src = this.media_element.getThumbnailURL();
1487                 //check if our thumbnail has a time attribute: 
1488                 if( my_thumb_src.indexOf('t=') !== -1){
1489                         var time_ntp =  seconds2npt ( options.time + parseInt(this.start_offset) );
1490                         my_thumb_src = getURLParamReplace( my_thumb_src, { 't':time_ntp, 'size': options.size } );
1491                 }
1492                 var thumb_class = (typeof options['thumb_class'] != 'undefined' ) ? options['thumb_class'] : '';
1493                 return '<div class="ui-corner-all ' + thumb_class + '" src="' + my_thumb_src + '" '+
1494                                 'style="height:' + options.height + 'px;' +
1495                                 'width:' + options.width + 'px" >' + 
1496                                          '<img src="' + my_thumb_src +'" '+
1497                                                 'style="height:' + options.height + 'px;' +
1498                                                 'width:' + options.width + 'px">' +
1499                                 '</div>';
1500         },
1501         updateThumbTimeNTP:function( time){
1502                 this.updateThumbTime( npt2seconds(time) - parseInt(this.start_offset) );
1503         },
1504         updateThumbTime:function( float_sec ){
1505                 //js_log('updateThumbTime:'+float_sec);
1506                 var _this = this;                                                                                                          
1507                 if( typeof this.org_thum_src=='undefined' ){            
1508                         this.org_thum_src = this.media_element.getThumbnailURL();
1509                 }                                                       
1510                 if( this.org_thum_src.indexOf('t=') !== -1){
1511                         this.last_thumb_url = getURLParamReplace(this.org_thum_src, 
1512                                 { 't' : seconds2npt( float_sec + parseInt(this.start_offset)) } );                                                                      
1513                         if(!this.thumbnail_updating){                           
1514                                 this.updateThumbnail(this.last_thumb_url ,false);
1515                                 this.last_thumb_url =null;
1516                         }
1517                 }
1518         },
1519         //for now provide a src url .. but need to figure out how to copy frames from video for plug-in based thumbs
1520         updateThumbPerc:function( perc ){       
1521                 return this.updateThumbTime( (this.getDuration() * perc) );
1522         },
1523         //updates the thumbnail if the thumbnail is being displayed
1524         updateThumbnail : function(src, quick_switch){                          
1525                 //make sure we don't go to the same url if we are not already updating: 
1526                 if( !this.thumbnail_updating && $j('#img_thumb_'+this.id).attr('src')== src )
1527                         return false;
1528                 //if we are already updating don't issue a new update: 
1529                 if( this.thumbnail_updating && $j('#new_img_thumb_'+this.id).attr('src')== src )
1530                         return false;
1531                 
1532                 js_log('update thumb: ' + src);
1533                 
1534                 if(quick_switch){
1535                         $j('#img_thumb_'+this.id).attr('src', src);
1536                 }else{
1537                         var _this = this;                       
1538                         //if still animating remove new_img_thumb_
1539                         if(this.thumbnail_updating==true)
1540                                 $j('#new_img_thumb_'+this.id).stop().remove();          
1541                                         
1542                         if(this.thumbnail_disp){
1543                                 js_log('set to thumb:'+ src);
1544                                 this.thumbnail_updating=true;
1545                                 $j('#dc_'+this.id).append('<img src="'+src+'" ' +
1546                                         'style="display:none;position:absolute;zindex:2;top:0px;left:0px;" ' +
1547                                         'width="'+this.width+'" height="'+this.height+'" '+
1548                                         'id = "new_img_thumb_'+this.id+'" />');                                         
1549                                 //js_log('appended: new_img_thumb_');           
1550                                 $j('#new_img_thumb_'+this.id).fadeIn("slow", function(){                                                
1551                                                 //once faded in remove org and rename new:
1552                                                 $j('#img_thumb_'+_this.id).remove();
1553                                                 $j('#new_img_thumb_'+_this.id).attr('id', 'img_thumb_'+_this.id);
1554                                                 $j('#img_thumb_'+_this.id).css('zindex','1');
1555                                                 _this.thumbnail_updating=false;                                         
1556                                                 //js_log("done fadding in "+ $j('#img_thumb_'+_this.id).attr("src"));
1557                                                 
1558                                                 //if we have a thumb queued update to that
1559                                                 if(_this.last_thumb_url){
1560                                                         var src_url =_this.last_thumb_url;
1561                                                         _this.last_thumb_url=null;
1562                                                         _this.updateThumbnail(src_url);
1563                                                 }
1564                                 });
1565                         }
1566                 }
1567         },
1568         /** Returns the HTML code for the video when it is in thumbnail mode.
1569                 This includes the specified thumbnail as well as buttons for
1570                 playing, configuring the player, inline cmml display, HTML linkback,
1571                 download, and embed code.
1572         */
1573         getThumbnailHTML : function ()
1574         {               
1575                 js_log('embedVideo:getThumbnailHTML::' + this.id);
1576                 var thumb_html = '';
1577                 var class_atr='';
1578                 var style_atr='';
1579                 //if(this.class)class_atr = ' class="'+this.class+'"';
1580                 //if(this.style)style_atr = ' style="'+this.style+'"';
1581                 //      else style_atr = 'overflow:hidden;height:'+this.height+'px;width:'+this.width+'px;';
1582                 this.thumbnail = this.media_element.getThumbnailURL();
1584                 //put it all in the div container dc_id
1585                 thumb_html+= '<div id="dc_'+this.id+'" style="position:absolute;'+
1586                         ' overflow:hidden; top:0px; left:0px; width:'+this.playerPixelWidth()+'px; height:'+this.playerPixelHeight()+'px; z-index:0;">'+
1587                         '<img width="'+this.playerPixelWidth()+'" height="'+this.playerPixelHeight()+'" style="position:relative;width:'+this.playerPixelWidth()+';height:'+this.playerPixelHeight()+'"' +
1588                         ' id="img_thumb_'+this.id+'" src="' + this.thumbnail + '">';
1589                 
1590                 if(this.play_button == true && this.controls == true)
1591                           thumb_html+=this.getPlayButton();
1592                           
1593                    thumb_html+='</div>';
1594                 return thumb_html;
1595         },
1596         getEmbeddingHTML:function()
1597         {
1598                 var thumbnail = this.media_element.getThumbnailURL();
1600                 var embed_thumb_html;
1601                 if(thumbnail.substring(0,1)=='/'){
1602                         eURL = parseUri(mv_embed_path);
1603                         embed_thumb_html = eURL.protocol + '://' + eURL.host + thumbnail;
1604                         //js_log('set from mv_embed_path:'+embed_thumb_html);
1605                 }else{
1606                         embed_thumb_html = (thumbnail.indexOf('http://')!=-1)?thumbnail:mv_embed_path + thumbnail;
1607                 }
1608                 var embed_code_html = '&lt;script type=&quot;text/javascript&quot; ' +
1609                                         'src=&quot;'+mv_embed_path+'mv_embed.js&quot;&gt;&lt;/script&gt' +
1610                                         '&lt;video ';
1611                 if(this.roe){
1612                         embed_code_html+='roe=&quot;'+this.roe+'&quot; &gt;';
1613                 }else{
1614                         embed_code_html+='src=&quot;'+this.src+'&quot; ' +
1615                                 'poster=&quot;'+embed_thumb_html+'&quot;&gt;';
1616                 }
1617                 //close the video tag
1618                 embed_code_html+='&lt;/video&gt;';
1620                 return embed_code_html;
1621         },
1622         doOptionsHTML:function()
1623         {
1624                 var sel_id = (this.pc!=null)?this.pc.pp.id:this.id;
1625                 var pos = $j('#options_button_'+sel_id).offset();
1626                 pos['top']=pos['top']+24;
1627                 pos['left']=pos['left']-124;
1628                 //js_log('pos of options button: t:'+pos['top']+' l:'+ pos['left']);
1629                 $j('#mv_vid_options_'+sel_id).css(pos).toggle();
1630                 return;
1631         },
1632         getPlayButton:function(id){
1633                 if(!id)id=this.id;
1634                 return '<div title="' + gM('mwe-play_clip') + '" id="big_play_link_'+id+'" class="large_play_button" '+
1635                         'style="left:'+((this.playerPixelWidth()-130)/2)+'px;'+
1636                         'top:' + ((this.playerPixelHeight()-96)/2) + 'px;">'+
1637                         '<img src="' + mv_skin_img_path + 'player_big_play_button.png">'+
1638                         '</div>';
1639         },
1640         doLinkBack:function(){
1641                 if(this.roe && this.media_element.addedROEData==false){
1642                         var _this = this;
1643                         this.displayHTML(gM('mwe-loading_txt'));
1644                         do_request(this.roe, function(data)
1645                            {
1646                                   _this.media_element.addROE(data);                                                      
1647                                   _this.doLinkBack();
1648                            });                     
1649                 }else{
1650                         if(this.media_element.linkback){                        
1651                                 window.location = this.media_element.linkback;
1652                         }else{
1653                                 this.displayHTML(gM('mwe-could_not_find_linkback'));
1654                         }
1655                 }          
1656         },
1657         //display the code to remotely embed this video:
1658         showEmbedCode : function(embed_code){
1659                 if(!embed_code)
1660                         embed_code = this.getEmbeddingHTML();
1661                 var o='';
1662                 if(this.linkback){
1663                         o+='<a class="email" href="'+this.linkback+'">Share Clip via Link</a> '+
1664                         '<p>or</p> ';
1665                 }
1666                 o+='<div>' +
1667                                 '<span style="color:#FFF;font-size:14px;">Embed Clip in Blog or Site</span><br>'+
1668                                 '<span style="color:#FFF;font-size:12px;"><a style="color:red" href="http://metavid.org/wiki/Security_Notes_on_Remote_Embedding">'+
1669                                         'Read This</a> before embeding.</span>'+
1670                                 '<div class="embed_code"> '+
1671                                         '<textarea onClick="this.select();" id="embedding_user_html_'+this.id+'" name="embed">' +
1672                                                 embed_code+
1673                                         '</textarea> '+
1674                                         '<button onClick="$j(\'#'+this.id+'\').get(0).copyText(); return false;" class="copy_to_clipboard">Copy to Clipboard</button> '+
1675                                 '</div> '+
1676                         '</div>';
1677                 this.displayHTML(o);
1678         },
1679         copyText:function(){
1680           $j('#embedding_user_html_'+this.id).focus().select();                 
1681           if(document.selection){         
1682                   CopiedTxt = document.selection.createRange(); 
1683                   CopiedTxt.execCommand("Copy");
1684           }
1685         },
1686         showTextInterface:function(){   
1687                 var _this = this;
1688                 //display the text container with loading text: 
1689                 //@@todo support position config
1690                 var loc = $j(this).position();                  
1691                 if($j('#metaBox_'+this.id).length==0){
1692                         $j(this).after('<div class="ui-widget ui-widget-content ui-corner-all" style="position:absolute;z-index:10;'+
1693                                                 'top:' + (loc.top) + 'px;' +
1694                                                 'left:' + (parseInt( loc.left ) + parseInt(this.width) + 10 )+'px;' +
1695                                                 'height:'+ parseInt( this.height )+'px;width:400px;' +                                          
1696                                                 'display:none;" ' +
1697                                                 'id="metaBox_' + this.id + '">'+
1698                                                         gM('mwe-loading_txt') +
1699                                                 '</div>');                                      
1700                 }
1701                 //fade in the text display
1702                 $j('#metaBox_'+this.id).fadeIn("fast"); 
1703                 //check if textObj present:
1704                 if(typeof this.textInterface == 'undefined' ){
1705                         //load the default text interface:
1706                         mvJsLoader.doLoad([
1707                                         'mvTextInterface', 
1708                                         '$j.fn.hoverIntent'
1709                                 ], function(){                                  
1710                                         _this.textInterface = new mvTextInterface( _this );                                                     
1711                                         //show interface
1712                                         _this.textInterface.show();
1713                                         js_log("NEW TEXT INTERFACE");
1714                                         for(var i in _this.textInterface.availableTracks){
1715                                                 js_log("tracks in new interface: "+_this.id+ ' tid:' + i);                                              
1716                                         }
1717                                 }
1718                         );
1719                 }else{
1720                         //show interface
1721                         this.textInterface.show();
1722                 }
1723         },
1724         closeTextInterface:function(){
1725                 js_log('closeTextInterface '+ typeof this.textInterface);
1726                 if(typeof this.textInterface !== 'undefined' ){
1727                         this.textInterface.close();
1728                 }
1729         },
1730         /** Generic function to display custom HTML inside the mv_embed element.
1731                 The code should call the closeDisplayedHTML function to close the
1732                 display of the custom HTML and restore the regular mv_embed display.            
1733                 @param {String} HTML code for the selection list.
1734         */
1735         displayHTML:function(html_code)
1736         {
1737                 var sel_id = (this.pc!=null)?this.pc.pp.id:this.id;
1738                 
1739                 if(!this.supports['overlays'])
1740                         this.stop();
1741                 
1742                 //put select list on-top
1743                 //make sure the parent is relatively positioned:
1744                 $j('#'+sel_id).css('position', 'relative');
1745                 //set height width (check for playlist container)
1746                 var width = (this.pc)?this.pc.pp.width:this.playerPixelWidth();
1747                 var height = (this.pc)?this.pc.pp.height:this.playerPixelHeight();
1748                 
1749                 if(this.pc)
1750                         height+=(this.pc.pp.pl_layout.title_bar_height + this.pc.pp.pl_layout.control_height);
1751           
1752                 var fade_in = true;
1753                 if($j('#blackbg_'+sel_id).length!=0)
1754                 {
1755                         fade_in = false;
1756                         $j('#blackbg_'+sel_id).remove();
1757                 }
1758                 //fade in a black bg div ontop of everything
1759                  var div_code = '<div id="blackbg_'+sel_id+'" class="videoComplete" ' +
1760                          'style="height:'+parseInt(height)+'px;width:'+parseInt(width)+'px;">'+
1761                           '<div class="videoOptionsComplete">'+
1762                         //@@TODO: this style should go to .css
1763                         '<span style="float:right;margin-right:10px">' +                        
1764                                         '<a href="#" style="color:white;" onClick="$j(\'#'+sel_id+'\').get(0).closeDisplayedHTML();return false;">close</a>' +
1765                         '</span>'+
1766                         '<div id="mv_disp_inner_'+sel_id+'" style="padding-top:10px;">'+
1767                                  html_code 
1768                            +'</div>'+
1769                            '</div></div>';
1770                 $j('#'+sel_id).prepend(div_code);
1771                 if (fade_in)
1772                         $j('#blackbg_'+sel_id).fadeIn("slow");
1773                 else
1774                         $j('#blackbg_'+sel_id).show();
1775                 return false; //onclick action return false
1776         },
1777         /** Close the custom HTML displayed using displayHTML and restores the
1778                 regular mv_embed display.
1779         */
1780         closeDisplayedHTML:function(){
1781                   var sel_id = (this.pc!=null)?this.pc.pp.id:this.id;
1782                  $j('#blackbg_'+sel_id).fadeOut("slow", function(){
1783                          $j('#blackbg_'+sel_id).remove();
1784                  });
1785                  return false; //onclick action return false
1786         },
1787         selectPlaybackMethod:function(){                
1788                 //get id (in case where we have a parent container)
1789                 var this_id = (this.pc!=null)?this.pc.pp.id:this.id;
1790                 
1791                 var _this=this;                    
1792                 var out= '<span style="color:#FFF;background-color:black;"><blockquote style="background-color:black;">';
1793                 var _this=this;
1794                 //js_log('selected src'+ _this.media_element.selected_source.url);
1795                 $j.each( this.media_element.getPlayableSources(), function(source_id, source){                   
1796                         var default_player = embedTypes.players.defaultPlayer( source.getMIMEType() );                                                             
1797                         
1798                         var is_selected = (source == _this.media_element.selected_source);
1799                         var image_src =  mv_skin_img_path ;
1800                         
1801                         //set the Playable source type: 
1802                         if( source.mime_type == 'video/x-flv' ){
1803                                 image_src += 'flash_icon_';
1804                         }else if( source.mime_type == 'video/h264'){
1805                                 //for now all mp4 content is pulled from archive.org (so use archive.org icon) 
1806                                 image_src += 'archive_org_';
1807                         }else{
1808                                 image_src += 'fish_xiph_org_';
1809                         }
1810                         image_src += is_selected ? 'color':'bw';
1811                         image_src += '.png';                                       
1812                         
1813                         if (default_player)
1814                         {
1815                                 out += '<img src="'+image_src+'"/>';
1816                                 if( ! is_selected )
1817                                         out+='<a href="#" class="sel_source" id="sc_' + source_id + '_' + default_player.id +'">';
1818                                 out += source.getTitle()+ (is_selected?'</a>':'') + ' ';
1819                                 
1820                                 //output the player select code: 
1821                                 var supporting_players = embedTypes.players.getMIMETypePlayers( source.getMIMEType() );         
1822                                 out+='<div id="player_select_list_' + source_id + '" class="player_select_list"><ul>';
1823                                 for(var i=0; i < supporting_players.length ; i++){                              
1824                                         if( _this.selected_player.id == supporting_players[i].id && is_selected ){
1825                                                 out+='<li style="border-style:dashed;margin-left:20px;">'+
1826                                                                         '<img border="0" width="16" height="16" src="' + mv_skin_img_path + 'plugin.png">' +
1827                                                                         supporting_players[i].getName() +
1828                                                         '</li>';
1829                                         }else{
1830                                                 //else gray plugin and the plugin with link to select
1831                                                 out+='<li style="margin-left:20px;">'+
1832                                                                 '<a href="#" class="sel_source" id="sc_' + source_id + '_' + supporting_players[i].id +'">'+
1833                                                                         '<img border="0" width="16" height="16" src="' + mv_skin_img_path + 'plugin_disabled.png">'+
1834                                                                         supporting_players[i].getName() +
1835                                                                 '</a>'+
1836                                                         '</li>';
1837                                         }
1838                                  }
1839                                  out+='</ul></div>';               
1840                         }else
1841                                 out+= source.getTitle() + ' - no player available';
1842                 });
1843                 out+='</blockquote></span>';
1844                 this.displayHTML(out);
1845                 
1846                 //set up the click bindings:
1847                 $j('.sel_source').each(function(){
1848                         $j(this).click(function(){
1849                                 var iparts = $j(this).attr( 'id' ).replace(/sc_/,'').split('_');
1850                                 var source_id = iparts[0];
1851                                 var default_player_id = iparts[1];
1852                                 js_log('source id: ' +  source_id + ' player id: ' + default_player_id);                                
1853                                  
1854                                 $j('#' + this_id  ).get(0).closeDisplayedHTML();                                
1855                                 $j('#' + _this.id ).get(0).media_element.selectSource( source_id );
1856                                 
1857                                 embedTypes.players.userSelectPlayer( default_player_id,
1858                                          _this.media_element.sources[ source_id ].getMIMEType() );
1859                                          
1860                                 //be sure to issue a stop
1861                                 $j('#' + this_id  ).get(0).stop();
1862                                 
1863                                 //don't follow the empty # link:
1864                                 return false;
1865                         });
1866                 });
1867         },      
1868         showVideoDownload:function(){           
1869                 //load the roe if available (to populate out download options:
1870                 //js_log('f:showVideoDownload '+ this.roe + ' ' + this.media_element.addedROEData);
1871                 if(this.roe && this.media_element.addedROEData == false){
1872                         var _this = this;
1873                         this.displayHTML(gM('mwe-loading_txt'));
1874                         do_request(this.roe, function(data)
1875                         {
1876                            _this.media_element.addROE(data);                                                     
1877                            $j('#mv_disp_inner_'+_this.id).html( _this.getShowVideoDownload() );
1878                         });                        
1879                 }else{
1880                         this.displayHTML( this.getShowVideoDownload() );
1881                 }          
1882         },
1883         getShowVideoDownload:function(){ 
1884                 var out='<div style="color:white">' +
1885                                 '<b style="color:white;">'+gM('mwe-download_segment')+'</b><br>';
1886                 out+='<blockquote style="background:#000">'+
1887                                 gM('mwe-download_right_click') + '</blockquote><br>';
1888                 var dl_list='';
1889                 var dl_txt_list='';             
1890                 $j.each(this.media_element.getSources(), function(index, source){
1891                         var dl_line = '<li>' + '<a style="color:white" href="' + source.getURI() +'"> '
1892                                 + source.getTitle()+'</a> '+ '</li>'+"\n";                      
1893                         if(      source.getURI().indexOf('?t=')!==-1){
1894                                 out+=dl_line;
1895                         }else if( this.getMIMEType()=="text/cmml" || this.getMIMEType()=="text/x-srt" ){
1896                                 dl_txt_list+=dl_line;
1897                         }else{
1898                                 dl_list+=dl_line;
1899                         }
1900                 });             
1901                 
1902                 if(dl_list!='')
1903                         out+=gM('mwe-download_full') + '<blockquote style="background:#000">' + dl_list + '</blockquote>';
1904                 if(dl_txt_list!='')
1905                         out+=gM('mwe-download_text')+'<blockquote style="background:#000">' + dl_txt_list +'</blockquote>';
1906                 out+='</div>';
1907                 return out;
1908         },
1909         /*
1910         *  base embed controls
1911         *       the play button calls
1912         */
1913         play:function(){
1914                 var this_id = (this.pc!=null)?this.pc.pp.id:this.id;
1915                                 
1916                 //js_log( "mv_embed play:" + this.id);          
1917                 //js_log('thum disp:'+this.thumbnail_disp);
1918                 //check if thumbnail is being displayed and embed html
1919                 if( this.thumbnail_disp ){                      
1920                         if( !this.selected_player ){
1921                                 js_log('no selected_player');
1922                                 //this.innerHTML = this.getPluginMissingHTML();
1923                                 //$j(this).html(this.getPluginMissingHTML());
1924                                 $j('#'+this.id).html( this.getPluginMissingHTML() );
1925                         }else{
1926                                 this.doEmbedHTML();
1927                                 this.onClipDone_disp=false;                        
1928                                 this.paused=false;         
1929                                 this.thumbnail_disp=false;               
1930                         }
1931                 }else{
1932                         //the plugin is already being displayed                 
1933                         this.paused=false; //make sure we are not "paused"
1934                         this.seeking=false;
1935                 }                               
1936                 
1937                  $j("#mv_play_pause_button_" + this_id + ' span').removeClass('ui-icon-play').addClass('ui-icon-pause');                           
1938                  $j("#mv_play_pause_button_" + this_id).unbind().btnBind().click(function(){                                    
1939                         $j('#' + this_id ).get(0).pause();
1940                  }).attr('title', gM('mwe-pause_clip'));
1941                    
1942         },
1943         load:function(){
1944                 //should be done by child (no base way to load assets)
1945                 js_log('baseEmbed:load call');
1946         },
1947         getSrc:function(){
1948            return this.media_element.selected_source.getURI( this.seek_time_sec );
1949         }, 
1950         /*
1951          * base embed pause
1952          *      there is no general way to pause the video
1953          *  must be overwritten by embed object to support this functionality.
1954          */
1955         pause: function(){
1956                 var this_id = (this.pc!=null)?this.pc.pp.id:this.id;                                                      
1957                 //js_log('mv_embed:do pause');          
1958                 //(playing) do pause            
1959                 this.paused = true; 
1960                 //update the ctrl "paused state"                                
1961                 $j("#mv_play_pause_button_" + this_id + ' span').removeClass('ui-icon-pause').addClass('ui-icon-play');
1962                 $j("#mv_play_pause_button_" + this_id).unbind().btnBind().click(function(){                                                      
1963                                 $j('#'+this_id).get(0).play();
1964                 }).attr('title', gM('mwe-play_clip'));
1965         },      
1966         /*
1967          * base embed stop (can be overwritten by the plugin)
1968          */
1969         stop: function(){
1970                 var _this = this;
1971                 js_log('mvEmbed:stop:'+this.id);
1972                 
1973                 //no longer seeking:
1974                 this.didSeekJump=false;
1975                 
1976                 //first issue pause to update interface (only call the parent) 
1977                 if(this['parent_pause']){
1978                         this.parent_pause();
1979                 }else{
1980                         this.pause();
1981                 }       
1982                 
1983                 //reset the currentTime: 
1984                 this.currentTime=0;
1985                 //check if thumbnail is being displayed in which case do nothing
1986                 if( this.thumbnail_disp ){
1987                         //already in stooped state
1988                         js_log('already in stopped state');
1989                 }else{
1990                         //rewrite the html to thumbnail disp
1991                         this.doThumbnailHTML();
1992                         this.bufferedPercent=0; //reset buffer state
1993                         this.setSliderValue(0);
1994                         this.setStatus( this.getTimeReq() );
1995                 }
1996                 
1997                 //make sure the big playbutton is has click action: 
1998                 $j('#big_play_link_' + _this.id).unbind('click').click(function(){
1999                         $j('#' +_this.id).get(0).play();
2000                 });
2001                 
2002                 if(this.update_interval)
2003                 {
2004                         clearInterval(this.update_interval);
2005                         this.update_interval = null;
2006                 }
2007         },
2008         toggleMute:function(){
2009                 var this_id = (this.pc!=null)?this.pc.pp.id:this.id;    
2010                 if(this.muted){
2011                         this.muted=false;
2012                         $j('#volume_control_'+this_id + ' span').removeClass('ui-icon-volume-off').addClass('ui-icon-volume-on');
2013                         $j('#volume_bar_'+this_id).slider('value', 100); 
2014                         this.updateVolumen(1);
2015                 }else{
2016                         this.muted=true;
2017                         $j('#volume_control_'+this_id + ' span').removeClass('ui-icon-volume-on').addClass('ui-icon-volume-off');
2018                         $j('#volume_bar_'+this_id).slider('value', 0);
2019                         this.updateVolumen(0); 
2020                 }
2021                 js_log('f:toggleMute::' + this.muted);          
2022         },
2023         updateVolumen:function(perc){
2024                 js_log('update volume not supported with current playback type');
2025         },
2026         fullscreen:function(){
2027                 js_log('fullscreen not supported with current playback type');
2028         },
2029         /* returns bool true if playing or paused, false if stooped
2030          */
2031         isPlaying : function(){
2032                 if(this.thumbnail_disp){
2033                         //in stoped state
2034                         return false;
2035                 }else if( this.paused ){
2036                         //paused state
2037                         return false;
2038                 }else{
2039                         return true;
2040                 }
2041         },
2042         isPaused : function(){
2043                 return this.isPlaying() && this.paused;
2044         },
2045         isStoped : function(){
2046                 return this.thumbnail_disp;
2047         },
2048         playlistSupport:function(){
2049                 //by default not supported (implemented in js)
2050                 return false;
2051         },
2052         postEmbedJS:function(){
2053                 return '';
2054         },
2055         //do common monitor code like update the playhead and play status 
2056         //plugin objects are responsible for updating currentTime
2057         monitor:function(){
2058                 if( this.currentTime && this.currentTime > 0 && this.duration){
2059                         if( !this.userSlide ){
2060                                 if( this.start_offset  ){ 
2061                                         //if start offset include that calculation 
2062                                         this.setSliderValue( ( this.currentTime - this.start_offset ) / this.duration );                        
2063                                         this.setStatus( seconds2npt(this.currentTime) + '/'+ seconds2npt(parseFloat(this.start_offset)+parseFloat(this.duration) ));            
2064                                 }else{
2065                                         this.setSliderValue( this.currentTime / this.duration );
2066                                         this.setStatus( seconds2npt(this.currentTime) + '/' + seconds2npt(this.duration ));
2067                                 }                               
2068                         }
2069                 }else{
2070                         //js_log(' ct:' + this.currentTime + ' dur: ' + this.duration);
2071                         if( this.isStoped() ){
2072                                 this.setStatus( this.getTimeReq() );
2073                         }else if( this.isPaused() ){
2074                                 this.setStatus( "paused" );
2075                         }else if( this.isPlaying() ){
2076                                 if( this.currentTime && ! this.duration ) 
2077                                         this.setStatus( seconds2npt( this.currentTime ) + ' /' );
2078                                 else   
2079                                         this.setStatus(" - - - ");
2080                         }else{
2081                                 this.setStatus( this.getTimeReq() );
2082                         }                       
2083                 }
2084                 //update buffer information 
2085                 this.updateBufferStatus();
2086                 
2087                 //check if we passed duration
2088                 if( this.duration && (this.currentTime > this.duration) ){
2089                         this.onClipDone();
2090                 }
2091                 
2092                 //update monitorTimerId to call child monitor
2093                 if( ! this.monitorTimerId ){
2094                         //make sure an instance of this.id exists: 
2095                         if( document.getElementById(this.id) ){
2096                                 this.monitorTimerId = setInterval('$j(\'#'+this.id+'\').get(0).monitor()', 250);
2097                         }
2098                 }
2099         },              
2100         stopMonitor:function(){
2101                 if( this.monitorTimerId != 0 )
2102                 {
2103                         clearInterval( this.monitorTimerId );
2104                         this.monitorTimerId = 0;
2105                 }
2106         },
2107         updateBufferStatus: function(){
2108                         
2109                 //build the buffer targeet based for playlist vs clip 
2110                 var buffer_select = (this.pc) ? 
2111                         '#cl_status_' + this.id + ' .mv_buffer': 
2112                         '#mv_play_head_' + this.id + ' .mv_buffer';
2113                         
2114                 //update the buffer progress bar (if available )
2115                 if( this.bufferedPercent != 0 ){
2116                         //js_log('bufferedPercent: ' + this.bufferedPercent);                   
2117                         if(this.bufferedPercent > 1)
2118                                 this.bufferedPercent=1;                                                 
2119                         
2120                         $j(buffer_select).css("width", (this.bufferedPercent*100) +'%' );
2121                 }else{
2122                         $j(buffer_select).css("width", '0px' );
2123                 }
2124         },
2125         relativeCurrentTime: function(){
2126                 if(!this.start_offset)
2127                         this.start_offset =0;
2128                 var rt = this.currentTime - this.start_offset;
2129                 if( rt < 0 ) //should not happen but does. 
2130                         return 0;
2131                 return rt;
2132         },
2133         getPluginEmbed : function(){
2134                 if (window.document[this.pid]){
2135                         return window.document[this.pid];
2136                 }
2137                 if ($j.browser.msie){
2138                         return document.getElementById(this.pid );
2139                 }else{
2140                          if (document.embeds && document.embeds[this.pid])
2141                                 return  document.embeds[this.pid];
2142                 }
2143                 return null;
2144         },
2145         //HELPER Functions for selected source  
2146         /*
2147         * returns the selected source url for players to play
2148         */
2149         getURI : function( seek_time_sec ){
2150                 return this.media_element.selected_source.getURI( this.seek_time_sec );
2151         },
2152         supportsURLTimeEncoding: function(){
2153                 //do head request if on the same domain
2154                 return this.media_element.selected_source.URLTimeEncoding;
2155         },
2156         setSliderValue: function(perc, hide_progress){          
2157                 if(this.controls){              
2158                         var this_id = (this.pc)?this.pc.pp.id:this.id;
2159                         var val = parseInt( perc*1000 ); 
2160                         $j('#mv_play_head_'+this_id).slider('value', val);
2161                         
2162                         //js_log("embed video set: " + '#mv_play_head_'+this_id + ' to ' + val);
2163                 }
2164                 //js_log('set#mv_seeker_slider_'+this_id + ' perc in: ' + perc + ' * ' + $j('#mv_seeker_'+this_id).width() + ' = set to: '+ val + ' - '+ Math.round(this.mv_seeker_width*perc) );
2165                 //js_log('op:' + offset_perc + ' *('+perc+' * ' + $j('#slider_'+id).width() + ')');
2166         },
2167         highlightPlaySection:function(options){
2168                 js_log('highlightPlaySection');
2169                 var this_id = (this.pc)?this.pc.pp.id:this.id;
2170                 var dur = this.getDuration();
2171                 var hide_progress = true;               
2172                 //set the left percet and update the slider: 
2173                 rel_start_sec = npt2seconds( options['start']);
2174                 //remove the start_offset if relevent: 
2175                 if(this.start_offset)
2176                         rel_start_sec = rel_start_sec - this.start_offset
2177                 
2178                 var slider_perc=0;
2179                 if( rel_start_sec <= 0 ){
2180                         left_perc =0;                    
2181                         options['start'] = seconds2npt( this.start_offset );
2182                         rel_start_sec=0;                        
2183                         this.setSliderValue( 0 , hide_progress);
2184                 }else{
2185                         left_perc = parseInt( (rel_start_sec / dur)*100 ) ;             
2186                         slider_perc = (left_perc / 100);
2187                 }               
2188                 js_log("slider perc:" + slider_perc);   
2189                 if( ! this.isPlaying() ){
2190                         this.setSliderValue( slider_perc , hide_progress);              
2191                 }
2192                 
2193                 width_perc = parseInt( (( npt2seconds( options['end'] ) - npt2seconds( options['start'] ) ) / dur)*100 ) ;                                                       
2194                 if( (width_perc + left_perc) > 100 ){
2195                         width_perc = 100 - left_perc; 
2196                 }               
2197                 //js_log('should hl: '+rel_start_sec+ '/' + dur + ' re:' + rel_end_sec+' lp:'  + left_perc + ' width: ' + width_perc);  
2198                 $j('#mv_seeker_' + this_id + ' .mv_highlight').css({
2199                         'left':left_perc+'%',
2200                         'width':width_perc+'%'                  
2201                 }).show();                              
2202                 
2203                 this.jump_time =  options['start'];
2204                 this.seek_time_sec = npt2seconds( options['start']);
2205                 //trim output to 
2206                 this.setStatus( gM('mwe-seek_to')+' '+ seconds2npt( this.seek_time_sec ) );
2207                 js_log('DO update: ' +  this.jump_time);
2208                 this.updateThumbTime( rel_start_sec );  
2209         },
2210         hideHighlight:function(){
2211                 var this_id = (this.pc)?this.pc.pp.id:this.id;
2212                 $j('#mv_seeker_' + this_id + ' .mv_highlight').hide();
2213                 this.setStatus( this.getTimeReq() );
2214                 this.setSliderValue( 0 );
2215         },
2216         setStatus:function(value){
2217                 var id = (this.pc)?this.pc.pp.id:this.id;
2218                 //update status:
2219                 $j('#mv_time_'+id).html(value);
2220         }       
2226   * mediaPlayer represents a media player plugin.
2227   * @param {String} id id used for the plugin.
2228   * @param {Array<String>} supported_types n array of supported MIME types.
2229   * @param {String} library external script containing the plugin interface code. (mv_<library>Embed.js)
2230   * @constructor
2231   */
2232 function mediaPlayer(id, supported_types, library)
2234         this.id=id;
2235         this.supported_types = supported_types;
2236         this.library = library;
2237         this.loaded = false;
2238         this.loading_callbacks = new Array();
2239         return this;
2241 mediaPlayer.prototype =
2243         id:null,
2244         supported_types:null,
2245         library:null,
2246         loaded:false,
2247         loading_callbacks:null, 
2248         supportsMIMEType : function(type)
2249         {               
2250                 for (var i=0; i < this.supported_types.length; i++)
2251                         if(this.supported_types[i] == type)
2252                                 return true;
2253                 return false;
2254         },
2255         getName : function()
2256         {
2257                 return gM('mwe-ogg-player-' + this.id);
2258         },
2259         load : function(callback){
2260                 var libName = this.library+'Embed';
2261                 if( mvJsLoader.checkObjPath( libName ) ){
2262                         js_log('plugin loaded, do callback:');
2263                         callback();
2264                 }else{
2265                         var _this = this;                                                                                       
2266                         //jQuery based get script does not work so well.                                                        
2267                         mvJsLoader.doLoad([ 
2268                                 libName
2269                         ],function(){
2270                                 callback();                                                     
2271                         });
2272                 }
2273         }       
2275 /* players and supported mime types 
2276 @@todo ideally we query the plugin to get what mime types it supports in practice not always reliable/avaliable
2278 var flowPlayer = new mediaPlayer('flowplayer',['video/x-flv', 'video/h264'],'flash');
2280 var omtkPlayer = new mediaPlayer('omtkplayer',['audio/ogg'], 'omtk' );
2282 var cortadoPlayer = new mediaPlayer('cortado',['video/ogg', 'audio/ogg'],'java');
2283 var videoElementPlayer = new mediaPlayer('videoElement',['video/ogg', 'audio/ogg'],'native');
2285 var vlcMineList = ['video/ogg','audio/ogg', 'video/x-flv', 'video/mp4',  'video/h264'];
2286 var vlcMozillaPlayer = new mediaPlayer('vlc-mozilla',vlcMineList,'vlc');
2287 var vlcActiveXPlayer = new mediaPlayer('vlc-activex',vlcMineList,'vlc');
2289 //add generic
2290 var oggPluginPlayer = new mediaPlayer('oggPlugin',['video/ogg'],'generic');
2292 //depricate quicktime in favor of safari native 
2293 //var quicktimeMozillaPlayer = new mediaPlayer('quicktime-mozilla',['video/ogg'],'quicktime');
2294 //var quicktimeActiveXPlayer = new mediaPlayer('quicktime-activex',['video/ogg'],'quicktime');
2296 var htmlPlayer = new mediaPlayer('html',['text/html', 'image/jpeg', 'image/png', 'image/svg'], 'html');
2299   * mediaPlayers is a collection of mediaPlayer objects supported by the client.
2300   * It could be merged with embedTypes, since there is one embedTypes per script
2301   * and one mediaPlayers per embedTypes.
2302   */
2303 function mediaPlayers()
2305         this.init();
2308 mediaPlayers.prototype =
2310         players : null,
2311         preference : null,
2312         default_players : {},
2313         init : function()
2314         {
2315                 this.players = new Array();
2316                 this.loadPreferences();
2317                 
2318                 //set up default players order for each library type            
2319                 this.default_players['video/x-flv'] = ['flash','vlc'];
2320                 this.default_players['video/h264'] = ['flash', 'vlc'];
2321                 
2322                 this.default_players['video/ogg'] = ['native','vlc','java', 'generic'];         
2323                 this.default_players['application/ogg'] = ['native','vlc','java', 'generic'];           
2324                 this.default_players['audio/ogg'] = ['native','vlc', 'java', 'omtk' ];          
2325                 this.default_players['video/mp4'] = ['vlc'];
2326                 
2327                 this.default_players['text/html'] = ['html'];
2328                 this.default_players['image/jpeg'] = ['html'];
2329                 this.default_players['image/png'] = ['html'];
2330                 this.default_players['image/svg'] = ['html'];
2331                 
2332         },
2333         addPlayer : function(player, mime_type)
2334         {               
2335                 //js_log('Adding ' + player.id + ' with mime_type ' + mime_type);
2336                 for (var i =0; i < this.players.length; i++){
2337                         if (this.players[i].id == player.id)
2338                         {
2339                                 if(mime_type!=null)
2340                                 {
2341                                         //make sure the mime_type is not already there:
2342                                         var add_mime = true; 
2343                                         for(var j=0; j < this.players[i].supported_types.length; j++ ){
2344                                                 if( this.players[i].supported_types[j]== mime_type)
2345                                                         add_mime=false;
2346                                         }                                       
2347                                         if(add_mime)
2348                                                 this.players[i].supported_types.push(mime_type);
2349                                 }
2350                                 return;
2351                         }
2352                 }
2353                 //player not found: 
2354                 if(mime_type!=null)
2355                         player.supported_types.push(mime_type);   
2356                                  
2357                 this.players.push( player );
2358         },
2359         getMIMETypePlayers : function(mime_type)
2360         {                               
2361                 var mime_players = new Array();
2362                 var _this = this;
2363                 var inx = 0;
2364                 if( this.default_players[mime_type] ){                                          
2365                         $j.each( this.default_players[mime_type], function(d, lib){                             
2366                                 var library = _this.default_players[mime_type][d];                              
2367                                 for ( var i=0; i < _this.players.length; i++ ){                                 
2368                                         if ( _this.players[i].library == library && _this.players[i].supportsMIMEType(mime_type) ){
2369                                                 mime_players[ inx ] = _this.players[i];                                         
2370                                                 inx++;
2371                                         }
2372                                 }
2373                         });
2374                 }               
2375                 return mime_players;
2376         },
2377         defaultPlayer : function(mime_type)
2378         {                               
2379                 js_log("get defaultPlayer for " + mime_type);           
2380                 var mime_players = this.getMIMETypePlayers(mime_type);                          
2381                 if( mime_players.length > 0)
2382                 {
2383                         // check for prior preference for this mime type
2384                         for( var i=0; i < mime_players.length; i++ ){
2385                                 if( mime_players[i].id==this.preference[mime_type] )
2386                                         return mime_players[i];
2387                         }                                       
2388                         // otherwise just return the first compatible player
2389                         // (it will be chosen according to the default_players list
2390                         return mime_players[0];
2391                 }
2392                 js_log( 'No default player found for ' + mime_type );
2393                 return null;
2394         },
2395         userSelectFormat : function (mime_format){
2396                  this.preference['format_prefrence'] = mime_format;
2397                  this.savePreferences();
2398         },
2399         userSelectPlayer : function(player_id, mime_type)
2400         {
2401                 var selected_player=null;
2402                 for(var i=0; i < this.players.length; i++){
2403                         if(this.players[i].id == player_id)
2404                         {
2405                                 selected_player = this.players[i];
2406                                 js_log('choosing ' + player_id + ' for ' + mime_type);
2407                                 this.preference[mime_type]=player_id;
2408                                 this.savePreferences();
2409                                 break;
2410                         }
2411                 }
2412                 if( selected_player )
2413                 {
2414                         for(var i=0; i < global_player_list.length; i++)
2415                         {
2416                                 var embed = $j('#'+global_player_list[i]).get(0);
2417                                 if(embed.media_element.selected_source && (embed.media_element.selected_source.mime_type == mime_type))
2418                                 {
2419                                         embed.selectPlayer(selected_player);
2420                                         js_log('using ' + embed.selected_player.getName() + ' for ' + embed.media_element.selected_source.getTitle());
2421                                 }
2422                         }
2423                 }
2424         },
2425         loadPreferences : function()
2426         {
2427                 this.preference = new Object();                         
2428                 // see if we have a cookie set to a clientSupported type:
2429                 var cookieVal = $j.cookie( 'ogg_player_exp' );
2430                 if (cookieVal)
2431                 {
2432                         var pairs = cookieVal.split('&');
2433                         for(var i=0; i < pairs.length; i++)
2434                         {
2435                                 var name_value = pairs[i].split('=');
2436                                 this.preference[name_value[0]]=name_value[1];
2437                                 //js_log('load preference for ' + name_value[0] + ' is ' + name_value[1]);
2438                         }
2439                 }
2440         },
2441         savePreferences : function()
2442         {
2443                 var cookieVal = '';             
2444                 for(var i in this.preference)
2445                         cookieVal+= i + '='+ this.preference[i] + '&';
2446                         
2447                 cookieVal=cookieVal.substr(0, cookieVal.length-1);              
2448                 var week = 7*86400*1000;
2449                 $j.cookie( 'ogg_player_exp', cookieVal, { 'expires':week } );
2450         }
2454  * embedTypes object handles setting and getting of supported embed types:
2455  * closely mirrors OggHandler so that its easier to share efforts in this area:
2456  * http://svn.wikimedia.org/viewvc/mediawiki/trunk/extensions/OggHandler/OggPlayer.js
2457  */
2458 var embedTypes = {
2459          // List of players
2460          players: null,
2461          detect_done:false,      
2462          init: function(){
2463                 //detect supported types
2464                 this.detect();
2465                 this.detect_done=true;
2466         },
2467         clientSupports: { 'thumbnail' : true },
2468         supportedMimeType: function(mimetype) {
2469                 for (var i = navigator.plugins.length; i-- > 0; ) {
2470                         var plugin = navigator.plugins[i];
2471                         if (typeof plugin[mimetype] != "undefined")
2472                           return true;
2473                 }
2474                 return false;
2475         },
2477          detect: function() {
2478                  js_log("running detect");
2479                 this.players = new mediaPlayers();
2480                 //every browser supports html rendering:
2481                 this.players.addPlayer( htmlPlayer );                   
2482                  // In Mozilla, navigator.javaEnabled() only tells us about preferences, we need to
2483                  // search navigator.mimeTypes to see if it's installed
2484                  var javaEnabled = navigator.javaEnabled();
2485                  // In Opera, navigator.javaEnabled() is all there is
2486                  var invisibleJava = $j.browser.opera;
2487                  // Some browsers filter out duplicate mime types, hiding some plugins
2488                  var uniqueMimesOnly = $j.browser.opera || $j.browser.safari;
2489                  // Opera will switch off javaEnabled in preferences if java can't be found.
2490                  // And it doesn't register an application/x-java-applet mime type like Mozilla does.
2491                  if ( invisibleJava && javaEnabled )
2492                          this.players.addPlayer( cortadoPlayer );
2493                 
2494                  // ActiveX plugins
2495                  if($j.browser.msie){
2496                           // check for flash             
2497                            if ( this.testActiveX( 'ShockwaveFlash.ShockwaveFlash')){                               
2498                                    //try to get the flash version for omtk include: 
2499                                    try {
2500                                         a = new ActiveXObject(SHOCKWAVE_FLASH_AX + ".7");
2501                                         d = a.GetVariable("$version");  // Will crash fp6.0.21/23/29
2502                                         if (d) {
2503                                                 d = d.split(" ")[1].split(",");                                         
2504                                                 //we need flash version 10 or greater:
2505                                                 if(parseInt( d[0]) >=10){
2506                                                         this.players.addPlayer( omtkPlayer );
2507                                                 }
2508                                                 
2509                                         }                                       
2510                                 }catch(e) {}                            
2511                                 
2512                                    //flowplayer has pretty good compatiablity 
2513                                    // (but if we wanted to be fancy we would check for version of flash and update the mp4/h.264 support
2514                                    this.players.addPlayer( flowPlayer );                                   
2515                            }
2516                          // VLC
2517                          if ( this.testActiveX( 'VideoLAN.VLCPlugin.2' ) )
2518                                  this.players.addPlayer(vlcActiveXPlayer);
2519                          // Java
2520                          if ( javaEnabled && this.testActiveX( 'JavaWebStart.isInstalled' ) )
2521                                  this.players.addPlayer(cortadoPlayer);
2522                          // quicktime
2523                          //if ( this.testActiveX( 'QuickTimeCheckObject.QuickTimeCheck.1' ) )
2524                          //     this.players.addPlayer(quicktimeActiveXPlayer);                  
2525                  }                               
2526                 // <video> element
2527                 if ( typeof HTMLVideoElement == 'object' // Firefox, Safari
2528                                 || typeof HTMLVideoElement == 'function' ) // Opera
2529                 {
2530                         //do another test for safari: 
2531                         if( $j.browser.safari ){                                
2532                                 try{
2533                                         var dummyvid = document.createElement("video");
2534                                         if (dummyvid.canPlayType && dummyvid.canPlayType("video/ogg;codecs=\"theora,vorbis\"") == "probably")
2535                                         {
2536                                                 this.players.addPlayer( videoElementPlayer );
2537                                         } else if(this.supportedMimeType( 'video/ogg' )) {
2538                                                 /* older versions of safari do not support canPlayType,
2539                                                    but xiph qt registers mimetype via quicktime plugin */
2540                                                 this.players.addPlayer( videoElementPlayer );
2541                                         } else {
2542                                                 //@@todo add some user nagging to install the xiph qt 
2543                                         }
2544                                 }catch(e){
2545                                         js_log('could not run canPlayType in safari');
2546                                 }
2547                         }else{
2548                                 this.players.addPlayer( videoElementPlayer );
2549                         }
2550                 }                
2551                 
2552                  // Mozilla plugins
2553                 if( navigator.mimeTypes && navigator.mimeTypes.length > 0) {
2554                         for ( var i = 0; i < navigator.mimeTypes.length; i++ ) {
2555                                 var type = navigator.mimeTypes[i].type;
2556                                 var semicolonPos = type.indexOf( ';' );
2557                                 if ( semicolonPos > -1 ) {
2558                                         type = type.substr( 0, semicolonPos );
2559                                 }
2560                                 //js_log('on type: '+type);
2561                                 var pluginName = navigator.mimeTypes[i].enabledPlugin ? navigator.mimeTypes[i].enabledPlugin.name : '';
2562                                 if ( !pluginName ) {
2563                                         // In case it is null or undefined
2564                                         pluginName = '';
2565                                 }                               
2566                                 if ( pluginName.toLowerCase() == 'vlc multimedia plugin' || pluginName.toLowerCase() == 'vlc multimedia plug-in' ) {
2567                                         this.players.addPlayer(vlcMozillaPlayer, type);
2568                                         continue;
2569                                 }
2570                 
2571                                 if ( javaEnabled && type == 'application/x-java-applet' ) {
2572                                         this.players.addPlayer(cortadoPlayer);
2573                                         continue;
2574                                 }                               
2575                 
2576                                 if ( type == 'application/ogg' ) {
2577                                         if ( pluginName.toLowerCase() == 'vlc multimedia plugin' ){
2578                                                 this.players.addPlayer(vlcMozillaPlayer, type);
2579                                         //else if ( pluginName.indexOf( 'QuickTime' ) > -1 )
2580                                         //      this.players.addPlayer(quicktimeMozillaPlayer);
2581                                         }else{
2582                                                 this.players.addPlayer(oggPluginPlayer);
2583                                         }
2584                                         continue;
2585                                 } else if ( uniqueMimesOnly ) {
2586                                         if ( type == 'application/x-vlc-player' ) {
2587                                                 this.players.addPlayer(vlcMozillaPlayer, type);
2588                                                 continue;
2589                                         } else if ( type == 'video/quicktime' ) {
2590                                                 //this.players.addPlayer(quicktimeMozillaPlayer);
2591                                                 continue;
2592                                         }
2593                                 }
2594                 
2595                                 /*if ( type == 'video/quicktime' ) {
2596                                         this.players.addPlayer(vlcMozillaPlayer, type);
2597                                         continue;
2598                                 }*/
2599                                 if(type=='application/x-shockwave-flash'){
2600                                         this.players.addPlayer( flowPlayer );
2601                                         
2602                                         //check version to add omtk:
2603                                         var flashDescription = navigator.plugins["Shockwave Flash"].description;
2604                                         var descArray = flashDescription.split(" ");
2605                                         var tempArrayMajor = descArray[2].split(".");
2606                                         var versionMajor = tempArrayMajor[0];
2607                                         //js_log("version of flash: " + versionMajor);
2608                                         if(versionMajor >= 10){
2609                                                 this.players.addPlayer( omtkPlayer );
2610                                         }                                       
2611                                         continue;
2612                                 }
2613                         }
2614                 }
2615                 //@@The xiph quicktime component does not work well with annodex streams (temporarly disable)
2616                 //this.clientSupports['quicktime-mozilla'] = false;
2617                 //this.clientSupports['quicktime-activex'] = false;
2618                 //js_log(this.clientSupports);
2619         },
2620         testActiveX : function ( name ) {
2621                  var hasObj = true;
2622                  try {
2623                          // No IE, not a class called "name", it's a variable
2624                          var obj = new ActiveXObject( '' + name );
2625                  } catch ( e ) {
2626                          hasObj = false;
2627                  }
2628                  return hasObj;
2629         }