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