1 /* the base video control JSON object with default attributes
2 * for supported attribute details see README
6 "mwe-loading_plugin" : "loading plugin <blink>...<\/blink>",
7 "mwe-select_playback" : "Set playback preference",
8 "mwe-link_back" : "Link back",
9 "mwe-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 "mwe-error_swap_vid" : "Error: mv_embed was unable to swap the video tag for the mv_embed interface",
11 "mwe-add_to_end_of_sequence" : "Add to end of sequence",
12 "mwe-missing_video_stream" : "The video file for this stream is missing",
13 "mwe-play_clip" : "Play clip",
14 "mwe-pause_clip" : "Pause clip",
15 "mwe-volume_control" : "Volume control",
16 "mwe-player_options" : "Player options",
17 "mwe-closed_captions" : "Close captions",
18 "mwe-player_fullscreen" : "Fullscreen",
19 "mwe-next_clip_msg" : "Play next clip",
20 "mwe-prev_clip_msg" : "Play previous clip",
21 "mwe-current_clip_msg" : "Continue playing this clip",
22 "mwe-seek_to" : "Seek to",
23 "mwe-download_segment" : "Download selection:",
24 "mwe-download_full" : "Download full video file:",
25 "mwe-download_right_click" : "To download, right click and select <i>Save target as...<\/i>",
26 "mwe-download_clip" : "Download video",
27 "mwe-download_text" : "Download text (<a style=\"color:white\" title=\"cmml\" href=\"http:\/\/wiki.xiph.org\/index.php\/CMML\">CMML<\/a> xml):",
28 "mwe-download" : "Download",
29 "mwe-share" : "Share",
30 "mwe-credits" : "Credits",
31 "mwe-clip_linkback" : "Clip source page",
32 "mwe-chose_player" : "Choose Video Player",
33 "mwe-share_this_video" : "Share this video",
34 "mwe-video_credits" : "Video credits",
35 "mwe-menu_btn" : "Menu",
36 "mwe-close_btn" : "Close",
37 "mwe-ogg-player-vlc-mozilla" : "VLC plugin",
38 "mwe-ogg-player-videoElement" : "Native Ogg video support",
39 "mwe-ogg-player-vlc-activex" : "VLC ActiveX",
40 "mwe-ogg-player-oggPlugin" : "Generic Ogg plugin",
41 "mwe-ogg-player-quicktime-mozilla" : "Quicktime plugin",
42 "mwe-ogg-player-quicktime-activex" : "Quicktime ActiveX",
43 "mwe-ogg-player-cortado" : "Java Cortado",
44 "mwe-ogg-player-flowplayer" : "Flowplayer",
45 "mwe-ogg-player-selected" : "(selected)",
46 "mwe-ogg-player-omtkplayer" : "OMTK Flash Vorbis",
47 "mwe-generic_missing_plugin" : "You browser does not appear to support the following playback type: <b>$1<\/b><br \/>Visit the <a href=\"http:\/\/commons.wikimedia.org\/wiki\/Commons:Media_help\">Playback Methods<\/a> page to download a player.<br \/>",
48 "mwe-for_best_experience" : "For a better video playback experience we recommend:<br \/><b><a href=\"http:\/\/www.mozilla.com\/en-US\/firefox\/upgrade.html?from=mwEmbed\">Firefox 3.5<\/a>.<\/b>",
49 "mwe-do_not_warn_again" : "Dismiss for now.",
50 "mwe-playerselect" : "Players",
51 "mwe-read_before_embed" : "Please <a href=\"http:\/\/mediawiki.org\/wiki\/Security_Notes_on_Remote_Embedding\" target=\"_new\">Read This<\/a> before embeding!",
52 "mwe-embed_site_or_blog" : "Embed on your site or blog",
53 "mwe_related_videos" : "Related Videos"
57 var commons_api_url = 'http://commons.wikimedia.org/w/api.php';
59 var default_video_attributes = {
77 //roe url (for xml based metadata)
79 //if roe includes metadata tracks we can expose a link to metadata
80 "show_meta_link":true,
82 //default state attributes per html5 spec:
83 //http://www.whatwg.org/specs/web-apps/current-work/#video)
85 "readyState":0, //http://www.whatwg.org/specs/web-apps/current-work/#readystate
86 "currentTime":0, //current playback position (should be updated by plugin)
87 "duration":null, //media duration (read from file or the temporal url)
90 "startOffset":null, //if serving an ogg_chop segment use this to offset the presentation time
92 //custom attributes for mv_embed:
98 "type":null //the content type of the media
101 * the base source attibute checks
103 var mv_default_source_attr= new Array(
107 'URLTimeEncoding', //boolean if we support temporal url requests on the source media
115 //set the dismissNativeWarn flag:
116 _global['dismissNativeWarn'] = false;
118 * Converts all occurrences of <video> tag into video object
120 function mv_video_embed(swap_done_callback, force_id){
121 mvEmbed.init( swap_done_callback, force_id );
124 //flist stores the set of functions to run after the video has been swaped in.
126 init:function( swap_done_callback, force_id ){
128 if(swap_done_callback)
129 mvEmbed.flist.push( swap_done_callback );
131 //get mv_embed location if it has not been set
132 js_log('mv_embed ' + MV_EMBED_VERSION);
134 var loadPlaylistLib=false;
136 var eAction = function(this_elm){
137 js_log( "Do SWAP: " + $j(this_elm).attr("id") + ' tag: '+ this_elm.tagName.toLowerCase() );
139 if( $j(this_elm).attr("id") == '' ){
140 $j(this_elm).attr("id", 'v'+ global_player_list.length);
142 //stre a global reference to the id
143 global_player_list.push( $j(this_elm).attr("id") );
145 switch( this_elm.tagName.toLowerCase()){
147 var videoInterface = new embedVideo(this_elm);
148 mvEmbed.swapEmbedVideoElement( this_elm, videoInterface );
151 var videoInterface = new embedVideo(this_elm);
152 videoInterface.type ='audio';
153 mvEmbed.swapEmbedVideoElement( this_elm, videoInterface );
156 loadPlaylistLib=true;
161 if( force_id == null && force_id != '' ){
162 var j_selector = 'video,audio,playlist';
164 var j_selector = '#' + force_id;
166 //process selected elements:
167 //ie8 does not play well with the jQuery video,audio,playlist selector use native:
168 if($j.browser.msie && $j.browser.version >= 8){
169 jtags = j_selector.split(',');
170 for( var i=0; i < jtags.length; i++){
171 $j( document.getElementsByTagName( jtags[i] )).each(function(){
176 $j( j_selector ).each(function(){
183 '$j.ui', //include dialog for pop-ing up thigns
186 $j('playlist').each(function(){
187 //create new playlist interface:
188 var plObj = new mvPlayList( this );
189 mvEmbed.swapEmbedVideoElement(this, plObj);
190 var added_height = plObj.pl_layout.title_bar_height + plObj.pl_layout.control_height;
191 //move into a blocking display container with height + controls + title height:
192 $j('#'+plObj.id).wrap('<div style="display:block;height:' + (plObj.height + added_height) + 'px;"></div>');
196 this.checkClipsReady();
199 * swapEmbedVideoElement
200 * takes a video element as input and swaps it out with
201 * an embed video interface based on the video_elements attributes
203 swapEmbedVideoElement:function(video_element, videoInterface){
204 js_log('do swap ' + videoInterface.id + ' for ' + video_element);
205 embed_video = document.createElement('div');
206 //make sure our div has a hight/width set:
208 $j(embed_video).css({
209 'width':videoInterface.width,
210 'height':videoInterface.height
211 }).html( mv_get_loading_img() );
212 //inherit the video interface
213 for(var method in videoInterface){ //for in loop oky in Element context
214 if(method!='readyState'){ //readyState crashes IE
216 embed_video.setAttribute('style', videoInterface[method]);
217 }else if(method=='class'){
218 if( $j.browser.msie )
219 embed_video.setAttribute("className", videoInterface['class']);
221 embed_video.setAttribute("class", videoInterface['class']);
224 embed_video[method]=videoInterface[method];
228 if(embed_video[method]=="false")embed_video[method]=false;
229 if(embed_video[method]=="true")embed_video[method]=true;
231 ///js_log('did vI style');
232 //now swap out the video element for the embed_video obj:
233 $j(video_element).after(embed_video).remove();
234 //js_log('did swap');
235 $j('#'+embed_video.id).get(0).on_dom_swap();
237 // now that "embed_video" is stable, do more initialization (if we are ready)
238 if($j('#'+embed_video.id).get(0).loading_external_data == false &&
239 $j('#'+embed_video.id).get(0).init_with_sources_loadedDone == false){
240 //load and set ready state since source are available:
241 $j('#'+embed_video.id).get(0).init_with_sources_loaded();
244 js_log('done with child: ' + embed_video.id + ' len:' + global_player_list.length);
247 //this should not be needed.
248 checkClipsReady : function(){
249 //js_log('checkClipsReady');
251 for(var i=0; i < global_player_list.length; i++){
252 if( $j('#'+global_player_list[i]).length !=0){
253 var cur_vid = $j('#'+global_player_list[i]).get(0);
254 is_ready = ( cur_vid.ready_to_play ) ? is_ready : false;
255 if( !is_ready && cur_vid.load_error ){
257 $j(cur_vid).html( cur_vid.load_error );
262 mvEmbed.allClipsReady = true;
263 // run queued functions
264 //js_log('run queded functions:' + mvEmbed.flist[0]);
267 setTimeout( 'mvEmbed.checkClipsReady()', 25 );
271 while (this.flist.length){
272 this.flist.shift()();
278 * mediaSource class represents a source for a media element.
279 * @param {String} type MIME type of the source.
280 * @param {String} uri URI of the source.
283 function mediaSource(element)
289 mediaSource.prototype =
291 /** MIME type of the source. */
293 /** URI of the source. */
295 /** Title of the source. */
297 /** True if the source has been marked as the default. */
298 marked_default:false,
299 /** True if the source supports url specification of offset and duration */
300 URLTimeEncoding:false,
301 /** Start offset of the requested segment */
303 /** Duration of the requested segment (0 if not known) */
306 upddate_interval:null,
312 init : function(element)
314 //js_log('adding mediaSource: ' + element);
315 this.src = $j(element).attr('src');
316 this.marked_default = false;
317 if ( element.tagName.toLowerCase() == 'video')
318 this.marked_default = true;
320 //set default URLTimeEncoding if we have a time url:
321 //not ideal way to discover if content is on an oggz_chop server.
322 //should check some other way.
323 var pUrl = parseUri ( this.src );
324 if(typeof pUrl['queryKey']['t'] != 'undefined'){
325 this['URLTimeEncoding']=true;
327 for(var i=0; i < mv_default_source_attr.length; i++){ //array loop:
328 var attr = mv_default_source_attr[ i ];
329 if( $j(element).attr( attr ) ) {
330 this[ attr ] = $j(element).attr( attr );
333 //update duration from hit if present:
334 if(this.durationHint)
335 this.duration = this.durationHint;
338 if ( $j(element).attr('type'))
339 this.mime_type = $j(element).attr('type');
340 else if ($j(element).attr('content-type'))
341 this.mime_type = $j(element).attr('content-type');
343 this.mime_type = this.detectType(this.src);
345 //set the title if unset:
347 this.title = this.mime_type;
349 this.parseURLDuration();
351 updateSource:function(element){
352 //for now just update the title:
353 if ($j(element).attr("title"))
354 this.title = $j(element).attr("title");
356 /** updates the src time and start & end
357 * @param {String} start_time in NTP format
358 * @param {String} end_time in NTP format
360 updateSrcTime:function (start_ntp, end_ntp){
361 //js_log("f:updateSrcTime: "+ start_ntp+'/'+ end_ntp + ' from org: ' + this.start_ntp+ '/'+this.end_ntp);
362 //js_log("pre uri:" + this.src);
363 //if we have time we can use:
364 if( this.URLTimeEncoding ){
365 //make sure its a valid start time / end time (else set default)
366 if( !npt2seconds(start_ntp) )
367 start_ntp = this.start_ntp;
369 if( !npt2seconds(end_ntp) )
370 end_ntp = this.end_ntp;
372 this.src = getURLParamReplace(this.src, { 't': start_ntp +'/'+ end_ntp } );
374 //update the duration
375 this.parseURLDuration();
378 setDuration:function (duration)
380 this.duration = duration;
382 this.end_ntp = seconds2npt( this.start_offset + duration);
385 /** MIME type accessor function.
386 @return the MIME type of the source.
389 getMIMEType : function()
391 return this.mime_type;
393 /** URI accessor function.
394 * @param int seek_time_sec (used to adjust the URI for url based seeks)
395 @return the URI of the source.
398 getURI : function( seek_time_sec )
400 if( !seek_time_sec || !this.URLTimeEncoding ){
406 var endvar = '/'+ this.end_ntp;
408 return getURLParamReplace(this.src, { 't': seconds2npt( seek_time_sec )+endvar } ); ;
410 /** Title accessor function.
411 @return the title of the source.
414 getTitle : function()
418 /** Index accessor function.
419 @return the source's index within the enclosing mediaElement container.
422 getIndex : function()
427 * function getDuration in milliseconds
428 * special case derive duration from request url
429 * supports media_url?t=ntp_start/ntp_end url request format
431 parseURLDuration : function(){
432 //check if we have a URLTimeEncoding:
433 if( this.URLTimeEncoding ){
434 var annoURL = parseUri( this.src );
435 if( annoURL.queryKey['t'] ){
436 var times = annoURL.queryKey['t'].split('/');
437 this.start_ntp = times[0];
438 this.end_ntp = times[1];
439 this.start_offset = npt2seconds( this.start_ntp );
440 this.duration = npt2seconds( this.end_ntp ) - this.start_offset;
442 //look for this info as attributes
443 if(this.startOffset){
444 this.start_offset = this.startOffset;
445 this.start_ntp = seconds2npt( this.startOffset);
448 this.end_ntp = seconds2npt( parseInt(this.duration) + parseInt(this.start_offset) );
452 //else nothing to parse just keep whatever info we already have
454 //js_log('f:parseURLDuration() for:' + this.src + ' d:' + this.duration);
456 /** Attempts to detect the type of a media file based on the URI.
457 @param {String} uri URI of the media file.
458 @returns The guessed MIME type of the file.
461 detectType:function(uri)
463 //@@todo if media is on the same server as the javascript or we have mv_proxy configured
464 //we can issue a HEAD request and read the mime type of the media...
465 // (this will detect media mime type independently of the url name)
466 //http://www.jibbering.com/2002/4/httprequest.html (this should be done by extending jquery's ajax objects)
467 var end_inx = (uri.indexOf('?')!=-1)? uri.indexOf('?') : uri.length;
468 var no_param_uri = uri.substr(0, end_inx);
469 switch( no_param_uri.substr(no_param_uri.lastIndexOf('.'),4).toLowerCase() ){
470 case '.flv':return 'video/x-flv';break;
471 case '.ogg': case '.ogv': return 'video/ogg';break;
472 case '.oga': return 'audio/ogg'; break;
473 case '.anx':return 'video/ogg';break;
478 /** A media element corresponding to a <video> element.
479 It is implemented as a collection of mediaSource objects. The media sources
480 will be initialized from the <video> element, its child <source> elements,
481 and/or the ROE file referenced by the <video> element.
482 @param {element} video_element <video> element used for initialization.
485 function mediaElement(video_element)
487 this.init(video_element);
490 mediaElement.prototype =
492 /** The array of mediaSource elements. */
495 /** Selected mediaSource element. */
496 selected_source:null,
501 init:function( video_element )
504 js_log('Initializing mediaElement...' );
505 this.sources = new Array();
506 this.thumbnail = mv_default_thumb_url;
507 // Process the source element:
508 if($j(video_element).attr("src"))
509 this.tryAddSource(video_element);
511 if($j(video_element).attr('thumbnail'))
512 this.thumbnail = $j(video_element).attr('thumbnail');
514 if($j(video_element).attr('poster'))
515 this.thumbnail = $j(video_element).attr('poster');
517 if($j(video_element).attr('wikiTitleKey'))
518 this.wikiTitleKey=$j(video_element).attr('wikiTitleKey');
520 // Process all inner <source> elements
521 //js_log("inner source count: " + video_element.getElementsByTagName('source').length );
523 $j(video_element).find('source,text').each(function(inx, inner_source){
524 _this.tryAddSource( inner_source );
527 /** Updates the time request for all sources that have a standard time request argument (ie &t=start_time/end_time)
529 updateSourceTimes:function(start_ntp, end_ntp){
531 $j.each(this.sources, function(inx, mediaSource){
532 mediaSource.updateSrcTime(start_ntp, end_ntp);
536 timedTextSources:function(){
537 for(var i=0; i < this.sources.length; i++){
538 if( this.sources[i].mime_type == 'text/cmml' ||
539 this.sources[i].mime_type == 'text/x-srt')
544 /** Returns the array of mediaSources of this element.
545 \returns {Array} Array of mediaSource elements.
547 getSources:function( mime_filter )
552 var source_set = new Array();
553 for(var i=0; i < this.sources.length ; i++){
554 if( this.sources[i].mime_type.indexOf( mime_filter ) != -1 )
555 source_set.push( this.sources[i] );
559 getSourceById:function( source_id ){
560 for(var i=0; i < this.sources.length ; i++){
561 if( this.sources[i].id == source_id)
562 return this.sources[i];
566 /** Selects a particular source for playback.
568 selectSource:function(index)
570 js_log('f:selectSource:'+index);
571 var playable_sources = this.getPlayableSources();
572 for(var i=0; i < playable_sources.length; i++){
574 this.selected_source = playable_sources[i];
575 //update the user selected format:
576 embedTypes.players.userSelectFormat( playable_sources[i].mime_type );
581 /** selects the default source via cookie preference, default marked, or by id order
583 autoSelectSource:function(){
584 js_log('f:autoSelectSource:');
585 //@@todo read user preference for source
586 // Select the default source
587 var playable_sources = this.getPlayableSources();
588 var flash_flag=ogg_flag=false;
590 for(var source=0; source < playable_sources.length; source++){
591 var mime_type =playable_sources[source].mime_type;
592 if( playable_sources[source].marked_default ){
593 js_log('set via marked default: ' + playable_sources[source].marked_default);
594 this.selected_source = playable_sources[source];
597 //set via user-preference
598 if(embedTypes.players.preference['format_prefrence'] == mime_type){
599 js_log('set via preference: '+playable_sources[source].mime_type);
600 this.selected_source = playable_sources[source];
604 //set Ogg via player support
605 for(var source=0; source < playable_sources.length; source++){
606 js_log('f:autoSelectSource:' + playable_sources[source].mime_type);
607 var mime_type =playable_sources[source].mime_type;
608 //set source via player
609 if(mime_type=='video/ogg' || mime_type=='ogg/video' || mime_type=='video/annodex' || mime_type=='application/ogg'){
610 for(var i=0; i < embedTypes.players.players.length; i++){ //for in loop on object oky
611 var player = embedTypes.players.players[i];
612 if(player.library=='vlc' || player.library=='native'){
613 js_log('set via ogg via order');
614 this.selected_source = playable_sources[source];
621 for(var source=0; source < playable_sources.length; source++){
622 var mime_type =playable_sources[source].mime_type;
623 if( mime_type=='video/x-flv' ){
624 js_log('set via by player preference normal flash')
625 this.selected_source = playable_sources[source];
630 for(var source=0; source < playable_sources.length; source++){
631 var mime_type =playable_sources[source].mime_type;
632 if( mime_type=='video/h264' ){
633 js_log('set via playable_sources preference h264 flash')
634 this.selected_source = playable_sources[source];
638 //select first source
639 if (!this.selected_source)
641 js_log('set via first source:' + playable_sources[0]);
642 this.selected_source = playable_sources[0];
646 /** Returns the thumbnail URL for the media element.
647 \returns {String} thumbnail URL
649 getThumbnailURL:function()
651 return this.thumbnail;
653 /** Checks whether there is a stream of a specified MIME type.
654 @param {String} mime_type MIME type to check.
655 @type {BooleanPrimitive}.
657 hasStreamOfMIMEType:function(mime_type)
659 for(source in this.sources)
661 if(this.sources[source].getMIMEType() == mime_type)
666 isPlayableType:function(mime_type)
668 if( embedTypes.players.defaultPlayer( mime_type ) ){
673 //if(this.selected_player){
674 //return mime_type=='video/ogg' || mime_type=='ogg/video' || mime_type=='video/annodex' || mime_type=='video/x-flv';
676 /** Adds a single mediaSource using the provided element if
677 the element has a 'src' attribute.
678 @param element {element} <video>, <source> or <mediaSource> element.
680 tryAddSource:function(element)
682 js_log('f:tryAddSource:'+ $j(element).attr("src"));
683 if (! $j(element).attr("src")){
684 //js_log("element has no src");
687 var new_src = $j(element).attr('src');
688 //make sure an existing element with the same src does not already exist:
689 for( var i=0; i < this.sources.length; i++ ){
690 if(this.sources[i].src == new_src){
691 //js_log('checking existing: '+this.sources[i].getURI() + ' != '+ new_src);
692 //can't add it all but try to update any additional attr:
693 this.sources[i].updateSource(element);
697 var source = new mediaSource( element );
698 this.sources.push(source);
699 //alert('pushed source to stack'+ source + 'sl:'+this.sources.length);
701 getPlayableSources: function(){
702 var playable_sources= new Array();
703 for(var i=0; i < this.sources.length; i++){
704 if( this.isPlayableType( this.sources[i].mime_type ) ){
705 playable_sources.push( this.sources[i] );
707 js_log("type "+ this.sources[i].mime_type + 'is not playable');
710 return playable_sources;
712 /* Imports media sources from ROE data.
713 * @param roe_data ROE data.
715 addROE:function(roe_data){
717 this.addedROEData=true;
719 if( typeof roe_data == 'string' )
721 var parser=new DOMParser();
722 js_log('ROE data:' + roe_data);
723 roe_data=parser.parseFromString(roe_data,"text/xml");
726 $j.each(roe_data.getElementsByTagName('mediaSource'), function(inx, source){
727 _this.tryAddSource(source);
730 $j.each(roe_data.getElementsByTagName('img'), function(inx, n){
731 if($j(n).attr("id")=="stream_thumb"){
732 js_log('roe:set thumb to '+$j(n).attr("src"));
733 _this['thumbnail'] =$j(n).attr("src");
737 $j.each(roe_data.getElementsByTagName('link'), function(inx, n){
738 if($j(n).attr('id')=='html_linkback'){
739 js_log('roe:set linkback to '+$j(n).attr("href"));
740 _this['linkback'] = $j(n).attr('href');
744 js_log('ROE data empty.');
750 /** base embedVideo object
751 @param element <video> tag used for initialization.
754 var embedVideo = function(element) {
755 return this.init(element);
758 embedVideo.prototype = {
759 /** The mediaElement object containing all mediaSource objects */
762 ready_to_play:false, //should use html5 ready state
763 load_error:false, //used to set error in case of error
764 loading_external_data:false,
765 thumbnail_updating:false,
767 init_with_sources_loadedDone:false,
769 //for onClip done stuff:
770 anno_data_cache:null,
772 base_seeker_slider_offset:null,
773 onClipDone_disp:false,
775 //for seek thumb updates:
776 cur_thumb_seek_time:0,
777 thumb_seek_interval:null,
780 //set the buffered percent:
782 //utility functions for property values:
783 hx : function ( s ) {
784 if ( typeof s != 'String' ) {
787 return s.replace( /&/g, '&' )
788 . replace( /</g, '<' )
789 . replace( />/g, '>' );
791 hq : function ( s ) {
792 return '"' + this.hx( s ) + '"';
794 playerPixelWidth : function()
796 var player = $j('#mv_embedded_player_'+this.id).get(0);
797 if(typeof player!='undefined' && player['offsetWidth'])
798 return player.offsetWidth;
800 return parseInt(this.width);
802 playerPixelHeight : function()
804 var player = $j('#mv_embedded_player_'+this.id).get(0);
805 if(typeof player!='undefined' && player['offsetHeight'])
806 return player.offsetHeight;
808 return parseInt(this.height);
810 init: function(element){
811 //this.element_pointer = element;
813 //inherit all the default video_attributes
814 for(var attr in default_video_attributes){ //for in loop oky on user object
815 if(element.getAttribute(attr)){
816 this[attr]=element.getAttribute(attr);
817 //js_log('attr:' + attr + ' val: ' + element.getAttribute(attr) +'(set by elm)');
819 this[attr]=default_video_attributes[attr];
820 //js_log('attr:' + attr + ' val: ' + video_attributes[attr] +" "+ 'elm_val:' + element.getAttribute(attr) + "\n (set by attr)");
823 //make sure startOffset is cast as an int
824 if( this.startOffset && this.startOffset.split(':').length >= 2)
825 this.startOffset = npt2seconds(this.startOffset);
826 //make sure offset is in float:
827 this.startOffset = parseFloat(this.startOffset);
829 if( this.duration && this.duration.split(':').length >= 2)
830 this.duration = npt2seconds( this.duration );
831 //make sure duration is in float:
832 this.duration = parseFloat(this.duration);
833 js_log("duration is: " + this.duration);
834 //if style is set override width and height
835 var dwh = mwConfig['video_size'].split('x');
836 this.width = element.style.width ? element.style.width : dwh[0];
837 this.height = element.style.height ? element.style.height : dwh[1];
839 this.pid = 'pid_' + this.id;
841 //grab any innerHTML and set it to missing_plugin_html
842 //@@todo we should strip source tags instead of checking and skipping
843 if(element.innerHTML!='' && element.getElementsByTagName('source').length==0){
844 js_log('innerHTML: ' + element.innerHTML);
845 this.user_missing_plugin_html=element.innerHTML;
847 // load all of the specified sources
848 this.media_element = new mediaElement(element);
850 on_dom_swap: function(){
851 js_log('f:on_dom_swap');
852 // Process the provided ROE file... if we don't yet have sources
853 if(this.roe && this.media_element.sources.length==0 ){
854 js_log('loading external data');
855 this.loading_external_data=true;
857 do_request(this.roe, function(data)
860 _this.media_element.addROE( data );
861 js_log('added_roe::' + _this.media_element.sources.length);
863 js_log('set loading_external_data=false');
864 _this.loading_external_data=false;
866 _this.init_with_sources_loaded();
870 init_with_sources_loaded : function()
872 js_log('f:init_with_sources_loaded');
873 //set flag that we have run this function:
874 this.init_with_sources_loadedDone=true;
875 //autoseletct the source
876 this.media_element.autoSelectSource();
877 //auto select player based on prefrence or default order
878 if( !this.media_element.selected_source )
880 //check for parent clip:
881 if( typeof this.pc != 'undefined' ){
882 js_log('no sources, type:' +this.type + ' check for html');
884 //do load player if just displaying innerHTML:
885 if( this.pc.type == 'text/html' ){
886 this.selected_player = embedTypes.players.defaultPlayer( 'text/html' );
887 js_log('set selected player:'+ this.selected_player.mime_type);
891 this.selected_player = embedTypes.players.defaultPlayer( this.media_element.selected_source.mime_type );
893 if( this.selected_player ){
894 js_log('selected ' + this.selected_player.getName());
895 js_log("PLAYBACK TYPE: "+this.selected_player.library);
896 this.thumbnail_disp = true;
897 this.inheritEmbedObj();
899 //no source's playable
900 var missing_type ='';
902 for( var i=0; i < this.media_element.sources.length; i++){
903 missing_type+= or + this.media_element.sources[i].mime_type;
907 var missing_type = this.pc.type;
908 js_log('no player found for given source type ' + missing_type);
909 this.load_error= this.getPluginMissingHTML(missing_type);
912 inheritEmbedObj:function(){
913 js_log("inheritEmbedObj:duration is: " + this.duration);
914 //@@note: tricky cuz direct overwrite is not so ideal.. since the extended object is already tied to the dom
915 //clear out any non-base embedObj stuff:
917 eval('tmpObj = '+this.instanceOf);
918 for(var i in tmpObj){ //for in loop oky for object
919 if(this['parent_'+i]){
920 this[i]=this['parent_'+i];
926 //set up the new embedObj
927 js_log('f: inheritEmbedObj: embedding with ' + this.selected_player.library);
929 this.selected_player.load( function()
931 js_log("selected_player::load:duration is: " + _this.duration);
932 //js_log('inheriting '+_this.selected_player.library +'Embed to ' + _this.id + ' ' + $j('#'+_this.id).length);
933 //var _this = $j('#'+_this.id).get(0);
934 //js_log( 'type of ' + _this.selected_player.library +'Embed + ' +
935 // eval('typeof '+_this.selected_player.library +'Embed'));
936 eval('embedObj = ' +_this.selected_player.library +'Embed;');
937 for(var method in embedObj){ //for in loop oky for object
938 //parent method preservation for local overwritten methods
940 _this['parent_' + method] = _this[method];
941 _this[method]=embedObj[method];
943 js_log('TYPEOF_ppause: ' + typeof _this['parent_pause']);
945 if(_this.inheritEmbedOverride){
946 _this.inheritEmbedOverride();
948 //update controls if possible
949 if(!_this.loading_external_data)
950 _this.refreshControlsHTML();
952 //js_log("READY TO PLAY:"+_this.id);
953 _this.ready_to_play=true;
958 selectPlayer:function(player)
961 if(this.selected_player.id != player.id){
962 this.selected_player = player;
963 this.inheritEmbedObj();
966 doNativeWarningCheck:function(){
967 if( $j.cookie('dismissNativeWarn') && $j.cookie('dismissNativeWarn')===true){
970 //see if we have native support for ogg:
971 var supporting_players = embedTypes.players.getMIMETypePlayers( 'video/ogg' );
972 for(var i=0; i < supporting_players.length; i++){
973 if(supporting_players[i].id == 'videoElement'){
977 //see if we are using mv_embed without a ogg source in which case no point in promoting firefox :P
978 if(this.media_element && this.media_element.sources){
979 var foundOgg = false;
980 var playable_sources = this.media_element.getPlayableSources();
981 for(var sInx=0; sInx < playable_sources.length; sInx++){
982 var mime_type = playable_sources[sInx].mime_type;
983 if( mime_type=='video/ogg' ){
984 //they have flash / h.264 fallback no need to push firefox :(
988 //no ogg no point in download firefox
996 getTimeReq:function(){
997 var default_time_req = '0:00:00/' + seconds2npt(this.getDuration());
998 if(!this.media_element)
999 return default_time_req;
1000 if(!this.media_element.selected_source)
1001 return default_time_req;
1002 if(!this.media_element.selected_source.end_ntp)
1003 return default_time_req;
1004 return this.media_element.selected_source.start_ntp+'/'+this.media_element.selected_source.end_ntp;
1006 getDuration:function(){
1007 //update some local pointers for the selected source:
1008 if(this.media_element && this.media_element.selected_source && this.media_element.selected_source.duration){
1009 this.duration = this.media_element.selected_source.duration;
1010 this.start_offset = this.media_element.selected_source.start_offset;
1011 this.start_ntp = this.media_element.selected_source.start_ntp;
1012 this.end_ntp = this.media_element.selected_source.end_ntp;
1014 //update start end_ntp if duration !=0 (set from plugin)
1016 this.start_ntp = '0:0:0';
1017 if(!this.end_ntp && this.duration)
1018 this.end_ntp = seconds2npt( this.duration );
1019 //return the duration
1020 return this.duration;
1023 * wrapEmebedContainer
1024 * wraps the embed code into a container to better support playlist function
1025 * (where embed element is swapped for next clip
1026 * (where plugin method does not support playlsits)
1028 wrapEmebedContainer:function(embed_code){
1029 //check if parent clip is set( ie we are in a playlist so name the embed container by playlistID)
1030 var id = (this.pc!=null)?this.pc.pp.id:this.id;
1031 return '<div id="mv_ebct_'+id+'" style="width:'+this.width+'px;height:'+this.height+'px;">' +
1035 getEmbedHTML : function(){
1036 //return this.wrapEmebedContainer( this.getEmbedObj() );
1037 return 'function getEmbedHTML should be overitten by embedLib ';
1039 //do seek function (should be overwritten by implementing embedLibs)
1040 // first check if seek can be done on locally downloaded content.
1041 doSeek : function( perc ){
1042 if( this.supportsURLTimeEncoding() ){
1043 //make sure this.seek_time_sec is up-to-date:
1044 this.seek_time_sec = npt2seconds( this.start_ntp ) + parseFloat( perc * this.getDuration() );
1045 js_log('updated seek_time_sec: ' + seconds2npt ( this.seek_time_sec) );
1047 this.didSeekJump=true;
1049 this.setSliderValue( perc );
1051 //do play in 100ms (give things time to clear)
1052 setTimeout('$j(\'#' + this.id + '\').get(0).play()',100);
1055 * seeks to the requested time and issues a callback when ready
1056 * (should be overwitten by client that supports frame serving)
1058 setCurrentTime:function( time, callback){
1059 js_log('error: base embed setCurrentTime can not frame serve (overide via plugin)');
1061 addPresTimeOffset:function(){
1062 //add in the offset:
1063 if(this.seek_time_sec && this.seek_time_sec!=0){
1064 this.currentTime+=this.seek_time_sec;
1065 }else if(this.start_offset && this.start_offset!=0){
1066 this.currentTime = parseFloat(this.currentTime) + parseFloat(this.start_offset);
1069 doEmbedHTML:function()
1071 js_log('f:doEmbedHTML');
1072 js_log('thum disp:'+this.thumbnail_disp);
1074 this.closeDisplayedHTML();
1076 // if(!this.selected_player){
1077 // return this.getPluginMissingHTML();
1078 //Set "loading" here
1079 $j('#mv_embedded_player_'+_this.id).html(''+
1080 '<div style="color:black;width:'+this.width+'px;height:'+this.height+'px;">' +
1081 gM('mwe-loading_plugin') +
1084 // schedule embedding
1085 this.selected_player.load(function()
1087 js_log('performing embed for ' + _this.id);
1088 var embed_code = _this.getEmbedHTML();
1089 //js_log('shopuld embed:' + embed_code);
1090 $j('#mv_embedded_player_'+_this.id).html(embed_code);
1093 mvVideoAudioSearch:function(){
1095 js_log('switch video Relational' );
1098 'titles' : this.wikiTitleKey,
1099 'generator' : 'categories'
1101 var req_categories= new Array();
1104 'url' : commons_api_url
1106 req_categories = Array();
1107 if(data.query && data.query.pages){
1108 for(var pageid in data.query.pages){
1109 if(data.query.pages[pageid].title)
1110 req_categories.push(data.query.pages[pageid].title);
1113 _this.getRelatedFromCat( req_categories );
1116 getRelatedFromCat:function(catAry){
1117 js_log('getRelatedFromCat');
1119 for ( var i= 0 ; i <= catAry.length ;i++ ){
1124 'generator' : 'categorymembers' ,
1125 'gcmtitle' : catAry[i],
1126 'prop' : 'imageinfo',
1132 'url': commons_api_url
1135 $j('#dc_'+ _this.id + ' .related_vids ul').html(' ');
1137 for(var j in data.query.pages){
1138 //setup poster default:
1139 var local_poster ="http://upload.wikimedia.org/wikipedia/commons/7/79/Wiki-commons.png";
1140 //make sure it exists:
1141 var page = data.query.pages[j];
1142 if( j > 0 && page && page['imageinfo'] ){
1143 if( page['imageinfo'][0].thumburl ){
1144 local_poster = page['imageinfo'][0].thumburl;
1146 var descriptionurl = page['imageinfo'][0].descriptionurl;
1147 var title_str = page.title.replace( /File:|.ogv$|.oga$|.ogg$/gi, "" );
1148 //only link to other videos:
1149 if ( descriptionurl.match( /\.ogg$|\.ogv$|\.oga$/gi ) != null) {
1150 var liout = '<li>' +
1151 '<a href="' + descriptionurl + '" >' +
1152 '<img src="' + local_poster + '">' +
1154 ' <a title="' + title_str + '" target="_blank" ' +
1155 'href="'+ descriptionurl +'">' + title_str + '</a>' +
1157 $j('#dc_'+ _this.id + ' .related_vids ul').append(liout) ;
1161 //js_log( 'content: ' + $j('#dc_'+ _this.id + ' .related_vids ul').html() );
1162 }); //end do_api_req
1165 onClipDone:function(){
1166 js_log('base:onClipDone');
1167 //stop the clip (load the thumbnail etc)
1169 this.seek_time_sec = 0;
1170 this.setSliderValue(0);
1173 if(this.width < 300){
1176 this.onClipDone_disp=true;
1177 this.thumbnail_disp=true;
1178 //make sure we are not in preview mode( no end clip actions in preview mode)
1179 if( this.preview_mode )
1182 $j('#img_thumb_'+this.id).css('zindex',1);
1183 $j('#big_play_link_'+this.id).hide();
1185 //add black background
1186 $j('#dc_'+this.id).append('<div id="black_back_'+this.id+'" ' +
1187 'style="z-index:-2;position:absolute;background:#000;' +
1188 'top:0px;left:0px;width:'+parseInt(this.width)+'px;' +
1189 'height:'+parseInt(this.height)+'px;">' +
1192 if( this.wikiTitleKey){
1193 $j('#dc_'+this.id).append(
1194 '<div class="related_vids" >' +
1195 '<h1>' + gM('mwe_related_videos') + '</h1>'+
1199 $j('#img_thumb_' + this.id).fadeOut("fast");
1200 $j('#dc_'+ _this.id + ' .related_vids ul').html( gM('mwe-loading_txt') );
1201 this.mvVideoAudioSearch();
1203 //add the liks_info_div black back
1204 $j('#dc_'+this.id).append('<div id="liks_info_'+this.id+'" ' +
1205 'style="width:' +parseInt(parseInt(this.width)/2)+'px;'+
1206 'height:'+ parseInt(parseInt(this.height)) +'px;'+
1207 'position:absolute;top:10px;overflow:auto'+
1208 'width: '+parseInt( ((parseInt(this.width)/2)-15) ) + 'px;'+
1209 'left:'+ parseInt( ((parseInt(this.width)/2)+15) ) +'px;">'+
1212 //start animation (make thumb small in upper left add in div for "loading"
1213 $j('#img_thumb_'+this.id).animate({
1214 width:parseInt(parseInt(_this.width)/2),
1215 height:parseInt(parseInt(_this.height)/2),
1221 //animation done.. add "loading" to div if empty
1222 if($j('#liks_info_'+_this.id).html()==''){
1223 $j('#liks_info_'+_this.id).html(gM('mwe-loading_txt'));
1227 //now load roe if run the showNextPrevLinks
1228 if(this.roe && this.media_element.addedROEData==false){
1229 do_request(this.roe, function(data)
1231 _this.media_element.addROE(data);
1232 _this.getNextPrevLinks();
1235 this.getNextPrevLinks();
1239 //@@todo we should merge getNextPrevLinks with textInterface .. there is repeated code between them.
1240 getNextPrevLinks:function(){
1241 js_log('f:getNextPrevLinks');
1242 var anno_track_url = null;
1244 //check for annoative track
1245 $j.each(this.media_element.sources, function(inx, n){
1246 if(n.mime_type=='text/cmml'){
1247 if( n.id == 'Anno_en'){
1248 anno_track_url = n.src;
1252 if( anno_track_url ){
1253 js_log('found annotative track:'+ anno_track_url);
1254 //zero out seconds (should improve cache hit rate and generally expands metadata search)
1255 //@@todo this could be repalced with a regExp
1256 var annoURL = parseUri(anno_track_url);
1257 var times = annoURL.queryKey['t'].split('/');
1258 var stime_parts = times[0].split(':');
1259 var etime_parts = times[1].split(':');
1260 //zero out the hour:
1261 var new_start = stime_parts[0]+':'+'0:0';
1262 //zero out the end sec
1263 var new_end = (etime_parts[0]== stime_parts[0])? (etime_parts[0]+1)+':0:0' :etime_parts[0]+':0:0';
1265 var etime_parts = times[1].split(':');
1267 var new_anno_track_url = annoURL.protocol +'://'+ annoURL.host + annoURL.path +'?';
1268 $j.each(annoURL.queryKey, function(i, val){
1269 new_anno_track_url +=(i=='t')?'t='+new_start+'/'+new_end +'&' :
1272 var request_key = new_start+'/'+new_end;
1273 //check the anno_data cache:
1274 //@@todo search cache see if current is in range.
1275 if(this.anno_data_cache){
1276 js_log('anno data found in cache: '+request_key);
1277 this.showNextPrevLinks();
1279 do_request(new_anno_track_url, function(cmml_data){
1280 js_log('raw response: '+ cmml_data);
1281 if(typeof cmml_data == 'string')
1283 var parser=new DOMParser();
1284 js_log('Parse CMML data:' + cmml_data);
1285 cmml_data=parser.parseFromString(cmml_data,"text/xml");
1287 //init anno_data_cache
1288 if(!_this.anno_data_cache)
1289 _this.anno_data_cache={};
1290 //grab all metadata and put it into the anno_data_cache:
1291 $j.each(cmml_data.getElementsByTagName('clip'), function(inx, clip){
1292 _this.anno_data_cache[ $j(clip).attr("id") ]={
1293 'start_time_sec':npt2seconds($j(clip).attr("start").replace('npt:','')),
1294 'end_time_sec':npt2seconds($j(clip).attr("end").replace('npt:','')),
1295 'time_req':$j(clip).attr("start").replace('npt:','')+'/'+$j(clip).attr("end").replace('npt:','')
1298 _this.anno_data_cache[ $j(clip).attr("id") ]['meta']={};
1299 $j.each(clip.getElementsByTagName('meta'),function(imx, meta){
1300 //js_log('adding meta: '+ $j(meta).attr("name")+ ' = '+ $j(meta).attr("content"));
1301 _this.anno_data_cache[$j(clip).attr("id")]['meta'][$j(meta).attr("name")]=$j(meta).attr("content");
1304 _this.showNextPrevLinks();
1308 js_log('no annotative track found');
1309 $j('#liks_info_'+this.id).html('no metadata found for related links');
1311 //query current request time +|- 60s to get prev next speech links.
1313 showNextPrevLinks:function(){
1314 //js_log('f:showNextPrevLinks');
1315 //int requested links:
1321 var curTime = this.getTimeReq().split('/');
1323 var s_sec = npt2seconds(curTime[0]);
1324 var e_sec = npt2seconds(curTime[1]);
1325 js_log('showNextPrevLinks: req time: '+ s_sec + ' to ' + e_sec);
1326 //now we have all the data in anno_data_cache
1327 var current_done=false;
1328 for(var clip_id in this.anno_data_cache){ //for in loop oky for object
1329 var clip = this.anno_data_cache[clip_id];
1330 //js_log('on clip:'+ clip_id);
1331 //set prev_link (if cur_link is still empty)
1332 if( s_sec > clip.end_time_sec){
1333 link.prev = clip_id;
1334 js_log('showNextPrevLinks: ' + s_sec + ' < ' + clip.end_time_sec + ' set prev');
1337 if(e_sec==clip.end_time_sec && s_sec== clip.start_time_sec)
1338 current_done = true;
1339 //current clip is not done:
1340 if( e_sec < clip.end_time_sec && link.current=='' && !current_done){
1341 link.current = clip_id;
1342 js_log('showNextPrevLinks: ' + e_sec + ' < ' + clip.end_time_sec + ' set current');
1345 //set end clip (first clip where start time is > end_time of req
1346 if( e_sec < clip.start_time_sec && link.next==''){
1347 link.next = clip_id;
1348 js_log('showNextPrevLinks: '+ e_sec + ' < '+ clip.start_time_sec + ' && ' + link.next );
1352 if(link.prev=='' && link.current=='' && link.next==''){
1353 html='<p><a href="'+this.media_element.linkbackgetMsg+'">clip page</a>';
1355 for(var link_type in link){
1356 var link_id = link[link_type];
1358 var clip = this.anno_data_cache[link_id];
1360 for(var j in clip['meta']){
1361 title_msg+=j.replace(/_/g,' ') +': ' +clip['meta'][j].replace(/_/g,' ') +" <br>";
1363 var time_req = clip.time_req;
1364 if(link_type=='current') //if current start from end of current clip play to end of current meta:
1365 time_req = curTime[1]+ '/' + seconds2npt( clip.end_time_sec );
1367 //do special linkbacks for metavid content:
1368 var regTimeCheck = new RegExp(/[0-9]+:[0-9]+:[0-9]+\/[0-9]+:[0-9]+:[0-9]+/);
1370 if( regTimeCheck.test( this.media_element.linkback ) ){
1371 html+=' href="'+ this.media_element.linkback.replace(regTimeCheck,time_req) +'" ';
1373 html+=' href="#" onClick="$j(\'#'+this.id+'\').get(0).playByTimeReq(\''+
1374 time_req + '\'); return false; "';
1376 html+=' title="' + title_msg + '">' +
1377 gM('mwe-' + link_type+'_clip_msg') +
1378 '</a><br><span style="font-size:small">'+ title_msg +'<span></p>';
1382 //js_og("should set html:"+ html);
1383 $j('#liks_info_'+this.id).html(html);
1385 playByTimeReq: function(time_req){
1386 js_log('f:playByTimeReq: '+time_req );
1388 this.updateVideoTimeReq(time_req);
1391 doThumbnailHTML:function()
1394 js_log('f:doThumbnailHTML'+ this.thumbnail_disp);
1395 this.closeDisplayedHTML();
1396 $j( '#mv_embedded_player_' + this.id ).html( this.getThumbnailHTML() );
1398 this.thumbnail_disp = true;
1400 refreshControlsHTML:function(){
1401 js_log('refreshing controls HTML');
1402 if($j('#mv_embedded_controls_'+this.id).length==0)
1404 js_log('#mv_embedded_controls_'+this.id + ' not present, returning');
1407 $j('#mv_embedded_controls_'+this.id).html( this.getControlsHTML() );
1408 ctrlBuilder.addControlHooks(this);
1411 getControlsHTML:function()
1413 return ctrlBuilder.getControls( this );
1415 getHTML : function (){
1416 //@@todo check if we have sources avaliable
1417 js_log('embedVideo:getHTML : ' + this.id + ' resource type: ' + this.type);
1420 html_code = '<div id="videoPlayer_'+this.id+'" style="width:'+this.width+'px;position:relative;" class="videoPlayer">';
1421 html_code += '<div style="width:'+parseInt(this.width)+'px;height:'+parseInt(this.height)+'px;" id="mv_embedded_player_'+this.id+'">' +
1422 this.getThumbnailHTML() +
1424 //js_log("mvEmbed:controls "+ typeof this.controls);
1427 js_log("f:getHTML:AddControls");
1428 html_code +='<div id="mv_embedded_controls_' + this.id + '" class="ui-widget ui-corner-bottom ui-state-default controls" >';
1429 html_code += this.getControlsHTML();
1430 html_code +='</div>';
1431 //block out some space by encapulating the top level div
1432 $j(this).wrap('<div style="width:'+parseInt(this.width)+'px;height:'
1433 +(parseInt(this.height)+ctrlBuilder.height)+'px"></div>');
1435 html_code += '</div>'; //videoPlayer div close
1436 //js_log('should set: '+this.id);
1437 $j(this).html( html_code );
1438 //add hooks once Controls are in DOM
1439 ctrlBuilder.addControlHooks(this);
1441 //js_log('set this to: ' + $j(this).html() );
1443 //if auto play==true directly embed the plugin
1446 js_log('activating autoplay');
1451 * get missing plugin html (check for user included code)
1453 getPluginMissingHTML : function(missing_type){
1454 //keep the box width hight:
1455 var out = '<div style="width:'+this.width+'px;height:'+this.height+'px">';
1456 if(this.user_missing_plugin_html){
1457 out+= this.user_missing_plugin_html;
1461 out+= gM('mwe-generic_missing_plugin', missing_type) + ' or <a title="'+gM('mwe-download_clip')+'" href="'+this.src +'">'+gM('mwe-download_clip')+'</a>';
1463 return out + '</div>';
1465 updateVideoTimeReq:function(time_req){
1466 js_log('f:updateVideoTimeReq');
1467 var time_parts =time_req.split('/');
1468 this.updateVideoTime(time_parts[0], time_parts[1]);
1471 updateVideoTime:function(start_ntp, end_ntp){
1473 this.media_element.updateSourceTimes( start_ntp, end_ntp );
1475 this.setStatus(start_ntp+'/'+end_ntp);
1477 this.setSliderValue(0);
1478 //reset seek_offset:
1479 if(this.media_element.selected_source.URLTimeEncoding )
1480 this.seek_time_sec=0;
1482 this.seek_time_sec=npt2seconds(start_ntp);
1484 //@@todo overwite by embed library if we can render frames natavily
1485 renderTimelineThumbnail:function( options ){
1486 var my_thumb_src = this.media_element.getThumbnailURL();
1487 //check if our thumbnail has a time attribute:
1488 if( my_thumb_src.indexOf('t=') !== -1){
1489 var time_ntp = seconds2npt ( options.time + parseInt(this.start_offset) );
1490 my_thumb_src = getURLParamReplace( my_thumb_src, { 't':time_ntp, 'size': options.size } );
1492 var thumb_class = (typeof options['thumb_class'] != 'undefined' ) ? options['thumb_class'] : '';
1493 return '<div class="ui-corner-all ' + thumb_class + '" src="' + my_thumb_src + '" '+
1494 'style="height:' + options.height + 'px;' +
1495 'width:' + options.width + 'px" >' +
1496 '<img src="' + my_thumb_src +'" '+
1497 'style="height:' + options.height + 'px;' +
1498 'width:' + options.width + 'px">' +
1501 updateThumbTimeNTP:function( time){
1502 this.updateThumbTime( npt2seconds(time) - parseInt(this.start_offset) );
1504 updateThumbTime:function( float_sec ){
1505 //js_log('updateThumbTime:'+float_sec);
1507 if( typeof this.org_thum_src=='undefined' ){
1508 this.org_thum_src = this.media_element.getThumbnailURL();
1510 if( this.org_thum_src.indexOf('t=') !== -1){
1511 this.last_thumb_url = getURLParamReplace(this.org_thum_src,
1512 { 't' : seconds2npt( float_sec + parseInt(this.start_offset)) } );
1513 if(!this.thumbnail_updating){
1514 this.updateThumbnail(this.last_thumb_url ,false);
1515 this.last_thumb_url =null;
1519 //for now provide a src url .. but need to figure out how to copy frames from video for plug-in based thumbs
1520 updateThumbPerc:function( perc ){
1521 return this.updateThumbTime( (this.getDuration() * perc) );
1523 //updates the thumbnail if the thumbnail is being displayed
1524 updateThumbnail : function(src, quick_switch){
1525 //make sure we don't go to the same url if we are not already updating:
1526 if( !this.thumbnail_updating && $j('#img_thumb_'+this.id).attr('src')== src )
1528 //if we are already updating don't issue a new update:
1529 if( this.thumbnail_updating && $j('#new_img_thumb_'+this.id).attr('src')== src )
1532 js_log('update thumb: ' + src);
1535 $j('#img_thumb_'+this.id).attr('src', src);
1538 //if still animating remove new_img_thumb_
1539 if(this.thumbnail_updating==true)
1540 $j('#new_img_thumb_'+this.id).stop().remove();
1542 if(this.thumbnail_disp){
1543 js_log('set to thumb:'+ src);
1544 this.thumbnail_updating=true;
1545 $j('#dc_'+this.id).append('<img src="'+src+'" ' +
1546 'style="display:none;position:absolute;zindex:2;top:0px;left:0px;" ' +
1547 'width="'+this.width+'" height="'+this.height+'" '+
1548 'id = "new_img_thumb_'+this.id+'" />');
1549 //js_log('appended: new_img_thumb_');
1550 $j('#new_img_thumb_'+this.id).fadeIn("slow", function(){
1551 //once faded in remove org and rename new:
1552 $j('#img_thumb_'+_this.id).remove();
1553 $j('#new_img_thumb_'+_this.id).attr('id', 'img_thumb_'+_this.id);
1554 $j('#img_thumb_'+_this.id).css('zindex','1');
1555 _this.thumbnail_updating=false;
1556 //js_log("done fadding in "+ $j('#img_thumb_'+_this.id).attr("src"));
1558 //if we have a thumb queued update to that
1559 if(_this.last_thumb_url){
1560 var src_url =_this.last_thumb_url;
1561 _this.last_thumb_url=null;
1562 _this.updateThumbnail(src_url);
1568 /** Returns the HTML code for the video when it is in thumbnail mode.
1569 This includes the specified thumbnail as well as buttons for
1570 playing, configuring the player, inline cmml display, HTML linkback,
1571 download, and embed code.
1573 getThumbnailHTML : function ()
1575 js_log('embedVideo:getThumbnailHTML::' + this.id);
1576 var thumb_html = '';
1579 //if(this.class)class_atr = ' class="'+this.class+'"';
1580 //if(this.style)style_atr = ' style="'+this.style+'"';
1581 // else style_atr = 'overflow:hidden;height:'+this.height+'px;width:'+this.width+'px;';
1582 this.thumbnail = this.media_element.getThumbnailURL();
1584 //put it all in the div container dc_id
1585 thumb_html+= '<div id="dc_'+this.id+'" style="position:absolute;'+
1586 ' overflow:hidden; top:0px; left:0px; width:'+this.playerPixelWidth()+'px; height:'+this.playerPixelHeight()+'px; z-index:0;">'+
1587 '<img width="'+this.playerPixelWidth()+'" height="'+this.playerPixelHeight()+'" style="position:relative;width:'+this.playerPixelWidth()+';height:'+this.playerPixelHeight()+'"' +
1588 ' id="img_thumb_'+this.id+'" src="' + this.thumbnail + '">';
1590 if(this.play_button == true && this.controls == true)
1591 thumb_html+=this.getPlayButton();
1593 thumb_html+='</div>';
1596 getEmbeddingHTML:function()
1598 var thumbnail = this.media_element.getThumbnailURL();
1600 var embed_thumb_html;
1601 if(thumbnail.substring(0,1)=='/'){
1602 eURL = parseUri(mv_embed_path);
1603 embed_thumb_html = eURL.protocol + '://' + eURL.host + thumbnail;
1604 //js_log('set from mv_embed_path:'+embed_thumb_html);
1606 embed_thumb_html = (thumbnail.indexOf('http://')!=-1)?thumbnail:mv_embed_path + thumbnail;
1608 var embed_code_html = '<script type="text/javascript" ' +
1609 'src="'+mv_embed_path+'mv_embed.js"></script>' +
1612 embed_code_html+='roe="'+this.roe+'" >';
1614 embed_code_html+='src="'+this.src+'" ' +
1615 'poster="'+embed_thumb_html+'">';
1617 //close the video tag
1618 embed_code_html+='</video>';
1620 return embed_code_html;
1622 doOptionsHTML:function()
1624 var sel_id = (this.pc!=null)?this.pc.pp.id:this.id;
1625 var pos = $j('#options_button_'+sel_id).offset();
1626 pos['top']=pos['top']+24;
1627 pos['left']=pos['left']-124;
1628 //js_log('pos of options button: t:'+pos['top']+' l:'+ pos['left']);
1629 $j('#mv_vid_options_'+sel_id).css(pos).toggle();
1632 getPlayButton:function(id){
1634 return '<div title="' + gM('mwe-play_clip') + '" id="big_play_link_'+id+'" class="large_play_button" '+
1635 'style="left:'+((this.playerPixelWidth()-130)/2)+'px;'+
1636 'top:' + ((this.playerPixelHeight()-96)/2) + 'px;">'+
1637 '<img src="' + mv_skin_img_path + 'player_big_play_button.png">'+
1640 doLinkBack:function(){
1641 if(this.roe && this.media_element.addedROEData==false){
1643 this.displayHTML(gM('mwe-loading_txt'));
1644 do_request(this.roe, function(data)
1646 _this.media_element.addROE(data);
1650 if(this.media_element.linkback){
1651 window.location = this.media_element.linkback;
1653 this.displayHTML(gM('mwe-could_not_find_linkback'));
1657 //display the code to remotely embed this video:
1658 showEmbedCode : function(embed_code){
1660 embed_code = this.getEmbeddingHTML();
1663 o+='<a class="email" href="'+this.linkback+'">Share Clip via Link</a> '+
1667 '<span style="color:#FFF;font-size:14px;">Embed Clip in Blog or Site</span><br>'+
1668 '<span style="color:#FFF;font-size:12px;"><a style="color:red" href="http://metavid.org/wiki/Security_Notes_on_Remote_Embedding">'+
1669 'Read This</a> before embeding.</span>'+
1670 '<div class="embed_code"> '+
1671 '<textarea onClick="this.select();" id="embedding_user_html_'+this.id+'" name="embed">' +
1674 '<button onClick="$j(\'#'+this.id+'\').get(0).copyText(); return false;" class="copy_to_clipboard">Copy to Clipboard</button> '+
1677 this.displayHTML(o);
1679 copyText:function(){
1680 $j('#embedding_user_html_'+this.id).focus().select();
1681 if(document.selection){
1682 CopiedTxt = document.selection.createRange();
1683 CopiedTxt.execCommand("Copy");
1686 showTextInterface:function(){
1688 //display the text container with loading text:
1689 //@@todo support position config
1690 var loc = $j(this).position();
1691 if($j('#metaBox_'+this.id).length==0){
1692 $j(this).after('<div class="ui-widget ui-widget-content ui-corner-all" style="position:absolute;z-index:10;'+
1693 'top:' + (loc.top) + 'px;' +
1694 'left:' + (parseInt( loc.left ) + parseInt(this.width) + 10 )+'px;' +
1695 'height:'+ parseInt( this.height )+'px;width:400px;' +
1697 'id="metaBox_' + this.id + '">'+
1698 gM('mwe-loading_txt') +
1701 //fade in the text display
1702 $j('#metaBox_'+this.id).fadeIn("fast");
1703 //check if textObj present:
1704 if(typeof this.textInterface == 'undefined' ){
1705 //load the default text interface:
1710 _this.textInterface = new mvTextInterface( _this );
1712 _this.textInterface.show();
1713 js_log("NEW TEXT INTERFACE");
1714 for(var i in _this.textInterface.availableTracks){
1715 js_log("tracks in new interface: "+_this.id+ ' tid:' + i);
1721 this.textInterface.show();
1724 closeTextInterface:function(){
1725 js_log('closeTextInterface '+ typeof this.textInterface);
1726 if(typeof this.textInterface !== 'undefined' ){
1727 this.textInterface.close();
1730 /** Generic function to display custom HTML inside the mv_embed element.
1731 The code should call the closeDisplayedHTML function to close the
1732 display of the custom HTML and restore the regular mv_embed display.
1733 @param {String} HTML code for the selection list.
1735 displayHTML:function(html_code)
1737 var sel_id = (this.pc!=null)?this.pc.pp.id:this.id;
1739 if(!this.supports['overlays'])
1742 //put select list on-top
1743 //make sure the parent is relatively positioned:
1744 $j('#'+sel_id).css('position', 'relative');
1745 //set height width (check for playlist container)
1746 var width = (this.pc)?this.pc.pp.width:this.playerPixelWidth();
1747 var height = (this.pc)?this.pc.pp.height:this.playerPixelHeight();
1750 height+=(this.pc.pp.pl_layout.title_bar_height + this.pc.pp.pl_layout.control_height);
1753 if($j('#blackbg_'+sel_id).length!=0)
1756 $j('#blackbg_'+sel_id).remove();
1758 //fade in a black bg div ontop of everything
1759 var div_code = '<div id="blackbg_'+sel_id+'" class="videoComplete" ' +
1760 'style="height:'+parseInt(height)+'px;width:'+parseInt(width)+'px;">'+
1761 '<div class="videoOptionsComplete">'+
1762 //@@TODO: this style should go to .css
1763 '<span style="float:right;margin-right:10px">' +
1764 '<a href="#" style="color:white;" onClick="$j(\'#'+sel_id+'\').get(0).closeDisplayedHTML();return false;">close</a>' +
1766 '<div id="mv_disp_inner_'+sel_id+'" style="padding-top:10px;">'+
1770 $j('#'+sel_id).prepend(div_code);
1772 $j('#blackbg_'+sel_id).fadeIn("slow");
1774 $j('#blackbg_'+sel_id).show();
1775 return false; //onclick action return false
1777 /** Close the custom HTML displayed using displayHTML and restores the
1778 regular mv_embed display.
1780 closeDisplayedHTML:function(){
1781 var sel_id = (this.pc!=null)?this.pc.pp.id:this.id;
1782 $j('#blackbg_'+sel_id).fadeOut("slow", function(){
1783 $j('#blackbg_'+sel_id).remove();
1785 return false; //onclick action return false
1787 selectPlaybackMethod:function(){
1788 //get id (in case where we have a parent container)
1789 var this_id = (this.pc!=null)?this.pc.pp.id:this.id;
1792 var out= '<span style="color:#FFF;background-color:black;"><blockquote style="background-color:black;">';
1794 //js_log('selected src'+ _this.media_element.selected_source.url);
1795 $j.each( this.media_element.getPlayableSources(), function(source_id, source){
1796 var default_player = embedTypes.players.defaultPlayer( source.getMIMEType() );
1798 var is_selected = (source == _this.media_element.selected_source);
1799 var image_src = mv_skin_img_path ;
1801 //set the Playable source type:
1802 if( source.mime_type == 'video/x-flv' ){
1803 image_src += 'flash_icon_';
1804 }else if( source.mime_type == 'video/h264'){
1805 //for now all mp4 content is pulled from archive.org (so use archive.org icon)
1806 image_src += 'archive_org_';
1808 image_src += 'fish_xiph_org_';
1810 image_src += is_selected ? 'color':'bw';
1811 image_src += '.png';
1815 out += '<img src="'+image_src+'"/>';
1817 out+='<a href="#" class="sel_source" id="sc_' + source_id + '_' + default_player.id +'">';
1818 out += source.getTitle()+ (is_selected?'</a>':'') + ' ';
1820 //output the player select code:
1821 var supporting_players = embedTypes.players.getMIMETypePlayers( source.getMIMEType() );
1822 out+='<div id="player_select_list_' + source_id + '" class="player_select_list"><ul>';
1823 for(var i=0; i < supporting_players.length ; i++){
1824 if( _this.selected_player.id == supporting_players[i].id && is_selected ){
1825 out+='<li style="border-style:dashed;margin-left:20px;">'+
1826 '<img border="0" width="16" height="16" src="' + mv_skin_img_path + 'plugin.png">' +
1827 supporting_players[i].getName() +
1830 //else gray plugin and the plugin with link to select
1831 out+='<li style="margin-left:20px;">'+
1832 '<a href="#" class="sel_source" id="sc_' + source_id + '_' + supporting_players[i].id +'">'+
1833 '<img border="0" width="16" height="16" src="' + mv_skin_img_path + 'plugin_disabled.png">'+
1834 supporting_players[i].getName() +
1841 out+= source.getTitle() + ' - no player available';
1843 out+='</blockquote></span>';
1844 this.displayHTML(out);
1846 //set up the click bindings:
1847 $j('.sel_source').each(function(){
1848 $j(this).click(function(){
1849 var iparts = $j(this).attr( 'id' ).replace(/sc_/,'').split('_');
1850 var source_id = iparts[0];
1851 var default_player_id = iparts[1];
1852 js_log('source id: ' + source_id + ' player id: ' + default_player_id);
1854 $j('#' + this_id ).get(0).closeDisplayedHTML();
1855 $j('#' + _this.id ).get(0).media_element.selectSource( source_id );
1857 embedTypes.players.userSelectPlayer( default_player_id,
1858 _this.media_element.sources[ source_id ].getMIMEType() );
1860 //be sure to issue a stop
1861 $j('#' + this_id ).get(0).stop();
1863 //don't follow the empty # link:
1868 showVideoDownload:function(){
1869 //load the roe if available (to populate out download options:
1870 //js_log('f:showVideoDownload '+ this.roe + ' ' + this.media_element.addedROEData);
1871 if(this.roe && this.media_element.addedROEData == false){
1873 this.displayHTML(gM('mwe-loading_txt'));
1874 do_request(this.roe, function(data)
1876 _this.media_element.addROE(data);
1877 $j('#mv_disp_inner_'+_this.id).html( _this.getShowVideoDownload() );
1880 this.displayHTML( this.getShowVideoDownload() );
1883 getShowVideoDownload:function(){
1884 var out='<div style="color:white">' +
1885 '<b style="color:white;">'+gM('mwe-download_segment')+'</b><br>';
1886 out+='<blockquote style="background:#000">'+
1887 gM('mwe-download_right_click') + '</blockquote><br>';
1890 $j.each(this.media_element.getSources(), function(index, source){
1891 var dl_line = '<li>' + '<a style="color:white" href="' + source.getURI() +'"> '
1892 + source.getTitle()+'</a> '+ '</li>'+"\n";
1893 if( source.getURI().indexOf('?t=')!==-1){
1895 }else if( this.getMIMEType()=="text/cmml" || this.getMIMEType()=="text/x-srt" ){
1896 dl_txt_list+=dl_line;
1903 out+=gM('mwe-download_full') + '<blockquote style="background:#000">' + dl_list + '</blockquote>';
1905 out+=gM('mwe-download_text')+'<blockquote style="background:#000">' + dl_txt_list +'</blockquote>';
1910 * base embed controls
1911 * the play button calls
1914 var this_id = (this.pc!=null)?this.pc.pp.id:this.id;
1916 //js_log( "mv_embed play:" + this.id);
1917 //js_log('thum disp:'+this.thumbnail_disp);
1918 //check if thumbnail is being displayed and embed html
1919 if( this.thumbnail_disp ){
1920 if( !this.selected_player ){
1921 js_log('no selected_player');
1922 //this.innerHTML = this.getPluginMissingHTML();
1923 //$j(this).html(this.getPluginMissingHTML());
1924 $j('#'+this.id).html( this.getPluginMissingHTML() );
1927 this.onClipDone_disp=false;
1929 this.thumbnail_disp=false;
1932 //the plugin is already being displayed
1933 this.paused=false; //make sure we are not "paused"
1937 $j("#mv_play_pause_button_" + this_id + ' span').removeClass('ui-icon-play').addClass('ui-icon-pause');
1938 $j("#mv_play_pause_button_" + this_id).unbind().btnBind().click(function(){
1939 $j('#' + this_id ).get(0).pause();
1940 }).attr('title', gM('mwe-pause_clip'));
1944 //should be done by child (no base way to load assets)
1945 js_log('baseEmbed:load call');
1948 return this.media_element.selected_source.getURI( this.seek_time_sec );
1952 * there is no general way to pause the video
1953 * must be overwritten by embed object to support this functionality.
1956 var this_id = (this.pc!=null)?this.pc.pp.id:this.id;
1957 //js_log('mv_embed:do pause');
1958 //(playing) do pause
1960 //update the ctrl "paused state"
1961 $j("#mv_play_pause_button_" + this_id + ' span').removeClass('ui-icon-pause').addClass('ui-icon-play');
1962 $j("#mv_play_pause_button_" + this_id).unbind().btnBind().click(function(){
1963 $j('#'+this_id).get(0).play();
1964 }).attr('title', gM('mwe-play_clip'));
1967 * base embed stop (can be overwritten by the plugin)
1971 js_log('mvEmbed:stop:'+this.id);
1973 //no longer seeking:
1974 this.didSeekJump=false;
1976 //first issue pause to update interface (only call the parent)
1977 if(this['parent_pause']){
1978 this.parent_pause();
1983 //reset the currentTime:
1985 //check if thumbnail is being displayed in which case do nothing
1986 if( this.thumbnail_disp ){
1987 //already in stooped state
1988 js_log('already in stopped state');
1990 //rewrite the html to thumbnail disp
1991 this.doThumbnailHTML();
1992 this.bufferedPercent=0; //reset buffer state
1993 this.setSliderValue(0);
1994 this.setStatus( this.getTimeReq() );
1997 //make sure the big playbutton is has click action:
1998 $j('#big_play_link_' + _this.id).unbind('click').click(function(){
1999 $j('#' +_this.id).get(0).play();
2002 if(this.update_interval)
2004 clearInterval(this.update_interval);
2005 this.update_interval = null;
2008 toggleMute:function(){
2009 var this_id = (this.pc!=null)?this.pc.pp.id:this.id;
2012 $j('#volume_control_'+this_id + ' span').removeClass('ui-icon-volume-off').addClass('ui-icon-volume-on');
2013 $j('#volume_bar_'+this_id).slider('value', 100);
2014 this.updateVolumen(1);
2017 $j('#volume_control_'+this_id + ' span').removeClass('ui-icon-volume-on').addClass('ui-icon-volume-off');
2018 $j('#volume_bar_'+this_id).slider('value', 0);
2019 this.updateVolumen(0);
2021 js_log('f:toggleMute::' + this.muted);
2023 updateVolumen:function(perc){
2024 js_log('update volume not supported with current playback type');
2026 fullscreen:function(){
2027 js_log('fullscreen not supported with current playback type');
2029 /* returns bool true if playing or paused, false if stooped
2031 isPlaying : function(){
2032 if(this.thumbnail_disp){
2035 }else if( this.paused ){
2042 isPaused : function(){
2043 return this.isPlaying() && this.paused;
2045 isStoped : function(){
2046 return this.thumbnail_disp;
2048 playlistSupport:function(){
2049 //by default not supported (implemented in js)
2052 postEmbedJS:function(){
2055 //do common monitor code like update the playhead and play status
2056 //plugin objects are responsible for updating currentTime
2058 if( this.currentTime && this.currentTime > 0 && this.duration){
2059 if( !this.userSlide ){
2060 if( this.start_offset ){
2061 //if start offset include that calculation
2062 this.setSliderValue( ( this.currentTime - this.start_offset ) / this.duration );
2063 this.setStatus( seconds2npt(this.currentTime) + '/'+ seconds2npt(parseFloat(this.start_offset)+parseFloat(this.duration) ));
2065 this.setSliderValue( this.currentTime / this.duration );
2066 this.setStatus( seconds2npt(this.currentTime) + '/' + seconds2npt(this.duration ));
2070 //js_log(' ct:' + this.currentTime + ' dur: ' + this.duration);
2071 if( this.isStoped() ){
2072 this.setStatus( this.getTimeReq() );
2073 }else if( this.isPaused() ){
2074 this.setStatus( "paused" );
2075 }else if( this.isPlaying() ){
2076 if( this.currentTime && ! this.duration )
2077 this.setStatus( seconds2npt( this.currentTime ) + ' /' );
2079 this.setStatus(" - - - ");
2081 this.setStatus( this.getTimeReq() );
2084 //update buffer information
2085 this.updateBufferStatus();
2087 //check if we passed duration
2088 if( this.duration && (this.currentTime > this.duration) ){
2092 //update monitorTimerId to call child monitor
2093 if( ! this.monitorTimerId ){
2094 //make sure an instance of this.id exists:
2095 if( document.getElementById(this.id) ){
2096 this.monitorTimerId = setInterval('$j(\'#'+this.id+'\').get(0).monitor()', 250);
2100 stopMonitor:function(){
2101 if( this.monitorTimerId != 0 )
2103 clearInterval( this.monitorTimerId );
2104 this.monitorTimerId = 0;
2107 updateBufferStatus: function(){
2109 //build the buffer targeet based for playlist vs clip
2110 var buffer_select = (this.pc) ?
2111 '#cl_status_' + this.id + ' .mv_buffer':
2112 '#mv_play_head_' + this.id + ' .mv_buffer';
2114 //update the buffer progress bar (if available )
2115 if( this.bufferedPercent != 0 ){
2116 //js_log('bufferedPercent: ' + this.bufferedPercent);
2117 if(this.bufferedPercent > 1)
2118 this.bufferedPercent=1;
2120 $j(buffer_select).css("width", (this.bufferedPercent*100) +'%' );
2122 $j(buffer_select).css("width", '0px' );
2125 relativeCurrentTime: function(){
2126 if(!this.start_offset)
2127 this.start_offset =0;
2128 var rt = this.currentTime - this.start_offset;
2129 if( rt < 0 ) //should not happen but does.
2133 getPluginEmbed : function(){
2134 if (window.document[this.pid]){
2135 return window.document[this.pid];
2137 if ($j.browser.msie){
2138 return document.getElementById(this.pid );
2140 if (document.embeds && document.embeds[this.pid])
2141 return document.embeds[this.pid];
2145 //HELPER Functions for selected source
2147 * returns the selected source url for players to play
2149 getURI : function( seek_time_sec ){
2150 return this.media_element.selected_source.getURI( this.seek_time_sec );
2152 supportsURLTimeEncoding: function(){
2153 //do head request if on the same domain
2154 return this.media_element.selected_source.URLTimeEncoding;
2156 setSliderValue: function(perc, hide_progress){
2158 var this_id = (this.pc)?this.pc.pp.id:this.id;
2159 var val = parseInt( perc*1000 );
2160 $j('#mv_play_head_'+this_id).slider('value', val);
2162 //js_log("embed video set: " + '#mv_play_head_'+this_id + ' to ' + val);
2164 //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) );
2165 //js_log('op:' + offset_perc + ' *('+perc+' * ' + $j('#slider_'+id).width() + ')');
2167 highlightPlaySection:function(options){
2168 js_log('highlightPlaySection');
2169 var this_id = (this.pc)?this.pc.pp.id:this.id;
2170 var dur = this.getDuration();
2171 var hide_progress = true;
2172 //set the left percet and update the slider:
2173 rel_start_sec = npt2seconds( options['start']);
2174 //remove the start_offset if relevent:
2175 if(this.start_offset)
2176 rel_start_sec = rel_start_sec - this.start_offset
2179 if( rel_start_sec <= 0 ){
2181 options['start'] = seconds2npt( this.start_offset );
2183 this.setSliderValue( 0 , hide_progress);
2185 left_perc = parseInt( (rel_start_sec / dur)*100 ) ;
2186 slider_perc = (left_perc / 100);
2188 js_log("slider perc:" + slider_perc);
2189 if( ! this.isPlaying() ){
2190 this.setSliderValue( slider_perc , hide_progress);
2193 width_perc = parseInt( (( npt2seconds( options['end'] ) - npt2seconds( options['start'] ) ) / dur)*100 ) ;
2194 if( (width_perc + left_perc) > 100 ){
2195 width_perc = 100 - left_perc;
2197 //js_log('should hl: '+rel_start_sec+ '/' + dur + ' re:' + rel_end_sec+' lp:' + left_perc + ' width: ' + width_perc);
2198 $j('#mv_seeker_' + this_id + ' .mv_highlight').css({
2199 'left':left_perc+'%',
2200 'width':width_perc+'%'
2203 this.jump_time = options['start'];
2204 this.seek_time_sec = npt2seconds( options['start']);
2206 this.setStatus( gM('mwe-seek_to')+' '+ seconds2npt( this.seek_time_sec ) );
2207 js_log('DO update: ' + this.jump_time);
2208 this.updateThumbTime( rel_start_sec );
2210 hideHighlight:function(){
2211 var this_id = (this.pc)?this.pc.pp.id:this.id;
2212 $j('#mv_seeker_' + this_id + ' .mv_highlight').hide();
2213 this.setStatus( this.getTimeReq() );
2214 this.setSliderValue( 0 );
2216 setStatus:function(value){
2217 var id = (this.pc)?this.pc.pp.id:this.id;
2219 $j('#mv_time_'+id).html(value);
2226 * mediaPlayer represents a media player plugin.
2227 * @param {String} id id used for the plugin.
2228 * @param {Array<String>} supported_types n array of supported MIME types.
2229 * @param {String} library external script containing the plugin interface code. (mv_<library>Embed.js)
2232 function mediaPlayer(id, supported_types, library)
2235 this.supported_types = supported_types;
2236 this.library = library;
2237 this.loaded = false;
2238 this.loading_callbacks = new Array();
2241 mediaPlayer.prototype =
2244 supported_types:null,
2247 loading_callbacks:null,
2248 supportsMIMEType : function(type)
2250 for (var i=0; i < this.supported_types.length; i++)
2251 if(this.supported_types[i] == type)
2255 getName : function()
2257 return gM('mwe-ogg-player-' + this.id);
2259 load : function(callback){
2260 var libName = this.library+'Embed';
2261 if( mvJsLoader.checkObjPath( libName ) ){
2262 js_log('plugin loaded, do callback:');
2266 //jQuery based get script does not work so well.
2275 /* players and supported mime types
2276 @@todo ideally we query the plugin to get what mime types it supports in practice not always reliable/avaliable
2278 var flowPlayer = new mediaPlayer('flowplayer',['video/x-flv', 'video/h264'],'flash');
2280 var omtkPlayer = new mediaPlayer('omtkplayer',['audio/ogg'], 'omtk' );
2282 var cortadoPlayer = new mediaPlayer('cortado',['video/ogg', 'audio/ogg'],'java');
2283 var videoElementPlayer = new mediaPlayer('videoElement',['video/ogg', 'audio/ogg'],'native');
2285 var vlcMineList = ['video/ogg','audio/ogg', 'video/x-flv', 'video/mp4', 'video/h264'];
2286 var vlcMozillaPlayer = new mediaPlayer('vlc-mozilla',vlcMineList,'vlc');
2287 var vlcActiveXPlayer = new mediaPlayer('vlc-activex',vlcMineList,'vlc');
2290 var oggPluginPlayer = new mediaPlayer('oggPlugin',['video/ogg'],'generic');
2292 //depricate quicktime in favor of safari native
2293 //var quicktimeMozillaPlayer = new mediaPlayer('quicktime-mozilla',['video/ogg'],'quicktime');
2294 //var quicktimeActiveXPlayer = new mediaPlayer('quicktime-activex',['video/ogg'],'quicktime');
2296 var htmlPlayer = new mediaPlayer('html',['text/html', 'image/jpeg', 'image/png', 'image/svg'], 'html');
2299 * mediaPlayers is a collection of mediaPlayer objects supported by the client.
2300 * It could be merged with embedTypes, since there is one embedTypes per script
2301 * and one mediaPlayers per embedTypes.
2303 function mediaPlayers()
2308 mediaPlayers.prototype =
2312 default_players : {},
2315 this.players = new Array();
2316 this.loadPreferences();
2318 //set up default players order for each library type
2319 this.default_players['video/x-flv'] = ['flash','vlc'];
2320 this.default_players['video/h264'] = ['flash', 'vlc'];
2322 this.default_players['video/ogg'] = ['native','vlc','java', 'generic'];
2323 this.default_players['application/ogg'] = ['native','vlc','java', 'generic'];
2324 this.default_players['audio/ogg'] = ['native','vlc', 'java', 'omtk' ];
2325 this.default_players['video/mp4'] = ['vlc'];
2327 this.default_players['text/html'] = ['html'];
2328 this.default_players['image/jpeg'] = ['html'];
2329 this.default_players['image/png'] = ['html'];
2330 this.default_players['image/svg'] = ['html'];
2333 addPlayer : function(player, mime_type)
2335 //js_log('Adding ' + player.id + ' with mime_type ' + mime_type);
2336 for (var i =0; i < this.players.length; i++){
2337 if (this.players[i].id == player.id)
2341 //make sure the mime_type is not already there:
2342 var add_mime = true;
2343 for(var j=0; j < this.players[i].supported_types.length; j++ ){
2344 if( this.players[i].supported_types[j]== mime_type)
2348 this.players[i].supported_types.push(mime_type);
2355 player.supported_types.push(mime_type);
2357 this.players.push( player );
2359 getMIMETypePlayers : function(mime_type)
2361 var mime_players = new Array();
2364 if( this.default_players[mime_type] ){
2365 $j.each( this.default_players[mime_type], function(d, lib){
2366 var library = _this.default_players[mime_type][d];
2367 for ( var i=0; i < _this.players.length; i++ ){
2368 if ( _this.players[i].library == library && _this.players[i].supportsMIMEType(mime_type) ){
2369 mime_players[ inx ] = _this.players[i];
2375 return mime_players;
2377 defaultPlayer : function(mime_type)
2379 js_log("get defaultPlayer for " + mime_type);
2380 var mime_players = this.getMIMETypePlayers(mime_type);
2381 if( mime_players.length > 0)
2383 // check for prior preference for this mime type
2384 for( var i=0; i < mime_players.length; i++ ){
2385 if( mime_players[i].id==this.preference[mime_type] )
2386 return mime_players[i];
2388 // otherwise just return the first compatible player
2389 // (it will be chosen according to the default_players list
2390 return mime_players[0];
2392 js_log( 'No default player found for ' + mime_type );
2395 userSelectFormat : function (mime_format){
2396 this.preference['format_prefrence'] = mime_format;
2397 this.savePreferences();
2399 userSelectPlayer : function(player_id, mime_type)
2401 var selected_player=null;
2402 for(var i=0; i < this.players.length; i++){
2403 if(this.players[i].id == player_id)
2405 selected_player = this.players[i];
2406 js_log('choosing ' + player_id + ' for ' + mime_type);
2407 this.preference[mime_type]=player_id;
2408 this.savePreferences();
2412 if( selected_player )
2414 for(var i=0; i < global_player_list.length; i++)
2416 var embed = $j('#'+global_player_list[i]).get(0);
2417 if(embed.media_element.selected_source && (embed.media_element.selected_source.mime_type == mime_type))
2419 embed.selectPlayer(selected_player);
2420 js_log('using ' + embed.selected_player.getName() + ' for ' + embed.media_element.selected_source.getTitle());
2425 loadPreferences : function()
2427 this.preference = new Object();
2428 // see if we have a cookie set to a clientSupported type:
2429 var cookieVal = $j.cookie( 'ogg_player_exp' );
2432 var pairs = cookieVal.split('&');
2433 for(var i=0; i < pairs.length; i++)
2435 var name_value = pairs[i].split('=');
2436 this.preference[name_value[0]]=name_value[1];
2437 //js_log('load preference for ' + name_value[0] + ' is ' + name_value[1]);
2441 savePreferences : function()
2444 for(var i in this.preference)
2445 cookieVal+= i + '='+ this.preference[i] + '&';
2447 cookieVal=cookieVal.substr(0, cookieVal.length-1);
2448 var week = 7*86400*1000;
2449 $j.cookie( 'ogg_player_exp', cookieVal, { 'expires':week } );
2454 * embedTypes object handles setting and getting of supported embed types:
2455 * closely mirrors OggHandler so that its easier to share efforts in this area:
2456 * http://svn.wikimedia.org/viewvc/mediawiki/trunk/extensions/OggHandler/OggPlayer.js
2463 //detect supported types
2465 this.detect_done=true;
2467 clientSupports: { 'thumbnail' : true },
2468 supportedMimeType: function(mimetype) {
2469 for (var i = navigator.plugins.length; i-- > 0; ) {
2470 var plugin = navigator.plugins[i];
2471 if (typeof plugin[mimetype] != "undefined")
2477 detect: function() {
2478 js_log("running detect");
2479 this.players = new mediaPlayers();
2480 //every browser supports html rendering:
2481 this.players.addPlayer( htmlPlayer );
2482 // In Mozilla, navigator.javaEnabled() only tells us about preferences, we need to
2483 // search navigator.mimeTypes to see if it's installed
2484 var javaEnabled = navigator.javaEnabled();
2485 // In Opera, navigator.javaEnabled() is all there is
2486 var invisibleJava = $j.browser.opera;
2487 // Some browsers filter out duplicate mime types, hiding some plugins
2488 var uniqueMimesOnly = $j.browser.opera || $j.browser.safari;
2489 // Opera will switch off javaEnabled in preferences if java can't be found.
2490 // And it doesn't register an application/x-java-applet mime type like Mozilla does.
2491 if ( invisibleJava && javaEnabled )
2492 this.players.addPlayer( cortadoPlayer );
2495 if($j.browser.msie){
2497 if ( this.testActiveX( 'ShockwaveFlash.ShockwaveFlash')){
2498 //try to get the flash version for omtk include:
2500 a = new ActiveXObject(SHOCKWAVE_FLASH_AX + ".7");
2501 d = a.GetVariable("$version"); // Will crash fp6.0.21/23/29
2503 d = d.split(" ")[1].split(",");
2504 //we need flash version 10 or greater:
2505 if(parseInt( d[0]) >=10){
2506 this.players.addPlayer( omtkPlayer );
2512 //flowplayer has pretty good compatiablity
2513 // (but if we wanted to be fancy we would check for version of flash and update the mp4/h.264 support
2514 this.players.addPlayer( flowPlayer );
2517 if ( this.testActiveX( 'VideoLAN.VLCPlugin.2' ) )
2518 this.players.addPlayer(vlcActiveXPlayer);
2520 if ( javaEnabled && this.testActiveX( 'JavaWebStart.isInstalled' ) )
2521 this.players.addPlayer(cortadoPlayer);
2523 //if ( this.testActiveX( 'QuickTimeCheckObject.QuickTimeCheck.1' ) )
2524 // this.players.addPlayer(quicktimeActiveXPlayer);
2527 if ( typeof HTMLVideoElement == 'object' // Firefox, Safari
2528 || typeof HTMLVideoElement == 'function' ) // Opera
2530 //do another test for safari:
2531 if( $j.browser.safari ){
2533 var dummyvid = document.createElement("video");
2534 if (dummyvid.canPlayType && dummyvid.canPlayType("video/ogg;codecs=\"theora,vorbis\"") == "probably")
2536 this.players.addPlayer( videoElementPlayer );
2537 } else if(this.supportedMimeType( 'video/ogg' )) {
2538 /* older versions of safari do not support canPlayType,
2539 but xiph qt registers mimetype via quicktime plugin */
2540 this.players.addPlayer( videoElementPlayer );
2542 //@@todo add some user nagging to install the xiph qt
2545 js_log('could not run canPlayType in safari');
2548 this.players.addPlayer( videoElementPlayer );
2553 if( navigator.mimeTypes && navigator.mimeTypes.length > 0) {
2554 for ( var i = 0; i < navigator.mimeTypes.length; i++ ) {
2555 var type = navigator.mimeTypes[i].type;
2556 var semicolonPos = type.indexOf( ';' );
2557 if ( semicolonPos > -1 ) {
2558 type = type.substr( 0, semicolonPos );
2560 //js_log('on type: '+type);
2561 var pluginName = navigator.mimeTypes[i].enabledPlugin ? navigator.mimeTypes[i].enabledPlugin.name : '';
2562 if ( !pluginName ) {
2563 // In case it is null or undefined
2566 if ( pluginName.toLowerCase() == 'vlc multimedia plugin' || pluginName.toLowerCase() == 'vlc multimedia plug-in' ) {
2567 this.players.addPlayer(vlcMozillaPlayer, type);
2571 if ( javaEnabled && type == 'application/x-java-applet' ) {
2572 this.players.addPlayer(cortadoPlayer);
2576 if ( type == 'application/ogg' ) {
2577 if ( pluginName.toLowerCase() == 'vlc multimedia plugin' ){
2578 this.players.addPlayer(vlcMozillaPlayer, type);
2579 //else if ( pluginName.indexOf( 'QuickTime' ) > -1 )
2580 // this.players.addPlayer(quicktimeMozillaPlayer);
2582 this.players.addPlayer(oggPluginPlayer);
2585 } else if ( uniqueMimesOnly ) {
2586 if ( type == 'application/x-vlc-player' ) {
2587 this.players.addPlayer(vlcMozillaPlayer, type);
2589 } else if ( type == 'video/quicktime' ) {
2590 //this.players.addPlayer(quicktimeMozillaPlayer);
2595 /*if ( type == 'video/quicktime' ) {
2596 this.players.addPlayer(vlcMozillaPlayer, type);
2599 if(type=='application/x-shockwave-flash'){
2600 this.players.addPlayer( flowPlayer );
2602 //check version to add omtk:
2603 var flashDescription = navigator.plugins["Shockwave Flash"].description;
2604 var descArray = flashDescription.split(" ");
2605 var tempArrayMajor = descArray[2].split(".");
2606 var versionMajor = tempArrayMajor[0];
2607 //js_log("version of flash: " + versionMajor);
2608 if(versionMajor >= 10){
2609 this.players.addPlayer( omtkPlayer );
2615 //@@The xiph quicktime component does not work well with annodex streams (temporarly disable)
2616 //this.clientSupports['quicktime-mozilla'] = false;
2617 //this.clientSupports['quicktime-activex'] = false;
2618 //js_log(this.clientSupports);
2620 testActiveX : function ( name ) {
2623 // No IE, not a class called "name", it's a variable
2624 var obj = new ActiveXObject( '' + name );