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