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