* related videos per commons wikiKey ( GerardoDoog )
[lhc/web/wiklou.git] / js2 / mwEmbed / libEmbedVideo / embedVideo.js
1 /* the base video control JSON object with default attributes
2 * for supported attribute details see README
3 */
4
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: <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" : "Dissmiss 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 });
55
56 //set the globals:
57 var commons_api_url = 'http://commons.wikimedia.org/w/api.php';
58
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",
67
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,
81
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,
89
90 "startOffset":null, //if serving an ogg_chop segment use this to offset the presentation time
91
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
99 };
100 /*
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'
114 );
115 //set the dismissNativeWarn flag:
116 _global['dismissNativeWarn'] = false;
117 /*
118 * Converts all occurrences of <video> tag into video object
119 */
120 function mv_video_embed(swap_done_callback, force_id){
121 mvEmbed.init( swap_done_callback, force_id );
122 }
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 }
275 }
276
277 /**
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)
284 {
285 this.init(element);
286 }
287
288
289 mediaSource.prototype =
290 {
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,
307
308 id:null,
309 start_ntp:null,
310 end_ntp:null,
311
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;
348
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 }
476 };
477
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
484 */
485 function mediaElement(video_element)
486 {
487 this.init(video_element);
488 };
489
490 mediaElement.prototype =
491 {
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,
499
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 }
747 };
748
749
750 /** base embedVideo object
751 @param element <video> tag used for initialization.
752 @constructor
753 */
754 var embedVideo = function(element) {
755 return this.init(element);
756 };
757
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;
812
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;
840
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();
1075
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 var leyenda="";
1137 var titule="";
1138
1139 for(var j in data.query.pages){
1140 //setup poster default:
1141 var local_poster ="http://upload.wikimedia.org/wikipedia/commons/7/79/Wiki-commons.png";
1142 //make sure it exists:
1143 var page = data.query.pages[j];
1144 if( j > 0 && page && page['imageinfo'] ){
1145 if( page['imageinfo'][0].thumburl ){
1146 local_poster = page['imageinfo'][0].thumburl;
1147 }
1148 var descriptionurl = page['imageinfo'][0].descriptionurl;
1149 var title_str = page.title.replace( /File:|.ogv$|.oga$|.ogg$/gi, "" );
1150 //only link to other videos:
1151 if ( descriptionurl.match( /\.ogg$|\.ogv$|\.oga$/gi ) != null) {
1152 var liout = '<li>' +
1153 '<a href="' + descriptionurl + '" >' +
1154 '<img src="' + local_poster + '">' +
1155 '</a>' +
1156 '<div>' +
1157 '<a title="' + title_str + '" target="_blank" ' +
1158 'href="'+ descriptionurl +'">' + title_str + '</a>' +
1159 '</div>' +
1160 '</li>';
1161 $j('#dc_'+ _this.id + ' .related_vids ul').append(liout) ;
1162 }
1163 }
1164 };
1165 js_log( 'content: ' + $j('#dc_'+ _this.id + ' .related_vids ul').html() );
1166 }); //end do_api_req
1167 };
1168 },
1169 onClipDone:function(){
1170 js_log('base:onClipDone');
1171 //stop the clip (load the thumbnail etc)
1172 this.stop();
1173 this.seek_time_sec = 0;
1174 this.setSliderValue(0);
1175 var _this = this;
1176
1177 if(this.width < 300){
1178 return ;
1179 }
1180 this.onClipDone_disp=true;
1181 this.thumbnail_disp=true;
1182 //make sure we are not in preview mode( no end clip actions in preview mode)
1183 if( this.preview_mode )
1184 return ;
1185
1186 $j('#img_thumb_'+this.id).css('zindex',1);
1187 $j('#big_play_link_'+this.id).hide();
1188
1189 //add black background
1190 $j('#dc_'+this.id).append('<div id="black_back_'+this.id+'" ' +
1191 'style="z-index:-2;position:absolute;background:#000;' +
1192 'top:0px;left:0px;width:'+parseInt(this.width)+'px;' +
1193 'height:'+parseInt(this.height)+'px;">' +
1194 '</div>');
1195
1196 if( this.wikiTitleKey){
1197 $j('#dc_'+this.id).append(
1198 '<div class="related_vids" >' +
1199 '<h1>' + gM('mwe_related_videos') + '</h1>'+
1200 '<ul>' +
1201 '</ul>' +
1202 '</div>');
1203 $j('#img_thumb_' + this.id).fadeOut("fast");
1204 $j('#dc_'+ _this.id + ' .related_vids ul').html( gM('mwe-loading_txt') );
1205 this.mvVideoAudioSearch();
1206 }else{
1207 //add the liks_info_div black back
1208 $j('#dc_'+this.id).append('<div id="liks_info_'+this.id+'" ' +
1209 'style="width:' +parseInt(parseInt(this.width)/2)+'px;'+
1210 'height:'+ parseInt(parseInt(this.height)) +'px;'+
1211 'position:absolute;top:10px;overflow:auto'+
1212 'width: '+parseInt( ((parseInt(this.width)/2)-15) ) + 'px;'+
1213 'left:'+ parseInt( ((parseInt(this.width)/2)+15) ) +'px;">'+
1214 '</div>'
1215 );
1216 //start animation (make thumb small in upper left add in div for "loading"
1217 $j('#img_thumb_'+this.id).animate({
1218 width:parseInt(parseInt(_this.width)/2),
1219 height:parseInt(parseInt(_this.height)/2),
1220 top:20,
1221 left:10
1222 },
1223 1000,
1224 function(){
1225 //animation done.. add "loading" to div if empty
1226 if($j('#liks_info_'+_this.id).html()==''){
1227 $j('#liks_info_'+_this.id).html(gM('mwe-loading_txt'));
1228 }
1229 }
1230 )
1231 //now load roe if run the showNextPrevLinks
1232 if(this.roe && this.media_element.addedROEData==false){
1233 do_request(this.roe, function(data)
1234 {
1235 _this.media_element.addROE(data);
1236 _this.getNextPrevLinks();
1237 });
1238 }else{
1239 this.getNextPrevLinks();
1240 }
1241 }
1242 },
1243 //@@todo we should merge getNextPrevLinks with textInterface .. there is repeated code between them.
1244 getNextPrevLinks:function(){
1245 js_log('f:getNextPrevLinks');
1246 var anno_track_url = null;
1247 var _this = this;
1248 //check for annoative track
1249 $j.each(this.media_element.sources, function(inx, n){
1250 if(n.mime_type=='text/cmml'){
1251 if( n.id == 'Anno_en'){
1252 anno_track_url = n.src;
1253 }
1254 }
1255 });
1256 if( anno_track_url ){
1257 js_log('found annotative track:'+ anno_track_url);
1258 //zero out seconds (should improve cache hit rate and generally expands metadata search)
1259 //@@todo this could be repalced with a regExp
1260 var annoURL = parseUri(anno_track_url);
1261 var times = annoURL.queryKey['t'].split('/');
1262 var stime_parts = times[0].split(':');
1263 var etime_parts = times[1].split(':');
1264 //zero out the hour:
1265 var new_start = stime_parts[0]+':'+'0:0';
1266 //zero out the end sec
1267 var new_end = (etime_parts[0]== stime_parts[0])? (etime_parts[0]+1)+':0:0' :etime_parts[0]+':0:0';
1268
1269 var etime_parts = times[1].split(':');
1270
1271 var new_anno_track_url = annoURL.protocol +'://'+ annoURL.host + annoURL.path +'?';
1272 $j.each(annoURL.queryKey, function(i, val){
1273 new_anno_track_url +=(i=='t')?'t='+new_start+'/'+new_end +'&' :
1274 i+'='+ val+'&';
1275 });
1276 var request_key = new_start+'/'+new_end;
1277 //check the anno_data cache:
1278 //@@todo search cache see if current is in range.
1279 if(this.anno_data_cache){
1280 js_log('anno data found in cache: '+request_key);
1281 this.showNextPrevLinks();
1282 }else{
1283 do_request(new_anno_track_url, function(cmml_data){
1284 js_log('raw response: '+ cmml_data);
1285 if(typeof cmml_data == 'string')
1286 {
1287 var parser=new DOMParser();
1288 js_log('Parse CMML data:' + cmml_data);
1289 cmml_data=parser.parseFromString(cmml_data,"text/xml");
1290 }
1291 //init anno_data_cache
1292 if(!_this.anno_data_cache)
1293 _this.anno_data_cache={};
1294 //grab all metadata and put it into the anno_data_cache:
1295 $j.each(cmml_data.getElementsByTagName('clip'), function(inx, clip){
1296 _this.anno_data_cache[ $j(clip).attr("id") ]={
1297 'start_time_sec':npt2seconds($j(clip).attr("start").replace('npt:','')),
1298 'end_time_sec':npt2seconds($j(clip).attr("end").replace('npt:','')),
1299 'time_req':$j(clip).attr("start").replace('npt:','')+'/'+$j(clip).attr("end").replace('npt:','')
1300 };
1301 //grab all its meta
1302 _this.anno_data_cache[ $j(clip).attr("id") ]['meta']={};
1303 $j.each(clip.getElementsByTagName('meta'),function(imx, meta){
1304 //js_log('adding meta: '+ $j(meta).attr("name")+ ' = '+ $j(meta).attr("content"));
1305 _this.anno_data_cache[$j(clip).attr("id")]['meta'][$j(meta).attr("name")]=$j(meta).attr("content");
1306 });
1307 });
1308 _this.showNextPrevLinks();
1309 });
1310 }
1311 }else{
1312 js_log('no annotative track found');
1313 $j('#liks_info_'+this.id).html('no metadata found for related links');
1314 }
1315 //query current request time +|- 60s to get prev next speech links.
1316 },
1317 showNextPrevLinks:function(){
1318 //js_log('f:showNextPrevLinks');
1319 //int requested links:
1320 var link = {
1321 'prev':'',
1322 'current':'',
1323 'next':''
1324 }
1325 var curTime = this.getTimeReq().split('/');
1326
1327 var s_sec = npt2seconds(curTime[0]);
1328 var e_sec = npt2seconds(curTime[1]);
1329 js_log('showNextPrevLinks: req time: '+ s_sec + ' to ' + e_sec);
1330 //now we have all the data in anno_data_cache
1331 var current_done=false;
1332 for(var clip_id in this.anno_data_cache){ //for in loop oky for object
1333 var clip = this.anno_data_cache[clip_id];
1334 //js_log('on clip:'+ clip_id);
1335 //set prev_link (if cur_link is still empty)
1336 if( s_sec > clip.end_time_sec){
1337 link.prev = clip_id;
1338 js_log('showNextPrevLinks: ' + s_sec + ' < ' + clip.end_time_sec + ' set prev');
1339 }
1340
1341 if(e_sec==clip.end_time_sec && s_sec== clip.start_time_sec)
1342 current_done = true;
1343 //current clip is not done:
1344 if( e_sec < clip.end_time_sec && link.current=='' && !current_done){
1345 link.current = clip_id;
1346 js_log('showNextPrevLinks: ' + e_sec + ' < ' + clip.end_time_sec + ' set current');
1347 }
1348
1349 //set end clip (first clip where start time is > end_time of req
1350 if( e_sec < clip.start_time_sec && link.next==''){
1351 link.next = clip_id;
1352 js_log('showNextPrevLinks: '+ e_sec + ' < '+ clip.start_time_sec + ' && ' + link.next );
1353 }
1354 }
1355 var html='';
1356 if(link.prev=='' && link.current=='' && link.next==''){
1357 html='<p><a href="'+this.media_element.linkbackgetMsg+'">clip page</a>';
1358 }else{
1359 for(var link_type in link){
1360 var link_id = link[link_type];
1361 if(link_id!=''){
1362 var clip = this.anno_data_cache[link_id];
1363 var title_msg='';
1364 for(var j in clip['meta']){
1365 title_msg+=j.replace(/_/g,' ') +': ' +clip['meta'][j].replace(/_/g,' ') +" <br>";
1366 }
1367 var time_req = clip.time_req;
1368 if(link_type=='current') //if current start from end of current clip play to end of current meta:
1369 time_req = curTime[1]+ '/' + seconds2npt( clip.end_time_sec );
1370
1371 //do special linkbacks for metavid content:
1372 var regTimeCheck = new RegExp(/[0-9]+:[0-9]+:[0-9]+\/[0-9]+:[0-9]+:[0-9]+/);
1373 html+='<p><a ';
1374 if( regTimeCheck.test( this.media_element.linkback ) ){
1375 html+=' href="'+ this.media_element.linkback.replace(regTimeCheck,time_req) +'" ';
1376 }else{
1377 html+=' href="#" onClick="$j(\'#'+this.id+'\').get(0).playByTimeReq(\''+
1378 time_req + '\'); return false; "';
1379 }
1380 html+=' title="' + title_msg + '">' +
1381 gM(link_type+'_clip_msg') +
1382 '</a><br><span style="font-size:small">'+ title_msg +'<span></p>';
1383 }
1384 }
1385 }
1386 //js_og("should set html:"+ html);
1387 $j('#liks_info_'+this.id).html(html);
1388 },
1389 playByTimeReq: function(time_req){
1390 js_log('f:playByTimeReq: '+time_req );
1391 this.stop();
1392 this.updateVideoTimeReq(time_req);
1393 this.play();
1394 },
1395 doThumbnailHTML:function()
1396 {
1397 var _this = this;
1398 js_log('f:doThumbnailHTML'+ this.thumbnail_disp);
1399 this.closeDisplayedHTML();
1400 $j( '#mv_embedded_player_' + this.id ).html( this.getThumbnailHTML() );
1401 this.paused = true;
1402 this.thumbnail_disp = true;
1403 },
1404 refreshControlsHTML:function(){
1405 js_log('refreshing controls HTML');
1406 if($j('#mv_embedded_controls_'+this.id).length==0)
1407 {
1408 js_log('#mv_embedded_controls_'+this.id + ' not present, returning');
1409 return;
1410 }else{
1411 $j('#mv_embedded_controls_'+this.id).html( this.getControlsHTML() );
1412 ctrlBuilder.addControlHooks(this);
1413 }
1414 },
1415 getControlsHTML:function()
1416 {
1417 return ctrlBuilder.getControls( this );
1418 },
1419 getHTML : function (){
1420 //@@todo check if we have sources avaliable
1421 js_log('embedVideo:getHTML : ' + this.id + ' resource type: ' + this.type);
1422 var _this = this;
1423 var html_code = '';
1424 html_code = '<div id="videoPlayer_'+this.id+'" style="width:'+this.width+'px;position:relative;" class="videoPlayer">';
1425 html_code += '<div style="width:'+parseInt(this.width)+'px;height:'+parseInt(this.height)+'px;" id="mv_embedded_player_'+this.id+'">' +
1426 this.getThumbnailHTML() +
1427 '</div>';
1428 //js_log("mvEmbed:controls "+ typeof this.controls);
1429 if( this.controls )
1430 {
1431 js_log("f:getHTML:AddControls");
1432 html_code +='<div id="mv_embedded_controls_' + this.id + '" class="ui-widget ui-corner-bottom ui-state-default controls" >';
1433 html_code += this.getControlsHTML();
1434 html_code +='</div>';
1435 //block out some space by encapulating the top level div
1436 $j(this).wrap('<div style="width:'+parseInt(this.width)+'px;height:'
1437 +(parseInt(this.height)+ctrlBuilder.height)+'px"></div>');
1438 }
1439 html_code += '</div>'; //videoPlayer div close
1440 //js_log('should set: '+this.id);
1441 $j(this).html( html_code );
1442 //add hooks once Controls are in DOM
1443 ctrlBuilder.addControlHooks(this);
1444
1445 //js_log('set this to: ' + $j(this).html() );
1446 //alert('stop');
1447 //if auto play==true directly embed the plugin
1448 if(this.autoplay)
1449 {
1450 js_log('activating autoplay');
1451 this.play();
1452 }
1453 },
1454 /*
1455 * get missing plugin html (check for user included code)
1456 */
1457 getPluginMissingHTML : function(missing_type){
1458 //keep the box width hight:
1459 var out = '<div style="width:'+this.width+'px;height:'+this.height+'px">';
1460 if(this.user_missing_plugin_html){
1461 out+= this.user_missing_plugin_html;
1462 }else{
1463 if(!missing_type)
1464 missing_type='';
1465 out+= gM('mwe-generic_missing_plugin', missing_type) + ' or <a title="'+gM('mwe-download_clip')+'" href="'+this.src +'">'+gM('mwe-download_clip')+'</a>';
1466 }
1467 return out + '</div>';
1468 },
1469 updateVideoTimeReq:function(time_req){
1470 js_log('f:updateVideoTimeReq');
1471 var time_parts =time_req.split('/');
1472 this.updateVideoTime(time_parts[0], time_parts[1]);
1473 },
1474 //update video time
1475 updateVideoTime:function(start_ntp, end_ntp){
1476 //update media
1477 this.media_element.updateSourceTimes( start_ntp, end_ntp );
1478 //update mv_time
1479 this.setStatus(start_ntp+'/'+end_ntp);
1480 //reset slider
1481 this.setSliderValue(0);
1482 //reset seek_offset:
1483 if(this.media_element.selected_source.URLTimeEncoding )
1484 this.seek_time_sec=0;
1485 else
1486 this.seek_time_sec=npt2seconds(start_ntp);
1487 },
1488 //@@todo overwite by embed library if we can render frames natavily
1489 renderTimelineThumbnail:function( options ){
1490 var my_thumb_src = this.media_element.getThumbnailURL();
1491 //check if our thumbnail has a time attribute:
1492 if( my_thumb_src.indexOf('t=') !== -1){
1493 var time_ntp = seconds2npt ( options.time + parseInt(this.start_offset) );
1494 my_thumb_src = getURLParamReplace( my_thumb_src, { 't':time_ntp, 'size': options.size } );
1495 }
1496 var thumb_class = (typeof options['thumb_class'] != 'undefined' ) ? options['thumb_class'] : '';
1497 return '<div class="ui-corner-all ' + thumb_class + '" src="' + my_thumb_src + '" '+
1498 'style="height:' + options.height + 'px;' +
1499 'width:' + options.width + 'px" >' +
1500 '<img src="' + my_thumb_src +'" '+
1501 'style="height:' + options.height + 'px;' +
1502 'width:' + options.width + 'px">' +
1503 '</div>';
1504 },
1505 updateThumbTimeNTP:function( time){
1506 this.updateThumbTime( npt2seconds(time) - parseInt(this.start_offset) );
1507 },
1508 updateThumbTime:function( float_sec ){
1509 //js_log('updateThumbTime:'+float_sec);
1510 var _this = this;
1511 if( typeof this.org_thum_src=='undefined' ){
1512 this.org_thum_src = this.media_element.getThumbnailURL();
1513 }
1514 if( this.org_thum_src.indexOf('t=') !== -1){
1515 this.last_thumb_url = getURLParamReplace(this.org_thum_src,
1516 { 't' : seconds2npt( float_sec + parseInt(this.start_offset)) } );
1517 if(!this.thumbnail_updating){
1518 this.updateThumbnail(this.last_thumb_url ,false);
1519 this.last_thumb_url =null;
1520 }
1521 }
1522 },
1523 //for now provide a src url .. but need to figure out how to copy frames from video for plug-in based thumbs
1524 updateThumbPerc:function( perc ){
1525 return this.updateThumbTime( (this.getDuration() * perc) );
1526 },
1527 //updates the thumbnail if the thumbnail is being displayed
1528 updateThumbnail : function(src, quick_switch){
1529 //make sure we don't go to the same url if we are not already updating:
1530 if( !this.thumbnail_updating && $j('#img_thumb_'+this.id).attr('src')== src )
1531 return false;
1532 //if we are already updating don't issue a new update:
1533 if( this.thumbnail_updating && $j('#new_img_thumb_'+this.id).attr('src')== src )
1534 return false;
1535
1536 js_log('update thumb: ' + src);
1537
1538 if(quick_switch){
1539 $j('#img_thumb_'+this.id).attr('src', src);
1540 }else{
1541 var _this = this;
1542 //if still animating remove new_img_thumb_
1543 if(this.thumbnail_updating==true)
1544 $j('#new_img_thumb_'+this.id).stop().remove();
1545
1546 if(this.thumbnail_disp){
1547 js_log('set to thumb:'+ src);
1548 this.thumbnail_updating=true;
1549 $j('#dc_'+this.id).append('<img src="'+src+'" ' +
1550 'style="display:none;position:absolute;zindex:2;top:0px;left:0px;" ' +
1551 'width="'+this.width+'" height="'+this.height+'" '+
1552 'id = "new_img_thumb_'+this.id+'" />');
1553 //js_log('appended: new_img_thumb_');
1554 $j('#new_img_thumb_'+this.id).fadeIn("slow", function(){
1555 //once faded in remove org and rename new:
1556 $j('#img_thumb_'+_this.id).remove();
1557 $j('#new_img_thumb_'+_this.id).attr('id', 'img_thumb_'+_this.id);
1558 $j('#img_thumb_'+_this.id).css('zindex','1');
1559 _this.thumbnail_updating=false;
1560 //js_log("done fadding in "+ $j('#img_thumb_'+_this.id).attr("src"));
1561
1562 //if we have a thumb queued update to that
1563 if(_this.last_thumb_url){
1564 var src_url =_this.last_thumb_url;
1565 _this.last_thumb_url=null;
1566 _this.updateThumbnail(src_url);
1567 }
1568 });
1569 }
1570 }
1571 },
1572 /** Returns the HTML code for the video when it is in thumbnail mode.
1573 This includes the specified thumbnail as well as buttons for
1574 playing, configuring the player, inline cmml display, HTML linkback,
1575 download, and embed code.
1576 */
1577 getThumbnailHTML : function ()
1578 {
1579 js_log('embedVideo:getThumbnailHTML::' + this.id);
1580 var thumb_html = '';
1581 var class_atr='';
1582 var style_atr='';
1583 //if(this.class)class_atr = ' class="'+this.class+'"';
1584 //if(this.style)style_atr = ' style="'+this.style+'"';
1585 // else style_atr = 'overflow:hidden;height:'+this.height+'px;width:'+this.width+'px;';
1586 this.thumbnail = this.media_element.getThumbnailURL();
1587
1588 //put it all in the div container dc_id
1589 thumb_html+= '<div id="dc_'+this.id+'" style="position:absolute;'+
1590 ' overflow:hidden; top:0px; left:0px; width:'+this.playerPixelWidth()+'px; height:'+this.playerPixelHeight()+'px; z-index:0;">'+
1591 '<img width="'+this.playerPixelWidth()+'" height="'+this.playerPixelHeight()+'" style="position:relative;width:'+this.playerPixelWidth()+';height:'+this.playerPixelHeight()+'"' +
1592 ' id="img_thumb_'+this.id+'" src="' + this.thumbnail + '">';
1593
1594 if(this.play_button == true && this.controls == true)
1595 thumb_html+=this.getPlayButton();
1596
1597 thumb_html+='</div>';
1598 return thumb_html;
1599 },
1600 getEmbeddingHTML:function()
1601 {
1602 var thumbnail = this.media_element.getThumbnailURL();
1603
1604 var embed_thumb_html;
1605 if(thumbnail.substring(0,1)=='/'){
1606 eURL = parseUri(mv_embed_path);
1607 embed_thumb_html = eURL.protocol + '://' + eURL.host + thumbnail;
1608 //js_log('set from mv_embed_path:'+embed_thumb_html);
1609 }else{
1610 embed_thumb_html = (thumbnail.indexOf('http://')!=-1)?thumbnail:mv_embed_path + thumbnail;
1611 }
1612 var embed_code_html = '&lt;script type=&quot;text/javascript&quot; ' +
1613 'src=&quot;'+mv_embed_path+'mv_embed.js&quot;&gt;&lt;/script&gt' +
1614 '&lt;video ';
1615 if(this.roe){
1616 embed_code_html+='roe=&quot;'+this.roe+'&quot; &gt;';
1617 }else{
1618 embed_code_html+='src=&quot;'+this.src+'&quot; ' +
1619 'poster=&quot;'+embed_thumb_html+'&quot;&gt;';
1620 }
1621 //close the video tag
1622 embed_code_html+='&lt;/video&gt;';
1623
1624 return embed_code_html;
1625 },
1626 doOptionsHTML:function()
1627 {
1628 var sel_id = (this.pc!=null)?this.pc.pp.id:this.id;
1629 var pos = $j('#options_button_'+sel_id).offset();
1630 pos['top']=pos['top']+24;
1631 pos['left']=pos['left']-124;
1632 //js_log('pos of options button: t:'+pos['top']+' l:'+ pos['left']);
1633 $j('#mv_vid_options_'+sel_id).css(pos).toggle();
1634 return;
1635 },
1636 getPlayButton:function(id){
1637 if(!id)id=this.id;
1638 return '<div title="' + gM('mwe-play_clip') + '" id="big_play_link_'+id+'" class="large_play_button" '+
1639 'style="left:'+((this.playerPixelWidth()-130)/2)+'px;'+
1640 'top:' + ((this.playerPixelHeight()-96)/2) + 'px;">'+
1641 '<img src="' + mv_skin_img_path + 'player_big_play_button.png">'+
1642 '</div>';
1643 },
1644 doLinkBack:function(){
1645 if(this.roe && this.media_element.addedROEData==false){
1646 var _this = this;
1647 this.displayHTML(gM('mwe-loading_txt'));
1648 do_request(this.roe, function(data)
1649 {
1650 _this.media_element.addROE(data);
1651 _this.doLinkBack();
1652 });
1653 }else{
1654 if(this.media_element.linkback){
1655 window.location = this.media_element.linkback;
1656 }else{
1657 this.displayHTML(gM('mwe-could_not_find_linkback'));
1658 }
1659 }
1660 },
1661 //display the code to remotely embed this video:
1662 showEmbedCode : function(embed_code){
1663 if(!embed_code)
1664 embed_code = this.getEmbeddingHTML();
1665 var o='';
1666 if(this.linkback){
1667 o+='<a class="email" href="'+this.linkback+'">Share Clip via Link</a> '+
1668 '<p>or</p> ';
1669 }
1670 o+='<div>' +
1671 '<span style="color:#FFF;font-size:14px;">Embed Clip in Blog or Site</span><br>'+
1672 '<span style="color:#FFF;font-size:12px;"><a style="color:red" href="http://metavid.org/wiki/Security_Notes_on_Remote_Embedding">'+
1673 'Read This</a> before embeding.</span>'+
1674 '<div class="embed_code"> '+
1675 '<textarea onClick="this.select();" id="embedding_user_html_'+this.id+'" name="embed">' +
1676 embed_code+
1677 '</textarea> '+
1678 '<button onClick="$j(\'#'+this.id+'\').get(0).copyText(); return false;" class="copy_to_clipboard">Copy to Clipboard</button> '+
1679 '</div> '+
1680 '</div>';
1681 this.displayHTML(o);
1682 },
1683 copyText:function(){
1684 $j('#embedding_user_html_'+this.id).focus().select();
1685 if(document.selection){
1686 CopiedTxt = document.selection.createRange();
1687 CopiedTxt.execCommand("Copy");
1688 }
1689 },
1690 showTextInterface:function(){
1691 var _this = this;
1692 //display the text container with loading text:
1693 //@@todo support position config
1694 var loc = $j(this).position();
1695 if($j('#metaBox_'+this.id).length==0){
1696 $j(this).after('<div class="ui-widget ui-widget-content ui-corner-all" style="position:absolute;z-index:10;'+
1697 'top:' + (loc.top) + 'px;' +
1698 'left:' + (parseInt( loc.left ) + parseInt(this.width) + 10 )+'px;' +
1699 'height:'+ parseInt( this.height )+'px;width:400px;' +
1700 'display:none;" ' +
1701 'id="metaBox_' + this.id + '">'+
1702 gM('mwe-loading_txt') +
1703 '</div>');
1704 }
1705 //fade in the text display
1706 $j('#metaBox_'+this.id).fadeIn("fast");
1707 //check if textObj present:
1708 if(typeof this.textInterface == 'undefined' ){
1709 //load the default text interface:
1710 mvJsLoader.doLoad([
1711 'mvTextInterface',
1712 '$j.fn.hoverIntent'
1713 ], function(){
1714 _this.textInterface = new mvTextInterface( _this );
1715 //show interface
1716 _this.textInterface.show();
1717 js_log("NEW TEXT INTERFACE");
1718 for(var i in _this.textInterface.availableTracks){
1719 js_log("tracks in new interface: "+_this.id+ ' tid:' + i);
1720 }
1721 }
1722 );
1723 }else{
1724 //show interface
1725 this.textInterface.show();
1726 }
1727 },
1728 closeTextInterface:function(){
1729 js_log('closeTextInterface '+ typeof this.textInterface);
1730 if(typeof this.textInterface !== 'undefined' ){
1731 this.textInterface.close();
1732 }
1733 },
1734 /** Generic function to display custom HTML inside the mv_embed element.
1735 The code should call the closeDisplayedHTML function to close the
1736 display of the custom HTML and restore the regular mv_embed display.
1737 @param {String} HTML code for the selection list.
1738 */
1739 displayHTML:function(html_code)
1740 {
1741 var sel_id = (this.pc!=null)?this.pc.pp.id:this.id;
1742
1743 if(!this.supports['overlays'])
1744 this.stop();
1745
1746 //put select list on-top
1747 //make sure the parent is relatively positioned:
1748 $j('#'+sel_id).css('position', 'relative');
1749 //set height width (check for playlist container)
1750 var width = (this.pc)?this.pc.pp.width:this.playerPixelWidth();
1751 var height = (this.pc)?this.pc.pp.height:this.playerPixelHeight();
1752
1753 if(this.pc)
1754 height+=(this.pc.pp.pl_layout.title_bar_height + this.pc.pp.pl_layout.control_height);
1755
1756 var fade_in = true;
1757 if($j('#blackbg_'+sel_id).length!=0)
1758 {
1759 fade_in = false;
1760 $j('#blackbg_'+sel_id).remove();
1761 }
1762 //fade in a black bg div ontop of everything
1763 var div_code = '<div id="blackbg_'+sel_id+'" class="videoComplete" ' +
1764 'style="height:'+parseInt(height)+'px;width:'+parseInt(width)+'px;">'+
1765 '<div class="videoOptionsComplete">'+
1766 //@@TODO: this style should go to .css
1767 '<span style="float:right;margin-right:10px">' +
1768 '<a href="#" style="color:white;" onClick="$j(\'#'+sel_id+'\').get(0).closeDisplayedHTML();return false;">close</a>' +
1769 '</span>'+
1770 '<div id="mv_disp_inner_'+sel_id+'" style="padding-top:10px;">'+
1771 html_code
1772 +'</div>'+
1773 '</div></div>';
1774 $j('#'+sel_id).prepend(div_code);
1775 if (fade_in)
1776 $j('#blackbg_'+sel_id).fadeIn("slow");
1777 else
1778 $j('#blackbg_'+sel_id).show();
1779 return false; //onclick action return false
1780 },
1781 /** Close the custom HTML displayed using displayHTML and restores the
1782 regular mv_embed display.
1783 */
1784 closeDisplayedHTML:function(){
1785 var sel_id = (this.pc!=null)?this.pc.pp.id:this.id;
1786 $j('#blackbg_'+sel_id).fadeOut("slow", function(){
1787 $j('#blackbg_'+sel_id).remove();
1788 });
1789 return false; //onclick action return false
1790 },
1791 selectPlaybackMethod:function(){
1792 //get id (in case where we have a parent container)
1793 var this_id = (this.pc!=null)?this.pc.pp.id:this.id;
1794
1795 var _this=this;
1796 var out= '<span style="color:#FFF;background-color:black;"><blockquote style="background-color:black;">';
1797 var _this=this;
1798 //js_log('selected src'+ _this.media_element.selected_source.url);
1799 $j.each( this.media_element.getPlayableSources(), function(source_id, source){
1800 var default_player = embedTypes.players.defaultPlayer( source.getMIMEType() );
1801
1802 var is_selected = (source == _this.media_element.selected_source);
1803 var image_src = mv_skin_img_path ;
1804
1805 //set the Playable source type:
1806 if( source.mime_type == 'video/x-flv' ){
1807 image_src += 'flash_icon_';
1808 }else if( source.mime_type == 'video/h264'){
1809 //for now all mp4 content is pulled from archive.org (so use archive.org icon)
1810 image_src += 'archive_org_';
1811 }else{
1812 image_src += 'fish_xiph_org_';
1813 }
1814 image_src += is_selected ? 'color':'bw';
1815 image_src += '.png';
1816
1817 if (default_player)
1818 {
1819 out += '<img src="'+image_src+'"/>';
1820 if( ! is_selected )
1821 out+='<a href="#" class="sel_source" id="sc_' + source_id + '_' + default_player.id +'">';
1822 out += source.getTitle()+ (is_selected?'</a>':'') + ' ';
1823
1824 //output the player select code:
1825 var supporting_players = embedTypes.players.getMIMETypePlayers( source.getMIMEType() );
1826 out+='<div id="player_select_list_' + source_id + '" class="player_select_list"><ul>';
1827 for(var i=0; i < supporting_players.length ; i++){
1828 if( _this.selected_player.id == supporting_players[i].id && is_selected ){
1829 out+='<li style="border-style:dashed;margin-left:20px;">'+
1830 '<img border="0" width="16" height="16" src="' + mv_skin_img_path + 'plugin.png">' +
1831 supporting_players[i].getName() +
1832 '</li>';
1833 }else{
1834 //else gray plugin and the plugin with link to select
1835 out+='<li style="margin-left:20px;">'+
1836 '<a href="#" class="sel_source" id="sc_' + source_id + '_' + supporting_players[i].id +'">'+
1837 '<img border="0" width="16" height="16" src="' + mv_skin_img_path + 'plugin_disabled.png">'+
1838 supporting_players[i].getName() +
1839 '</a>'+
1840 '</li>';
1841 }
1842 }
1843 out+='</ul></div>';
1844 }else
1845 out+= source.getTitle() + ' - no player available';
1846 });
1847 out+='</blockquote></span>';
1848 this.displayHTML(out);
1849
1850 //set up the click bindings:
1851 $j('.sel_source').each(function(){
1852 $j(this).click(function(){
1853 var iparts = $j(this).attr( 'id' ).replace(/sc_/,'').split('_');
1854 var source_id = iparts[0];
1855 var default_player_id = iparts[1];
1856 js_log('source id: ' + source_id + ' player id: ' + default_player_id);
1857
1858 $j('#' + this_id ).get(0).closeDisplayedHTML();
1859 $j('#' + _this.id ).get(0).media_element.selectSource( source_id );
1860
1861 embedTypes.players.userSelectPlayer( default_player_id,
1862 _this.media_element.sources[ source_id ].getMIMEType() );
1863
1864 //be sure to issue a stop
1865 $j('#' + this_id ).get(0).stop();
1866
1867 //don't follow the empty # link:
1868 return false;
1869 });
1870 });
1871 },
1872 showVideoDownload:function(){
1873 //load the roe if available (to populate out download options:
1874 //js_log('f:showVideoDownload '+ this.roe + ' ' + this.media_element.addedROEData);
1875 if(this.roe && this.media_element.addedROEData == false){
1876 var _this = this;
1877 this.displayHTML(gM('mwe-loading_txt'));
1878 do_request(this.roe, function(data)
1879 {
1880 _this.media_element.addROE(data);
1881 $j('#mv_disp_inner_'+_this.id).html( _this.getShowVideoDownload() );
1882 });
1883 }else{
1884 this.displayHTML( this.getShowVideoDownload() );
1885 }
1886 },
1887 getShowVideoDownload:function(){
1888 var out='<div style="color:white">' +
1889 '<b style="color:white;">'+gM('mwe-download_segment')+'</b><br>';
1890 out+='<blockquote style="background:#000">'+
1891 gM('mwe-download_right_click') + '</blockquote><br>';
1892 var dl_list='';
1893 var dl_txt_list='';
1894 $j.each(this.media_element.getSources(), function(index, source){
1895 var dl_line = '<li>' + '<a style="color:white" href="' + source.getURI() +'"> '
1896 + source.getTitle()+'</a> '+ '</li>'+"\n";
1897 if( source.getURI().indexOf('?t=')!==-1){
1898 out+=dl_line;
1899 }else if( this.getMIMEType()=="text/cmml" || this.getMIMEType()=="text/x-srt" ){
1900 dl_txt_list+=dl_line;
1901 }else{
1902 dl_list+=dl_line;
1903 }
1904 });
1905
1906 if(dl_list!='')
1907 out+=gM('mwe-download_full') + '<blockquote style="background:#000">' + dl_list + '</blockquote>';
1908 if(dl_txt_list!='')
1909 out+=gM('mwe-download_text')+'<blockquote style="background:#000">' + dl_txt_list +'</blockquote>';
1910 out+='</div>';
1911 return out;
1912 },
1913 /*
1914 * base embed controls
1915 * the play button calls
1916 */
1917 play:function(){
1918 var this_id = (this.pc!=null)?this.pc.pp.id:this.id;
1919
1920 //js_log( "mv_embed play:" + this.id);
1921 //js_log('thum disp:'+this.thumbnail_disp);
1922 //check if thumbnail is being displayed and embed html
1923 if( this.thumbnail_disp ){
1924 if( !this.selected_player ){
1925 js_log('no selected_player');
1926 //this.innerHTML = this.getPluginMissingHTML();
1927 //$j(this).html(this.getPluginMissingHTML());
1928 $j('#'+this.id).html( this.getPluginMissingHTML() );
1929 }else{
1930 this.doEmbedHTML();
1931 this.onClipDone_disp=false;
1932 this.paused=false;
1933 this.thumbnail_disp=false;
1934 }
1935 }else{
1936 //the plugin is already being displayed
1937 this.paused=false; //make sure we are not "paused"
1938 this.seeking=false;
1939 }
1940
1941 $j("#mv_play_pause_button_" + this_id + ' span').removeClass('ui-icon-play').addClass('ui-icon-pause');
1942 $j("#mv_play_pause_button_" + this_id).unbind().btnBind().click(function(){
1943 $j('#' + this_id ).get(0).pause();
1944 }).attr('title', gM('mwe-pause_clip'));
1945
1946 },
1947 load:function(){
1948 //should be done by child (no base way to load assets)
1949 js_log('baseEmbed:load call');
1950 },
1951 getSrc:function(){
1952 return this.media_element.selected_source.getURI( this.seek_time_sec );
1953 },
1954 /*
1955 * base embed pause
1956 * there is no general way to pause the video
1957 * must be overwritten by embed object to support this functionality.
1958 */
1959 pause: function(){
1960 var this_id = (this.pc!=null)?this.pc.pp.id:this.id;
1961 //js_log('mv_embed:do pause');
1962 //(playing) do pause
1963 this.paused = true;
1964 //update the ctrl "paused state"
1965 $j("#mv_play_pause_button_" + this_id + ' span').removeClass('ui-icon-pause').addClass('ui-icon-play');
1966 $j("#mv_play_pause_button_" + this_id).unbind().btnBind().click(function(){
1967 $j('#'+this_id).get(0).play();
1968 }).attr('title', gM('mwe-play_clip'));
1969 },
1970 /*
1971 * base embed stop (can be overwritten by the plugin)
1972 */
1973 stop: function(){
1974 var _this = this;
1975 js_log('mvEmbed:stop:'+this.id);
1976
1977 //no longer seeking:
1978 this.didSeekJump=false;
1979
1980 //first issue pause to update interface (only call the parent)
1981 if(this['parent_pause']){
1982 this.parent_pause();
1983 }else{
1984 this.pause();
1985 }
1986
1987 //reset the currentTime:
1988 this.currentTime=0;
1989 //check if thumbnail is being displayed in which case do nothing
1990 if( this.thumbnail_disp ){
1991 //already in stooped state
1992 js_log('already in stopped state');
1993 }else{
1994 //rewrite the html to thumbnail disp
1995 this.doThumbnailHTML();
1996 this.bufferedPercent=0; //reset buffer state
1997 this.setSliderValue(0);
1998 this.setStatus( this.getTimeReq() );
1999 }
2000
2001 //make sure the big playbutton is has click action:
2002 $j('#big_play_link_' + _this.id).unbind('click').click(function(){
2003 $j('#' +_this.id).get(0).play();
2004 });
2005
2006 if(this.update_interval)
2007 {
2008 clearInterval(this.update_interval);
2009 this.update_interval = null;
2010 }
2011 },
2012 toggleMute:function(){
2013 var this_id = (this.pc!=null)?this.pc.pp.id:this.id;
2014 if(this.muted){
2015 this.muted=false;
2016 $j('#volume_control_'+this_id + ' span').removeClass('ui-icon-volume-off').addClass('ui-icon-volume-on');
2017 $j('#volume_bar_'+this_id).slider('value', 100);
2018 this.updateVolumen(1);
2019 }else{
2020 this.muted=true;
2021 $j('#volume_control_'+this_id + ' span').removeClass('ui-icon-volume-on').addClass('ui-icon-volume-off');
2022 $j('#volume_bar_'+this_id).slider('value', 0);
2023 this.updateVolumen(0);
2024 }
2025 js_log('f:toggleMute::' + this.muted);
2026 },
2027 updateVolumen:function(perc){
2028 js_log('update volume not supported with current playback type');
2029 },
2030 fullscreen:function(){
2031 js_log('fullscreen not supported with current playback type');
2032 },
2033 /* returns bool true if playing or paused, false if stooped
2034 */
2035 isPlaying : function(){
2036 if(this.thumbnail_disp){
2037 //in stoped state
2038 return false;
2039 }else if( this.paused ){
2040 //paused state
2041 return false;
2042 }else{
2043 return true;
2044 }
2045 },
2046 isPaused : function(){
2047 return this.isPlaying() && this.paused;
2048 },
2049 isStoped : function(){
2050 return this.thumbnail_disp;
2051 },
2052 playlistSupport:function(){
2053 //by default not supported (implemented in js)
2054 return false;
2055 },
2056 postEmbedJS:function(){
2057 return '';
2058 },
2059 //do common monitor code like update the playhead and play status
2060 //plugin objects are responsible for updating currentTime
2061 monitor:function(){
2062 if( this.currentTime && this.currentTime > 0 && this.duration){
2063 if( !this.userSlide ){
2064 if( this.start_offset ){
2065 //if start offset include that calculation
2066 this.setSliderValue( ( this.currentTime - this.start_offset ) / this.duration );
2067 this.setStatus( seconds2npt(this.currentTime) + '/'+ seconds2npt(parseFloat(this.start_offset)+parseFloat(this.duration) ));
2068 }else{
2069 this.setSliderValue( this.currentTime / this.duration );
2070 this.setStatus( seconds2npt(this.currentTime) + '/' + seconds2npt(this.duration ));
2071 }
2072 }
2073 }else{
2074 //js_log(' ct:' + this.currentTime + ' dur: ' + this.duration);
2075 if( this.isStoped() ){
2076 this.setStatus( this.getTimeReq() );
2077 }else if( this.isPaused() ){
2078 this.setStatus( "paused" );
2079 }else if( this.isPlaying() ){
2080 if( this.currentTime && ! this.duration )
2081 this.setStatus( seconds2npt( this.currentTime ) + ' /' );
2082 else
2083 this.setStatus(" - - - ");
2084 }else{
2085 this.setStatus( this.getTimeReq() );
2086 }
2087 }
2088 //update buffer information
2089 this.updateBufferStatus();
2090
2091 //check if we passed duration
2092 if( this.duration && (this.currentTime > this.duration) ){
2093 this.onClipDone();
2094 }
2095
2096 //update monitorTimerId to call child monitor
2097 if( ! this.monitorTimerId ){
2098 //make sure an instance of this.id exists:
2099 if( document.getElementById(this.id) ){
2100 this.monitorTimerId = setInterval('$j(\'#'+this.id+'\').get(0).monitor()', 250);
2101 }
2102 }
2103 },
2104 stopMonitor:function(){
2105 if( this.monitorTimerId != 0 )
2106 {
2107 clearInterval( this.monitorTimerId );
2108 this.monitorTimerId = 0;
2109 }
2110 },
2111 updateBufferStatus: function(){
2112
2113 //build the buffer targeet based for playlist vs clip
2114 var buffer_select = (this.pc) ?
2115 '#cl_status_' + this.id + ' .mv_buffer':
2116 '#mv_play_head_' + this.id + ' .mv_buffer';
2117
2118 //update the buffer progress bar (if available )
2119 if( this.bufferedPercent != 0 ){
2120 //js_log('bufferedPercent: ' + this.bufferedPercent);
2121 if(this.bufferedPercent > 1)
2122 this.bufferedPercent=1;
2123
2124 $j(buffer_select).css("width", (this.bufferedPercent*100) +'%' );
2125 }else{
2126 $j(buffer_select).css("width", '0px' );
2127 }
2128 },
2129 relativeCurrentTime: function(){
2130 if(!this.start_offset)
2131 this.start_offset =0;
2132 var rt = this.currentTime - this.start_offset;
2133 if( rt < 0 ) //should not happen but does.
2134 return 0;
2135 return rt;
2136 },
2137 getPluginEmbed : function(){
2138 if (window.document[this.pid]){
2139 return window.document[this.pid];
2140 }
2141 if ($j.browser.msie){
2142 return document.getElementById(this.pid );
2143 }else{
2144 if (document.embeds && document.embeds[this.pid])
2145 return document.embeds[this.pid];
2146 }
2147 return null;
2148 },
2149 //HELPER Functions for selected source
2150 /*
2151 * returns the selected source url for players to play
2152 */
2153 getURI : function( seek_time_sec ){
2154 return this.media_element.selected_source.getURI( this.seek_time_sec );
2155 },
2156 supportsURLTimeEncoding: function(){
2157 //do head request if on the same domain
2158 return this.media_element.selected_source.URLTimeEncoding;
2159 },
2160 setSliderValue: function(perc, hide_progress){
2161 if(this.controls){
2162 var this_id = (this.pc)?this.pc.pp.id:this.id;
2163 var val = parseInt( perc*1000 );
2164 $j('#mv_play_head_'+this_id).slider('value', val);
2165
2166 //js_log("embed video set: " + '#mv_play_head_'+this_id + ' to ' + val);
2167 }
2168 //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) );
2169 //js_log('op:' + offset_perc + ' *('+perc+' * ' + $j('#slider_'+id).width() + ')');
2170 },
2171 highlightPlaySection:function(options){
2172 js_log('highlightPlaySection');
2173 var this_id = (this.pc)?this.pc.pp.id:this.id;
2174 var dur = this.getDuration();
2175 var hide_progress = true;
2176 //set the left percet and update the slider:
2177 rel_start_sec = npt2seconds( options['start']);
2178 //remove the start_offset if relevent:
2179 if(this.start_offset)
2180 rel_start_sec = rel_start_sec - this.start_offset
2181
2182 var slider_perc=0;
2183 if( rel_start_sec <= 0 ){
2184 left_perc =0;
2185 options['start'] = seconds2npt( this.start_offset );
2186 rel_start_sec=0;
2187 this.setSliderValue( 0 , hide_progress);
2188 }else{
2189 left_perc = parseInt( (rel_start_sec / dur)*100 ) ;
2190 slider_perc = (left_perc / 100);
2191 }
2192 js_log("slider perc:" + slider_perc);
2193 if( ! this.isPlaying() ){
2194 this.setSliderValue( slider_perc , hide_progress);
2195 }
2196
2197 width_perc = parseInt( (( npt2seconds( options['end'] ) - npt2seconds( options['start'] ) ) / dur)*100 ) ;
2198 if( (width_perc + left_perc) > 100 ){
2199 width_perc = 100 - left_perc;
2200 }
2201 //js_log('should hl: '+rel_start_sec+ '/' + dur + ' re:' + rel_end_sec+' lp:' + left_perc + ' width: ' + width_perc);
2202 $j('#mv_seeker_' + this_id + ' .mv_highlight').css({
2203 'left':left_perc+'%',
2204 'width':width_perc+'%'
2205 }).show();
2206
2207 this.jump_time = options['start'];
2208 this.seek_time_sec = npt2seconds( options['start']);
2209 //trim output to
2210 this.setStatus( gM('mwe-seek_to')+' '+ seconds2npt( this.seek_time_sec ) );
2211 js_log('DO update: ' + this.jump_time);
2212 this.updateThumbTime( rel_start_sec );
2213 },
2214 hideHighlight:function(){
2215 var this_id = (this.pc)?this.pc.pp.id:this.id;
2216 $j('#mv_seeker_' + this_id + ' .mv_highlight').hide();
2217 this.setStatus( this.getTimeReq() );
2218 this.setSliderValue( 0 );
2219 },
2220 setStatus:function(value){
2221 var id = (this.pc)?this.pc.pp.id:this.id;
2222 //update status:
2223 $j('#mv_time_'+id).html(value);
2224 }
2225 }
2226
2227
2228
2229 /**
2230 * mediaPlayer represents a media player plugin.
2231 * @param {String} id id used for the plugin.
2232 * @param {Array<String>} supported_types n array of supported MIME types.
2233 * @param {String} library external script containing the plugin interface code. (mv_<library>Embed.js)
2234 * @constructor
2235 */
2236 function mediaPlayer(id, supported_types, library)
2237 {
2238 this.id=id;
2239 this.supported_types = supported_types;
2240 this.library = library;
2241 this.loaded = false;
2242 this.loading_callbacks = new Array();
2243 return this;
2244 }
2245 mediaPlayer.prototype =
2246 {
2247 id:null,
2248 supported_types:null,
2249 library:null,
2250 loaded:false,
2251 loading_callbacks:null,
2252 supportsMIMEType : function(type)
2253 {
2254 for (var i=0; i < this.supported_types.length; i++)
2255 if(this.supported_types[i] == type)
2256 return true;
2257 return false;
2258 },
2259 getName : function()
2260 {
2261 return gM('mwe-ogg-player-' + this.id);
2262 },
2263 load : function(callback){
2264 var libName = this.library+'Embed';
2265 if( mvJsLoader.checkObjPath( libName ) ){
2266 js_log('plugin loaded, do callback:');
2267 callback();
2268 }else{
2269 var _this = this;
2270 //jQuery based get script does not work so well.
2271 mvJsLoader.doLoad([
2272 libName
2273 ],function(){
2274 callback();
2275 });
2276 }
2277 }
2278 }
2279 /* players and supported mime types
2280 @@todo ideally we query the plugin to get what mime types it supports in practice not always reliable/avaliable
2281 */
2282 var flowPlayer = new mediaPlayer('flowplayer',['video/x-flv', 'video/h264'],'flash');
2283
2284 var omtkPlayer = new mediaPlayer('omtkplayer',['audio/ogg'], 'omtk' );
2285
2286 var cortadoPlayer = new mediaPlayer('cortado',['video/ogg', 'audio/ogg'],'java');
2287 var videoElementPlayer = new mediaPlayer('videoElement',['video/ogg', 'audio/ogg'],'native');
2288
2289 var vlcMineList = ['video/ogg','audio/ogg', 'video/x-flv', 'video/mp4', 'video/h264'];
2290 var vlcMozillaPlayer = new mediaPlayer('vlc-mozilla',vlcMineList,'vlc');
2291 var vlcActiveXPlayer = new mediaPlayer('vlc-activex',vlcMineList,'vlc');
2292
2293 //add generic
2294 var oggPluginPlayer = new mediaPlayer('oggPlugin',['video/ogg'],'generic');
2295
2296 //depricate quicktime in favor of safari native
2297 //var quicktimeMozillaPlayer = new mediaPlayer('quicktime-mozilla',['video/ogg'],'quicktime');
2298 //var quicktimeActiveXPlayer = new mediaPlayer('quicktime-activex',['video/ogg'],'quicktime');
2299
2300 var htmlPlayer = new mediaPlayer('html',['text/html', 'image/jpeg', 'image/png', 'image/svg'], 'html');
2301
2302 /**
2303 * mediaPlayers is a collection of mediaPlayer objects supported by the client.
2304 * It could be merged with embedTypes, since there is one embedTypes per script
2305 * and one mediaPlayers per embedTypes.
2306 */
2307 function mediaPlayers()
2308 {
2309 this.init();
2310 }
2311
2312 mediaPlayers.prototype =
2313 {
2314 players : null,
2315 preference : null,
2316 default_players : {},
2317 init : function()
2318 {
2319 this.players = new Array();
2320 this.loadPreferences();
2321
2322 //set up default players order for each library type
2323 this.default_players['video/x-flv'] = ['flash','vlc'];
2324 this.default_players['video/h264'] = ['flash', 'vlc'];
2325
2326 this.default_players['video/ogg'] = ['native','vlc','java', 'generic'];
2327 this.default_players['application/ogg'] = ['native','vlc','java', 'generic'];
2328 this.default_players['audio/ogg'] = ['native','vlc', 'java', 'omtk' ];
2329 this.default_players['video/mp4'] = ['vlc'];
2330
2331 this.default_players['text/html'] = ['html'];
2332 this.default_players['image/jpeg'] = ['html'];
2333 this.default_players['image/png'] = ['html'];
2334 this.default_players['image/svg'] = ['html'];
2335
2336 },
2337 addPlayer : function(player, mime_type)
2338 {
2339 //js_log('Adding ' + player.id + ' with mime_type ' + mime_type);
2340 for (var i =0; i < this.players.length; i++){
2341 if (this.players[i].id == player.id)
2342 {
2343 if(mime_type!=null)
2344 {
2345 //make sure the mime_type is not already there:
2346 var add_mime = true;
2347 for(var j=0; j < this.players[i].supported_types.length; j++ ){
2348 if( this.players[i].supported_types[j]== mime_type)
2349 add_mime=false;
2350 }
2351 if(add_mime)
2352 this.players[i].supported_types.push(mime_type);
2353 }
2354 return;
2355 }
2356 }
2357 //player not found:
2358 if(mime_type!=null)
2359 player.supported_types.push(mime_type);
2360
2361 this.players.push( player );
2362 },
2363 getMIMETypePlayers : function(mime_type)
2364 {
2365 var mime_players = new Array();
2366 var _this = this;
2367 var inx = 0;
2368 if( this.default_players[mime_type] ){
2369 $j.each( this.default_players[mime_type], function(d, lib){
2370 var library = _this.default_players[mime_type][d];
2371 for ( var i=0; i < _this.players.length; i++ ){
2372 if ( _this.players[i].library == library && _this.players[i].supportsMIMEType(mime_type) ){
2373 mime_players[ inx ] = _this.players[i];
2374 inx++;
2375 }
2376 }
2377 });
2378 }
2379 return mime_players;
2380 },
2381 defaultPlayer : function(mime_type)
2382 {
2383 js_log("get defaultPlayer for " + mime_type);
2384 var mime_players = this.getMIMETypePlayers(mime_type);
2385 if( mime_players.length > 0)
2386 {
2387 // check for prior preference for this mime type
2388 for( var i=0; i < mime_players.length; i++ ){
2389 if( mime_players[i].id==this.preference[mime_type] )
2390 return mime_players[i];
2391 }
2392 // otherwise just return the first compatible player
2393 // (it will be chosen according to the default_players list
2394 return mime_players[0];
2395 }
2396 js_log( 'No default player found for ' + mime_type );
2397 return null;
2398 },
2399 userSelectFormat : function (mime_format){
2400 this.preference['format_prefrence'] = mime_format;
2401 this.savePreferences();
2402 },
2403 userSelectPlayer : function(player_id, mime_type)
2404 {
2405 var selected_player=null;
2406 for(var i=0; i < this.players.length; i++){
2407 if(this.players[i].id == player_id)
2408 {
2409 selected_player = this.players[i];
2410 js_log('choosing ' + player_id + ' for ' + mime_type);
2411 this.preference[mime_type]=player_id;
2412 this.savePreferences();
2413 break;
2414 }
2415 }
2416 if( selected_player )
2417 {
2418 for(var i=0; i < global_player_list.length; i++)
2419 {
2420 var embed = $j('#'+global_player_list[i]).get(0);
2421 if(embed.media_element.selected_source && (embed.media_element.selected_source.mime_type == mime_type))
2422 {
2423 embed.selectPlayer(selected_player);
2424 js_log('using ' + embed.selected_player.getName() + ' for ' + embed.media_element.selected_source.getTitle());
2425 }
2426 }
2427 }
2428 },
2429 loadPreferences : function()
2430 {
2431 this.preference = new Object();
2432 // see if we have a cookie set to a clientSupported type:
2433 var cookieVal = $j.cookie( 'ogg_player_exp' );
2434 if (cookieVal)
2435 {
2436 var pairs = cookieVal.split('&');
2437 for(var i=0; i < pairs.length; i++)
2438 {
2439 var name_value = pairs[i].split('=');
2440 this.preference[name_value[0]]=name_value[1];
2441 //js_log('load preference for ' + name_value[0] + ' is ' + name_value[1]);
2442 }
2443 }
2444 },
2445 savePreferences : function()
2446 {
2447 var cookieVal = '';
2448 for(var i in this.preference)
2449 cookieVal+= i + '='+ this.preference[i] + '&';
2450
2451 cookieVal=cookieVal.substr(0, cookieVal.length-1);
2452 var week = 7*86400*1000;
2453 $j.cookie( 'ogg_player_exp', cookieVal, { 'expires':week } );
2454 }
2455 };
2456
2457 /*
2458 * embedTypes object handles setting and getting of supported embed types:
2459 * closely mirrors OggHandler so that its easier to share efforts in this area:
2460 * http://svn.wikimedia.org/viewvc/mediawiki/trunk/extensions/OggHandler/OggPlayer.js
2461 */
2462 var embedTypes = {
2463 // List of players
2464 players: null,
2465 detect_done:false,
2466 init: function(){
2467 //detect supported types
2468 this.detect();
2469 this.detect_done=true;
2470 },
2471 clientSupports: { 'thumbnail' : true },
2472 supportedMimeType: function(mimetype) {
2473 for (var i = navigator.plugins.length; i-- > 0; ) {
2474 var plugin = navigator.plugins[i];
2475 if (typeof plugin[mimetype] != "undefined")
2476 return true;
2477 }
2478 return false;
2479 },
2480
2481 detect: function() {
2482 js_log("running detect");
2483 this.players = new mediaPlayers();
2484 //every browser supports html rendering:
2485 this.players.addPlayer( htmlPlayer );
2486 // In Mozilla, navigator.javaEnabled() only tells us about preferences, we need to
2487 // search navigator.mimeTypes to see if it's installed
2488 var javaEnabled = navigator.javaEnabled();
2489 // In Opera, navigator.javaEnabled() is all there is
2490 var invisibleJava = $j.browser.opera;
2491 // Some browsers filter out duplicate mime types, hiding some plugins
2492 var uniqueMimesOnly = $j.browser.opera || $j.browser.safari;
2493 // Opera will switch off javaEnabled in preferences if java can't be found.
2494 // And it doesn't register an application/x-java-applet mime type like Mozilla does.
2495 if ( invisibleJava && javaEnabled )
2496 this.players.addPlayer( cortadoPlayer );
2497
2498 // ActiveX plugins
2499 if($j.browser.msie){
2500 // check for flash
2501 if ( this.testActiveX( 'ShockwaveFlash.ShockwaveFlash')){
2502 //try to get the flash version for omtk include:
2503 try {
2504 a = new ActiveXObject(SHOCKWAVE_FLASH_AX + ".7");
2505 d = a.GetVariable("$version"); // Will crash fp6.0.21/23/29
2506 if (d) {
2507 d = d.split(" ")[1].split(",");
2508 //we need flash version 10 or greater:
2509 if(parseInt( d[0]) >=10){
2510 this.players.addPlayer( omtkPlayer );
2511 }
2512
2513 }
2514 }catch(e) {}
2515
2516 //flowplayer has pretty good compatiablity
2517 // (but if we wanted to be fancy we would check for version of flash and update the mp4/h.264 support
2518 this.players.addPlayer( flowPlayer );
2519 }
2520 // VLC
2521 if ( this.testActiveX( 'VideoLAN.VLCPlugin.2' ) )
2522 this.players.addPlayer(vlcActiveXPlayer);
2523 // Java
2524 if ( javaEnabled && this.testActiveX( 'JavaWebStart.isInstalled' ) )
2525 this.players.addPlayer(cortadoPlayer);
2526 // quicktime
2527 //if ( this.testActiveX( 'QuickTimeCheckObject.QuickTimeCheck.1' ) )
2528 // this.players.addPlayer(quicktimeActiveXPlayer);
2529 }
2530 // <video> element
2531 if ( typeof HTMLVideoElement == 'object' // Firefox, Safari
2532 || typeof HTMLVideoElement == 'function' ) // Opera
2533 {
2534 //do another test for safari:
2535 if( $j.browser.safari ){
2536 try{
2537 var dummyvid = document.createElement("video");
2538 if (dummyvid.canPlayType && dummyvid.canPlayType("video/ogg;codecs=\"theora,vorbis\"") == "probably")
2539 {
2540 this.players.addPlayer( videoElementPlayer );
2541 } else if(this.supportedMimeType( 'video/ogg' )) {
2542 /* older versions of safari do not support canPlayType,
2543 but xiph qt registers mimetype via quicktime plugin */
2544 this.players.addPlayer( videoElementPlayer );
2545 } else {
2546 //@@todo add some user nagging to install the xiph qt
2547 }
2548 }catch(e){
2549 js_log('could not run canPlayType in safari');
2550 }
2551 }else{
2552 this.players.addPlayer( videoElementPlayer );
2553 }
2554 }
2555
2556 // Mozilla plugins
2557 if( navigator.mimeTypes && navigator.mimeTypes.length > 0) {
2558 for ( var i = 0; i < navigator.mimeTypes.length; i++ ) {
2559 var type = navigator.mimeTypes[i].type;
2560 var semicolonPos = type.indexOf( ';' );
2561 if ( semicolonPos > -1 ) {
2562 type = type.substr( 0, semicolonPos );
2563 }
2564 //js_log('on type: '+type);
2565 var pluginName = navigator.mimeTypes[i].enabledPlugin ? navigator.mimeTypes[i].enabledPlugin.name : '';
2566 if ( !pluginName ) {
2567 // In case it is null or undefined
2568 pluginName = '';
2569 }
2570 if ( pluginName.toLowerCase() == 'vlc multimedia plugin' || pluginName.toLowerCase() == 'vlc multimedia plug-in' ) {
2571 this.players.addPlayer(vlcMozillaPlayer, type);
2572 continue;
2573 }
2574
2575 if ( javaEnabled && type == 'application/x-java-applet' ) {
2576 this.players.addPlayer(cortadoPlayer);
2577 continue;
2578 }
2579
2580 if ( type == 'application/ogg' ) {
2581 if ( pluginName.toLowerCase() == 'vlc multimedia plugin' ){
2582 this.players.addPlayer(vlcMozillaPlayer, type);
2583 //else if ( pluginName.indexOf( 'QuickTime' ) > -1 )
2584 // this.players.addPlayer(quicktimeMozillaPlayer);
2585 }else{
2586 this.players.addPlayer(oggPluginPlayer);
2587 }
2588 continue;
2589 } else if ( uniqueMimesOnly ) {
2590 if ( type == 'application/x-vlc-player' ) {
2591 this.players.addPlayer(vlcMozillaPlayer, type);
2592 continue;
2593 } else if ( type == 'video/quicktime' ) {
2594 //this.players.addPlayer(quicktimeMozillaPlayer);
2595 continue;
2596 }
2597 }
2598
2599 /*if ( type == 'video/quicktime' ) {
2600 this.players.addPlayer(vlcMozillaPlayer, type);
2601 continue;
2602 }*/
2603 if(type=='application/x-shockwave-flash'){
2604 this.players.addPlayer( flowPlayer );
2605
2606 //check version to add omtk:
2607 var flashDescription = navigator.plugins["Shockwave Flash"].description;
2608 var descArray = flashDescription.split(" ");
2609 var tempArrayMajor = descArray[2].split(".");
2610 var versionMajor = tempArrayMajor[0];
2611 //js_log("version of flash: " + versionMajor);
2612 if(versionMajor >= 10){
2613 this.players.addPlayer( omtkPlayer );
2614 }
2615 continue;
2616 }
2617 }
2618 }
2619 //@@The xiph quicktime component does not work well with annodex streams (temporarly disable)
2620 //this.clientSupports['quicktime-mozilla'] = false;
2621 //this.clientSupports['quicktime-activex'] = false;
2622 //js_log(this.clientSupports);
2623 },
2624 testActiveX : function ( name ) {
2625 var hasObj = true;
2626 try {
2627 // No IE, not a class called "name", it's a variable
2628 var obj = new ActiveXObject( '' + name );
2629 } catch ( e ) {
2630 hasObj = false;
2631 }
2632 return hasObj;
2633 }
2634 };