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