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