1 /* the base video control JSON object with default attributes
2 * for supported attribute details see README
6 "mwe-loading_plugin" : "loading plugin ...",
7 "mwe-select_playback" : "Set playback preference",
8 "mwe-link_back" : "Link back",
9 "mwe-error_swap_vid" : "Error: mv_embed was unable to swap the video tag for the mv_embed interface",
10 "mwe-add_to_end_of_sequence" : "Add to end of sequence",
11 "mwe-missing_video_stream" : "The video file for this stream is missing",
12 "mwe-play_clip" : "Play clip",
13 "mwe-pause_clip" : "Pause clip",
14 "mwe-volume_control" : "Volume control",
15 "mwe-player_options" : "Player options",
16 "mwe-closed_captions" : "Close captions",
17 "mwe-player_fullscreen" : "Fullscreen",
18 "mwe-next_clip_msg" : "Play next clip",
19 "mwe-prev_clip_msg" : "Play previous clip",
20 "mwe-current_clip_msg" : "Continue playing this clip",
21 "mwe-seek_to" : "Seek $1",
22 "mwe-paused" : "paused",
23 "mwe-download_segment" : "Download selection:",
24 "mwe-download_full" : "Download full video file:",
25 "mwe-download_right_click" : "To download, right click and select <i>Save link as...<\/i>",
26 "mwe-download_clip" : "Download video",
27 "mwe-download_text" : "Download text (<a style=\"color:white\" title=\"cmml\" href=\"http:\/\/wiki.xiph.org\/index.php\/CMML\">CMML<\/a> xml):",
28 "mwe-download" : "Download",
29 "mwe-share" : "Share",
30 "mwe-credits" : "Credits",
31 "mwe-clip_linkback" : "Clip source page",
32 "mwe-chose_player" : "Choose video player",
33 "mwe-share_this_video" : "Share this video",
34 "mwe-video_credits" : "Video credits",
35 "mwe-menu_btn" : "Menu",
36 "mwe-close_btn" : "Close",
37 "mwe-ogg-player-vlc-mozilla" : "VLC plugin",
38 "mwe-ogg-player-videoElement" : "Native Ogg video",
39 "mwe-ogg-player-vlc-activex" : "VLC ActiveX",
40 "mwe-ogg-player-oggPlugin" : "Generic Ogg plugin",
41 "mwe-ogg-player-quicktime-mozilla" : "QuickTime plugin",
42 "mwe-ogg-player-quicktime-activex" : "QuickTime ActiveX",
43 "mwe-ogg-player-cortado" : "Java Cortado",
44 "mwe-ogg-player-flowplayer" : "Flowplayer",
45 "mwe-ogg-player-selected" : "(selected)",
46 "mwe-ogg-player-omtkplayer" : "OMTK Flash Vorbis",
47 "mwe-generic_missing_plugin" : "You browser does not appear to support the following playback type: <b>$1<\/b><br \/>Visit the <a href=\"http:\/\/commons.wikimedia.org\/wiki\/Commons:Media_help\">Playback Methods<\/a> page to download a player.<br \/>",
48 "mwe-for_best_experience" : "For a better video playback experience we recommend:<br \/><b><a href=\"http:\/\/www.mozilla.com\/en-US\/firefox\/upgrade.html?from=mwEmbed\">Firefox 3.5<\/a>.<\/b>",
49 "mwe-do_not_warn_again" : "Dismiss for now.",
50 "mwe-playerselect" : "Players",
51 "mwe-read_before_embed" : "<a href=\"http:\/\/mediawiki.org\/wiki\/Security_Notes_on_Remote_Embedding\" target=\"_new\">Read this<\/a> before embedding.",
52 "mwe-embed_site_or_blog" : "Embed on a page",
53 "mwe-related_videos" : "Related videos",
54 "mwe-seeking" : "seeking",
55 "mwe-copy-code" : "Copy code"
59 var commons_api_url
= 'http://commons.wikimedia.org/w/api.php';
61 var default_video_attributes
= {
79 //roe url (for xml based metadata)
81 //if roe includes metadata tracks we can expose a link to metadata
82 "show_meta_link":true,
84 //default state attributes per html5 spec:
85 //http://www.whatwg.org/specs/web-apps/current-work/#video)
87 "readyState":0, //http://www.whatwg.org/specs/web-apps/current-work/#readystate
88 "currentTime":0, //current playback position (should be updated by plugin)
89 "duration":null, //media duration (read from file or the temporal url)
92 "startOffset":null, //if serving an ogg_chop segment use this to offset the presentation time
94 //custom attributes for mv_embed:
100 "type":null, //the content type of the media
102 "skin_name":null //if you want to select a custom skin per video tag.
105 * the base source attribute checks
107 var mv_default_source_attr
= new Array(
111 'URLTimeEncoding', //boolean if we support temporal url requests on the source media
119 //set the dismissNativeWarn flag:
120 _global
['dismissNativeWarn'] = false;
122 * Converts all occurrences of <video> tag into video object
124 function mv_video_embed(swap_done_callback
, force_id
){
126 mvEmbed
.init( swap_done_callback
, force_id
);
129 //flist stores the set of functions to run after the video has been swapped in.
131 init:function( swap_done_callback
, force_id
){
132 if(swap_done_callback
)
133 mvEmbed
.flist
.push( swap_done_callback
);
135 //get mv_embed location if it has not been set
136 js_log('mv_video_embed:: ' + $mw
.version
);
138 var loadPlaylistLib
=false;
140 var eAction = function(this_elm
){
141 js_log( "Do SWAP: " + $j(this_elm
).attr("id") + ' tag: '+ this_elm
.tagName
.toLowerCase() );
143 if( $j(this_elm
).attr("id") == '' ){
144 $j(this_elm
).attr("id", 'v'+ $mw
.player_list
.length
);
146 //store a global reference to the id
147 $mw
.player_list
.push( $j(this_elm
).attr("id") );
150 switch( this_elm
.tagName
.toLowerCase()){
152 var videoInterface
= new embedVideo(this_elm
);
153 mvEmbed
.swapEmbedVideoElement( this_elm
, videoInterface
);
156 var videoInterface
= new embedVideo(this_elm
);
157 mvEmbed
.swapEmbedVideoElement( this_elm
, videoInterface
);
160 loadPlaylistLib
=true;
164 if( force_id
== null && force_id
!= '' ){
165 var j_selector
= 'video,audio,playlist';
167 var j_selector
= '#' + force_id
;
170 js_log('j_selector:: ' + j_selector
);
171 //process selected elements:
172 //ie8 does not play well with the jQuery video,audio,playlist selector use native:
173 if($j
.browser
.msie
&& $j
.browser
.version
>= 8){
174 jtags
= j_selector
.split(',');
175 for( var i
=0; i
< jtags
.length
; i
++){
176 $j( document
.getElementsByTagName( jtags
[i
] )).each(function(){
181 $j( j_selector
).each(function(){
188 '$j.ui', //include dialog for pop-ing up thigns
191 //deal with each playlist instance
192 $j('playlist').each(function(){
193 //create new playlist interface:
194 var plObj
= new mvPlayList( this );
195 mvEmbed
.swapEmbedVideoElement(this, plObj
);
196 var added_height
= plObj
.pl_layout
.title_bar_height
+ plObj
.pl_layout
.control_height
;
197 //move into a blocking display container with height + controls + title height:
198 $j('#'+plObj
.id
).wrap('<div style="display:block;height:' + (plObj
.height
+ added_height
) + 'px;"></div>');
202 this.checkClipsReady();
205 * swapEmbedVideoElement
206 * takes a video element as input and swaps it out with
207 * an embed video interface based on the video_elements attributes
209 swapEmbedVideoElement:function(video_element
, videoInterface
){
210 js_log('do swap ' + videoInterface
.id
+ ' for ' + video_element
);
211 embed_video
= document
.createElement('div');
212 //make sure our div has a hight/width set:
214 $j(embed_video
).css({
215 'width' : videoInterface
.width
,
216 'height' : videoInterface
.height
217 }).html( mv_get_loading_img() );
218 //inherit the video interface
219 for(var method
in videoInterface
){ //for in loop oky in Element context
220 if(method
!='readyState'){ //readyState crashes IE
222 embed_video
.setAttribute('style', videoInterface
[method
]);
223 }else if(method
=='class'){
224 if( $j
.browser
.msie
)
225 embed_video
.setAttribute("className", videoInterface
['class']);
227 embed_video
.setAttribute("class", videoInterface
['class']);
230 embed_video
[method
]=videoInterface
[method
];
234 if(embed_video
[method
]=="false")embed_video
[method
]=false;
235 if(embed_video
[method
]=="true")embed_video
[method
]=true;
237 ///js_log('did vI style');
238 //now swap out the video element for the embed_video obj:
239 $j(video_element
).after(embed_video
).remove();
240 //js_log('did swap');
241 $j('#'+embed_video
.id
).get(0).on_dom_swap();
243 // now that "embed_video" is stable, do more initialization (if we are ready)
244 if($j('#'+embed_video
.id
).get(0).loading_external_data
== false
245 && $j('#'+embed_video
.id
).get(0).init_with_sources_loadedDone
== false){
246 //load and set ready state since source are available:
247 $j('#'+embed_video
.id
).get(0).init_with_sources_loaded();
250 js_log('done with child: ' + embed_video
.id
+ ' len:' + $mw
.player_list
.length
);
253 //this should not be needed.
254 checkClipsReady : function(){
255 //js_log('checkClipsReady');
257 for(var i
=0; i
< $mw
.player_list
.length
; i
++){
258 if( $j('#'+$mw
.player_list
[i
]).length
!=0){
259 var cur_vid
= $j('#'+$mw
.player_list
[i
]).get(0);
260 is_ready
= ( cur_vid
.ready_to_play
) ? is_ready
: false;
261 if( !is_ready
&& cur_vid
.load_error
){
263 $j(cur_vid
).html( cur_vid
.load_error
);
268 mvEmbed
.allClipsReady
= true;
269 // run queued functions
270 //js_log('run queded functions:' + mvEmbed.flist[0]);
273 setTimeout( 'mvEmbed.checkClipsReady()', 25 );
277 while (this.flist
.length
){
278 this.flist
.shift()();
284 * mediaSource class represents a source for a media element.
285 * @param {String} type MIME type of the source.
286 * @param {String} uri URI of the source.
289 function mediaSource(element
)
295 mediaSource
.prototype =
297 /** MIME type of the source. */
299 /** URI of the source. */
301 /** Title of the source. */
303 /** True if the source has been marked as the default. */
304 marked_default
:false,
305 /** True if the source supports url specification of offset and duration */
306 URLTimeEncoding
:false,
307 /** Start offset of the requested segment */
309 /** Duration of the requested segment (0 if not known) */
312 upddate_interval
:null,
318 init : function(element
)
320 //js_log('adding mediaSource: ' + element);
321 this.src
= $j(element
).attr('src');
322 this.marked_default
= false;
323 if ( element
.tagName
.toLowerCase() == 'video')
324 this.marked_default
= true;
326 //set default URLTimeEncoding if we have a time url:
327 //not ideal way to discover if content is on an oggz_chop server.
328 //should check some other way.
329 var pUrl
= parseUri ( this.src
);
330 if(typeof pUrl
['queryKey']['t'] != 'undefined'){
331 this['URLTimeEncoding']=true;
333 for(var i
=0; i
< mv_default_source_attr
.length
; i
++){ //array loop:
334 var attr
= mv_default_source_attr
[ i
];
335 if( $j(element
).attr( attr
) ) {
336 this[ attr
] = $j(element
).attr( attr
);
339 //update duration from hit if present:
340 if(this.durationHint
)
341 this.duration
= this.durationHint
;
344 if ( $j(element
).attr('type'))
345 this.mime_type
= $j(element
).attr('type');
346 else if ($j(element
).attr('content-type'))
347 this.mime_type
= $j(element
).attr('content-type');
349 this.mime_type
= this.detectType(this.src
);
351 //set the title if unset:
353 this.title
= this.mime_type
;
355 this.parseURLDuration();
357 updateSource:function(element
){
358 //for now just update the title:
359 if ($j(element
).attr("title"))
360 this.title
= $j(element
).attr("title");
362 /** updates the src time and start & end
363 * @param {String} start_time in NTP format
364 * @param {String} end_time in NTP format
366 updateSrcTime:function (start_ntp
, end_ntp
){
367 //js_log("f:updateSrcTime: "+ start_ntp+'/'+ end_ntp + ' from org: ' + this.start_ntp+ '/'+this.end_ntp);
368 //js_log("pre uri:" + this.src);
369 //if we have time we can use:
370 if( this.URLTimeEncoding
){
371 //make sure its a valid start time / end time (else set default)
372 if( !npt2seconds(start_ntp
) )
373 start_ntp
= this.start_ntp
;
375 if( !npt2seconds(end_ntp
) )
376 end_ntp
= this.end_ntp
;
378 this.src
= getURLParamReplace(this.src
, { 't': start_ntp
+'/'+ end_ntp
} );
380 //update the duration
381 this.parseURLDuration();
384 setDuration:function (duration
)
386 this.duration
= duration
;
388 this.end_ntp
= seconds2npt( this.start_offset
+ duration
);
391 /** MIME type accessor function.
392 @return the MIME type of the source.
395 getMIMEType : function()
397 return this.mime_type
;
399 /** URI accessor function.
400 * @param int seek_time_sec (used to adjust the URI for url based seeks)
401 @return the URI of the source.
404 getURI : function( seek_time_sec
)
406 if( !seek_time_sec
|| !this.URLTimeEncoding
){
412 var endvar
= '/'+ this.end_ntp
;
414 return getURLParamReplace(this.src
, { 't': seconds2npt( seek_time_sec
)+endvar
} ); ;
416 /** Title accessor function.
417 @return the title of the source.
420 getTitle : function()
424 /** Index accessor function.
425 @return the source's index within the enclosing mediaElement container.
428 getIndex : function()
433 * function getDuration in milliseconds
434 * special case derive duration from request url
435 * supports media_url?t=ntp_start/ntp_end url request format
437 parseURLDuration : function(){
438 //check if we have a URLTimeEncoding:
439 if( this.URLTimeEncoding
){
440 var annoURL
= parseUri( this.src
);
441 if( annoURL
.queryKey
['t'] ){
442 var times
= annoURL
.queryKey
['t'].split('/');
443 this.start_ntp
= times
[0];
444 this.end_ntp
= times
[1];
445 this.start_offset
= npt2seconds( this.start_ntp
);
446 this.duration
= npt2seconds( this.end_ntp
) - this.start_offset
;
448 //look for this info as attributes
449 if(this.startOffset
){
450 this.start_offset
= this.startOffset
;
451 this.start_ntp
= seconds2npt( this.startOffset
);
454 this.end_ntp
= seconds2npt( parseInt(this.duration
) + parseInt(this.start_offset
) );
458 //else nothing to parse just keep whatever info we already have
460 //js_log('f:parseURLDuration() for:' + this.src + ' d:' + this.duration);
462 /** Attempts to detect the type of a media file based on the URI.
463 @param {String} uri URI of the media file.
464 @returns The guessed MIME type of the file.
467 detectType:function(uri
)
469 //@@todo if media is on the same server as the javascript or we have mv_proxy configured
470 //we can issue a HEAD request and read the mime type of the media...
471 // (this will detect media mime type independently of the url name)
472 //http://www.jibbering.com/2002/4/httprequest.html (this should be done by extending jquery's ajax objects)
473 var end_inx
= (uri
.indexOf('?')!=-1)? uri
.indexOf('?') : uri
.length
;
474 var no_param_uri
= uri
.substr(0, end_inx
);
475 switch( no_param_uri
.substr(no_param_uri
.lastIndexOf('.'),4).toLowerCase() ){
476 case '.flv':return 'video/x-flv';break;
477 case '.ogg': case '.ogv': return 'video/ogg';break;
478 case '.oga': return 'audio/ogg'; break;
479 case '.anx':return 'video/ogg';break;
484 /** A media element corresponding to a <video> element.
485 It is implemented as a collection of mediaSource objects. The media sources
486 will be initialized from the <video> element, its child <source> elements,
487 and/or the ROE file referenced by the <video> element.
488 @param {element} video_element <video> element used for initialization.
491 function mediaElement(video_element
)
493 this.init(video_element
);
496 mediaElement
.prototype =
498 /** The array of mediaSource elements. */
501 /** Selected mediaSource element. */
502 selected_source
:null,
507 init:function( video_element
)
510 js_log('Initializing mediaElement...' );
511 this.sources
= new Array();
512 this.thumbnail
= mv_default_thumb_url
;
513 // Process the source element:
514 if($j(video_element
).attr("src"))
515 this.tryAddSource(video_element
);
517 if($j(video_element
).attr('thumbnail'))
518 this.thumbnail
= $j(video_element
).attr('thumbnail');
520 if($j(video_element
).attr('poster'))
521 this.thumbnail
= $j(video_element
).attr('poster');
523 if($j(video_element
).attr('wikiTitleKey'))
524 this.wikiTitleKey
=$j(video_element
).attr('wikiTitleKey');
526 // Process all inner <source> elements
527 //js_log("inner source count: " + video_element.getElementsByTagName('source').length );
529 $j(video_element
).find('source,text').each(function(inx
, inner_source
){
530 _this
.tryAddSource( inner_source
);
533 /** Updates the time request for all sources that have a standard time request argument (ie &t=start_time/end_time)
535 updateSourceTimes:function(start_ntp
, end_ntp
){
537 $j
.each(this.sources
, function(inx
, mediaSource
){
538 mediaSource
.updateSrcTime(start_ntp
, end_ntp
);
542 timedTextSources:function(){
543 for(var i
=0; i
< this.sources
.length
; i
++){
544 if( this.sources
[i
].mime_type
== 'text/cmml' ||
545 this.sources
[i
].mime_type
== 'text/x-srt')
550 /** Returns the array of mediaSources of this element.
551 \returns {Array} Array of mediaSource elements.
553 getSources:function( mime_filter
)
558 var source_set
= new Array();
559 for(var i
=0; i
< this.sources
.length
; i
++){
560 if( this.sources
[i
].mime_type
.indexOf( mime_filter
) != -1 )
561 source_set
.push( this.sources
[i
] );
565 getSourceById:function( source_id
){
566 for(var i
=0; i
< this.sources
.length
; i
++){
567 if( this.sources
[i
].id
== source_id
)
568 return this.sources
[i
];
572 /** Selects a particular source for playback.
574 selectSource:function(index
)
576 js_log('f:selectSource:'+index
);
577 var playable_sources
= this.getPlayableSources();
578 for(var i
=0; i
< playable_sources
.length
; i
++){
580 this.selected_source
= playable_sources
[i
];
581 //update the user selected format:
582 embedTypes
.players
.userSelectFormat( playable_sources
[i
].mime_type
);
587 /** selects the default source via cookie preference, default marked, or by id order
589 autoSelectSource:function(){
590 js_log('f:autoSelectSource:');
591 //@@todo read user preference for source
592 // Select the default source
593 var playable_sources
= this.getPlayableSources();
594 var flash_flag
=ogg_flag
=false;
596 for(var source
=0; source
< playable_sources
.length
; source
++){
597 var mime_type
=playable_sources
[source
].mime_type
;
598 if( playable_sources
[source
].marked_default
){
599 js_log('set via marked default: ' + playable_sources
[source
].marked_default
);
600 this.selected_source
= playable_sources
[source
];
603 //set via user-preference
604 if(embedTypes
.players
.preference
['format_prefrence'] == mime_type
){
605 js_log('set via preference: '+playable_sources
[source
].mime_type
);
606 this.selected_source
= playable_sources
[source
];
610 //set Ogg via player support
611 for(var source
=0; source
< playable_sources
.length
; source
++){
612 js_log('f:autoSelectSource:' + playable_sources
[source
].mime_type
);
613 var mime_type
=playable_sources
[source
].mime_type
;
614 //set source via player
615 if(mime_type
=='video/ogg' || mime_type
=='ogg/video' || mime_type
=='video/annodex' || mime_type
=='application/ogg'){
616 for(var i
=0; i
< embedTypes
.players
.players
.length
; i
++){ //for in loop on object oky
617 var player
= embedTypes
.players
.players
[i
];
618 if(player
.library
=='vlc' || player
.library
=='native'){
619 js_log('set via ogg via order');
620 this.selected_source
= playable_sources
[source
];
627 for(var source
=0; source
< playable_sources
.length
; source
++){
628 var mime_type
=playable_sources
[source
].mime_type
;
629 if( mime_type
=='video/x-flv' ){
630 js_log('set via by player preference normal flash')
631 this.selected_source
= playable_sources
[source
];
636 for(var source
=0; source
< playable_sources
.length
; source
++){
637 var mime_type
=playable_sources
[source
].mime_type
;
638 if( mime_type
=='video/h264' ){
639 js_log('set via playable_sources preference h264 flash')
640 this.selected_source
= playable_sources
[source
];
644 //select first source
645 if (!this.selected_source
)
647 js_log('set via first source:' + playable_sources
[0]);
648 this.selected_source
= playable_sources
[0];
652 /** Returns the thumbnail URL for the media element.
653 \returns {String} thumbnail URL
655 getThumbnailURL:function()
657 return this.thumbnail
;
659 /** Checks whether there is a stream of a specified MIME type.
660 @param {String} mime_type MIME type to check.
661 @type {BooleanPrimitive}.
663 hasStreamOfMIMEType:function(mime_type
)
665 for(source
in this.sources
)
667 if(this.sources
[source
].getMIMEType() == mime_type
)
672 isPlayableType:function(mime_type
)
674 if( embedTypes
.players
.defaultPlayer( mime_type
) ){
679 //if(this.selected_player){
680 //return mime_type=='video/ogg' || mime_type=='ogg/video' || mime_type=='video/annodex' || mime_type=='video/x-flv';
682 /** Adds a single mediaSource using the provided element if
683 the element has a 'src' attribute.
684 @param element {element} <video>, <source> or <mediaSource> element.
686 tryAddSource:function(element
)
688 js_log('f:tryAddSource:'+ $j(element
).attr("src"));
689 if (! $j(element
).attr("src")){
690 //js_log("element has no src");
693 var new_src
= $j(element
).attr('src');
694 //make sure an existing element with the same src does not already exist:
695 for( var i
=0; i
< this.sources
.length
; i
++ ){
696 if(this.sources
[i
].src
== new_src
){
697 //js_log('checking existing: '+this.sources[i].getURI() + ' != '+ new_src);
698 //can't add it all but try to update any additional attr:
699 this.sources
[i
].updateSource(element
);
703 var source
= new mediaSource( element
);
704 this.sources
.push(source
);
705 //alert('pushed source to stack'+ source + 'sl:'+this.sources.length);
707 getPlayableSources: function(){
708 var playable_sources
= new Array();
709 for(var i
=0; i
< this.sources
.length
; i
++){
710 if( this.isPlayableType( this.sources
[i
].mime_type
) ){
711 playable_sources
.push( this.sources
[i
] );
713 js_log("type "+ this.sources
[i
].mime_type
+ 'is not playable');
716 return playable_sources
;
718 /* Imports media sources from ROE data.
719 * @param roe_data ROE data.
721 addROE:function(roe_data
){
723 this.addedROEData
=true;
725 if( typeof roe_data
== 'string' )
727 var parser
=new DOMParser();
728 js_log('ROE data:' + roe_data
);
729 roe_data
=parser
.parseFromString(roe_data
,"text/xml");
732 $j
.each(roe_data
.getElementsByTagName('mediaSource'), function(inx
, source
){
733 _this
.tryAddSource(source
);
736 $j
.each(roe_data
.getElementsByTagName('img'), function(inx
, n
){
737 if($j(n
).attr("id")=="stream_thumb"){
738 js_log('roe:set thumb to '+$j(n
).attr("src"));
739 _this
['thumbnail'] =$j(n
).attr("src");
743 $j
.each(roe_data
.getElementsByTagName('link'), function(inx
, n
){
744 if($j(n
).attr('id')=='html_linkback'){
745 js_log('roe:set linkback to '+$j(n
).attr("href"));
746 _this
['linkback'] = $j(n
).attr('href');
750 js_log('ROE data empty.');
756 /** base embedVideo object
757 @param element <video> tag used for initialization.
760 var embedVideo = function(element
) {
761 return this.init(element
);
764 embedVideo
.prototype = {
765 /** The mediaElement object containing all mediaSource objects */
768 ready_to_play
:false, //should use html5 ready state
769 load_error
:false, //used to set error in case of error
770 loading_external_data
:false,
771 thumbnail_updating
:false,
773 init_with_sources_loadedDone
:false,
775 //for onClip done stuff:
776 anno_data_cache
:null,
778 base_seeker_slider_offset
:null,
779 onClipDone_disp
:false,
781 //for seek thumb updates:
782 cur_thumb_seek_time
:0,
783 thumb_seek_interval
:null,
784 //set the default tag type to video:
786 //set the buffered percent:
788 //utility functions for property values:
789 hx : function ( s
) {
790 if ( typeof s
!= 'String' ) {
793 return s
.replace( /&/g
, '&' )
794 . replace( /</g
, '<' )
795 . replace( />/g
, '>' );
797 hq : function ( s
) {
798 return '"' + this.hx( s
) + '"';
800 playerPixelWidth : function()
802 var player
= $j('#mv_embedded_player_'+this.id
).get(0);
803 if(typeof player
!='undefined' && player
['offsetWidth'])
804 return player
.offsetWidth
;
806 return parseInt(this.width
);
808 playerPixelHeight : function()
810 var player
= $j('#mv_embedded_player_'+this.id
).get(0);
811 if(typeof player
!='undefined' && player
['offsetHeight'])
812 return player
.offsetHeight
;
814 return parseInt(this.height
);
816 init: function(element
){
817 //inherit all the default video_attributes
818 for(var attr
in default_video_attributes
){ //for in loop oky on user object
819 if(element
.getAttribute(attr
)){
820 this[ attr
]=element
.getAttribute(attr
);
822 this[attr
]=default_video_attributes
[attr
];
826 //set the skin name from the config (if not set locally)
827 if( !this.skin_name
)
828 this.skin_name
= $mw
.conf
['skin_name'];
830 //make sure startOffset is cast as an int
831 if( this.startOffset
&& this.startOffset
.split(':').length
>= 2)
832 this.startOffset
= npt2seconds(this.startOffset
);
833 //make sure offset is in float:
834 this.startOffset
= parseFloat(this.startOffset
);
836 if( this.duration
&& this.duration
.split(':').length
>= 2)
837 this.duration
= npt2seconds( this.duration
);
838 //make sure duration is in float:
839 this.duration
= parseFloat(this.duration
);
840 js_log("duration is: " + this.duration
);
843 var dwh
= $mw
.conf
['video_size'].split('x');
844 this.width
= element
.style
.width
? element
.style
.width
: dwh
[0];
845 if( element
.tagName
== 'AUDIO' ){
846 this.height
= element
.style
.height
? element
.style
.height
: 0;
848 this.height
= element
.style
.height
? element
.style
.height
: dwh
[1];
852 this.pid
= 'pid_' + this.id
;
854 //grab any innerHTML and set it to missing_plugin_html
855 //@@todo we should strip source tags instead of checking and skipping
856 if(element
.innerHTML
!='' && element
.getElementsByTagName('source').length
==0){
857 js_log('innerHTML: ' + element
.innerHTML
);
858 this.user_missing_plugin_html
=element
.innerHTML
;
860 // load all of the specified sources
861 this.media_element
= new mediaElement(element
);
863 //if we are displaying controls setup the ctrlBuilder
865 //set-up the local ctrlBuilder instance:
866 this.ctrlBuilder
= new ctrlBuilder( this );
867 //load the css for the current player
871 loadExternalCss( mv_embed_path
+ 'skins/' + this.skin_name
+ '/playerSkin.css');
873 on_dom_swap: function(){
874 js_log('f:on_dom_swap');
875 // Process the provided ROE file... if we don't yet have sources
876 if(this.roe
&& this.media_element
.sources
.length
==0 ){
877 js_log('loading external data');
878 this.loading_external_data
=true;
880 do_request(this.roe
, function(data
)
883 _this
.media_element
.addROE( data
);
884 js_log('added_roe::' + _this
.media_element
.sources
.length
);
886 js_log('set loading_external_data=false');
887 _this
.loading_external_data
=false;
889 _this
.init_with_sources_loaded();
893 init_with_sources_loaded : function()
895 js_log('f:init_with_sources_loaded');
896 //set flag that we have run this function:
897 this.init_with_sources_loadedDone
=true;
898 //autoseletct the source
899 this.media_element
.autoSelectSource();
900 //auto select player based on default order
901 if( !this.media_element
.selected_source
)
903 //check for parent clip:
904 if( typeof this.pc
!= 'undefined' ){
905 js_log('no sources, type:' +this.type
+ ' check for html');
907 //do load player if just displaying innerHTML:
908 if( this.pc
.type
== 'text/html' ){
909 this.selected_player
= embedTypes
.players
.defaultPlayer( 'text/html' );
910 js_log('set selected player:'+ this.selected_player
.mime_type
);
914 this.selected_player
= embedTypes
.players
.defaultPlayer( this.media_element
.selected_source
.mime_type
);
916 if( this.selected_player
){
917 js_log('selected ' + this.selected_player
.getName());
918 js_log("PLAYBACK TYPE: "+this.selected_player
.library
);
919 this.thumbnail_disp
= true;
920 this.inheritEmbedObj();
922 //no source's playable
923 var missing_type
='';
925 for( var i
=0; i
< this.media_element
.sources
.length
; i
++){
926 missing_type
+= or
+ this.media_element
.sources
[i
].mime_type
;
930 var missing_type
= this.pc
.type
;
931 js_log('no player found for given source type ' + missing_type
);
932 this.load_error
= this.getPluginMissingHTML(missing_type
);
935 inheritEmbedObj:function(){
936 js_log("inheritEmbedObj:duration is: " + this.duration
);
937 //@@note: tricky cuz direct overwrite is not so ideal.. since the extended object is already tied to the dom
938 //clear out any non-base embedObj stuff:
940 eval('tmpObj = '+this.instanceOf
);
941 for(var i
in tmpObj
){ //for in loop oky for object
942 if(this['parent_'+i
]){
943 this[i
]=this['parent_'+i
];
949 //set up the new embedObj
950 js_log('f: inheritEmbedObj: embedding with ' + this.selected_player
.library
);
952 this.selected_player
.load( function(){
953 //js_log('inheriting '+_this.selected_player.library +'Embed to ' + _this.id + ' ' + $j('#'+_this.id).length);
954 eval('embedObj = ' +_this
.selected_player
.library
+'Embed;');
955 for(var method
in embedObj
){ //for in loop oky for object
956 //parent method preservation for local overwritten methods
958 _this
['parent_' + method
] = _this
[method
];
959 _this
[method
]=embedObj
[method
];
961 js_log('TYPEOF_ppause: ' + typeof _this
['parent_pause']);
963 if(_this
.inheritEmbedOverride
){
964 _this
.inheritEmbedOverride();
966 //update controls if possible
967 if(!_this
.loading_external_data
)
968 _this
.refreshControlsHTML();
970 //js_log("READY TO PLAY:"+_this.id);
971 _this
.ready_to_play
=true;
976 selectPlayer:function(player
)
979 if(this.selected_player
.id
!= player
.id
){
980 this.selected_player
= player
;
981 this.inheritEmbedObj();
984 doNativeWarningCheck:function(){
985 if( $j
.cookie('dismissNativeWarn') && $j
.cookie('dismissNativeWarn')===true){
988 //see if we have native support for ogg:
989 var supporting_players
= embedTypes
.players
.getMIMETypePlayers( 'video/ogg' );
990 for(var i
=0; i
< supporting_players
.length
; i
++){
991 if(supporting_players
[i
].id
== 'videoElement'){
995 //see if we are using mv_embed without a ogg source in which case no point in promoting firefox :P
996 if(this.media_element
&& this.media_element
.sources
){
997 var foundOgg
= false;
998 var playable_sources
= this.media_element
.getPlayableSources();
999 for(var sInx
=0; sInx
< playable_sources
.length
; sInx
++){
1000 var mime_type
= playable_sources
[sInx
].mime_type
;
1001 if( mime_type
=='video/ogg' ){
1005 //no ogg src... no point in download firefox link
1013 getTimeReq:function(){
1014 var et
= (this.ctrlBuilder
.long_time_disp
)? '/' + seconds2npt( this.getDuration() ) : '';
1015 var default_time_req
= '0:00:00' + et
;
1016 if(!this.media_element
)
1017 return default_time_req
;
1018 if(!this.media_element
.selected_source
)
1019 return default_time_req
;
1020 if(!this.media_element
.selected_source
.end_ntp
)
1021 return default_time_req
;
1022 var et
= (this.ctrlBuilder
.long_time_disp
) ?'/'+this.media_element
.selected_source
.end_ntp
: '';
1023 return this.media_element
.selected_source
.start_ntp
+ et
;
1025 getDuration:function(){
1026 //update some local pointers for the selected source:
1027 if(this.media_element
&& this.media_element
.selected_source
&& this.media_element
.selected_source
.duration
){
1028 this.duration
= this.media_element
.selected_source
.duration
;
1029 this.start_offset
= this.media_element
.selected_source
.start_offset
;
1030 this.start_ntp
= this.media_element
.selected_source
.start_ntp
;
1031 this.end_ntp
= this.media_element
.selected_source
.end_ntp
;
1033 //update start end_ntp if duration !=0 (set from plugin)
1035 this.start_ntp
= '0:0:0';
1036 if(!this.end_ntp
&& this.duration
)
1037 this.end_ntp
= seconds2npt( this.duration
);
1038 //return the duration
1039 return this.duration
;
1042 * wrapEmebedContainer
1043 * wraps the embed code into a container to better support playlist function
1044 * (where embed element is swapped for next clip
1045 * (where plugin method does not support playlist)
1047 wrapEmebedContainer:function(embed_code
){
1048 //check if parent clip is set( ie we are in a playlist so name the embed container by playlistID)
1049 var id
= (this.pc
!=null)?this.pc
.pp
.id
:this.id
;
1050 return '<div id="mv_ebct_'+id
+'" style="width:'+this.width
+'px;height:'+this.height
+'px;">' +
1054 getEmbedHTML : function(){
1055 //return this.wrapEmebedContainer( this.getEmbedObj() );
1056 return 'function getEmbedHTML should be overitten by embedLib ';
1058 //do seek function (should be overwritten by implementing embedLibs)
1059 // to check if seek can be done on locally downloaded content.
1060 doSeek : function( perc
){
1061 if( this.supportsURLTimeEncoding() ){
1062 //make sure this.seek_time_sec is up-to-date:
1063 this.seek_time_sec
= npt2seconds( this.start_ntp
) + parseFloat( perc
* this.getDuration() );
1064 js_log('updated seek_time_sec: ' + seconds2npt ( this.seek_time_sec
) );
1066 this.didSeekJump
=true;
1068 this.setSliderValue( perc
);
1070 //do play in 100ms (give things time to clear)
1071 setTimeout('$j(\'#' + this.id
+ '\').get(0).play()',100);
1074 * seeks to the requested time and issues a callback when ready
1075 * (should be overwitten by client that supports frame serving)
1077 setCurrentTime:function( time
, callback
){
1078 js_log('error: base embed setCurrentTime can not frame serve (override via plugin)');
1080 addPresTimeOffset:function(){
1081 //add in the offset:
1082 if(this.seek_time_sec
&& this.seek_time_sec
!=0){
1083 this.currentTime
+=this.seek_time_sec
;
1084 }else if(this.start_offset
&& this.start_offset
!=0){
1085 this.currentTime
= parseFloat(this.currentTime
) + parseFloat(this.start_offset
);
1088 doEmbedHTML:function()
1090 js_log('f:doEmbedHTML');
1091 js_log('thum disp:'+this.thumbnail_disp
);
1093 this.closeDisplayedHTML();
1095 // if(!this.selected_player){
1096 // return this.getPluginMissingHTML();
1097 //Set "loading" here
1098 $j('#mv_embedded_player_'+_this
.id
).html(''+
1099 '<div style="color:black;width:'+this.width
+'px;height:'+this.height
+'px;">' +
1100 gM('mwe-loading_plugin') +
1103 // schedule embedding
1104 this.selected_player
.load(function()
1106 js_log('performing embed for ' + _this
.id
);
1107 var embed_code
= _this
.getEmbedHTML();
1108 //js_log('shopuld embed:' + embed_code);
1109 $j('#mv_embedded_player_'+_this
.id
).html(embed_code
);
1112 mvVideoAudioSearch:function(){
1114 js_log('switch video Relational' );
1117 'titles' : this.wikiTitleKey
,
1118 'generator' : 'categories'
1120 var req_categories
= new Array();
1123 'url' : commons_api_url
1125 req_categories
= Array();
1126 if(data
.query
&& data
.query
.pages
){
1127 for(var pageid
in data
.query
.pages
){
1128 if(data
.query
.pages
[pageid
].title
)
1129 req_categories
.push(data
.query
.pages
[pageid
].title
);
1132 _this
.getRelatedFromCat( req_categories
);
1135 getRelatedFromCat:function(catAry
){
1136 js_log('getRelatedFromCat');
1138 for ( var i
= 0 ; i
<= catAry
.length
;i
++ ){
1143 'generator' : 'categorymembers' ,
1144 'gcmtitle' : catAry
[i
],
1145 'prop' : 'imageinfo',
1151 'url': commons_api_url
1154 $j('#dc_'+ _this
.id
+ ' .related_vids ul').html(' ');
1156 for(var j
in data
.query
.pages
){
1157 //setup poster default:
1158 var local_poster
="http://upload.wikimedia.org/wikipedia/commons/7/79/Wiki-commons.png";
1159 //make sure it exists:
1160 var page
= data
.query
.pages
[j
];
1161 if( j
> 0 && page
&& page
['imageinfo'] ){
1162 if( page
['imageinfo'][0].thumburl
){
1163 local_poster
= page
['imageinfo'][0].thumburl
;
1165 var descriptionurl
= page
['imageinfo'][0].descriptionurl
;
1166 var title_str
= page
.title
.replace( /File:|.ogv$|.oga$|.ogg$/gi, "" );
1167 //only link to other videos:
1168 if ( descriptionurl
.match( /\.ogg$|\.ogv$|\.oga$/gi ) != null) {
1169 var liout
= '<li>' +
1170 '<a href="' + descriptionurl
+ '" >' +
1171 '<img src="' + local_poster
+ '">' +
1173 ' <a title="' + title_str
+ '" target="_blank" ' +
1174 'href="'+ descriptionurl
+'">' + title_str
+ '</a>' +
1176 $j('#dc_'+ _this
.id
+ ' .related_vids ul').append(liout
) ;
1180 }); //end do_api_req
1183 onClipDone:function(){
1184 js_log('base:onClipDone');
1185 //stop the clip (load the thumbnail etc)
1187 this.seek_time_sec
= 0;
1188 this.setSliderValue(0);
1191 if(this.width
< 300){
1194 this.onClipDone_disp
=true;
1195 this.thumbnail_disp
=true;
1197 //make sure we are not in preview mode( no end clip actions in preview mode)
1198 if( this.preview_mode
)
1201 $j('#img_thumb_'+this.id
).css('zindex',1);
1202 $j('#'+ this.id
+ ' .play-btn-large').hide();
1204 //add black background
1205 $j('#dc_'+this.id
).append( '<div id="black_back_' + this.id
+ '" ' +
1206 'style="z-index:-2;position:absolute;background:#000;' +
1207 'top:0px;left:0px;width:' + parseInt( this.width
) + 'px;' +
1208 'height:' + parseInt( this.height
) + 'px;">' +
1211 if( this.wikiTitleKey
){
1212 $j('#dc_'+this.id
).append(
1213 '<div class="related_vids" >' +
1214 '<h1>' + gM('mwe-related_videos') + '</h1>'+
1218 $j('#img_thumb_' + this.id
).fadeOut("fast");
1219 $j('#dc_'+ _this
.id
+ ' .related_vids ul').html( gM('mwe-loading_txt') );
1220 this.mvVideoAudioSearch();
1222 //add the liks_info_div black back
1223 $j('#dc_'+this.id
).append('<div id="liks_info_'+this.id
+'" ' +
1224 'style="width:' +parseInt(parseInt(this.width
)/2)+'px;'+
1225 'height:'+ parseInt(parseInt(this.height
)) +'px;'+
1226 'position:absolute;top:10px;overflow:auto'+
1227 'width: '+parseInt( ((parseInt(this.width
)/2)-15) ) + 'px;'+
1228 'left:'+ parseInt( ((parseInt(this.width
)/2)+15) ) +'px;">'+
1231 //start animation (make thumb small in upper left add in div for "loading"
1232 $j('#img_thumb_'+this.id
).animate({
1233 width
:parseInt(parseInt(_this
.width
)/2),
1234 height
:parseInt(parseInt(_this
.height
)/2),
1240 //animation done.. add "loading" to div if empty
1241 if($j('#liks_info_'+_this
.id
).html()==''){
1242 $j('#liks_info_'+_this
.id
).html(gM('mwe-loading_txt'));
1246 //now load roe if run the showNextPrevLinks
1247 if(this.roe
&& this.media_element
.addedROEData
==false){
1248 do_request(this.roe
, function(data
)
1250 _this
.media_element
.addROE(data
);
1251 _this
.getNextPrevLinks();
1254 this.getNextPrevLinks();
1258 //@@todo we should merge getNextPrevLinks with textInterface .. there is repeated code between them.
1259 getNextPrevLinks:function(){
1260 js_log('f:getNextPrevLinks');
1261 var anno_track_url
= null;
1263 //check for annoative track
1264 $j
.each(this.media_element
.sources
, function(inx
, n
){
1265 if(n
.mime_type
=='text/cmml'){
1266 if( n
.id
== 'Anno_en'){
1267 anno_track_url
= n
.src
;
1271 if( anno_track_url
){
1272 js_log('found annotative track:'+ anno_track_url
);
1273 //zero out seconds (should improve cache hit rate and generally expands metadata search)
1274 //@@todo this could be repalced with a regExp
1275 var annoURL
= parseUri(anno_track_url
);
1276 var times
= annoURL
.queryKey
['t'].split('/');
1277 var stime_parts
= times
[0].split(':');
1278 var etime_parts
= times
[1].split(':');
1279 //zero out the hour:
1280 var new_start
= stime_parts
[0]+':'+'0:0';
1281 //zero out the end sec
1282 var new_end
= (etime_parts
[0]== stime_parts
[0])? (etime_parts
[0]+1)+':0:0' :etime_parts
[0]+':0:0';
1284 var etime_parts
= times
[1].split(':');
1286 var new_anno_track_url
= annoURL
.protocol
+'://'+ annoURL
.host
+ annoURL
.path
+'?';
1287 $j
.each(annoURL
.queryKey
, function(i
, val
){
1288 new_anno_track_url
+=(i
=='t')?'t='+new_start
+'/'+new_end
+'&' :
1291 var request_key
= new_start
+'/'+new_end
;
1292 //check the anno_data cache:
1293 //@@todo search cache see if current is in range.
1294 if(this.anno_data_cache
){
1295 js_log('anno data found in cache: '+request_key
);
1296 this.showNextPrevLinks();
1298 do_request(new_anno_track_url
, function(cmml_data
){
1299 js_log('raw response: '+ cmml_data
);
1300 if(typeof cmml_data
== 'string')
1302 var parser
=new DOMParser();
1303 js_log('Parse CMML data:' + cmml_data
);
1304 cmml_data
=parser
.parseFromString(cmml_data
,"text/xml");
1306 //init anno_data_cache
1307 if(!_this
.anno_data_cache
)
1308 _this
.anno_data_cache
={};
1309 //grab all metadata and put it into the anno_data_cache:
1310 $j
.each(cmml_data
.getElementsByTagName('clip'), function(inx
, clip
){
1311 _this
.anno_data_cache
[ $j(clip
).attr("id") ]={
1312 'start_time_sec':npt2seconds($j(clip
).attr("start").replace('npt:','')),
1313 'end_time_sec':npt2seconds($j(clip
).attr("end").replace('npt:','')),
1314 'time_req':$j(clip
).attr("start").replace('npt:','')+'/'+$j(clip
).attr("end").replace('npt:','')
1317 _this
.anno_data_cache
[ $j(clip
).attr("id") ]['meta']={};
1318 $j
.each(clip
.getElementsByTagName('meta'),function(imx
, meta
){
1319 //js_log('adding meta: '+ $j(meta).attr("name")+ ' = '+ $j(meta).attr("content"));
1320 _this
.anno_data_cache
[$j(clip
).attr("id")]['meta'][$j(meta
).attr("name")]=$j(meta
).attr("content");
1323 _this
.showNextPrevLinks();
1327 js_log('no annotative track found');
1328 $j('#liks_info_'+this.id
).html('no metadata found for related links');
1330 //query current request time +|- 60s to get prev next speech links.
1332 showNextPrevLinks:function(){
1333 //js_log('f:showNextPrevLinks');
1334 //int requested links:
1340 var curTime
= this.getTimeReq().split('/');
1342 var s_sec
= npt2seconds(curTime
[0]);
1343 var e_sec
= npt2seconds(curTime
[1]);
1344 js_log('showNextPrevLinks: req time: '+ s_sec
+ ' to ' + e_sec
);
1345 //now we have all the data in anno_data_cache
1346 var current_done
=false;
1347 for(var clip_id
in this.anno_data_cache
){ //for in loop oky for object
1348 var clip
= this.anno_data_cache
[clip_id
];
1349 //js_log('on clip:'+ clip_id);
1350 //set prev_link (if cur_link is still empty)
1351 if( s_sec
> clip
.end_time_sec
){
1352 link
.prev
= clip_id
;
1353 js_log('showNextPrevLinks: ' + s_sec
+ ' < ' + clip
.end_time_sec
+ ' set prev');
1356 if(e_sec
==clip
.end_time_sec
&& s_sec
== clip
.start_time_sec
)
1357 current_done
= true;
1358 //current clip is not done:
1359 if( e_sec
< clip
.end_time_sec
&& link
.current
=='' && !current_done
){
1360 link
.current
= clip_id
;
1361 js_log('showNextPrevLinks: ' + e_sec
+ ' < ' + clip
.end_time_sec
+ ' set current');
1364 //set end clip (first clip where start time is > end_time of req
1365 if( e_sec
< clip
.start_time_sec
&& link
.next
==''){
1366 link
.next
= clip_id
;
1367 js_log('showNextPrevLinks: '+ e_sec
+ ' < '+ clip
.start_time_sec
+ ' && ' + link
.next
);
1371 if(link
.prev
=='' && link
.current
=='' && link
.next
==''){
1372 html
='<p><a href="'+this.media_element
.linkbackgetMsg
+'">clip page</a>';
1374 for(var link_type
in link
){
1375 var link_id
= link
[link_type
];
1377 var clip
= this.anno_data_cache
[link_id
];
1379 for(var j
in clip
['meta']){
1380 title_msg
+=j
.replace(/_/g,' ') +': ' +clip['meta'][j].replace(/_
/g
,' ') +" <br>";
1382 var time_req
= clip
.time_req
;
1383 if(link_type
=='current') //if current start from end of current clip play to end of current meta:
1384 time_req
= curTime
[1]+ '/' + seconds2npt( clip
.end_time_sec
);
1386 //do special linkbacks for metavid content:
1387 var regTimeCheck
= new RegExp(/[0-9]+:[0-9]+:[0-9]+\/[0-9]+:[0-9]+:[0-9]+/);
1389 if( regTimeCheck
.test( this.media_element
.linkback
) ){
1390 html
+=' href="'+ this.media_element
.linkback
.replace(regTimeCheck
,time_req
) +'" ';
1392 html
+=' href="#" onClick="$j(\'#'+this.id
+'\').get(0).playByTimeReq(\''+
1393 time_req
+ '\'); return false; "';
1395 html
+=' title="' + title_msg
+ '">' +
1396 gM('mwe-' + link_type
+'_clip_msg') +
1397 '</a><br><span style="font-size:small">'+ title_msg
+'<span></p>';
1401 //js_og("should set html:"+ html);
1402 $j('#liks_info_'+this.id
).html(html
);
1404 playByTimeReq: function(time_req
){
1405 js_log('f:playByTimeReq: '+time_req
);
1407 this.updateVideoTimeReq(time_req
);
1410 doThumbnailHTML:function()
1413 js_log('f:doThumbnailHTML'+ this.thumbnail_disp
);
1414 this.closeDisplayedHTML();
1415 $j( '#mv_embedded_player_' + this.id
).html( this.getThumbnailHTML() );
1417 this.thumbnail_disp
= true;
1418 //make sure the ctrlBuilder remain active:
1419 this.ctrlBuilder
.addControlHooks(this);
1421 refreshControlsHTML:function(){
1422 js_log('refreshControlsHTML::');
1423 if($j('#' + this.id
+ ' .control-bar').length
== 0)
1425 js_log('control-bar not present, returning');
1428 $j('#' + this.id
+ ' .control-bar').html( this.getControlsHTML() );
1429 this.ctrlBuilder
.addControlHooks(this);
1432 getControlsHTML:function()
1434 return this.ctrlBuilder
.getControls( this );
1436 getHTML : function (){
1437 //@@todo check if we have sources available
1438 js_log('embedVideo:getHTML : ' + this.id
+ ' resource type: ' + this.type
);
1440 //set-up the local ctrlBuilder instance:
1441 this.ctrlBuilder
= new ctrlBuilder( this );
1445 html_code
= '<div id="videoPlayer_' + this.id
+ '" style="width:' + this.width
+ 'px;position:relative;"'+
1446 'class="' + this.ctrlBuilder
.pClass
+ '">';
1447 html_code
+= '<div style="width:'+parseInt(this.width
)+'px;height:'+parseInt(this.height
)+'px;" id="mv_embedded_player_'+this.id
+'">' +
1448 this.getThumbnailHTML() +
1450 //js_log("mvEmbed:controls "+ typeof this.controls);
1453 js_log("f:getHTML:AddControls");
1454 html_code
+='<div class="ui-state-default ui-widget-header ui-helper-clearfix control-bar" >';
1455 html_code
+= this.getControlsHTML();
1456 html_code
+='</div>';
1457 //block out some space by encapulating the top level div
1458 $j(this).wrap('<div style="width:'+parseInt(this.width
)+'px;height:'
1459 +( parseInt(this.height
) + this.ctrlBuilder
.height
)+'px"></div>');
1461 html_code
+= '</div>'; //videoPlayer div close
1462 //js_log('should set: '+this.id);
1463 $j(this).html( html_code
);
1464 //add hooks once Controls are in DOM
1465 this.ctrlBuilder
.addControlHooks(this);
1467 //js_log('set this to: ' + $j(this).html() );
1469 //if auto play==true directly embed the plugin
1472 js_log('activating autoplay');
1477 * get missing plugin html (check for user included code)
1479 getPluginMissingHTML : function(missing_type
){
1480 //keep the box width hight:
1481 var out
= '<div style="width:'+this.width
+'px;height:'+this.height
+'px">';
1482 if(this.user_missing_plugin_html
){
1483 out
+= this.user_missing_plugin_html
;
1487 out
+= gM('mwe-generic_missing_plugin', missing_type
) + ' or <a title="'+gM('mwe-download_clip')+'" href="'+this.src
+'">'+gM('mwe-download_clip')+'</a>';
1489 return out
+ '</div>';
1491 updateVideoTimeReq:function(time_req
){
1492 js_log('f:updateVideoTimeReq');
1493 var time_parts
=time_req
.split('/');
1494 this.updateVideoTime(time_parts
[0], time_parts
[1]);
1497 updateVideoTime:function(start_ntp
, end_ntp
){
1499 this.media_element
.updateSourceTimes( start_ntp
, end_ntp
);
1501 this.setStatus(start_ntp
+'/'+end_ntp
);
1503 this.setSliderValue(0);
1504 //reset seek_offset:
1505 if(this.media_element
.selected_source
.URLTimeEncoding
)
1506 this.seek_time_sec
=0;
1508 this.seek_time_sec
=npt2seconds(start_ntp
);
1510 //@@todo overwite by embed library if we can render frames natavily
1511 renderTimelineThumbnail:function( options
){
1512 var my_thumb_src
= this.media_element
.getThumbnailURL();
1513 //check if our thumbnail has a time attribute:
1514 if( my_thumb_src
.indexOf('t=') !== -1){
1515 var time_ntp
= seconds2npt ( options
.time
+ parseInt(this.start_offset
) );
1516 my_thumb_src
= getURLParamReplace( my_thumb_src
, { 't':time_ntp
, 'size': options
.size
} );
1518 var thumb_class
= (typeof options
['thumb_class'] != 'undefined' ) ? options
['thumb_class'] : '';
1519 return '<div class="ui-corner-all ' + thumb_class
+ '" src="' + my_thumb_src
+ '" '+
1520 'style="height:' + options
.height
+ 'px;' +
1521 'width:' + options
.width
+ 'px" >' +
1522 '<img src="' + my_thumb_src
+'" '+
1523 'style="height:' + options
.height
+ 'px;' +
1524 'width:' + options
.width
+ 'px">' +
1527 updateThumbTimeNTP:function( time
){
1528 this.updateThumbTime( npt2seconds(time
) - parseInt(this.start_offset
) );
1530 updateThumbTime:function( float_sec
){
1531 //js_log('updateThumbTime:'+float_sec);
1533 if( typeof this.org_thum_src
=='undefined' ){
1534 this.org_thum_src
= this.media_element
.getThumbnailURL();
1536 if( this.org_thum_src
.indexOf('t=') !== -1){
1537 this.last_thumb_url
= getURLParamReplace(this.org_thum_src
,
1538 { 't' : seconds2npt( float_sec
+ parseInt(this.start_offset
)) } );
1539 if(!this.thumbnail_updating
){
1540 this.updateThumbnail(this.last_thumb_url
,false);
1541 this.last_thumb_url
=null;
1545 //for now provide a src url .. but need to figure out how to copy frames from video for plug-in based thumbs
1546 updateThumbPerc:function( perc
){
1547 return this.updateThumbTime( (this.getDuration() * perc
) );
1549 //updates the thumbnail if the thumbnail is being displayed
1550 updateThumbnail : function(src
, quick_switch
){
1551 //make sure we don't go to the same url if we are not already updating:
1552 if( !this.thumbnail_updating
&& $j('#img_thumb_'+this.id
).attr('src')== src
)
1554 //if we are already updating don't issue a new update:
1555 if( this.thumbnail_updating
&& $j('#new_img_thumb_'+this.id
).attr('src')== src
)
1558 js_log('update thumb: ' + src
);
1561 $j('#img_thumb_'+this.id
).attr('src', src
);
1564 //if still animating remove new_img_thumb_
1565 if(this.thumbnail_updating
==true)
1566 $j('#new_img_thumb_'+this.id
).stop().remove();
1568 if(this.thumbnail_disp
){
1569 js_log('set to thumb:'+ src
);
1570 this.thumbnail_updating
=true;
1571 $j('#dc_'+this.id
).append('<img src="'+src
+'" ' +
1572 'style="display:none;position:absolute;zindex:2;top:0px;left:0px;" ' +
1573 'width="'+this.width
+'" height="'+this.height
+'" '+
1574 'id = "new_img_thumb_'+this.id
+'" />');
1575 //js_log('appended: new_img_thumb_');
1576 $j('#new_img_thumb_'+this.id
).fadeIn("slow", function(){
1577 //once faded in remove org and rename new:
1578 $j('#img_thumb_'+_this
.id
).remove();
1579 $j('#new_img_thumb_'+_this
.id
).attr('id', 'img_thumb_'+_this
.id
);
1580 $j('#img_thumb_'+_this
.id
).css('zindex','1');
1581 _this
.thumbnail_updating
=false;
1582 //js_log("done fadding in "+ $j('#img_thumb_'+_this.id).attr("src"));
1584 //if we have a thumb queued update to that
1585 if(_this
.last_thumb_url
){
1586 var src_url
=_this
.last_thumb_url
;
1587 _this
.last_thumb_url
=null;
1588 _this
.updateThumbnail(src_url
);
1594 /** Returns the HTML code for the video when it is in thumbnail mode.
1595 This includes the specified thumbnail as well as buttons for
1596 playing, configuring the player, inline cmml display, HTML linkback,
1597 download, and embed code.
1599 getThumbnailHTML : function ()
1601 js_log('embedVideo:getThumbnailHTML::' + this.id
);
1602 var thumb_html
= '';
1605 //if(this.class)class_atr = ' class="'+this.class+'"';
1606 //if(this.style)style_atr = ' style="'+this.style+'"';
1607 // else style_atr = 'overflow:hidden;height:'+this.height+'px;width:'+this.width+'px;';
1608 this.thumbnail
= this.media_element
.getThumbnailURL();
1610 //put it all in the div container dc_id
1611 thumb_html
+= '<div id="dc_'+this.id
+'" style="position:absolute;'+
1612 ' overflow:hidden; top:0px; left:0px; width:'+this.playerPixelWidth()+'px; height:'+this.playerPixelHeight()+'px; z-index:0;">'+
1613 '<img width="'+this.playerPixelWidth()+'" height="'+this.playerPixelHeight()+'" style="position:relative;width:'+this.playerPixelWidth()+';height:'+this.playerPixelHeight()+'"' +
1614 ' id="img_thumb_'+this.id
+'" src="' + this.thumbnail
+ '">';
1616 if(this.play_button
== true && this.controls
== true)
1617 thumb_html
+= this.ctrlBuilder
.getComponent( 'play-btn-large' );
1619 thumb_html
+='</div>';
1622 getEmbeddingHTML:function()
1624 var thumbnail
= this.media_element
.getThumbnailURL();
1626 var embed_thumb_html
;
1627 if(thumbnail
.substring(0,1)=='/'){
1628 eURL
= parseUri(mv_embed_path
);
1629 embed_thumb_html
= eURL
.protocol
+ '://' + eURL
.host
+ thumbnail
;
1630 //js_log('set from mv_embed_path:'+embed_thumb_html);
1632 embed_thumb_html
= (thumbnail
.indexOf('http://')!=-1)?thumbnail
:mv_embed_path
+ thumbnail
;
1634 var embed_code_html
= '<script type="text/javascript" ' +
1635 'src="'+mv_embed_path
+'mv_embed.js"></script>' +
1638 embed_code_html
+='roe="'+this.roe
+'" >';
1640 embed_code_html
+='src="'+this.src
+'" ' +
1641 'poster="'+embed_thumb_html
+'">';
1643 //close the video tag
1644 embed_code_html
+='</video>';
1646 return embed_code_html
;
1648 doOptionsHTML:function()
1650 var sel_id
= (this.pc
!=null)?this.pc
.pp
.id
:this.id
;
1651 var pos
= $j('#'+sel_id
+ ' .options-btn').offset();
1652 pos
['top']=pos
['top']+24;
1653 pos
['left']=pos
['left']-124;
1654 //js_log('pos of options button: t:'+pos['top']+' l:'+ pos['left']);
1655 $j('#mv_vid_options_'+sel_id
).css(pos
).toggle();
1658 doLinkBack:function(){
1659 if(this.roe
&& this.media_element
.addedROEData
==false){
1661 this.displayHTML(gM('mwe-loading_txt'));
1662 do_request(this.roe
, function(data
)
1664 _this
.media_element
.addROE(data
);
1668 if(this.media_element
.linkback
){
1669 window
.location
= this.media_element
.linkback
;
1671 this.displayHTML(gM('mwe-could_not_find_linkback'));
1675 showShare:function($target
){
1676 var embed_code
= this.getEmbeddingHTML();
1679 //@todo: hook events to two a's for swapping in and out code for link vs. embed;
1680 // hook events for changing active class of li based on a.
1681 o
+= '<h2>' + gM('mwe-share_this_video') + '</h2>\n' +
1683 ' <li><a href="#" class="active">'+gM('mwe-embed_site_or_blog')+'</a></li>\n';
1685 o
+= ' <li><a href="#" id="k-share-link">' + this.linkback
+ '</a></li>\n';
1688 '<div class="source_wrap"><textarea>' + embed_code
+ '</textarea></div>' +
1689 '<button class="ui-state-default ui-corner-all copycode">' + gM('mwe-copy-code') + '</button>' +
1690 '<div class="ui-state-highlight ui-corner-all">' + gM('mwe-read_before_embed') + '</div>' +
1693 $cpBtn
= $j( '#' + this.id
+ ' .copycode');
1694 $cpTxt
= $j( '#' + this.id
+ ' .source_wrap textarea');
1696 $cpTxt
.click(function(){
1697 $j(this).get(0).select();
1700 $cpBtn
.click(function(){
1701 $cpTxt
.focus().get(0).select();
1702 if(document
.selection
){
1703 CopiedTxt
= document
.selection
.createRange();
1704 CopiedTxt
.execCommand("Copy");
1708 showTextInterface:function(){
1710 //display the text container with loading text:
1711 //@@todo support position config
1712 var loc
= $j(this).position();
1713 if($j('#metaBox_'+this.id
).length
==0){
1714 $j(this).after('<div class="ui-widget ui-widget-content ui-corner-all" style="position:absolute;z-index:10;'+
1715 'top:' + (loc
.top
) + 'px;' +
1716 'left:' + (parseInt( loc
.left
) + parseInt(this.width
) + 10 )+'px;' +
1717 'height:'+ parseInt( this.height
)+'px;width:400px;' +
1719 'id="metaBox_' + this.id
+ '">'+
1720 gM('mwe-loading_txt') +
1723 //fade in the text display
1724 $j('#metaBox_'+this.id
).fadeIn("fast");
1725 //check if textObj present:
1726 if(typeof this.textInterface
== 'undefined' ){
1727 //load the default text interface:
1732 _this
.textInterface
= new mvTextInterface( _this
);
1734 _this
.textInterface
.show();
1735 js_log("NEW TEXT INTERFACE");
1736 for(var i
in _this
.textInterface
.availableTracks
){
1737 js_log("tracks in new interface: "+_this
.id
+ ' tid:' + i
);
1743 this.textInterface
.show();
1746 closeTextInterface:function(){
1747 js_log('closeTextInterface '+ typeof this.textInterface
);
1748 if(typeof this.textInterface
!== 'undefined' ){
1749 this.textInterface
.close();
1752 /** Generic function to display custom HTML inside the mv_embed element.
1753 The code should call the closeDisplayedHTML function to close the
1754 display of the custom HTML and restore the regular mv_embed display.
1755 @param {String} HTML code for the selection list.
1757 displayHTML:function(html_code
)
1759 var sel_id
= (this.pc
!=null)?this.pc
.pp
.id
:this.id
;
1761 if(!this.supports
['overlays'])
1764 //put select list on-top
1765 //make sure the parent is relatively positioned:
1766 $j('#'+sel_id
).css('position', 'relative');
1767 //set height width (check for playlist container)
1768 var width
= (this.pc
)?this.pc
.pp
.width
:this.playerPixelWidth();
1769 var height
= (this.pc
)?this.pc
.pp
.height
:this.playerPixelHeight();
1772 height
+=(this.pc
.pp
.pl_layout
.title_bar_height
+ this.pc
.pp
.pl_layout
.control_height
);
1775 if($j('#blackbg_'+sel_id
).length
!=0)
1778 $j('#blackbg_'+sel_id
).remove();
1780 //fade in a black bg div ontop of everything
1781 var div_code
= '<div id="blackbg_'+sel_id
+'" class="videoComplete" ' +
1782 'style="height:'+parseInt(height
)+'px;width:'+parseInt(width
)+'px;">'+
1783 '<div class="videoOptionsComplete">'+
1784 //@@TODO: this style should go to .css
1785 '<span style="float:right;margin-right:10px">' +
1786 '<a href="#" style="color:white;" onClick="$j(\'#'+sel_id
+'\').get(0).closeDisplayedHTML();return false;">close</a>' +
1788 '<div id="mv_disp_inner_'+sel_id
+'" style="padding-top:10px;">'+
1792 $j('#'+sel_id
).prepend(div_code
);
1794 $j('#blackbg_'+sel_id
).fadeIn("slow");
1796 $j('#blackbg_'+sel_id
).show();
1797 return false; //onclick action return false
1799 /** Close the custom HTML displayed using displayHTML and restores the
1800 regular mv_embed display.
1802 closeDisplayedHTML:function(){
1803 var sel_id
= (this.pc
!=null)?this.pc
.pp
.id
:this.id
;
1804 $j('#blackbg_'+sel_id
).fadeOut("slow", function(){
1805 $j('#blackbg_'+sel_id
).remove();
1807 return false; //onclick action return false
1809 showPlayerselect:function( $target
){
1810 //get id (in case where we have a parent container)
1811 var this_id
= (this.pc
!=null)?this.pc
.pp
.id
:this.id
;
1814 o
+='<h2>' + gM('mwe-chose_player') + '</h2>';
1816 //js_log('selected src'+ _this.media_element.selected_source.url);
1817 $j
.each( this.media_element
.getPlayableSources(), function(source_id
, source
){
1818 var default_player
= embedTypes
.players
.defaultPlayer( source
.getMIMEType() );
1820 var is_selected
= (source
== _this
.media_element
.selected_source
);
1821 var image_src
= mv_skin_img_path
;
1823 if (default_player
){
1825 //output the player select code:
1826 var supporting_players
= embedTypes
.players
.getMIMETypePlayers( source
.getMIMEType() );
1828 for(var i
=0; i
< supporting_players
.length
; i
++){
1829 if( _this
.selected_player
.id
== supporting_players
[i
].id
&& is_selected
){
1831 '<a href="#" class="active" rel="sel_source" id="sc_' + source_id
+ '_' + supporting_players
[i
].id
+'">' +
1832 supporting_players
[i
].getName() +
1836 '<a href="#" rel="sel_source" id="sc_' + source_id
+ '_' + supporting_players
[i
].id
+'">' +
1837 supporting_players
[i
].getName() + '</a>' +
1843 o
+= source
.getTitle() + ' - no player available';
1848 //set up the click bindings:
1849 $target
.find("[rel='sel_source']").each(function(){
1850 $j(this).click(function(){
1851 var iparts
= $j(this).attr( 'id' ).replace(/sc_/,'').split('_');
1852 var source_id
= iparts
[0];
1853 var default_player_id
= iparts
[1];
1854 js_log('source id: ' + source_id
+ ' player id: ' + default_player_id
);
1856 $j('#' + this_id
).get(0).closeDisplayedHTML();
1857 $j('#' + _this
.id
).get(0).media_element
.selectSource( source_id
);
1859 embedTypes
.players
.userSelectPlayer( default_player_id
,
1860 _this
.media_element
.sources
[ source_id
].getMIMEType() );
1862 //be sure to issue a stop
1863 $j('#' + this_id
).get(0).stop();
1865 //don't follow the empty # link:
1870 showDownload:function( $target
){
1872 //load the roe if available (to populate out download options:
1873 function getShowVideoDownload(){
1874 var out
='<div style="color:white">';
1877 $j
.each(_this
.media_element
.getSources(), function(index
, source
){
1878 var dl_line
= '<li>' + '<a style="color:white" href="' + source
.getURI() +'"> '
1879 + source
.getTitle()+'</a> '+ '</li>'+"\n";
1880 if( source
.getURI().indexOf('?t=')!==-1){
1882 }else if( this.getMIMEType()=="text/cmml" || this.getMIMEType()=="text/x-srt" ){
1883 dl_txt_list
+=dl_line
;
1890 out
+=gM('mwe-download_full') + '<blockquote style="background:#000">' + dl_list
+ '</blockquote>';
1892 out
+=gM('mwe-download_text') + '<blockquote style="background:#000">' + dl_txt_list
+'</blockquote>';
1896 //js_log('f:showDownload '+ this.roe + ' ' + this.media_element.addedROEData);
1897 if(this.roe
&& this.media_element
.addedROEData
== false){
1899 $target
.html( gM('loading_txt') );
1900 do_request(this.roe
, function(data
)
1902 _this
.media_element
.addROE(data
);
1903 $target
.html( getShowVideoDownload() );
1906 $target
.html( getShowVideoDownload() );
1909 showCredits:function( $target
){
1910 $target
.html('<h2>' + gM('mwe-credits') + '</h2>');
1913 * base embed controls
1914 * the play button calls
1917 var eid
= (this.pc
!=null)?this.pc
.pp
.id
:this.id
;
1919 //js_log( "mv_embed play:" + this.id);
1920 //js_log('thum disp:'+this.thumbnail_disp);
1921 //check if thumbnail is being displayed and embed html
1922 if( this.thumbnail_disp
){
1923 if( !this.selected_player
){
1924 js_log('no selected_player');
1925 //this.innerHTML = this.getPluginMissingHTML();
1926 //$j(this).html(this.getPluginMissingHTML());
1927 $j('#'+this.id
).html( this.getPluginMissingHTML() );
1930 this.onClipDone_disp
=false;
1932 this.thumbnail_disp
=false;
1935 //the plugin is already being displayed
1936 this.paused
=false; //make sure we are not "paused"
1940 $j('#' + eid
+ ' .play-btn span').removeClass('ui-icon-play').addClass('ui-icon-pause');
1941 $j('#' + eid
+ ' .play-btn').unbind().btnBind().click(function(){
1942 $j('#' + eid
).get(0).pause();
1943 }).attr('title', gM('mwe-pause_clip'));
1947 //should be done by child (no base way to pre-buffer video)
1948 js_log('baseEmbed:load call');
1951 return this.media_element
.selected_source
.getURI( this.seek_time_sec
);
1955 * there is no general way to pause the video
1956 * must be overwritten by embed object to support this functionality.
1959 var eid
= (this.pc
!=null)?this.pc
.pp
.id
:this.id
;
1960 //js_log('mv_embed:do pause');
1961 //(playing) do pause
1963 //update the ctrl "paused state"
1964 $j('#' + eid
+ ' .play-btn span').removeClass('ui-icon-pause').addClass('ui-icon-play');
1965 $j('#' + eid
+ ' .play-btn').unbind().btnBind().click(function(){
1966 $j('#'+eid
).get(0).play();
1967 }).attr('title', gM('mwe-play_clip'));
1970 * base embed stop (can be overwritten by the plugin)
1974 js_log('mvEmbed:stop:'+this.id
);
1976 //no longer seeking:
1977 this.didSeekJump
=false;
1979 //first issue pause to update interface (only call the parent)
1980 if(this['parent_pause']){
1981 this.parent_pause();
1986 //reset the currentTime:
1988 //check if thumbnail is being displayed in which case do nothing
1989 if( this.thumbnail_disp
){
1990 //already in stooped state
1991 js_log('already in stopped state');
1993 //rewrite the html to thumbnail disp
1994 this.doThumbnailHTML();
1995 this.bufferedPercent
=0; //reset buffer state
1996 this.setSliderValue(0);
1997 this.setStatus( this.getTimeReq() );
2000 //make sure the big playbutton is has click action:
2001 $j('#' + _this
.id
+ ' .play-btn-large').unbind('click').click(function(){
2002 $j('#' +_this
.id
).get(0).play();
2005 if(this.update_interval
)
2007 clearInterval(this.update_interval
);
2008 this.update_interval
= null;
2011 toggleMute:function(){
2012 var eid
= (this.pc
!=null)?this.pc
.pp
.id
:this.id
;
2015 $j('#' + eid
+ ' .volume-slider').slider('value', 100);
2016 this.updateVolumen(1);
2019 $j('#' + eid
+ ' .volume-slider').slider('value', 0);
2020 this.updateVolumen(0);
2022 js_log('f:toggleMute::' + this.muted
);
2024 updateVolumen:function(perc
){
2025 js_log('update volume not supported with current playback type');
2027 fullscreen:function(){
2028 js_log('fullscreen not supported with current playback type');
2030 /* returns bool true if playing or paused, false if stooped
2032 isPlaying : function(){
2033 if(this.thumbnail_disp
){
2036 }else if( this.paused
){
2043 isPaused : function(){
2044 return this.isPlaying() && this.paused
;
2046 isStoped : function(){
2047 return this.thumbnail_disp
;
2049 playlistSupport:function(){
2050 //by default not supported (implemented in js)
2053 postEmbedJS:function(){
2056 //do common monitor code like update the playhead and play status
2057 //plugin objects are responsible for updating currentTime
2059 //js_log(' ct: ' + this.currentTime + ' dur: ' + ( parseInt( this.duration ) + 1 ) + ' is seek: ' + this.seeking );
2060 if( this.currentTime
&& this.currentTime
> 0 && this.duration
){
2061 if( !this.userSlide
&& !this.seeking
){
2062 if( this.start_offset
){
2063 //if start offset include that calculation
2064 this.setSliderValue( ( this.currentTime
- this.start_offset
) / this.duration
);
2065 var et
= (this.ctrlBuilder
.long_time_disp
)? '/'+ seconds2npt(parseFloat(this.start_offset
)+parseFloat(this.duration
) ) : '';
2066 this.setStatus( seconds2npt(this.currentTime
) + et
);
2068 this.setSliderValue( this.currentTime
/ this.duration
);
2069 var et
= (this.ctrlBuilder
.long_time_disp
)? '/' + seconds2npt( this.duration
):'';
2070 this.setStatus( seconds2npt( this.currentTime
) + et
);
2073 //check if we are "done"
2074 if( this.currentTime
> ( parseInt(this.duration
) + 1 ) ){
2075 js_log("should run clip done");
2079 //media lacks duration just show end time
2080 //js_log(' ct:' + this.currentTime + ' dur: ' + this.duration);
2081 if( this.isStoped() ){
2082 this.setStatus( this.getTimeReq() );
2083 }else if( this.isPaused() ){
2084 this.setStatus( gM('mwe-paused') );
2085 }else if( this.isPlaying() ){
2086 if( this.currentTime
&& ! this.duration
)
2087 this.setStatus( seconds2npt( this.currentTime
) + ' /' );
2089 this.setStatus(" - - - ");
2091 this.setStatus( this.getTimeReq() );
2094 //could check if time > duration here and stop playback
2096 //update buffer information
2097 this.updateBufferStatus();
2099 //update monitorTimerId to call child monitor
2100 if( ! this.monitorTimerId
){
2101 //make sure an instance of this.id exists:
2102 if( document
.getElementById(this.id
) ){
2103 this.monitorTimerId
= setInterval(function(){
2104 if(_this
.id
&& $j( '#'+_this
.id
).length
!= 0){
2105 $j( '#'+_this
.id
).get(0).monitor();
2111 stopMonitor:function(){
2112 if( this.monitorTimerId
!= 0 )
2114 clearInterval( this.monitorTimerId
);
2115 this.monitorTimerId
= 0;
2118 updateBufferStatus: function(){
2120 //build the buffer targeet based for playlist vs clip
2121 var buffer_select
= (this.pc
) ?
2122 '#cl_status_' + this.id
+ ' .mv_buffer':
2123 '#' + this.id
+ ' .play_head .mv_buffer';
2125 //update the buffer progress bar (if available )
2126 if( this.bufferedPercent
!= 0 ){
2127 //js_log('bufferedPercent: ' + this.bufferedPercent);
2128 if(this.bufferedPercent
> 1)
2129 this.bufferedPercent
=1;
2131 $j(buffer_select
).css("width", (this.bufferedPercent
*100) +'%' );
2133 $j(buffer_select
).css("width", '0px' );
2136 relativeCurrentTime: function(){
2137 if(!this.start_offset
)
2138 this.start_offset
=0;
2139 var rt
= this.currentTime
- this.start_offset
;
2140 if( rt
< 0 ) //should not happen but does.
2144 getPluginEmbed : function(){
2145 if (window
.document
[this.pid
]){
2146 return window
.document
[this.pid
];
2148 if ($j
.browser
.msie
){
2149 return document
.getElementById(this.pid
);
2151 if (document
.embeds
&& document
.embeds
[this.pid
])
2152 return document
.embeds
[this.pid
];
2156 //HELPER Functions for selected source
2158 * returns the selected source url for players to play
2160 getURI : function( seek_time_sec
){
2161 return this.media_element
.selected_source
.getURI( this.seek_time_sec
);
2163 supportsURLTimeEncoding: function(){
2164 //do head request if on the same domain
2165 return this.media_element
.selected_source
.URLTimeEncoding
;
2167 setSliderValue: function(perc
, hide_progress
){
2168 var eid
= (this.pc
)?this.pc
.pp
.id
:this.id
;
2169 if(this.controls
&& $j('#' + eid
+ ' .play_head').length
!= 0){
2170 var val
= parseInt( perc
*1000 );
2171 $j('#' + eid
+ ' .play_head').slider('value', val
);
2173 //js_log('set#mv_seeker_slider_'+eid + ' perc in: ' + perc + ' * ' + $j('#mv_seeker_'+eid).width() + ' = set to: '+ val + ' - '+ Math.round(this.mv_seeker_width*perc) );
2174 //js_log('op:' + offset_perc + ' *('+perc+' * ' + $j('#slider_'+id).width() + ')');
2176 highlightPlaySection:function(options
){
2177 js_log('highlightPlaySection');
2178 var eid
= (this.pc
)?this.pc
.pp
.id
:this.id
;
2179 var dur
= this.getDuration();
2180 var hide_progress
= true;
2181 //set the left percet and update the slider:
2182 rel_start_sec
= npt2seconds( options
['start']);
2183 //remove the start_offset if relevent:
2184 if(this.start_offset
)
2185 rel_start_sec
= rel_start_sec
- this.start_offset
2188 if( rel_start_sec
<= 0 ){
2190 options
['start'] = seconds2npt( this.start_offset
);
2192 this.setSliderValue( 0 , hide_progress
);
2194 left_perc
= parseInt( (rel_start_sec
/ dur
)*100 ) ;
2195 slider_perc
= (left_perc
/ 100);
2198 js_log("slider perc:" + slider_perc
);
2199 if( ! this.isPlaying() ){
2200 this.setSliderValue( slider_perc
, hide_progress
);
2203 width_perc
= parseInt( (( npt2seconds( options
['end'] ) - npt2seconds( options
['start'] ) ) / dur
)*100 ) ;
2204 if( (width_perc
+ left_perc
) > 100 ){
2205 width_perc
= 100 - left_perc
;
2207 //js_log('should hl: '+rel_start_sec+ '/' + dur + ' re:' + rel_end_sec+' lp:' + left_perc + ' width: ' + width_perc);
2208 $j('#mv_seeker_' + eid
+ ' .mv_highlight').css({
2209 'left' : left_perc
+'%',
2210 'width' : width_perc
+'%'
2213 this.jump_time
= options
['start'];
2214 this.seek_time_sec
= npt2seconds( options
['start']);
2216 this.setStatus( gM('mwe-seek_to', seconds2npt( this.seek_time_sec
) ) );
2217 js_log('DO update: ' + this.jump_time
);
2218 this.updateThumbTime( rel_start_sec
);
2220 hideHighlight:function(){
2221 var eid
= (this.pc
)?this.pc
.pp
.id
:this.id
;
2222 $j('#mv_seeker_' + eid
+ ' .mv_highlight').hide();
2223 this.setStatus( this.getTimeReq() );
2224 this.setSliderValue( 0 );
2226 setStatus:function(value
){
2227 var eid
= (this.pc
)?this.pc
.pp
.id
:this.id
;
2229 $j('#' + eid
+ ' .time-disp').html(value
);
2236 * mediaPlayer represents a media player plugin.
2237 * @param {String} id id used for the plugin.
2238 * @param {Array<String>} supported_types n array of supported MIME types.
2239 * @param {String} library external script containing the plugin interface code. (mv_<library>Embed.js)
2242 function mediaPlayer(id
, supported_types
, library
)
2245 this.supported_types
= supported_types
;
2246 this.library
= library
;
2247 this.loaded
= false;
2248 this.loading_callbacks
= new Array();
2251 mediaPlayer
.prototype =
2254 supported_types
:null,
2257 loading_callbacks
:null,
2258 supportsMIMEType : function(type
)
2260 for (var i
=0; i
< this.supported_types
.length
; i
++)
2261 if(this.supported_types
[i
] == type
)
2265 getName : function()
2267 return gM('mwe-ogg-player-' + this.id
);
2269 load : function(callback
){
2271 this.library
+ 'Embed'
2277 /* players and supported mime types
2278 @@todo ideally we query the plugin to get what mime types it supports in practice not always reliable/avaliable
2280 var flowPlayer
= new mediaPlayer('flowplayer',['video/x-flv', 'video/h264'],'flash');
2282 var omtkPlayer
= new mediaPlayer('omtkplayer',['audio/ogg'], 'omtk' );
2284 var cortadoPlayer
= new mediaPlayer('cortado',['video/ogg', 'audio/ogg'],'java');
2285 var videoElementPlayer
= new mediaPlayer('videoElement',['video/ogg', 'audio/ogg'],'native');
2287 var vlcMineList
= ['video/ogg','audio/ogg', 'video/x-flv', 'video/mp4', 'video/h264'];
2288 var vlcMozillaPlayer
= new mediaPlayer('vlc-mozilla',vlcMineList
,'vlc');
2289 var vlcActiveXPlayer
= new mediaPlayer('vlc-activex',vlcMineList
,'vlc');
2292 var oggPluginPlayer
= new mediaPlayer('oggPlugin',['video/ogg'],'generic');
2294 //depricate quicktime in favor of safari native
2295 //var quicktimeMozillaPlayer = new mediaPlayer('quicktime-mozilla',['video/ogg'],'quicktime');
2296 //var quicktimeActiveXPlayer = new mediaPlayer('quicktime-activex',['video/ogg'],'quicktime');
2298 var htmlPlayer
= new mediaPlayer('html',['text/html', 'image/jpeg', 'image/png', 'image/svg'], 'html');
2301 * mediaPlayers is a collection of mediaPlayer objects supported by the client.
2302 * It could be merged with embedTypes, since there is one embedTypes per script
2303 * and one mediaPlayers per embedTypes.
2305 function mediaPlayers()
2310 mediaPlayers
.prototype =
2314 default_players
: {},
2317 this.players
= new Array();
2318 this.loadPreferences();
2320 //set up default players order for each library type
2321 this.default_players
['video/x-flv'] = ['flash','vlc'];
2322 this.default_players
['video/h264'] = ['flash', 'vlc'];
2324 this.default_players
['video/ogg'] = ['native','vlc','java', 'generic'];
2325 this.default_players
['application/ogg'] = ['native','vlc','java', 'generic'];
2326 this.default_players
['audio/ogg'] = ['native','vlc', 'java', 'omtk' ];
2327 this.default_players
['video/mp4'] = ['vlc'];
2329 this.default_players
['text/html'] = ['html'];
2330 this.default_players
['image/jpeg'] = ['html'];
2331 this.default_players
['image/png'] = ['html'];
2332 this.default_players
['image/svg'] = ['html'];
2335 addPlayer : function(player
, mime_type
)
2337 //js_log('Adding ' + player.id + ' with mime_type ' + mime_type);
2338 for (var i
=0; i
< this.players
.length
; i
++){
2339 if (this.players
[i
].id
== player
.id
)
2343 //make sure the mime_type is not already there:
2344 var add_mime
= true;
2345 for(var j
=0; j
< this.players
[i
].supported_types
.length
; j
++ ){
2346 if( this.players
[i
].supported_types
[j
]== mime_type
)
2350 this.players
[i
].supported_types
.push(mime_type
);
2357 player
.supported_types
.push(mime_type
);
2359 this.players
.push( player
);
2361 getMIMETypePlayers : function(mime_type
)
2363 var mime_players
= new Array();
2366 if( this.default_players
[mime_type
] ){
2367 $j
.each( this.default_players
[mime_type
], function(d
, lib
){
2368 var library
= _this
.default_players
[mime_type
][d
];
2369 for ( var i
=0; i
< _this
.players
.length
; i
++ ){
2370 if ( _this
.players
[i
].library
== library
&& _this
.players
[i
].supportsMIMEType(mime_type
) ){
2371 mime_players
[ inx
] = _this
.players
[i
];
2377 return mime_players
;
2379 defaultPlayer : function(mime_type
)
2381 js_log("get defaultPlayer for " + mime_type
);
2382 var mime_players
= this.getMIMETypePlayers(mime_type
);
2383 if( mime_players
.length
> 0)
2385 // check for prior preference for this mime type
2386 for( var i
=0; i
< mime_players
.length
; i
++ ){
2387 if( mime_players
[i
].id
==this.preference
[mime_type
] )
2388 return mime_players
[i
];
2390 // otherwise just return the first compatible player
2391 // (it will be chosen according to the default_players list
2392 return mime_players
[0];
2394 js_log( 'No default player found for ' + mime_type
);
2397 userSelectFormat : function (mime_format
){
2398 this.preference
['format_prefrence'] = mime_format
;
2399 this.savePreferences();
2401 userSelectPlayer : function(player_id
, mime_type
)
2403 var selected_player
=null;
2404 for(var i
=0; i
< this.players
.length
; i
++){
2405 if(this.players
[i
].id
== player_id
)
2407 selected_player
= this.players
[i
];
2408 js_log('choosing ' + player_id
+ ' for ' + mime_type
);
2409 this.preference
[mime_type
]=player_id
;
2410 this.savePreferences();
2414 if( selected_player
)
2416 for(var i
=0; i
< $mw
.player_list
.length
; i
++)
2418 var embed
= $j('#'+$mw
.player_list
[i
]).get(0);
2419 if(embed
.media_element
.selected_source
&& (embed
.media_element
.selected_source
.mime_type
== mime_type
))
2421 embed
.selectPlayer(selected_player
);
2422 js_log('using ' + embed
.selected_player
.getName() + ' for ' + embed
.media_element
.selected_source
.getTitle());
2427 loadPreferences : function()
2429 this.preference
= new Object();
2430 // see if we have a cookie set to a clientSupported type:
2431 var cookieVal
= $j
.cookie( 'ogg_player_exp' );
2434 var pairs
= cookieVal
.split('&');
2435 for(var i
=0; i
< pairs
.length
; i
++)
2437 var name_value
= pairs
[i
].split('=');
2438 this.preference
[name_value
[0]]=name_value
[1];
2439 //js_log('load preference for ' + name_value[0] + ' is ' + name_value[1]);
2443 savePreferences : function()
2446 for(var i
in this.preference
)
2447 cookieVal
+= i
+ '='+ this.preference
[i
] + '&';
2449 cookieVal
=cookieVal
.substr(0, cookieVal
.length
-1);
2450 var week
= 7*86400*1000;
2451 $j
.cookie( 'ogg_player_exp', cookieVal
, { 'expires':week
} );
2456 * embedTypes object handles setting and getting of supported embed types:
2457 * closely mirrors OggHandler so that its easier to share efforts in this area:
2458 * http://svn.wikimedia.org/viewvc/mediawiki/trunk/extensions/OggHandler/OggPlayer.js
2465 //detect supported types
2467 this.detect_done
=true;
2469 clientSupports
: { 'thumbnail' : true },
2470 supportedMimeType: function(mimetype
) {
2471 for (var i
= navigator
.plugins
.length
; i
-- > 0; ) {
2472 var plugin
= navigator
.plugins
[i
];
2473 if (typeof plugin
[mimetype
] != "undefined")
2479 detect: function() {
2480 js_log("running detect");
2481 this.players
= new mediaPlayers();
2482 //every browser supports html rendering:
2483 this.players
.addPlayer( htmlPlayer
);
2484 // In Mozilla, navigator.javaEnabled() only tells us about preferences, we need to
2485 // search navigator.mimeTypes to see if it's installed
2486 var javaEnabled
= navigator
.javaEnabled();
2487 // In Opera, navigator.javaEnabled() is all there is
2488 var invisibleJava
= $j
.browser
.opera
;
2489 // Some browsers filter out duplicate mime types, hiding some plugins
2490 var uniqueMimesOnly
= $j
.browser
.opera
|| $j
.browser
.safari
;
2491 // Opera will switch off javaEnabled in preferences if java can't be found.
2492 // And it doesn't register an application/x-java-applet mime type like Mozilla does.
2493 if ( invisibleJava
&& javaEnabled
)
2494 this.players
.addPlayer( cortadoPlayer
);
2497 if($j
.browser
.msie
){
2499 if ( this.testActiveX( 'ShockwaveFlash.ShockwaveFlash')){
2500 //try to get the flash version for omtk include:
2502 a
= new ActiveXObject(SHOCKWAVE_FLASH_AX
+ ".7");
2503 d
= a
.GetVariable("$version"); // Will crash fp6.0.21/23/29
2505 d
= d
.split(" ")[1].split(",");
2506 //we need flash version 10 or greater:
2507 if(parseInt( d
[0]) >=10){
2508 this.players
.addPlayer( omtkPlayer
);
2514 //flowplayer has pretty good compatiablity
2515 // (but if we wanted to be fancy we would check for version of flash and update the mp4/h.264 support
2516 this.players
.addPlayer( flowPlayer
);
2519 if ( this.testActiveX( 'VideoLAN.VLCPlugin.2' ) )
2520 this.players
.addPlayer(vlcActiveXPlayer
);
2522 if ( javaEnabled
&& this.testActiveX( 'JavaWebStart.isInstalled' ) )
2523 this.players
.addPlayer(cortadoPlayer
);
2525 //if ( this.testActiveX( 'QuickTimeCheckObject.QuickTimeCheck.1' ) )
2526 // this.players.addPlayer(quicktimeActiveXPlayer);
2529 if ( typeof HTMLVideoElement
== 'object' // Firefox, Safari
2530 || typeof HTMLVideoElement
== 'function' ) // Opera
2532 //do another test for safari:
2533 if( $j
.browser
.safari
){
2535 var dummyvid
= document
.createElement("video");
2536 if (dummyvid
.canPlayType
&& dummyvid
.canPlayType("video/ogg;codecs=\"theora,vorbis\"") == "probably")
2538 this.players
.addPlayer( videoElementPlayer
);
2539 } else if(this.supportedMimeType( 'video/ogg' )) {
2540 /* older versions of safari do not support canPlayType,
2541 but xiph qt registers mimetype via quicktime plugin */
2542 this.players
.addPlayer( videoElementPlayer
);
2544 //@@todo add some user nagging to install the xiph qt
2547 js_log('could not run canPlayType in safari');
2550 this.players
.addPlayer( videoElementPlayer
);
2555 if( navigator
.mimeTypes
&& navigator
.mimeTypes
.length
> 0) {
2556 for ( var i
= 0; i
< navigator
.mimeTypes
.length
; i
++ ) {
2557 var type
= navigator
.mimeTypes
[i
].type
;
2558 var semicolonPos
= type
.indexOf( ';' );
2559 if ( semicolonPos
> -1 ) {
2560 type
= type
.substr( 0, semicolonPos
);
2562 //js_log('on type: '+type);
2563 var pluginName
= navigator
.mimeTypes
[i
].enabledPlugin
? navigator
.mimeTypes
[i
].enabledPlugin
.name
: '';
2564 if ( !pluginName
) {
2565 // In case it is null or undefined
2568 if ( pluginName
.toLowerCase() == 'vlc multimedia plugin' || pluginName
.toLowerCase() == 'vlc multimedia plug-in' ) {
2569 this.players
.addPlayer(vlcMozillaPlayer
, type
);
2573 if ( javaEnabled
&& type
== 'application/x-java-applet' ) {
2574 this.players
.addPlayer(cortadoPlayer
);
2578 if ( type
== 'application/ogg' ) {
2579 if ( pluginName
.toLowerCase() == 'vlc multimedia plugin' ){
2580 this.players
.addPlayer(vlcMozillaPlayer
, type
);
2581 //else if ( pluginName.indexOf( 'QuickTime' ) > -1 )
2582 // this.players.addPlayer(quicktimeMozillaPlayer);
2584 this.players
.addPlayer(oggPluginPlayer
);
2587 } else if ( uniqueMimesOnly
) {
2588 if ( type
== 'application/x-vlc-player' ) {
2589 this.players
.addPlayer(vlcMozillaPlayer
, type
);
2591 } else if ( type
== 'video/quicktime' ) {
2592 //this.players.addPlayer(quicktimeMozillaPlayer);
2597 /*if ( type == 'video/quicktime' ) {
2598 this.players.addPlayer(vlcMozillaPlayer, type);
2601 if(type
=='application/x-shockwave-flash'){
2602 this.players
.addPlayer( flowPlayer
);
2604 //check version to add omtk:
2605 var flashDescription
= navigator
.plugins
["Shockwave Flash"].description
;
2606 var descArray
= flashDescription
.split(" ");
2607 var tempArrayMajor
= descArray
[2].split(".");
2608 var versionMajor
= tempArrayMajor
[0];
2609 //js_log("version of flash: " + versionMajor);
2610 if(versionMajor
>= 10){
2611 this.players
.addPlayer( omtkPlayer
);
2617 //@@The xiph quicktime component does not work well with annodex streams (temporarly disable)
2618 //this.clientSupports['quicktime-mozilla'] = false;
2619 //this.clientSupports['quicktime-activex'] = false;
2620 //js_log(this.clientSupports);
2622 testActiveX : function ( name
) {
2625 // No IE, not a class called "name", it's a variable
2626 var obj
= new ActiveXObject( '' + name
);