* volume control / skin fixes to support both horz and vert volume control
[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 //make sure the ctrlBuilder remain active:
1414 this.ctrlBuilder.addControlHooks(this);
1415 },
1416 refreshControlsHTML:function(){
1417 js_log('refreshControlsHTML::');
1418 if($j('#' + this.id + ' .control-bar').length == 0)
1419 {
1420 js_log('control-bar not present, returning');
1421 return ;
1422 }else{
1423 $j('#' + this.id + ' .control-bar').html( this.getControlsHTML() );
1424 this.ctrlBuilder.addControlHooks(this);
1425 }
1426 },
1427 getControlsHTML:function()
1428 {
1429 return this.ctrlBuilder.getControls( this );
1430 },
1431 getHTML : function (){
1432 //@@todo check if we have sources available
1433 js_log('embedVideo:getHTML : ' + this.id + ' resource type: ' + this.type);
1434
1435 //set-up the local ctrlBuilder instance:
1436 this.ctrlBuilder = new ctrlBuilder( this );
1437
1438 var _this = this;
1439 var html_code = '';
1440 html_code = '<div id="videoPlayer_' + this.id + '" style="width:' + this.width + 'px;position:relative;"'+
1441 'class="' + this.ctrlBuilder.pClass + '">';
1442 html_code += '<div style="width:'+parseInt(this.width)+'px;height:'+parseInt(this.height)+'px;" id="mv_embedded_player_'+this.id+'">' +
1443 this.getThumbnailHTML() +
1444 '</div>';
1445 //js_log("mvEmbed:controls "+ typeof this.controls);
1446 if( this.controls )
1447 {
1448 js_log("f:getHTML:AddControls");
1449 html_code +='<div class="ui-state-default ui-widget-header ui-helper-clearfix control-bar" >';
1450 html_code += this.getControlsHTML();
1451 html_code +='</div>';
1452 //block out some space by encapulating the top level div
1453 $j(this).wrap('<div style="width:'+parseInt(this.width)+'px;height:'
1454 +( parseInt(this.height) + this.ctrlBuilder.height )+'px"></div>');
1455 }
1456 html_code += '</div>'; //videoPlayer div close
1457 //js_log('should set: '+this.id);
1458 $j(this).html( html_code );
1459 //add hooks once Controls are in DOM
1460 this.ctrlBuilder.addControlHooks(this);
1461
1462 //js_log('set this to: ' + $j(this).html() );
1463 //alert('stop');
1464 //if auto play==true directly embed the plugin
1465 if(this.autoplay)
1466 {
1467 js_log('activating autoplay');
1468 this.play();
1469 }
1470 },
1471 /*
1472 * get missing plugin html (check for user included code)
1473 */
1474 getPluginMissingHTML : function(missing_type){
1475 //keep the box width hight:
1476 var out = '<div style="width:'+this.width+'px;height:'+this.height+'px">';
1477 if(this.user_missing_plugin_html){
1478 out+= this.user_missing_plugin_html;
1479 }else{
1480 if(!missing_type)
1481 missing_type='';
1482 out+= gM('mwe-generic_missing_plugin', missing_type) + ' or <a title="'+gM('mwe-download_clip')+'" href="'+this.src +'">'+gM('mwe-download_clip')+'</a>';
1483 }
1484 return out + '</div>';
1485 },
1486 updateVideoTimeReq:function(time_req){
1487 js_log('f:updateVideoTimeReq');
1488 var time_parts =time_req.split('/');
1489 this.updateVideoTime(time_parts[0], time_parts[1]);
1490 },
1491 //update video time
1492 updateVideoTime:function(start_ntp, end_ntp){
1493 //update media
1494 this.media_element.updateSourceTimes( start_ntp, end_ntp );
1495 //update mv_time
1496 this.setStatus(start_ntp+'/'+end_ntp);
1497 //reset slider
1498 this.setSliderValue(0);
1499 //reset seek_offset:
1500 if(this.media_element.selected_source.URLTimeEncoding )
1501 this.seek_time_sec=0;
1502 else
1503 this.seek_time_sec=npt2seconds(start_ntp);
1504 },
1505 //@@todo overwite by embed library if we can render frames natavily
1506 renderTimelineThumbnail:function( options ){
1507 var my_thumb_src = this.media_element.getThumbnailURL();
1508 //check if our thumbnail has a time attribute:
1509 if( my_thumb_src.indexOf('t=') !== -1){
1510 var time_ntp = seconds2npt ( options.time + parseInt(this.start_offset) );
1511 my_thumb_src = getURLParamReplace( my_thumb_src, { 't':time_ntp, 'size': options.size } );
1512 }
1513 var thumb_class = (typeof options['thumb_class'] != 'undefined' ) ? options['thumb_class'] : '';
1514 return '<div class="ui-corner-all ' + thumb_class + '" src="' + my_thumb_src + '" '+
1515 'style="height:' + options.height + 'px;' +
1516 'width:' + options.width + 'px" >' +
1517 '<img src="' + my_thumb_src +'" '+
1518 'style="height:' + options.height + 'px;' +
1519 'width:' + options.width + 'px">' +
1520 '</div>';
1521 },
1522 updateThumbTimeNTP:function( time){
1523 this.updateThumbTime( npt2seconds(time) - parseInt(this.start_offset) );
1524 },
1525 updateThumbTime:function( float_sec ){
1526 //js_log('updateThumbTime:'+float_sec);
1527 var _this = this;
1528 if( typeof this.org_thum_src=='undefined' ){
1529 this.org_thum_src = this.media_element.getThumbnailURL();
1530 }
1531 if( this.org_thum_src.indexOf('t=') !== -1){
1532 this.last_thumb_url = getURLParamReplace(this.org_thum_src,
1533 { 't' : seconds2npt( float_sec + parseInt(this.start_offset)) } );
1534 if(!this.thumbnail_updating){
1535 this.updateThumbnail(this.last_thumb_url ,false);
1536 this.last_thumb_url =null;
1537 }
1538 }
1539 },
1540 //for now provide a src url .. but need to figure out how to copy frames from video for plug-in based thumbs
1541 updateThumbPerc:function( perc ){
1542 return this.updateThumbTime( (this.getDuration() * perc) );
1543 },
1544 //updates the thumbnail if the thumbnail is being displayed
1545 updateThumbnail : function(src, quick_switch){
1546 //make sure we don't go to the same url if we are not already updating:
1547 if( !this.thumbnail_updating && $j('#img_thumb_'+this.id).attr('src')== src )
1548 return false;
1549 //if we are already updating don't issue a new update:
1550 if( this.thumbnail_updating && $j('#new_img_thumb_'+this.id).attr('src')== src )
1551 return false;
1552
1553 js_log('update thumb: ' + src);
1554
1555 if(quick_switch){
1556 $j('#img_thumb_'+this.id).attr('src', src);
1557 }else{
1558 var _this = this;
1559 //if still animating remove new_img_thumb_
1560 if(this.thumbnail_updating==true)
1561 $j('#new_img_thumb_'+this.id).stop().remove();
1562
1563 if(this.thumbnail_disp){
1564 js_log('set to thumb:'+ src);
1565 this.thumbnail_updating=true;
1566 $j('#dc_'+this.id).append('<img src="'+src+'" ' +
1567 'style="display:none;position:absolute;zindex:2;top:0px;left:0px;" ' +
1568 'width="'+this.width+'" height="'+this.height+'" '+
1569 'id = "new_img_thumb_'+this.id+'" />');
1570 //js_log('appended: new_img_thumb_');
1571 $j('#new_img_thumb_'+this.id).fadeIn("slow", function(){
1572 //once faded in remove org and rename new:
1573 $j('#img_thumb_'+_this.id).remove();
1574 $j('#new_img_thumb_'+_this.id).attr('id', 'img_thumb_'+_this.id);
1575 $j('#img_thumb_'+_this.id).css('zindex','1');
1576 _this.thumbnail_updating=false;
1577 //js_log("done fadding in "+ $j('#img_thumb_'+_this.id).attr("src"));
1578
1579 //if we have a thumb queued update to that
1580 if(_this.last_thumb_url){
1581 var src_url =_this.last_thumb_url;
1582 _this.last_thumb_url=null;
1583 _this.updateThumbnail(src_url);
1584 }
1585 });
1586 }
1587 }
1588 },
1589 /** Returns the HTML code for the video when it is in thumbnail mode.
1590 This includes the specified thumbnail as well as buttons for
1591 playing, configuring the player, inline cmml display, HTML linkback,
1592 download, and embed code.
1593 */
1594 getThumbnailHTML : function ()
1595 {
1596 js_log('embedVideo:getThumbnailHTML::' + this.id);
1597 var thumb_html = '';
1598 var class_atr='';
1599 var style_atr='';
1600 //if(this.class)class_atr = ' class="'+this.class+'"';
1601 //if(this.style)style_atr = ' style="'+this.style+'"';
1602 // else style_atr = 'overflow:hidden;height:'+this.height+'px;width:'+this.width+'px;';
1603 this.thumbnail = this.media_element.getThumbnailURL();
1604
1605 //put it all in the div container dc_id
1606 thumb_html+= '<div id="dc_'+this.id+'" style="position:absolute;'+
1607 ' overflow:hidden; top:0px; left:0px; width:'+this.playerPixelWidth()+'px; height:'+this.playerPixelHeight()+'px; z-index:0;">'+
1608 '<img width="'+this.playerPixelWidth()+'" height="'+this.playerPixelHeight()+'" style="position:relative;width:'+this.playerPixelWidth()+';height:'+this.playerPixelHeight()+'"' +
1609 ' id="img_thumb_'+this.id+'" src="' + this.thumbnail + '">';
1610
1611 if(this.play_button == true && this.controls == true)
1612 thumb_html+= this.ctrlBuilder.getComponent( 'play-btn-large' );
1613
1614 thumb_html+='</div>';
1615 return thumb_html;
1616 },
1617 getEmbeddingHTML:function()
1618 {
1619 var thumbnail = this.media_element.getThumbnailURL();
1620
1621 var embed_thumb_html;
1622 if(thumbnail.substring(0,1)=='/'){
1623 eURL = parseUri(mv_embed_path);
1624 embed_thumb_html = eURL.protocol + '://' + eURL.host + thumbnail;
1625 //js_log('set from mv_embed_path:'+embed_thumb_html);
1626 }else{
1627 embed_thumb_html = (thumbnail.indexOf('http://')!=-1)?thumbnail:mv_embed_path + thumbnail;
1628 }
1629 var embed_code_html = '&lt;script type=&quot;text/javascript&quot; ' +
1630 'src=&quot;'+mv_embed_path+'mv_embed.js&quot;&gt;&lt;/script&gt' +
1631 '&lt;video ';
1632 if(this.roe){
1633 embed_code_html+='roe=&quot;'+this.roe+'&quot; &gt;';
1634 }else{
1635 embed_code_html+='src=&quot;'+this.src+'&quot; ' +
1636 'poster=&quot;'+embed_thumb_html+'&quot;&gt;';
1637 }
1638 //close the video tag
1639 embed_code_html+='&lt;/video&gt;';
1640
1641 return embed_code_html;
1642 },
1643 doOptionsHTML:function()
1644 {
1645 var sel_id = (this.pc!=null)?this.pc.pp.id:this.id;
1646 var pos = $j('#'+sel_id + ' .options-btn').offset();
1647 pos['top']=pos['top']+24;
1648 pos['left']=pos['left']-124;
1649 //js_log('pos of options button: t:'+pos['top']+' l:'+ pos['left']);
1650 $j('#mv_vid_options_'+sel_id).css(pos).toggle();
1651 return;
1652 },
1653 doLinkBack:function(){
1654 if(this.roe && this.media_element.addedROEData==false){
1655 var _this = this;
1656 this.displayHTML(gM('mwe-loading_txt'));
1657 do_request(this.roe, function(data)
1658 {
1659 _this.media_element.addROE(data);
1660 _this.doLinkBack();
1661 });
1662 }else{
1663 if(this.media_element.linkback){
1664 window.location = this.media_element.linkback;
1665 }else{
1666 this.displayHTML(gM('mwe-could_not_find_linkback'));
1667 }
1668 }
1669 },
1670 showShare:function($target){
1671 var embed_code = this.getEmbeddingHTML();
1672 var o = '';
1673 var _this = this;
1674 //@todo: hook events to two a's for swapping in and out code for link vs. embed;
1675 // hook events for changing active class of li based on a.
1676 o+= '<h2>' + gM('mwe-share_this_video') + '</h2>\n' +
1677 ' <ul>\n' +
1678 ' <li><a href="#" class="active">'+gM('mwe-embed_site_or_blog')+'</a></li>\n';
1679 if(this.linkback) {
1680 o+= ' <li><a href="#" id="k-share-link">' + this.linkback + '</a></li>\n';
1681 }
1682 o+= '</ul>' +
1683 '<div class="source_wrap"><textarea>' + embed_code + '</textarea></div>' +
1684 '<button class="ui-state-default ui-corner-all copycode">' + gM('mwe-copy-code') + '</button>' +
1685 '<div class="ui-state-highlight ui-corner-all">' + gM('mwe-read_before_embed') + '</div>' +
1686 '</div>'
1687 $target.html(o);
1688 $cpBtn = $j( '#' + this.id + ' .copycode');
1689 $cpTxt = $j( '#' + this.id + ' .source_wrap textarea');
1690
1691 $cpTxt.click(function(){
1692 $j(this).get(0).select();
1693 });
1694 //add copy binding:
1695 $cpBtn.click(function(){
1696 $cpTxt.focus().get(0).select();
1697 if(document.selection){
1698 CopiedTxt = document.selection.createRange();
1699 CopiedTxt.execCommand("Copy");
1700 }
1701 });
1702 },
1703 showTextInterface:function(){
1704 var _this = this;
1705 //display the text container with loading text:
1706 //@@todo support position config
1707 var loc = $j(this).position();
1708 if($j('#metaBox_'+this.id).length==0){
1709 $j(this).after('<div class="ui-widget ui-widget-content ui-corner-all" style="position:absolute;z-index:10;'+
1710 'top:' + (loc.top) + 'px;' +
1711 'left:' + (parseInt( loc.left ) + parseInt(this.width) + 10 )+'px;' +
1712 'height:'+ parseInt( this.height )+'px;width:400px;' +
1713 'display:none;" ' +
1714 'id="metaBox_' + this.id + '">'+
1715 gM('mwe-loading_txt') +
1716 '</div>');
1717 }
1718 //fade in the text display
1719 $j('#metaBox_'+this.id).fadeIn("fast");
1720 //check if textObj present:
1721 if(typeof this.textInterface == 'undefined' ){
1722 //load the default text interface:
1723 mvJsLoader.doLoad([
1724 'mvTextInterface',
1725 '$j.fn.hoverIntent'
1726 ], function(){
1727 _this.textInterface = new mvTextInterface( _this );
1728 //show interface
1729 _this.textInterface.show();
1730 js_log("NEW TEXT INTERFACE");
1731 for(var i in _this.textInterface.availableTracks){
1732 js_log("tracks in new interface: "+_this.id+ ' tid:' + i);
1733 }
1734 }
1735 );
1736 }else{
1737 //show interface
1738 this.textInterface.show();
1739 }
1740 },
1741 closeTextInterface:function(){
1742 js_log('closeTextInterface '+ typeof this.textInterface);
1743 if(typeof this.textInterface !== 'undefined' ){
1744 this.textInterface.close();
1745 }
1746 },
1747 /** Generic function to display custom HTML inside the mv_embed element.
1748 The code should call the closeDisplayedHTML function to close the
1749 display of the custom HTML and restore the regular mv_embed display.
1750 @param {String} HTML code for the selection list.
1751 */
1752 displayHTML:function(html_code)
1753 {
1754 var sel_id = (this.pc!=null)?this.pc.pp.id:this.id;
1755
1756 if(!this.supports['overlays'])
1757 this.stop();
1758
1759 //put select list on-top
1760 //make sure the parent is relatively positioned:
1761 $j('#'+sel_id).css('position', 'relative');
1762 //set height width (check for playlist container)
1763 var width = (this.pc)?this.pc.pp.width:this.playerPixelWidth();
1764 var height = (this.pc)?this.pc.pp.height:this.playerPixelHeight();
1765
1766 if(this.pc)
1767 height+=(this.pc.pp.pl_layout.title_bar_height + this.pc.pp.pl_layout.control_height);
1768
1769 var fade_in = true;
1770 if($j('#blackbg_'+sel_id).length!=0)
1771 {
1772 fade_in = false;
1773 $j('#blackbg_'+sel_id).remove();
1774 }
1775 //fade in a black bg div ontop of everything
1776 var div_code = '<div id="blackbg_'+sel_id+'" class="videoComplete" ' +
1777 'style="height:'+parseInt(height)+'px;width:'+parseInt(width)+'px;">'+
1778 '<div class="videoOptionsComplete">'+
1779 //@@TODO: this style should go to .css
1780 '<span style="float:right;margin-right:10px">' +
1781 '<a href="#" style="color:white;" onClick="$j(\'#'+sel_id+'\').get(0).closeDisplayedHTML();return false;">close</a>' +
1782 '</span>'+
1783 '<div id="mv_disp_inner_'+sel_id+'" style="padding-top:10px;">'+
1784 html_code
1785 +'</div>'+
1786 '</div></div>';
1787 $j('#'+sel_id).prepend(div_code);
1788 if (fade_in)
1789 $j('#blackbg_'+sel_id).fadeIn("slow");
1790 else
1791 $j('#blackbg_'+sel_id).show();
1792 return false; //onclick action return false
1793 },
1794 /** Close the custom HTML displayed using displayHTML and restores the
1795 regular mv_embed display.
1796 */
1797 closeDisplayedHTML:function(){
1798 var sel_id = (this.pc!=null)?this.pc.pp.id:this.id;
1799 $j('#blackbg_'+sel_id).fadeOut("slow", function(){
1800 $j('#blackbg_'+sel_id).remove();
1801 });
1802 return false; //onclick action return false
1803 },
1804 showPlayerselect:function( $target ){
1805 //get id (in case where we have a parent container)
1806 var this_id = (this.pc!=null)?this.pc.pp.id:this.id;
1807 var _this=this;
1808 var o= '';
1809 o+='<h2>' + gM('mwe-chose_player') + '</h2>';
1810 var _this=this;
1811 //js_log('selected src'+ _this.media_element.selected_source.url);
1812 $j.each( this.media_element.getPlayableSources(), function(source_id, source){
1813 var default_player = embedTypes.players.defaultPlayer( source.getMIMEType() );
1814
1815 var is_selected = (source == _this.media_element.selected_source);
1816 var image_src = mv_skin_img_path ;
1817
1818 if (default_player){
1819 o+='<ul>';
1820 //output the player select code:
1821 var supporting_players = embedTypes.players.getMIMETypePlayers( source.getMIMEType() );
1822
1823 for(var i=0; i < supporting_players.length ; i++){
1824 if( _this.selected_player.id == supporting_players[i].id && is_selected ){
1825 o+='<li>' +
1826 '<a href="#" class="active" rel="sel_source" id="sc_' + source_id + '_' + supporting_players[i].id +'">' +
1827 supporting_players[i].getName() +
1828 '</li>';
1829 }else{
1830 o+='<li>' +
1831 '<a href="#" rel="sel_source" id="sc_' + source_id + '_' + supporting_players[i].id +'">' +
1832 supporting_players[i].getName() + '</a>' +
1833 '</li>';
1834 }
1835 }
1836 o+='</ul>';
1837 }else{
1838 o+= source.getTitle() + ' - no player available';
1839 }
1840 });
1841 $target.html(o);
1842
1843 //set up the click bindings:
1844 $target.find("[rel='sel_source']").each(function(){
1845 $j(this).click(function(){
1846 var iparts = $j(this).attr( 'id' ).replace(/sc_/,'').split('_');
1847 var source_id = iparts[0];
1848 var default_player_id = iparts[1];
1849 js_log('source id: ' + source_id + ' player id: ' + default_player_id);
1850
1851 $j('#' + this_id ).get(0).closeDisplayedHTML();
1852 $j('#' + _this.id ).get(0).media_element.selectSource( source_id );
1853
1854 embedTypes.players.userSelectPlayer( default_player_id,
1855 _this.media_element.sources[ source_id ].getMIMEType() );
1856
1857 //be sure to issue a stop
1858 $j('#' + this_id ).get(0).stop();
1859
1860 //don't follow the empty # link:
1861 return false;
1862 });
1863 });
1864 },
1865 showDownload:function( $target ){
1866 var _this = this;
1867 //load the roe if available (to populate out download options:
1868 function getShowVideoDownload(){
1869 var out='<div style="color:white">' +
1870 '<b style="color:white;">'+gM('mwe-download_segment')+'</b><br>';
1871 out+='<blockquote style="background:#000">'+
1872 gM('mwe-download_right_click') + '</blockquote><br>';
1873 var dl_list='';
1874 var dl_txt_list='';
1875 $j.each(_this.media_element.getSources(), function(index, source){
1876 var dl_line = '<li>' + '<a style="color:white" href="' + source.getURI() +'"> '
1877 + source.getTitle()+'</a> '+ '</li>'+"\n";
1878 if( source.getURI().indexOf('?t=')!==-1){
1879 out+=dl_line;
1880 }else if( this.getMIMEType()=="text/cmml" || this.getMIMEType()=="text/x-srt" ){
1881 dl_txt_list+=dl_line;
1882 }else{
1883 dl_list+=dl_line;
1884 }
1885 });
1886
1887 if(dl_list!='')
1888 out+=gM('mwe-download_full') + '<blockquote style="background:#000">' + dl_list + '</blockquote>';
1889 if(dl_txt_list!='')
1890 out+=gM('mwe-download_text') + '<blockquote style="background:#000">' + dl_txt_list +'</blockquote>';
1891 out+='</div>';
1892 return out;
1893 }
1894 //js_log('f:showDownload '+ this.roe + ' ' + this.media_element.addedROEData);
1895 if(this.roe && this.media_element.addedROEData == false){
1896 var _this = this;
1897 $target.html( gM('loading_txt') );
1898 do_request(this.roe, function(data)
1899 {
1900 _this.media_element.addROE(data);
1901 $target.html( getShowVideoDownload() );
1902 });
1903 }else{
1904 $target.html( getShowVideoDownload() );
1905 }
1906 },
1907 showCredits:function( $target ){
1908 $target.html('<h2>' + gM('mwe-credits') + '</h2>');
1909 },
1910 /*
1911 * base embed controls
1912 * the play button calls
1913 */
1914 play:function(){
1915 var eid = (this.pc!=null)?this.pc.pp.id:this.id;
1916
1917 //js_log( "mv_embed play:" + this.id);
1918 //js_log('thum disp:'+this.thumbnail_disp);
1919 //check if thumbnail is being displayed and embed html
1920 if( this.thumbnail_disp ){
1921 if( !this.selected_player ){
1922 js_log('no selected_player');
1923 //this.innerHTML = this.getPluginMissingHTML();
1924 //$j(this).html(this.getPluginMissingHTML());
1925 $j('#'+this.id).html( this.getPluginMissingHTML() );
1926 }else{
1927 this.doEmbedHTML();
1928 this.onClipDone_disp=false;
1929 this.paused=false;
1930 this.thumbnail_disp=false;
1931 }
1932 }else{
1933 //the plugin is already being displayed
1934 this.paused=false; //make sure we are not "paused"
1935 this.seeking=false;
1936 }
1937
1938 $j('#' + eid + ' .play-btn span').removeClass('ui-icon-play').addClass('ui-icon-pause');
1939 $j('#' + eid + ' .play-btn').unbind().btnBind().click(function(){
1940 $j('#' + eid ).get(0).pause();
1941 }).attr('title', gM('mwe-pause_clip'));
1942
1943 },
1944 load:function(){
1945 //should be done by child (no base way to pre-buffer video)
1946 js_log('baseEmbed:load call');
1947 },
1948 getSrc:function(){
1949 return this.media_element.selected_source.getURI( this.seek_time_sec );
1950 },
1951 /*
1952 * base embed pause
1953 * there is no general way to pause the video
1954 * must be overwritten by embed object to support this functionality.
1955 */
1956 pause: function(){
1957 var eid = (this.pc!=null)?this.pc.pp.id:this.id;
1958 //js_log('mv_embed:do pause');
1959 //(playing) do pause
1960 this.paused = true;
1961 //update the ctrl "paused state"
1962 $j('#' + eid + ' .play-btn span').removeClass('ui-icon-pause').addClass('ui-icon-play');
1963 $j('#' + eid + ' .play-btn').unbind().btnBind().click(function(){
1964 $j('#'+eid).get(0).play();
1965 }).attr('title', gM('mwe-play_clip'));
1966 },
1967 /*
1968 * base embed stop (can be overwritten by the plugin)
1969 */
1970 stop: function(){
1971 var _this = this;
1972 js_log('mvEmbed:stop:'+this.id);
1973
1974 //no longer seeking:
1975 this.didSeekJump=false;
1976
1977 //first issue pause to update interface (only call the parent)
1978 if(this['parent_pause']){
1979 this.parent_pause();
1980 }else{
1981 this.pause();
1982 }
1983
1984 //reset the currentTime:
1985 this.currentTime=0;
1986 //check if thumbnail is being displayed in which case do nothing
1987 if( this.thumbnail_disp ){
1988 //already in stooped state
1989 js_log('already in stopped state');
1990 }else{
1991 //rewrite the html to thumbnail disp
1992 this.doThumbnailHTML();
1993 this.bufferedPercent=0; //reset buffer state
1994 this.setSliderValue(0);
1995 this.setStatus( this.getTimeReq() );
1996 }
1997
1998 //make sure the big playbutton is has click action:
1999 $j('#' + _this.id + ' .play-btn-large').unbind('click').click(function(){
2000 $j('#' +_this.id).get(0).play();
2001 });
2002
2003 if(this.update_interval)
2004 {
2005 clearInterval(this.update_interval);
2006 this.update_interval = null;
2007 }
2008 },
2009 toggleMute:function(){
2010 var eid = (this.pc!=null)?this.pc.pp.id:this.id;
2011 if( this.muted ){
2012 this.muted=false;
2013 $j('#' + eid + ' .volume-slider').slider('value', 100);
2014 this.updateVolumen(1);
2015 }else{
2016 this.muted=true;
2017 $j('#' + eid + ' .volume-slider').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 };