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 support",
39 "mwe-ogg-player-vlc-activex" : "VLC ActiveX",
40 "mwe-ogg-player-oggPlugin" : "Generic Ogg plugin",
41 "mwe-ogg-player-quicktime-mozilla" : "QuickTime plugin",
42 "mwe-ogg-player-quicktime-activex" : "QuickTime ActiveX",
43 "mwe-ogg-player-cortado" : "Java Cortado",
44 "mwe-ogg-player-flowplayer" : "Flowplayer",
45 "mwe-ogg-player-selected" : "(selected)",
46 "mwe-ogg-player-omtkplayer" : "OMTK Flash Vorbis",
47 "mwe-generic_missing_plugin" : "You browser does not appear to support the following playback type: <b>$1<\/b><br \/>Visit the <a href=\"http:\/\/commons.wikimedia.org\/wiki\/Commons:Media_help\">Playback Methods<\/a> page to download a player.<br \/>",
48 "mwe-for_best_experience" : "For a better video playback experience we recommend:<br \/><b><a href=\"http:\/\/www.mozilla.com\/en-US\/firefox\/upgrade.html?from=mwEmbed\">Firefox 3.5<\/a>.<\/b>",
49 "mwe-do_not_warn_again" : "Dismiss for now.",
50 "mwe-playerselect" : "Players",
51 "mwe-read_before_embed" : "<a href=\"http:\/\/mediawiki.org\/wiki\/Security_Notes_on_Remote_Embedding\" target=\"_new\">Read this<\/a> before embedding.",
52 "mwe-embed_site_or_blog" : "Embed on your site or blog",
53 "mwe-related_videos" : "Related videos",
54 "mwe-seeking" : "seeking",
55 "mwe-copy-code" : "Copy code"
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 videoInterface
.type
='audio';
158 mvEmbed
.swapEmbedVideoElement( this_elm
, videoInterface
);
161 loadPlaylistLib
=true;
165 if( force_id
== null && force_id
!= '' ){
166 var j_selector
= 'video,audio,playlist';
168 var j_selector
= '#' + force_id
;
171 js_log('j_selector:: ' + j_selector
);
172 //process selected elements:
173 //ie8 does not play well with the jQuery video,audio,playlist selector use native:
174 if($j
.browser
.msie
&& $j
.browser
.version
>= 8){
175 jtags
= j_selector
.split(',');
176 for( var i
=0; i
< jtags
.length
; i
++){
177 $j( document
.getElementsByTagName( jtags
[i
] )).each(function(){
182 $j( j_selector
).each(function(){
189 '$j.ui', //include dialog for pop-ing up thigns
192 //deal with each playlist instance
193 $j('playlist').each(function(){
194 //create new playlist interface:
195 var plObj
= new mvPlayList( this );
196 mvEmbed
.swapEmbedVideoElement(this, plObj
);
197 var added_height
= plObj
.pl_layout
.title_bar_height
+ plObj
.pl_layout
.control_height
;
198 //move into a blocking display container with height + controls + title height:
199 $j('#'+plObj
.id
).wrap('<div style="display:block;height:' + (plObj
.height
+ added_height
) + 'px;"></div>');
203 this.checkClipsReady();
206 * swapEmbedVideoElement
207 * takes a video element as input and swaps it out with
208 * an embed video interface based on the video_elements attributes
210 swapEmbedVideoElement:function(video_element
, videoInterface
){
211 js_log('do swap ' + videoInterface
.id
+ ' for ' + video_element
);
212 embed_video
= document
.createElement('div');
213 //make sure our div has a hight/width set:
215 $j(embed_video
).css({
216 'width':videoInterface
.width
,
217 'height':videoInterface
.height
218 }).html( mv_get_loading_img() );
219 //inherit the video interface
220 for(var method
in videoInterface
){ //for in loop oky in Element context
221 if(method
!='readyState'){ //readyState crashes IE
223 embed_video
.setAttribute('style', videoInterface
[method
]);
224 }else if(method
=='class'){
225 if( $j
.browser
.msie
)
226 embed_video
.setAttribute("className", videoInterface
['class']);
228 embed_video
.setAttribute("class", videoInterface
['class']);
231 embed_video
[method
]=videoInterface
[method
];
235 if(embed_video
[method
]=="false")embed_video
[method
]=false;
236 if(embed_video
[method
]=="true")embed_video
[method
]=true;
238 ///js_log('did vI style');
239 //now swap out the video element for the embed_video obj:
240 $j(video_element
).after(embed_video
).remove();
241 //js_log('did swap');
242 $j('#'+embed_video
.id
).get(0).on_dom_swap();
244 // now that "embed_video" is stable, do more initialization (if we are ready)
245 if($j('#'+embed_video
.id
).get(0).loading_external_data
== false
246 && $j('#'+embed_video
.id
).get(0).init_with_sources_loadedDone
== false){
247 //load and set ready state since source are available:
248 $j('#'+embed_video
.id
).get(0).init_with_sources_loaded();
251 js_log('done with child: ' + embed_video
.id
+ ' len:' + $mw
.player_list
.length
);
254 //this should not be needed.
255 checkClipsReady : function(){
256 //js_log('checkClipsReady');
258 for(var i
=0; i
< $mw
.player_list
.length
; i
++){
259 if( $j('#'+$mw
.player_list
[i
]).length
!=0){
260 var cur_vid
= $j('#'+$mw
.player_list
[i
]).get(0);
261 is_ready
= ( cur_vid
.ready_to_play
) ? is_ready
: false;
262 if( !is_ready
&& cur_vid
.load_error
){
264 $j(cur_vid
).html( cur_vid
.load_error
);
269 mvEmbed
.allClipsReady
= true;
270 // run queued functions
271 //js_log('run queded functions:' + mvEmbed.flist[0]);
274 setTimeout( 'mvEmbed.checkClipsReady()', 25 );
278 while (this.flist
.length
){
279 this.flist
.shift()();
285 * mediaSource class represents a source for a media element.
286 * @param {String} type MIME type of the source.
287 * @param {String} uri URI of the source.
290 function mediaSource(element
)
296 mediaSource
.prototype =
298 /** MIME type of the source. */
300 /** URI of the source. */
302 /** Title of the source. */
304 /** True if the source has been marked as the default. */
305 marked_default
:false,
306 /** True if the source supports url specification of offset and duration */
307 URLTimeEncoding
:false,
308 /** Start offset of the requested segment */
310 /** Duration of the requested segment (0 if not known) */
313 upddate_interval
:null,
319 init : function(element
)
321 //js_log('adding mediaSource: ' + element);
322 this.src
= $j(element
).attr('src');
323 this.marked_default
= false;
324 if ( element
.tagName
.toLowerCase() == 'video')
325 this.marked_default
= true;
327 //set default URLTimeEncoding if we have a time url:
328 //not ideal way to discover if content is on an oggz_chop server.
329 //should check some other way.
330 var pUrl
= parseUri ( this.src
);
331 if(typeof pUrl
['queryKey']['t'] != 'undefined'){
332 this['URLTimeEncoding']=true;
334 for(var i
=0; i
< mv_default_source_attr
.length
; i
++){ //array loop:
335 var attr
= mv_default_source_attr
[ i
];
336 if( $j(element
).attr( attr
) ) {
337 this[ attr
] = $j(element
).attr( attr
);
340 //update duration from hit if present:
341 if(this.durationHint
)
342 this.duration
= this.durationHint
;
345 if ( $j(element
).attr('type'))
346 this.mime_type
= $j(element
).attr('type');
347 else if ($j(element
).attr('content-type'))
348 this.mime_type
= $j(element
).attr('content-type');
350 this.mime_type
= this.detectType(this.src
);
352 //set the title if unset:
354 this.title
= this.mime_type
;
356 this.parseURLDuration();
358 updateSource:function(element
){
359 //for now just update the title:
360 if ($j(element
).attr("title"))
361 this.title
= $j(element
).attr("title");
363 /** updates the src time and start & end
364 * @param {String} start_time in NTP format
365 * @param {String} end_time in NTP format
367 updateSrcTime:function (start_ntp
, end_ntp
){
368 //js_log("f:updateSrcTime: "+ start_ntp+'/'+ end_ntp + ' from org: ' + this.start_ntp+ '/'+this.end_ntp);
369 //js_log("pre uri:" + this.src);
370 //if we have time we can use:
371 if( this.URLTimeEncoding
){
372 //make sure its a valid start time / end time (else set default)
373 if( !npt2seconds(start_ntp
) )
374 start_ntp
= this.start_ntp
;
376 if( !npt2seconds(end_ntp
) )
377 end_ntp
= this.end_ntp
;
379 this.src
= getURLParamReplace(this.src
, { 't': start_ntp
+'/'+ end_ntp
} );
381 //update the duration
382 this.parseURLDuration();
385 setDuration:function (duration
)
387 this.duration
= duration
;
389 this.end_ntp
= seconds2npt( this.start_offset
+ duration
);
392 /** MIME type accessor function.
393 @return the MIME type of the source.
396 getMIMEType : function()
398 return this.mime_type
;
400 /** URI accessor function.
401 * @param int seek_time_sec (used to adjust the URI for url based seeks)
402 @return the URI of the source.
405 getURI : function( seek_time_sec
)
407 if( !seek_time_sec
|| !this.URLTimeEncoding
){
413 var endvar
= '/'+ this.end_ntp
;
415 return getURLParamReplace(this.src
, { 't': seconds2npt( seek_time_sec
)+endvar
} ); ;
417 /** Title accessor function.
418 @return the title of the source.
421 getTitle : function()
425 /** Index accessor function.
426 @return the source's index within the enclosing mediaElement container.
429 getIndex : function()
434 * function getDuration in milliseconds
435 * special case derive duration from request url
436 * supports media_url?t=ntp_start/ntp_end url request format
438 parseURLDuration : function(){
439 //check if we have a URLTimeEncoding:
440 if( this.URLTimeEncoding
){
441 var annoURL
= parseUri( this.src
);
442 if( annoURL
.queryKey
['t'] ){
443 var times
= annoURL
.queryKey
['t'].split('/');
444 this.start_ntp
= times
[0];
445 this.end_ntp
= times
[1];
446 this.start_offset
= npt2seconds( this.start_ntp
);
447 this.duration
= npt2seconds( this.end_ntp
) - this.start_offset
;
449 //look for this info as attributes
450 if(this.startOffset
){
451 this.start_offset
= this.startOffset
;
452 this.start_ntp
= seconds2npt( this.startOffset
);
455 this.end_ntp
= seconds2npt( parseInt(this.duration
) + parseInt(this.start_offset
) );
459 //else nothing to parse just keep whatever info we already have
461 //js_log('f:parseURLDuration() for:' + this.src + ' d:' + this.duration);
463 /** Attempts to detect the type of a media file based on the URI.
464 @param {String} uri URI of the media file.
465 @returns The guessed MIME type of the file.
468 detectType:function(uri
)
470 //@@todo if media is on the same server as the javascript or we have mv_proxy configured
471 //we can issue a HEAD request and read the mime type of the media...
472 // (this will detect media mime type independently of the url name)
473 //http://www.jibbering.com/2002/4/httprequest.html (this should be done by extending jquery's ajax objects)
474 var end_inx
= (uri
.indexOf('?')!=-1)? uri
.indexOf('?') : uri
.length
;
475 var no_param_uri
= uri
.substr(0, end_inx
);
476 switch( no_param_uri
.substr(no_param_uri
.lastIndexOf('.'),4).toLowerCase() ){
477 case '.flv':return 'video/x-flv';break;
478 case '.ogg': case '.ogv': return 'video/ogg';break;
479 case '.oga': return 'audio/ogg'; break;
480 case '.anx':return 'video/ogg';break;
485 /** A media element corresponding to a <video> element.
486 It is implemented as a collection of mediaSource objects. The media sources
487 will be initialized from the <video> element, its child <source> elements,
488 and/or the ROE file referenced by the <video> element.
489 @param {element} video_element <video> element used for initialization.
492 function mediaElement(video_element
)
494 this.init(video_element
);
497 mediaElement
.prototype =
499 /** The array of mediaSource elements. */
502 /** Selected mediaSource element. */
503 selected_source
:null,
508 init:function( video_element
)
511 js_log('Initializing mediaElement...' );
512 this.sources
= new Array();
513 this.thumbnail
= mv_default_thumb_url
;
514 // Process the source element:
515 if($j(video_element
).attr("src"))
516 this.tryAddSource(video_element
);
518 if($j(video_element
).attr('thumbnail'))
519 this.thumbnail
= $j(video_element
).attr('thumbnail');
521 if($j(video_element
).attr('poster'))
522 this.thumbnail
= $j(video_element
).attr('poster');
524 if($j(video_element
).attr('wikiTitleKey'))
525 this.wikiTitleKey
=$j(video_element
).attr('wikiTitleKey');
527 // Process all inner <source> elements
528 //js_log("inner source count: " + video_element.getElementsByTagName('source').length );
530 $j(video_element
).find('source,text').each(function(inx
, inner_source
){
531 _this
.tryAddSource( inner_source
);
534 /** Updates the time request for all sources that have a standard time request argument (ie &t=start_time/end_time)
536 updateSourceTimes:function(start_ntp
, end_ntp
){
538 $j
.each(this.sources
, function(inx
, mediaSource
){
539 mediaSource
.updateSrcTime(start_ntp
, end_ntp
);
543 timedTextSources:function(){
544 for(var i
=0; i
< this.sources
.length
; i
++){
545 if( this.sources
[i
].mime_type
== 'text/cmml' ||
546 this.sources
[i
].mime_type
== 'text/x-srt')
551 /** Returns the array of mediaSources of this element.
552 \returns {Array} Array of mediaSource elements.
554 getSources:function( mime_filter
)
559 var source_set
= new Array();
560 for(var i
=0; i
< this.sources
.length
; i
++){
561 if( this.sources
[i
].mime_type
.indexOf( mime_filter
) != -1 )
562 source_set
.push( this.sources
[i
] );
566 getSourceById:function( source_id
){
567 for(var i
=0; i
< this.sources
.length
; i
++){
568 if( this.sources
[i
].id
== source_id
)
569 return this.sources
[i
];
573 /** Selects a particular source for playback.
575 selectSource:function(index
)
577 js_log('f:selectSource:'+index
);
578 var playable_sources
= this.getPlayableSources();
579 for(var i
=0; i
< playable_sources
.length
; i
++){
581 this.selected_source
= playable_sources
[i
];
582 //update the user selected format:
583 embedTypes
.players
.userSelectFormat( playable_sources
[i
].mime_type
);
588 /** selects the default source via cookie preference, default marked, or by id order
590 autoSelectSource:function(){
591 js_log('f:autoSelectSource:');
592 //@@todo read user preference for source
593 // Select the default source
594 var playable_sources
= this.getPlayableSources();
595 var flash_flag
=ogg_flag
=false;
597 for(var source
=0; source
< playable_sources
.length
; source
++){
598 var mime_type
=playable_sources
[source
].mime_type
;
599 if( playable_sources
[source
].marked_default
){
600 js_log('set via marked default: ' + playable_sources
[source
].marked_default
);
601 this.selected_source
= playable_sources
[source
];
604 //set via user-preference
605 if(embedTypes
.players
.preference
['format_prefrence'] == mime_type
){
606 js_log('set via preference: '+playable_sources
[source
].mime_type
);
607 this.selected_source
= playable_sources
[source
];
611 //set Ogg via player support
612 for(var source
=0; source
< playable_sources
.length
; source
++){
613 js_log('f:autoSelectSource:' + playable_sources
[source
].mime_type
);
614 var mime_type
=playable_sources
[source
].mime_type
;
615 //set source via player
616 if(mime_type
=='video/ogg' || mime_type
=='ogg/video' || mime_type
=='video/annodex' || mime_type
=='application/ogg'){
617 for(var i
=0; i
< embedTypes
.players
.players
.length
; i
++){ //for in loop on object oky
618 var player
= embedTypes
.players
.players
[i
];
619 if(player
.library
=='vlc' || player
.library
=='native'){
620 js_log('set via ogg via order');
621 this.selected_source
= playable_sources
[source
];
628 for(var source
=0; source
< playable_sources
.length
; source
++){
629 var mime_type
=playable_sources
[source
].mime_type
;
630 if( mime_type
=='video/x-flv' ){
631 js_log('set via by player preference normal flash')
632 this.selected_source
= playable_sources
[source
];
637 for(var source
=0; source
< playable_sources
.length
; source
++){
638 var mime_type
=playable_sources
[source
].mime_type
;
639 if( mime_type
=='video/h264' ){
640 js_log('set via playable_sources preference h264 flash')
641 this.selected_source
= playable_sources
[source
];
645 //select first source
646 if (!this.selected_source
)
648 js_log('set via first source:' + playable_sources
[0]);
649 this.selected_source
= playable_sources
[0];
653 /** Returns the thumbnail URL for the media element.
654 \returns {String} thumbnail URL
656 getThumbnailURL:function()
658 return this.thumbnail
;
660 /** Checks whether there is a stream of a specified MIME type.
661 @param {String} mime_type MIME type to check.
662 @type {BooleanPrimitive}.
664 hasStreamOfMIMEType:function(mime_type
)
666 for(source
in this.sources
)
668 if(this.sources
[source
].getMIMEType() == mime_type
)
673 isPlayableType:function(mime_type
)
675 if( embedTypes
.players
.defaultPlayer( mime_type
) ){
680 //if(this.selected_player){
681 //return mime_type=='video/ogg' || mime_type=='ogg/video' || mime_type=='video/annodex' || mime_type=='video/x-flv';
683 /** Adds a single mediaSource using the provided element if
684 the element has a 'src' attribute.
685 @param element {element} <video>, <source> or <mediaSource> element.
687 tryAddSource:function(element
)
689 js_log('f:tryAddSource:'+ $j(element
).attr("src"));
690 if (! $j(element
).attr("src")){
691 //js_log("element has no src");
694 var new_src
= $j(element
).attr('src');
695 //make sure an existing element with the same src does not already exist:
696 for( var i
=0; i
< this.sources
.length
; i
++ ){
697 if(this.sources
[i
].src
== new_src
){
698 //js_log('checking existing: '+this.sources[i].getURI() + ' != '+ new_src);
699 //can't add it all but try to update any additional attr:
700 this.sources
[i
].updateSource(element
);
704 var source
= new mediaSource( element
);
705 this.sources
.push(source
);
706 //alert('pushed source to stack'+ source + 'sl:'+this.sources.length);
708 getPlayableSources: function(){
709 var playable_sources
= new Array();
710 for(var i
=0; i
< this.sources
.length
; i
++){
711 if( this.isPlayableType( this.sources
[i
].mime_type
) ){
712 playable_sources
.push( this.sources
[i
] );
714 js_log("type "+ this.sources
[i
].mime_type
+ 'is not playable');
717 return playable_sources
;
719 /* Imports media sources from ROE data.
720 * @param roe_data ROE data.
722 addROE:function(roe_data
){
724 this.addedROEData
=true;
726 if( typeof roe_data
== 'string' )
728 var parser
=new DOMParser();
729 js_log('ROE data:' + roe_data
);
730 roe_data
=parser
.parseFromString(roe_data
,"text/xml");
733 $j
.each(roe_data
.getElementsByTagName('mediaSource'), function(inx
, source
){
734 _this
.tryAddSource(source
);
737 $j
.each(roe_data
.getElementsByTagName('img'), function(inx
, n
){
738 if($j(n
).attr("id")=="stream_thumb"){
739 js_log('roe:set thumb to '+$j(n
).attr("src"));
740 _this
['thumbnail'] =$j(n
).attr("src");
744 $j
.each(roe_data
.getElementsByTagName('link'), function(inx
, n
){
745 if($j(n
).attr('id')=='html_linkback'){
746 js_log('roe:set linkback to '+$j(n
).attr("href"));
747 _this
['linkback'] = $j(n
).attr('href');
751 js_log('ROE data empty.');
757 /** base embedVideo object
758 @param element <video> tag used for initialization.
761 var embedVideo = function(element
) {
762 return this.init(element
);
765 embedVideo
.prototype = {
766 /** The mediaElement object containing all mediaSource objects */
769 ready_to_play
:false, //should use html5 ready state
770 load_error
:false, //used to set error in case of error
771 loading_external_data
:false,
772 thumbnail_updating
:false,
774 init_with_sources_loadedDone
:false,
776 //for onClip done stuff:
777 anno_data_cache
:null,
779 base_seeker_slider_offset
:null,
780 onClipDone_disp
:false,
782 //for seek thumb updates:
783 cur_thumb_seek_time
:0,
784 thumb_seek_interval
:null,
787 //set the buffered percent:
789 //utility functions for property values:
790 hx : function ( s
) {
791 if ( typeof s
!= 'String' ) {
794 return s
.replace( /&/g
, '&' )
795 . replace( /</g
, '<' )
796 . replace( />/g
, '>' );
798 hq : function ( s
) {
799 return '"' + this.hx( s
) + '"';
801 playerPixelWidth : function()
803 var player
= $j('#mv_embedded_player_'+this.id
).get(0);
804 if(typeof player
!='undefined' && player
['offsetWidth'])
805 return player
.offsetWidth
;
807 return parseInt(this.width
);
809 playerPixelHeight : function()
811 var player
= $j('#mv_embedded_player_'+this.id
).get(0);
812 if(typeof player
!='undefined' && player
['offsetHeight'])
813 return player
.offsetHeight
;
815 return parseInt(this.height
);
817 init: function(element
){
818 //inherit all the default video_attributes
819 for(var attr
in default_video_attributes
){ //for in loop oky on user object
820 if(element
.getAttribute(attr
)){
821 this[ attr
]=element
.getAttribute(attr
);
823 this[attr
]=default_video_attributes
[attr
];
827 //set the skin name from the config (if not set locally)
828 if( !this.skin_name
)
829 this.skin_name
= $mw
.conf
['skin_name'];
831 //make sure startOffset is cast as an int
832 if( this.startOffset
&& this.startOffset
.split(':').length
>= 2)
833 this.startOffset
= npt2seconds(this.startOffset
);
834 //make sure offset is in float:
835 this.startOffset
= parseFloat(this.startOffset
);
837 if( this.duration
&& this.duration
.split(':').length
>= 2)
838 this.duration
= npt2seconds( this.duration
);
839 //make sure duration is in float:
840 this.duration
= parseFloat(this.duration
);
841 js_log("duration is: " + this.duration
);
842 //if style is set override width and height
843 var dwh
= $mw
.conf
['video_size'].split('x');
844 this.width
= element
.style
.width
? element
.style
.width
: dwh
[0];
845 this.height
= element
.style
.height
? element
.style
.height
: dwh
[1];
847 this.pid
= 'pid_' + this.id
;
849 //grab any innerHTML and set it to missing_plugin_html
850 //@@todo we should strip source tags instead of checking and skipping
851 if(element
.innerHTML
!='' && element
.getElementsByTagName('source').length
==0){
852 js_log('innerHTML: ' + element
.innerHTML
);
853 this.user_missing_plugin_html
=element
.innerHTML
;
855 // load all of the specified sources
856 this.media_element
= new mediaElement(element
);
858 //if we are displaying controls setup the ctrlBuilder
860 //set-up the local ctrlBuilder instance:
861 this.ctrlBuilder
= new ctrlBuilder( this );
862 //load the css for the current player
866 loadExternalCss( mv_embed_path
+ 'skins/' + this.skin_name
+ '/playerSkin.css');
868 on_dom_swap: function(){
869 js_log('f:on_dom_swap');
870 // Process the provided ROE file... if we don't yet have sources
871 if(this.roe
&& this.media_element
.sources
.length
==0 ){
872 js_log('loading external data');
873 this.loading_external_data
=true;
875 do_request(this.roe
, function(data
)
878 _this
.media_element
.addROE( data
);
879 js_log('added_roe::' + _this
.media_element
.sources
.length
);
881 js_log('set loading_external_data=false');
882 _this
.loading_external_data
=false;
884 _this
.init_with_sources_loaded();
888 init_with_sources_loaded : function()
890 js_log('f:init_with_sources_loaded');
891 //set flag that we have run this function:
892 this.init_with_sources_loadedDone
=true;
893 //autoseletct the source
894 this.media_element
.autoSelectSource();
895 //auto select player based on default order
896 if( !this.media_element
.selected_source
)
898 //check for parent clip:
899 if( typeof this.pc
!= 'undefined' ){
900 js_log('no sources, type:' +this.type
+ ' check for html');
902 //do load player if just displaying innerHTML:
903 if( this.pc
.type
== 'text/html' ){
904 this.selected_player
= embedTypes
.players
.defaultPlayer( 'text/html' );
905 js_log('set selected player:'+ this.selected_player
.mime_type
);
909 this.selected_player
= embedTypes
.players
.defaultPlayer( this.media_element
.selected_source
.mime_type
);
911 if( this.selected_player
){
912 js_log('selected ' + this.selected_player
.getName());
913 js_log("PLAYBACK TYPE: "+this.selected_player
.library
);
914 this.thumbnail_disp
= true;
915 this.inheritEmbedObj();
917 //no source's playable
918 var missing_type
='';
920 for( var i
=0; i
< this.media_element
.sources
.length
; i
++){
921 missing_type
+= or
+ this.media_element
.sources
[i
].mime_type
;
925 var missing_type
= this.pc
.type
;
926 js_log('no player found for given source type ' + missing_type
);
927 this.load_error
= this.getPluginMissingHTML(missing_type
);
930 inheritEmbedObj:function(){
931 js_log("inheritEmbedObj:duration is: " + this.duration
);
932 //@@note: tricky cuz direct overwrite is not so ideal.. since the extended object is already tied to the dom
933 //clear out any non-base embedObj stuff:
935 eval('tmpObj = '+this.instanceOf
);
936 for(var i
in tmpObj
){ //for in loop oky for object
937 if(this['parent_'+i
]){
938 this[i
]=this['parent_'+i
];
944 //set up the new embedObj
945 js_log('f: inheritEmbedObj: embedding with ' + this.selected_player
.library
);
947 this.selected_player
.load( function(){
948 //js_log('inheriting '+_this.selected_player.library +'Embed to ' + _this.id + ' ' + $j('#'+_this.id).length);
949 eval('embedObj = ' +_this
.selected_player
.library
+'Embed;');
950 for(var method
in embedObj
){ //for in loop oky for object
951 //parent method preservation for local overwritten methods
953 _this
['parent_' + method
] = _this
[method
];
954 _this
[method
]=embedObj
[method
];
956 js_log('TYPEOF_ppause: ' + typeof _this
['parent_pause']);
958 if(_this
.inheritEmbedOverride
){
959 _this
.inheritEmbedOverride();
961 //update controls if possible
962 if(!_this
.loading_external_data
)
963 _this
.refreshControlsHTML();
965 //js_log("READY TO PLAY:"+_this.id);
966 _this
.ready_to_play
=true;
971 selectPlayer:function(player
)
974 if(this.selected_player
.id
!= player
.id
){
975 this.selected_player
= player
;
976 this.inheritEmbedObj();
979 doNativeWarningCheck:function(){
980 if( $j
.cookie('dismissNativeWarn') && $j
.cookie('dismissNativeWarn')===true){
983 //see if we have native support for ogg:
984 var supporting_players
= embedTypes
.players
.getMIMETypePlayers( 'video/ogg' );
985 for(var i
=0; i
< supporting_players
.length
; i
++){
986 if(supporting_players
[i
].id
== 'videoElement'){
990 //see if we are using mv_embed without a ogg source in which case no point in promoting firefox :P
991 if(this.media_element
&& this.media_element
.sources
){
992 var foundOgg
= false;
993 var playable_sources
= this.media_element
.getPlayableSources();
994 for(var sInx
=0; sInx
< playable_sources
.length
; sInx
++){
995 var mime_type
= playable_sources
[sInx
].mime_type
;
996 if( mime_type
=='video/ogg' ){
1000 //no ogg src... no point in download firefox link
1008 getTimeReq:function(){
1009 var et
= (this.ctrlBuilder
.long_time_disp
)? '/' + seconds2npt( this.getDuration() ) : '';
1010 var default_time_req
= '0:00:00' + et
;
1011 if(!this.media_element
)
1012 return default_time_req
;
1013 if(!this.media_element
.selected_source
)
1014 return default_time_req
;
1015 if(!this.media_element
.selected_source
.end_ntp
)
1016 return default_time_req
;
1017 var et
= (this.ctrlBuilder
.long_time_disp
) ?'/'+this.media_element
.selected_source
.end_ntp
: '';
1018 return this.media_element
.selected_source
.start_ntp
+ et
;
1020 getDuration:function(){
1021 //update some local pointers for the selected source:
1022 if(this.media_element
&& this.media_element
.selected_source
&& this.media_element
.selected_source
.duration
){
1023 this.duration
= this.media_element
.selected_source
.duration
;
1024 this.start_offset
= this.media_element
.selected_source
.start_offset
;
1025 this.start_ntp
= this.media_element
.selected_source
.start_ntp
;
1026 this.end_ntp
= this.media_element
.selected_source
.end_ntp
;
1028 //update start end_ntp if duration !=0 (set from plugin)
1030 this.start_ntp
= '0:0:0';
1031 if(!this.end_ntp
&& this.duration
)
1032 this.end_ntp
= seconds2npt( this.duration
);
1033 //return the duration
1034 return this.duration
;
1037 * wrapEmebedContainer
1038 * wraps the embed code into a container to better support playlist function
1039 * (where embed element is swapped for next clip
1040 * (where plugin method does not support playlist)
1042 wrapEmebedContainer:function(embed_code
){
1043 //check if parent clip is set( ie we are in a playlist so name the embed container by playlistID)
1044 var id
= (this.pc
!=null)?this.pc
.pp
.id
:this.id
;
1045 return '<div id="mv_ebct_'+id
+'" style="width:'+this.width
+'px;height:'+this.height
+'px;">' +
1049 getEmbedHTML : function(){
1050 //return this.wrapEmebedContainer( this.getEmbedObj() );
1051 return 'function getEmbedHTML should be overitten by embedLib ';
1053 //do seek function (should be overwritten by implementing embedLibs)
1054 // to check if seek can be done on locally downloaded content.
1055 doSeek : function( perc
){
1056 if( this.supportsURLTimeEncoding() ){
1057 //make sure this.seek_time_sec is up-to-date:
1058 this.seek_time_sec
= npt2seconds( this.start_ntp
) + parseFloat( perc
* this.getDuration() );
1059 js_log('updated seek_time_sec: ' + seconds2npt ( this.seek_time_sec
) );
1061 this.didSeekJump
=true;
1063 this.setSliderValue( perc
);
1065 //do play in 100ms (give things time to clear)
1066 setTimeout('$j(\'#' + this.id
+ '\').get(0).play()',100);
1069 * seeks to the requested time and issues a callback when ready
1070 * (should be overwitten by client that supports frame serving)
1072 setCurrentTime:function( time
, callback
){
1073 js_log('error: base embed setCurrentTime can not frame serve (override via plugin)');
1075 addPresTimeOffset:function(){
1076 //add in the offset:
1077 if(this.seek_time_sec
&& this.seek_time_sec
!=0){
1078 this.currentTime
+=this.seek_time_sec
;
1079 }else if(this.start_offset
&& this.start_offset
!=0){
1080 this.currentTime
= parseFloat(this.currentTime
) + parseFloat(this.start_offset
);
1083 doEmbedHTML:function()
1085 js_log('f:doEmbedHTML');
1086 js_log('thum disp:'+this.thumbnail_disp
);
1088 this.closeDisplayedHTML();
1090 // if(!this.selected_player){
1091 // return this.getPluginMissingHTML();
1092 //Set "loading" here
1093 $j('#mv_embedded_player_'+_this
.id
).html(''+
1094 '<div style="color:black;width:'+this.width
+'px;height:'+this.height
+'px;">' +
1095 gM('mwe-loading_plugin') +
1098 // schedule embedding
1099 this.selected_player
.load(function()
1101 js_log('performing embed for ' + _this
.id
);
1102 var embed_code
= _this
.getEmbedHTML();
1103 //js_log('shopuld embed:' + embed_code);
1104 $j('#mv_embedded_player_'+_this
.id
).html(embed_code
);
1107 mvVideoAudioSearch:function(){
1109 js_log('switch video Relational' );
1112 'titles' : this.wikiTitleKey
,
1113 'generator' : 'categories'
1115 var req_categories
= new Array();
1118 'url' : commons_api_url
1120 req_categories
= Array();
1121 if(data
.query
&& data
.query
.pages
){
1122 for(var pageid
in data
.query
.pages
){
1123 if(data
.query
.pages
[pageid
].title
)
1124 req_categories
.push(data
.query
.pages
[pageid
].title
);
1127 _this
.getRelatedFromCat( req_categories
);
1130 getRelatedFromCat:function(catAry
){
1131 js_log('getRelatedFromCat');
1133 for ( var i
= 0 ; i
<= catAry
.length
;i
++ ){
1138 'generator' : 'categorymembers' ,
1139 'gcmtitle' : catAry
[i
],
1140 'prop' : 'imageinfo',
1146 'url': commons_api_url
1149 $j('#dc_'+ _this
.id
+ ' .related_vids ul').html(' ');
1151 for(var j
in data
.query
.pages
){
1152 //setup poster default:
1153 var local_poster
="http://upload.wikimedia.org/wikipedia/commons/7/79/Wiki-commons.png";
1154 //make sure it exists:
1155 var page
= data
.query
.pages
[j
];
1156 if( j
> 0 && page
&& page
['imageinfo'] ){
1157 if( page
['imageinfo'][0].thumburl
){
1158 local_poster
= page
['imageinfo'][0].thumburl
;
1160 var descriptionurl
= page
['imageinfo'][0].descriptionurl
;
1161 var title_str
= page
.title
.replace( /File:|.ogv$|.oga$|.ogg$/gi, "" );
1162 //only link to other videos:
1163 if ( descriptionurl
.match( /\.ogg$|\.ogv$|\.oga$/gi ) != null) {
1164 var liout
= '<li>' +
1165 '<a href="' + descriptionurl
+ '" >' +
1166 '<img src="' + local_poster
+ '">' +
1168 ' <a title="' + title_str
+ '" target="_blank" ' +
1169 'href="'+ descriptionurl
+'">' + title_str
+ '</a>' +
1171 $j('#dc_'+ _this
.id
+ ' .related_vids ul').append(liout
) ;
1175 }); //end do_api_req
1178 onClipDone:function(){
1179 js_log('base:onClipDone');
1180 //stop the clip (load the thumbnail etc)
1182 this.seek_time_sec
= 0;
1183 this.setSliderValue(0);
1186 if(this.width
< 300){
1189 this.onClipDone_disp
=true;
1190 this.thumbnail_disp
=true;
1192 //make sure we are not in preview mode( no end clip actions in preview mode)
1193 if( this.preview_mode
)
1196 $j('#img_thumb_'+this.id
).css('zindex',1);
1197 $j('#'+ this.id
+ ' .play-btn-large').hide();
1199 //add black background
1200 $j('#dc_'+this.id
).append( '<div id="black_back_' + this.id
+ '" ' +
1201 'style="z-index:-2;position:absolute;background:#000;' +
1202 'top:0px;left:0px;width:' + parseInt( this.width
) + 'px;' +
1203 'height:' + parseInt( this.height
) + 'px;">' +
1206 if( this.wikiTitleKey
){
1207 $j('#dc_'+this.id
).append(
1208 '<div class="related_vids" >' +
1209 '<h1>' + gM('mwe-related_videos') + '</h1>'+
1213 $j('#img_thumb_' + this.id
).fadeOut("fast");
1214 $j('#dc_'+ _this
.id
+ ' .related_vids ul').html( gM('mwe-loading_txt') );
1215 this.mvVideoAudioSearch();
1217 //add the liks_info_div black back
1218 $j('#dc_'+this.id
).append('<div id="liks_info_'+this.id
+'" ' +
1219 'style="width:' +parseInt(parseInt(this.width
)/2)+'px;'+
1220 'height:'+ parseInt(parseInt(this.height
)) +'px;'+
1221 'position:absolute;top:10px;overflow:auto'+
1222 'width: '+parseInt( ((parseInt(this.width
)/2)-15) ) + 'px;'+
1223 'left:'+ parseInt( ((parseInt(this.width
)/2)+15) ) +'px;">'+
1226 //start animation (make thumb small in upper left add in div for "loading"
1227 $j('#img_thumb_'+this.id
).animate({
1228 width
:parseInt(parseInt(_this
.width
)/2),
1229 height
:parseInt(parseInt(_this
.height
)/2),
1235 //animation done.. add "loading" to div if empty
1236 if($j('#liks_info_'+_this
.id
).html()==''){
1237 $j('#liks_info_'+_this
.id
).html(gM('mwe-loading_txt'));
1241 //now load roe if run the showNextPrevLinks
1242 if(this.roe
&& this.media_element
.addedROEData
==false){
1243 do_request(this.roe
, function(data
)
1245 _this
.media_element
.addROE(data
);
1246 _this
.getNextPrevLinks();
1249 this.getNextPrevLinks();
1253 //@@todo we should merge getNextPrevLinks with textInterface .. there is repeated code between them.
1254 getNextPrevLinks:function(){
1255 js_log('f:getNextPrevLinks');
1256 var anno_track_url
= null;
1258 //check for annoative track
1259 $j
.each(this.media_element
.sources
, function(inx
, n
){
1260 if(n
.mime_type
=='text/cmml'){
1261 if( n
.id
== 'Anno_en'){
1262 anno_track_url
= n
.src
;
1266 if( anno_track_url
){
1267 js_log('found annotative track:'+ anno_track_url
);
1268 //zero out seconds (should improve cache hit rate and generally expands metadata search)
1269 //@@todo this could be repalced with a regExp
1270 var annoURL
= parseUri(anno_track_url
);
1271 var times
= annoURL
.queryKey
['t'].split('/');
1272 var stime_parts
= times
[0].split(':');
1273 var etime_parts
= times
[1].split(':');
1274 //zero out the hour:
1275 var new_start
= stime_parts
[0]+':'+'0:0';
1276 //zero out the end sec
1277 var new_end
= (etime_parts
[0]== stime_parts
[0])? (etime_parts
[0]+1)+':0:0' :etime_parts
[0]+':0:0';
1279 var etime_parts
= times
[1].split(':');
1281 var new_anno_track_url
= annoURL
.protocol
+'://'+ annoURL
.host
+ annoURL
.path
+'?';
1282 $j
.each(annoURL
.queryKey
, function(i
, val
){
1283 new_anno_track_url
+=(i
=='t')?'t='+new_start
+'/'+new_end
+'&' :
1286 var request_key
= new_start
+'/'+new_end
;
1287 //check the anno_data cache:
1288 //@@todo search cache see if current is in range.
1289 if(this.anno_data_cache
){
1290 js_log('anno data found in cache: '+request_key
);
1291 this.showNextPrevLinks();
1293 do_request(new_anno_track_url
, function(cmml_data
){
1294 js_log('raw response: '+ cmml_data
);
1295 if(typeof cmml_data
== 'string')
1297 var parser
=new DOMParser();
1298 js_log('Parse CMML data:' + cmml_data
);
1299 cmml_data
=parser
.parseFromString(cmml_data
,"text/xml");
1301 //init anno_data_cache
1302 if(!_this
.anno_data_cache
)
1303 _this
.anno_data_cache
={};
1304 //grab all metadata and put it into the anno_data_cache:
1305 $j
.each(cmml_data
.getElementsByTagName('clip'), function(inx
, clip
){
1306 _this
.anno_data_cache
[ $j(clip
).attr("id") ]={
1307 'start_time_sec':npt2seconds($j(clip
).attr("start").replace('npt:','')),
1308 'end_time_sec':npt2seconds($j(clip
).attr("end").replace('npt:','')),
1309 'time_req':$j(clip
).attr("start").replace('npt:','')+'/'+$j(clip
).attr("end").replace('npt:','')
1312 _this
.anno_data_cache
[ $j(clip
).attr("id") ]['meta']={};
1313 $j
.each(clip
.getElementsByTagName('meta'),function(imx
, meta
){
1314 //js_log('adding meta: '+ $j(meta).attr("name")+ ' = '+ $j(meta).attr("content"));
1315 _this
.anno_data_cache
[$j(clip
).attr("id")]['meta'][$j(meta
).attr("name")]=$j(meta
).attr("content");
1318 _this
.showNextPrevLinks();
1322 js_log('no annotative track found');
1323 $j('#liks_info_'+this.id
).html('no metadata found for related links');
1325 //query current request time +|- 60s to get prev next speech links.
1327 showNextPrevLinks:function(){
1328 //js_log('f:showNextPrevLinks');
1329 //int requested links:
1335 var curTime
= this.getTimeReq().split('/');
1337 var s_sec
= npt2seconds(curTime
[0]);
1338 var e_sec
= npt2seconds(curTime
[1]);
1339 js_log('showNextPrevLinks: req time: '+ s_sec
+ ' to ' + e_sec
);
1340 //now we have all the data in anno_data_cache
1341 var current_done
=false;
1342 for(var clip_id
in this.anno_data_cache
){ //for in loop oky for object
1343 var clip
= this.anno_data_cache
[clip_id
];
1344 //js_log('on clip:'+ clip_id);
1345 //set prev_link (if cur_link is still empty)
1346 if( s_sec
> clip
.end_time_sec
){
1347 link
.prev
= clip_id
;
1348 js_log('showNextPrevLinks: ' + s_sec
+ ' < ' + clip
.end_time_sec
+ ' set prev');
1351 if(e_sec
==clip
.end_time_sec
&& s_sec
== clip
.start_time_sec
)
1352 current_done
= true;
1353 //current clip is not done:
1354 if( e_sec
< clip
.end_time_sec
&& link
.current
=='' && !current_done
){
1355 link
.current
= clip_id
;
1356 js_log('showNextPrevLinks: ' + e_sec
+ ' < ' + clip
.end_time_sec
+ ' set current');
1359 //set end clip (first clip where start time is > end_time of req
1360 if( e_sec
< clip
.start_time_sec
&& link
.next
==''){
1361 link
.next
= clip_id
;
1362 js_log('showNextPrevLinks: '+ e_sec
+ ' < '+ clip
.start_time_sec
+ ' && ' + link
.next
);
1366 if(link
.prev
=='' && link
.current
=='' && link
.next
==''){
1367 html
='<p><a href="'+this.media_element
.linkbackgetMsg
+'">clip page</a>';
1369 for(var link_type
in link
){
1370 var link_id
= link
[link_type
];
1372 var clip
= this.anno_data_cache
[link_id
];
1374 for(var j
in clip
['meta']){
1375 title_msg
+=j
.replace(/_/g,' ') +': ' +clip['meta'][j].replace(/_
/g
,' ') +" <br>";
1377 var time_req
= clip
.time_req
;
1378 if(link_type
=='current') //if current start from end of current clip play to end of current meta:
1379 time_req
= curTime
[1]+ '/' + seconds2npt( clip
.end_time_sec
);
1381 //do special linkbacks for metavid content:
1382 var regTimeCheck
= new RegExp(/[0-9]+:[0-9]+:[0-9]+\/[0-9]+:[0-9]+:[0-9]+/);
1384 if( regTimeCheck
.test( this.media_element
.linkback
) ){
1385 html
+=' href="'+ this.media_element
.linkback
.replace(regTimeCheck
,time_req
) +'" ';
1387 html
+=' href="#" onClick="$j(\'#'+this.id
+'\').get(0).playByTimeReq(\''+
1388 time_req
+ '\'); return false; "';
1390 html
+=' title="' + title_msg
+ '">' +
1391 gM('mwe-' + link_type
+'_clip_msg') +
1392 '</a><br><span style="font-size:small">'+ title_msg
+'<span></p>';
1396 //js_og("should set html:"+ html);
1397 $j('#liks_info_'+this.id
).html(html
);
1399 playByTimeReq: function(time_req
){
1400 js_log('f:playByTimeReq: '+time_req
);
1402 this.updateVideoTimeReq(time_req
);
1405 doThumbnailHTML:function()
1408 js_log('f:doThumbnailHTML'+ this.thumbnail_disp
);
1409 this.closeDisplayedHTML();
1410 $j( '#mv_embedded_player_' + this.id
).html( this.getThumbnailHTML() );
1412 this.thumbnail_disp
= true;
1414 refreshControlsHTML:function(){
1415 js_log('refreshControlsHTML::');
1416 if($j('#' + this.id
+ ' .control-bar').length
== 0)
1418 js_log('control-bar not present, returning');
1421 $j('#' + this.id
+ ' .control-bar').html( this.getControlsHTML() );
1422 this.ctrlBuilder
.addControlHooks(this);
1425 getControlsHTML:function()
1427 return this.ctrlBuilder
.getControls( this );
1429 getHTML : function (){
1430 //@@todo check if we have sources available
1431 js_log('embedVideo:getHTML : ' + this.id
+ ' resource type: ' + this.type
);
1433 //set-up the local ctrlBuilder instance:
1434 this.ctrlBuilder
= new ctrlBuilder( this );
1438 html_code
= '<div id="videoPlayer_' + this.id
+ '" style="width:' + this.width
+ 'px;position:relative;"'+
1439 'class="' + this.ctrlBuilder
.pClass
+ '">';
1440 html_code
+= '<div style="width:'+parseInt(this.width
)+'px;height:'+parseInt(this.height
)+'px;" id="mv_embedded_player_'+this.id
+'">' +
1441 this.getThumbnailHTML() +
1443 //js_log("mvEmbed:controls "+ typeof this.controls);
1446 js_log("f:getHTML:AddControls");
1447 html_code
+='<div class="ui-state-default ui-widget-header ui-helper-clearfix control-bar" >';
1448 html_code
+= this.getControlsHTML();
1449 html_code
+='</div>';
1450 //block out some space by encapulating the top level div
1451 $j(this).wrap('<div style="width:'+parseInt(this.width
)+'px;height:'
1452 +( parseInt(this.height
) + this.ctrlBuilder
.height
)+'px"></div>');
1454 html_code
+= '</div>'; //videoPlayer div close
1455 //js_log('should set: '+this.id);
1456 $j(this).html( html_code
);
1457 //add hooks once Controls are in DOM
1458 this.ctrlBuilder
.addControlHooks(this);
1460 //js_log('set this to: ' + $j(this).html() );
1462 //if auto play==true directly embed the plugin
1465 js_log('activating autoplay');
1470 * get missing plugin html (check for user included code)
1472 getPluginMissingHTML : function(missing_type
){
1473 //keep the box width hight:
1474 var out
= '<div style="width:'+this.width
+'px;height:'+this.height
+'px">';
1475 if(this.user_missing_plugin_html
){
1476 out
+= this.user_missing_plugin_html
;
1480 out
+= gM('mwe-generic_missing_plugin', missing_type
) + ' or <a title="'+gM('mwe-download_clip')+'" href="'+this.src
+'">'+gM('mwe-download_clip')+'</a>';
1482 return out
+ '</div>';
1484 updateVideoTimeReq:function(time_req
){
1485 js_log('f:updateVideoTimeReq');
1486 var time_parts
=time_req
.split('/');
1487 this.updateVideoTime(time_parts
[0], time_parts
[1]);
1490 updateVideoTime:function(start_ntp
, end_ntp
){
1492 this.media_element
.updateSourceTimes( start_ntp
, end_ntp
);
1494 this.setStatus(start_ntp
+'/'+end_ntp
);
1496 this.setSliderValue(0);
1497 //reset seek_offset:
1498 if(this.media_element
.selected_source
.URLTimeEncoding
)
1499 this.seek_time_sec
=0;
1501 this.seek_time_sec
=npt2seconds(start_ntp
);
1503 //@@todo overwite by embed library if we can render frames natavily
1504 renderTimelineThumbnail:function( options
){
1505 var my_thumb_src
= this.media_element
.getThumbnailURL();
1506 //check if our thumbnail has a time attribute:
1507 if( my_thumb_src
.indexOf('t=') !== -1){
1508 var time_ntp
= seconds2npt ( options
.time
+ parseInt(this.start_offset
) );
1509 my_thumb_src
= getURLParamReplace( my_thumb_src
, { 't':time_ntp
, 'size': options
.size
} );
1511 var thumb_class
= (typeof options
['thumb_class'] != 'undefined' ) ? options
['thumb_class'] : '';
1512 return '<div class="ui-corner-all ' + thumb_class
+ '" src="' + my_thumb_src
+ '" '+
1513 'style="height:' + options
.height
+ 'px;' +
1514 'width:' + options
.width
+ 'px" >' +
1515 '<img src="' + my_thumb_src
+'" '+
1516 'style="height:' + options
.height
+ 'px;' +
1517 'width:' + options
.width
+ 'px">' +
1520 updateThumbTimeNTP:function( time
){
1521 this.updateThumbTime( npt2seconds(time
) - parseInt(this.start_offset
) );
1523 updateThumbTime:function( float_sec
){
1524 //js_log('updateThumbTime:'+float_sec);
1526 if( typeof this.org_thum_src
=='undefined' ){
1527 this.org_thum_src
= this.media_element
.getThumbnailURL();
1529 if( this.org_thum_src
.indexOf('t=') !== -1){
1530 this.last_thumb_url
= getURLParamReplace(this.org_thum_src
,
1531 { 't' : seconds2npt( float_sec
+ parseInt(this.start_offset
)) } );
1532 if(!this.thumbnail_updating
){
1533 this.updateThumbnail(this.last_thumb_url
,false);
1534 this.last_thumb_url
=null;
1538 //for now provide a src url .. but need to figure out how to copy frames from video for plug-in based thumbs
1539 updateThumbPerc:function( perc
){
1540 return this.updateThumbTime( (this.getDuration() * perc
) );
1542 //updates the thumbnail if the thumbnail is being displayed
1543 updateThumbnail : function(src
, quick_switch
){
1544 //make sure we don't go to the same url if we are not already updating:
1545 if( !this.thumbnail_updating
&& $j('#img_thumb_'+this.id
).attr('src')== src
)
1547 //if we are already updating don't issue a new update:
1548 if( this.thumbnail_updating
&& $j('#new_img_thumb_'+this.id
).attr('src')== src
)
1551 js_log('update thumb: ' + src
);
1554 $j('#img_thumb_'+this.id
).attr('src', src
);
1557 //if still animating remove new_img_thumb_
1558 if(this.thumbnail_updating
==true)
1559 $j('#new_img_thumb_'+this.id
).stop().remove();
1561 if(this.thumbnail_disp
){
1562 js_log('set to thumb:'+ src
);
1563 this.thumbnail_updating
=true;
1564 $j('#dc_'+this.id
).append('<img src="'+src
+'" ' +
1565 'style="display:none;position:absolute;zindex:2;top:0px;left:0px;" ' +
1566 'width="'+this.width
+'" height="'+this.height
+'" '+
1567 'id = "new_img_thumb_'+this.id
+'" />');
1568 //js_log('appended: new_img_thumb_');
1569 $j('#new_img_thumb_'+this.id
).fadeIn("slow", function(){
1570 //once faded in remove org and rename new:
1571 $j('#img_thumb_'+_this
.id
).remove();
1572 $j('#new_img_thumb_'+_this
.id
).attr('id', 'img_thumb_'+_this
.id
);
1573 $j('#img_thumb_'+_this
.id
).css('zindex','1');
1574 _this
.thumbnail_updating
=false;
1575 //js_log("done fadding in "+ $j('#img_thumb_'+_this.id).attr("src"));
1577 //if we have a thumb queued update to that
1578 if(_this
.last_thumb_url
){
1579 var src_url
=_this
.last_thumb_url
;
1580 _this
.last_thumb_url
=null;
1581 _this
.updateThumbnail(src_url
);
1587 /** Returns the HTML code for the video when it is in thumbnail mode.
1588 This includes the specified thumbnail as well as buttons for
1589 playing, configuring the player, inline cmml display, HTML linkback,
1590 download, and embed code.
1592 getThumbnailHTML : function ()
1594 js_log('embedVideo:getThumbnailHTML::' + this.id
);
1595 var thumb_html
= '';
1598 //if(this.class)class_atr = ' class="'+this.class+'"';
1599 //if(this.style)style_atr = ' style="'+this.style+'"';
1600 // else style_atr = 'overflow:hidden;height:'+this.height+'px;width:'+this.width+'px;';
1601 this.thumbnail
= this.media_element
.getThumbnailURL();
1603 //put it all in the div container dc_id
1604 thumb_html
+= '<div id="dc_'+this.id
+'" style="position:absolute;'+
1605 ' overflow:hidden; top:0px; left:0px; width:'+this.playerPixelWidth()+'px; height:'+this.playerPixelHeight()+'px; z-index:0;">'+
1606 '<img width="'+this.playerPixelWidth()+'" height="'+this.playerPixelHeight()+'" style="position:relative;width:'+this.playerPixelWidth()+';height:'+this.playerPixelHeight()+'"' +
1607 ' id="img_thumb_'+this.id
+'" src="' + this.thumbnail
+ '">';
1609 if(this.play_button
== true && this.controls
== true)
1610 thumb_html
+= this.ctrlBuilder
.getComponent( 'play-btn-large' );
1612 thumb_html
+='</div>';
1615 getEmbeddingHTML:function()
1617 var thumbnail
= this.media_element
.getThumbnailURL();
1619 var embed_thumb_html
;
1620 if(thumbnail
.substring(0,1)=='/'){
1621 eURL
= parseUri(mv_embed_path
);
1622 embed_thumb_html
= eURL
.protocol
+ '://' + eURL
.host
+ thumbnail
;
1623 //js_log('set from mv_embed_path:'+embed_thumb_html);
1625 embed_thumb_html
= (thumbnail
.indexOf('http://')!=-1)?thumbnail
:mv_embed_path
+ thumbnail
;
1627 var embed_code_html
= '<script type="text/javascript" ' +
1628 'src="'+mv_embed_path
+'mv_embed.js"></script>' +
1631 embed_code_html
+='roe="'+this.roe
+'" >';
1633 embed_code_html
+='src="'+this.src
+'" ' +
1634 'poster="'+embed_thumb_html
+'">';
1636 //close the video tag
1637 embed_code_html
+='</video>';
1639 return embed_code_html
;
1641 doOptionsHTML:function()
1643 var sel_id
= (this.pc
!=null)?this.pc
.pp
.id
:this.id
;
1644 var pos
= $j('#'+sel_id
+ ' .options-btn').offset();
1645 pos
['top']=pos
['top']+24;
1646 pos
['left']=pos
['left']-124;
1647 //js_log('pos of options button: t:'+pos['top']+' l:'+ pos['left']);
1648 $j('#mv_vid_options_'+sel_id
).css(pos
).toggle();
1651 doLinkBack:function(){
1652 if(this.roe
&& this.media_element
.addedROEData
==false){
1654 this.displayHTML(gM('mwe-loading_txt'));
1655 do_request(this.roe
, function(data
)
1657 _this
.media_element
.addROE(data
);
1661 if(this.media_element
.linkback
){
1662 window
.location
= this.media_element
.linkback
;
1664 this.displayHTML(gM('mwe-could_not_find_linkback'));
1668 showShare:function($target
){
1669 var embed_code
= this.getEmbeddingHTML();
1672 //@todo: hook events to two a's for swapping in and out code for link vs. embed;
1673 // hook events for changing active class of li based on a.
1674 o
+= '<h2>' + gM('mwe-share_this_video') + '</h2>\n' +
1676 ' <li><a href="#" class="active">'+gM('mwe-embed_site_or_blog')+'</a></li>\n';
1678 o
+= ' <li><a href="#" id="k-share-link">' + this.linkback
+ '</a></li>\n';
1681 '<div class="source_wrap"><textarea>' + embed_code
+ '</textarea></div>' +
1682 '<button class="ui-state-default ui-corner-all copycode">' + gM('mwe-copy-code') + '</button>' +
1683 '<div class="ui-state-highlight ui-corner-all">' + gM('mwe-read_before_embed') + '</div>' +
1686 $cpBtn
= $j( '#' + this.id
+ ' .copycode');
1687 $cpTxt
= $j( '#' + this.id
+ ' .source_wrap textarea');
1689 $cpTxt
.click(function(){
1690 $j(this).get(0).select();
1693 $cpBtn
.click(function(){
1694 $cpTxt
.focus().get(0).select();
1695 if(document
.selection
){
1696 CopiedTxt
= document
.selection
.createRange();
1697 CopiedTxt
.execCommand("Copy");
1701 showTextInterface:function(){
1703 //display the text container with loading text:
1704 //@@todo support position config
1705 var loc
= $j(this).position();
1706 if($j('#metaBox_'+this.id
).length
==0){
1707 $j(this).after('<div class="ui-widget ui-widget-content ui-corner-all" style="position:absolute;z-index:10;'+
1708 'top:' + (loc
.top
) + 'px;' +
1709 'left:' + (parseInt( loc
.left
) + parseInt(this.width
) + 10 )+'px;' +
1710 'height:'+ parseInt( this.height
)+'px;width:400px;' +
1712 'id="metaBox_' + this.id
+ '">'+
1713 gM('mwe-loading_txt') +
1716 //fade in the text display
1717 $j('#metaBox_'+this.id
).fadeIn("fast");
1718 //check if textObj present:
1719 if(typeof this.textInterface
== 'undefined' ){
1720 //load the default text interface:
1725 _this
.textInterface
= new mvTextInterface( _this
);
1727 _this
.textInterface
.show();
1728 js_log("NEW TEXT INTERFACE");
1729 for(var i
in _this
.textInterface
.availableTracks
){
1730 js_log("tracks in new interface: "+_this
.id
+ ' tid:' + i
);
1736 this.textInterface
.show();
1739 closeTextInterface:function(){
1740 js_log('closeTextInterface '+ typeof this.textInterface
);
1741 if(typeof this.textInterface
!== 'undefined' ){
1742 this.textInterface
.close();
1745 /** Generic function to display custom HTML inside the mv_embed element.
1746 The code should call the closeDisplayedHTML function to close the
1747 display of the custom HTML and restore the regular mv_embed display.
1748 @param {String} HTML code for the selection list.
1750 displayHTML:function(html_code
)
1752 var sel_id
= (this.pc
!=null)?this.pc
.pp
.id
:this.id
;
1754 if(!this.supports
['overlays'])
1757 //put select list on-top
1758 //make sure the parent is relatively positioned:
1759 $j('#'+sel_id
).css('position', 'relative');
1760 //set height width (check for playlist container)
1761 var width
= (this.pc
)?this.pc
.pp
.width
:this.playerPixelWidth();
1762 var height
= (this.pc
)?this.pc
.pp
.height
:this.playerPixelHeight();
1765 height
+=(this.pc
.pp
.pl_layout
.title_bar_height
+ this.pc
.pp
.pl_layout
.control_height
);
1768 if($j('#blackbg_'+sel_id
).length
!=0)
1771 $j('#blackbg_'+sel_id
).remove();
1773 //fade in a black bg div ontop of everything
1774 var div_code
= '<div id="blackbg_'+sel_id
+'" class="videoComplete" ' +
1775 'style="height:'+parseInt(height
)+'px;width:'+parseInt(width
)+'px;">'+
1776 '<div class="videoOptionsComplete">'+
1777 //@@TODO: this style should go to .css
1778 '<span style="float:right;margin-right:10px">' +
1779 '<a href="#" style="color:white;" onClick="$j(\'#'+sel_id
+'\').get(0).closeDisplayedHTML();return false;">close</a>' +
1781 '<div id="mv_disp_inner_'+sel_id
+'" style="padding-top:10px;">'+
1785 $j('#'+sel_id
).prepend(div_code
);
1787 $j('#blackbg_'+sel_id
).fadeIn("slow");
1789 $j('#blackbg_'+sel_id
).show();
1790 return false; //onclick action return false
1792 /** Close the custom HTML displayed using displayHTML and restores the
1793 regular mv_embed display.
1795 closeDisplayedHTML:function(){
1796 var sel_id
= (this.pc
!=null)?this.pc
.pp
.id
:this.id
;
1797 $j('#blackbg_'+sel_id
).fadeOut("slow", function(){
1798 $j('#blackbg_'+sel_id
).remove();
1800 return false; //onclick action return false
1802 showPlayerselect:function( $target
){
1803 //get id (in case where we have a parent container)
1804 var this_id
= (this.pc
!=null)?this.pc
.pp
.id
:this.id
;
1807 o
+='<h2>' + gM('mwe-chose_player') + '</h2>';
1809 //js_log('selected src'+ _this.media_element.selected_source.url);
1810 $j
.each( this.media_element
.getPlayableSources(), function(source_id
, source
){
1811 var default_player
= embedTypes
.players
.defaultPlayer( source
.getMIMEType() );
1813 var is_selected
= (source
== _this
.media_element
.selected_source
);
1814 var image_src
= mv_skin_img_path
;
1816 if (default_player
){
1818 //output the player select code:
1819 var supporting_players
= embedTypes
.players
.getMIMETypePlayers( source
.getMIMEType() );
1821 for(var i
=0; i
< supporting_players
.length
; i
++){
1822 if( _this
.selected_player
.id
== supporting_players
[i
].id
&& is_selected
){
1824 '<a href="#" class="active" rel="sel_source" id="sc_' + source_id
+ '_' + supporting_players
[i
].id
+'">' +
1825 supporting_players
[i
].getName() +
1829 '<a href="#" rel="sel_source" id="sc_' + source_id
+ '_' + supporting_players
[i
].id
+'">' +
1830 supporting_players
[i
].getName() + '</a>' +
1836 o
+= source
.getTitle() + ' - no player available';
1841 //set up the click bindings:
1842 $target
.find("[rel='sel_source']").each(function(){
1843 $j(this).click(function(){
1844 var iparts
= $j(this).attr( 'id' ).replace(/sc_/,'').split('_');
1845 var source_id
= iparts
[0];
1846 var default_player_id
= iparts
[1];
1847 js_log('source id: ' + source_id
+ ' player id: ' + default_player_id
);
1849 $j('#' + this_id
).get(0).closeDisplayedHTML();
1850 $j('#' + _this
.id
).get(0).media_element
.selectSource( source_id
);
1852 embedTypes
.players
.userSelectPlayer( default_player_id
,
1853 _this
.media_element
.sources
[ source_id
].getMIMEType() );
1855 //be sure to issue a stop
1856 $j('#' + this_id
).get(0).stop();
1858 //don't follow the empty # link:
1863 showDownload:function( $target
){
1865 //load the roe if available (to populate out download options:
1866 function getShowVideoDownload(){
1867 var out
='<div style="color:white">' +
1868 '<b style="color:white;">'+gM('mwe-download_segment')+'</b><br>';
1869 out
+='<blockquote style="background:#000">'+
1870 gM('mwe-download_right_click') + '</blockquote><br>';
1873 $j
.each(_this
.media_element
.getSources(), function(index
, source
){
1874 var dl_line
= '<li>' + '<a style="color:white" href="' + source
.getURI() +'"> '
1875 + source
.getTitle()+'</a> '+ '</li>'+"\n";
1876 if( source
.getURI().indexOf('?t=')!==-1){
1878 }else if( this.getMIMEType()=="text/cmml" || this.getMIMEType()=="text/x-srt" ){
1879 dl_txt_list
+=dl_line
;
1886 out
+=gM('mwe-download_full') + '<blockquote style="background:#000">' + dl_list
+ '</blockquote>';
1888 out
+=gM('mwe-download_text')+'<blockquote style="background:#000">' + dl_txt_list
+'</blockquote>';
1892 //js_log('f:showDownload '+ this.roe + ' ' + this.media_element.addedROEData);
1893 if(this.roe
&& this.media_element
.addedROEData
== false){
1895 $target
.html( gM('loading_txt') );
1896 do_request(this.roe
, function(data
)
1898 _this
.media_element
.addROE(data
);
1899 $target
.html( getShowVideoDownload() );
1902 $target
.html( getShowVideoDownload() );
1906 * base embed controls
1907 * the play button calls
1910 var eid
= (this.pc
!=null)?this.pc
.pp
.id
:this.id
;
1912 //js_log( "mv_embed play:" + this.id);
1913 //js_log('thum disp:'+this.thumbnail_disp);
1914 //check if thumbnail is being displayed and embed html
1915 if( this.thumbnail_disp
){
1916 if( !this.selected_player
){
1917 js_log('no selected_player');
1918 //this.innerHTML = this.getPluginMissingHTML();
1919 //$j(this).html(this.getPluginMissingHTML());
1920 $j('#'+this.id
).html( this.getPluginMissingHTML() );
1923 this.onClipDone_disp
=false;
1925 this.thumbnail_disp
=false;
1928 //the plugin is already being displayed
1929 this.paused
=false; //make sure we are not "paused"
1933 $j('#' + eid
+ ' .play-btn span').removeClass('ui-icon-play').addClass('ui-icon-pause');
1934 $j('#' + eid
+ ' .play-btn').unbind().btnBind().click(function(){
1935 $j('#' + eid
).get(0).pause();
1936 }).attr('title', gM('mwe-pause_clip'));
1940 //should be done by child (no base way to load assets)
1941 js_log('baseEmbed:load call');
1944 return this.media_element
.selected_source
.getURI( this.seek_time_sec
);
1948 * there is no general way to pause the video
1949 * must be overwritten by embed object to support this functionality.
1952 var eid
= (this.pc
!=null)?this.pc
.pp
.id
:this.id
;
1953 //js_log('mv_embed:do pause');
1954 //(playing) do pause
1956 //update the ctrl "paused state"
1957 $j('#' + eid
+ ' .play-btn span').removeClass('ui-icon-pause').addClass('ui-icon-play');
1958 $j('#' + eid
+ ' .play-btn').unbind().btnBind().click(function(){
1959 $j('#'+eid
).get(0).play();
1960 }).attr('title', gM('mwe-play_clip'));
1963 * base embed stop (can be overwritten by the plugin)
1967 js_log('mvEmbed:stop:'+this.id
);
1969 //no longer seeking:
1970 this.didSeekJump
=false;
1972 //first issue pause to update interface (only call the parent)
1973 if(this['parent_pause']){
1974 this.parent_pause();
1979 //reset the currentTime:
1981 //check if thumbnail is being displayed in which case do nothing
1982 if( this.thumbnail_disp
){
1983 //already in stooped state
1984 js_log('already in stopped state');
1986 //rewrite the html to thumbnail disp
1987 this.doThumbnailHTML();
1988 this.bufferedPercent
=0; //reset buffer state
1989 this.setSliderValue(0);
1990 this.setStatus( this.getTimeReq() );
1993 //make sure the big playbutton is has click action:
1994 $j('#' + _this
.id
+ ' .play-btn-large').unbind('click').click(function(){
1995 $j('#' +_this
.id
).get(0).play();
1998 if(this.update_interval
)
2000 clearInterval(this.update_interval
);
2001 this.update_interval
= null;
2004 toggleMute:function(){
2005 var eid
= (this.pc
!=null)?this.pc
.pp
.id
:this.id
;
2008 $j('#volume_control_'+eid
+ ' span').removeClass('ui-icon-volume-off').addClass('ui-icon-volume-on');
2009 $j('#volume_bar_'+eid
).slider('value', 100);
2010 this.updateVolumen(1);
2013 $j('#volume_control_'+eid
+ ' span').removeClass('ui-icon-volume-on').addClass('ui-icon-volume-off');
2014 $j('#volume_bar_'+eid
).slider('value', 0);
2015 this.updateVolumen(0);
2017 js_log('f:toggleMute::' + this.muted
);
2019 updateVolumen:function(perc
){
2020 js_log('update volume not supported with current playback type');
2022 fullscreen:function(){
2023 js_log('fullscreen not supported with current playback type');
2025 /* returns bool true if playing or paused, false if stooped
2027 isPlaying : function(){
2028 if(this.thumbnail_disp
){
2031 }else if( this.paused
){
2038 isPaused : function(){
2039 return this.isPlaying() && this.paused
;
2041 isStoped : function(){
2042 return this.thumbnail_disp
;
2044 playlistSupport:function(){
2045 //by default not supported (implemented in js)
2048 postEmbedJS:function(){
2051 //do common monitor code like update the playhead and play status
2052 //plugin objects are responsible for updating currentTime
2054 js_log(' ct: ' + this.currentTime
+ ' dur: ' + ( parseInt( this.duration
) + 1 ) + ' is seek: ' + this.seeking
);
2055 if( this.currentTime
&& this.currentTime
> 0 && this.duration
){
2056 if( !this.userSlide
&& !this.seeking
){
2057 if( this.start_offset
){
2058 //if start offset include that calculation
2059 this.setSliderValue( ( this.currentTime
- this.start_offset
) / this.duration
);
2060 var et
= (this.ctrlBuilder
.long_time_disp
)? '/'+ seconds2npt(parseFloat(this.start_offset
)+parseFloat(this.duration
) ) : '';
2061 this.setStatus( seconds2npt(this.currentTime
) + et
);
2063 this.setSliderValue( this.currentTime
/ this.duration
);
2064 var et
= (this.ctrlBuilder
.long_time_disp
)? '/' + seconds2npt( this.duration
):'';
2065 this.setStatus( seconds2npt( this.currentTime
) + et
);
2068 //check if we are "done"
2069 if( this.currentTime
> ( parseInt(this.duration
) + 1 ) ){
2070 js_log("should run clip done");
2074 //media lacks duration just show end time
2075 //js_log(' ct:' + this.currentTime + ' dur: ' + this.duration);
2076 if( this.isStoped() ){
2077 this.setStatus( this.getTimeReq() );
2078 }else if( this.isPaused() ){
2079 this.setStatus( gM('mwe-paused') );
2080 }else if( this.isPlaying() ){
2081 if( this.currentTime
&& ! this.duration
)
2082 this.setStatus( seconds2npt( this.currentTime
) + ' /' );
2084 this.setStatus(" - - - ");
2086 this.setStatus( this.getTimeReq() );
2089 //could check if time > duration here and stop playback
2091 //update buffer information
2092 this.updateBufferStatus();
2094 //update monitorTimerId to call child monitor
2095 if( ! this.monitorTimerId
){
2096 //make sure an instance of this.id exists:
2097 if( document
.getElementById(this.id
) ){
2098 this.monitorTimerId
= setInterval(function(){
2099 if(_this
.id
&& $j( '#'+_this
.id
).length
!= 0){
2100 $j( '#'+_this
.id
).get(0).monitor();
2106 stopMonitor:function(){
2107 if( this.monitorTimerId
!= 0 )
2109 clearInterval( this.monitorTimerId
);
2110 this.monitorTimerId
= 0;
2113 updateBufferStatus: function(){
2115 //build the buffer targeet based for playlist vs clip
2116 var buffer_select
= (this.pc
) ?
2117 '#cl_status_' + this.id
+ ' .mv_buffer':
2118 '#' + this.id
+ ' .play_head .mv_buffer';
2120 //update the buffer progress bar (if available )
2121 if( this.bufferedPercent
!= 0 ){
2122 //js_log('bufferedPercent: ' + this.bufferedPercent);
2123 if(this.bufferedPercent
> 1)
2124 this.bufferedPercent
=1;
2126 $j(buffer_select
).css("width", (this.bufferedPercent
*100) +'%' );
2128 $j(buffer_select
).css("width", '0px' );
2131 relativeCurrentTime: function(){
2132 if(!this.start_offset
)
2133 this.start_offset
=0;
2134 var rt
= this.currentTime
- this.start_offset
;
2135 if( rt
< 0 ) //should not happen but does.
2139 getPluginEmbed : function(){
2140 if (window
.document
[this.pid
]){
2141 return window
.document
[this.pid
];
2143 if ($j
.browser
.msie
){
2144 return document
.getElementById(this.pid
);
2146 if (document
.embeds
&& document
.embeds
[this.pid
])
2147 return document
.embeds
[this.pid
];
2151 //HELPER Functions for selected source
2153 * returns the selected source url for players to play
2155 getURI : function( seek_time_sec
){
2156 return this.media_element
.selected_source
.getURI( this.seek_time_sec
);
2158 supportsURLTimeEncoding: function(){
2159 //do head request if on the same domain
2160 return this.media_element
.selected_source
.URLTimeEncoding
;
2162 setSliderValue: function(perc
, hide_progress
){
2163 var eid
= (this.pc
)?this.pc
.pp
.id
:this.id
;
2164 if(this.controls
&& $j('#' + eid
+ ' .play_head').length
!= 0){
2165 var val
= parseInt( perc
*1000 );
2166 $j('#' + eid
+ ' .play_head').slider('value', val
);
2168 //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) );
2169 //js_log('op:' + offset_perc + ' *('+perc+' * ' + $j('#slider_'+id).width() + ')');
2171 highlightPlaySection:function(options
){
2172 js_log('highlightPlaySection');
2173 var eid
= (this.pc
)?this.pc
.pp
.id
:this.id
;
2174 var dur
= this.getDuration();
2175 var hide_progress
= true;
2176 //set the left percet and update the slider:
2177 rel_start_sec
= npt2seconds( options
['start']);
2178 //remove the start_offset if relevent:
2179 if(this.start_offset
)
2180 rel_start_sec
= rel_start_sec
- this.start_offset
2183 if( rel_start_sec
<= 0 ){
2185 options
['start'] = seconds2npt( this.start_offset
);
2187 this.setSliderValue( 0 , hide_progress
);
2189 left_perc
= parseInt( (rel_start_sec
/ dur
)*100 ) ;
2190 slider_perc
= (left_perc
/ 100);
2193 js_log("slider perc:" + slider_perc
);
2194 if( ! this.isPlaying() ){
2195 this.setSliderValue( slider_perc
, hide_progress
);
2198 width_perc
= parseInt( (( npt2seconds( options
['end'] ) - npt2seconds( options
['start'] ) ) / dur
)*100 ) ;
2199 if( (width_perc
+ left_perc
) > 100 ){
2200 width_perc
= 100 - left_perc
;
2202 //js_log('should hl: '+rel_start_sec+ '/' + dur + ' re:' + rel_end_sec+' lp:' + left_perc + ' width: ' + width_perc);
2203 $j('#mv_seeker_' + eid
+ ' .mv_highlight').css({
2204 'left' : left_perc
+'%',
2205 'width' : width_perc
+'%'
2208 this.jump_time
= options
['start'];
2209 this.seek_time_sec
= npt2seconds( options
['start']);
2211 this.setStatus( gM('mwe-seek_to', seconds2npt( this.seek_time_sec
) ) );
2212 js_log('DO update: ' + this.jump_time
);
2213 this.updateThumbTime( rel_start_sec
);
2215 hideHighlight:function(){
2216 var eid
= (this.pc
)?this.pc
.pp
.id
:this.id
;
2217 $j('#mv_seeker_' + eid
+ ' .mv_highlight').hide();
2218 this.setStatus( this.getTimeReq() );
2219 this.setSliderValue( 0 );
2221 setStatus:function(value
){
2222 var eid
= (this.pc
)?this.pc
.pp
.id
:this.id
;
2224 $j('#' + eid
+ ' .time-disp').html(value
);
2231 * mediaPlayer represents a media player plugin.
2232 * @param {String} id id used for the plugin.
2233 * @param {Array<String>} supported_types n array of supported MIME types.
2234 * @param {String} library external script containing the plugin interface code. (mv_<library>Embed.js)
2237 function mediaPlayer(id
, supported_types
, library
)
2240 this.supported_types
= supported_types
;
2241 this.library
= library
;
2242 this.loaded
= false;
2243 this.loading_callbacks
= new Array();
2246 mediaPlayer
.prototype =
2249 supported_types
:null,
2252 loading_callbacks
:null,
2253 supportsMIMEType : function(type
)
2255 for (var i
=0; i
< this.supported_types
.length
; i
++)
2256 if(this.supported_types
[i
] == type
)
2260 getName : function()
2262 return gM('mwe-ogg-player-' + this.id
);
2264 load : function(callback
){
2266 this.library
+ 'Embed'
2272 /* players and supported mime types
2273 @@todo ideally we query the plugin to get what mime types it supports in practice not always reliable/avaliable
2275 var flowPlayer
= new mediaPlayer('flowplayer',['video/x-flv', 'video/h264'],'flash');
2277 var omtkPlayer
= new mediaPlayer('omtkplayer',['audio/ogg'], 'omtk' );
2279 var cortadoPlayer
= new mediaPlayer('cortado',['video/ogg', 'audio/ogg'],'java');
2280 var videoElementPlayer
= new mediaPlayer('videoElement',['video/ogg', 'audio/ogg'],'native');
2282 var vlcMineList
= ['video/ogg','audio/ogg', 'video/x-flv', 'video/mp4', 'video/h264'];
2283 var vlcMozillaPlayer
= new mediaPlayer('vlc-mozilla',vlcMineList
,'vlc');
2284 var vlcActiveXPlayer
= new mediaPlayer('vlc-activex',vlcMineList
,'vlc');
2287 var oggPluginPlayer
= new mediaPlayer('oggPlugin',['video/ogg'],'generic');
2289 //depricate quicktime in favor of safari native
2290 //var quicktimeMozillaPlayer = new mediaPlayer('quicktime-mozilla',['video/ogg'],'quicktime');
2291 //var quicktimeActiveXPlayer = new mediaPlayer('quicktime-activex',['video/ogg'],'quicktime');
2293 var htmlPlayer
= new mediaPlayer('html',['text/html', 'image/jpeg', 'image/png', 'image/svg'], 'html');
2296 * mediaPlayers is a collection of mediaPlayer objects supported by the client.
2297 * It could be merged with embedTypes, since there is one embedTypes per script
2298 * and one mediaPlayers per embedTypes.
2300 function mediaPlayers()
2305 mediaPlayers
.prototype =
2309 default_players
: {},
2312 this.players
= new Array();
2313 this.loadPreferences();
2315 //set up default players order for each library type
2316 this.default_players
['video/x-flv'] = ['flash','vlc'];
2317 this.default_players
['video/h264'] = ['flash', 'vlc'];
2319 this.default_players
['video/ogg'] = ['native','vlc','java', 'generic'];
2320 this.default_players
['application/ogg'] = ['native','vlc','java', 'generic'];
2321 this.default_players
['audio/ogg'] = ['native','vlc', 'java', 'omtk' ];
2322 this.default_players
['video/mp4'] = ['vlc'];
2324 this.default_players
['text/html'] = ['html'];
2325 this.default_players
['image/jpeg'] = ['html'];
2326 this.default_players
['image/png'] = ['html'];
2327 this.default_players
['image/svg'] = ['html'];
2330 addPlayer : function(player
, mime_type
)
2332 //js_log('Adding ' + player.id + ' with mime_type ' + mime_type);
2333 for (var i
=0; i
< this.players
.length
; i
++){
2334 if (this.players
[i
].id
== player
.id
)
2338 //make sure the mime_type is not already there:
2339 var add_mime
= true;
2340 for(var j
=0; j
< this.players
[i
].supported_types
.length
; j
++ ){
2341 if( this.players
[i
].supported_types
[j
]== mime_type
)
2345 this.players
[i
].supported_types
.push(mime_type
);
2352 player
.supported_types
.push(mime_type
);
2354 this.players
.push( player
);
2356 getMIMETypePlayers : function(mime_type
)
2358 var mime_players
= new Array();
2361 if( this.default_players
[mime_type
] ){
2362 $j
.each( this.default_players
[mime_type
], function(d
, lib
){
2363 var library
= _this
.default_players
[mime_type
][d
];
2364 for ( var i
=0; i
< _this
.players
.length
; i
++ ){
2365 if ( _this
.players
[i
].library
== library
&& _this
.players
[i
].supportsMIMEType(mime_type
) ){
2366 mime_players
[ inx
] = _this
.players
[i
];
2372 return mime_players
;
2374 defaultPlayer : function(mime_type
)
2376 js_log("get defaultPlayer for " + mime_type
);
2377 var mime_players
= this.getMIMETypePlayers(mime_type
);
2378 if( mime_players
.length
> 0)
2380 // check for prior preference for this mime type
2381 for( var i
=0; i
< mime_players
.length
; i
++ ){
2382 if( mime_players
[i
].id
==this.preference
[mime_type
] )
2383 return mime_players
[i
];
2385 // otherwise just return the first compatible player
2386 // (it will be chosen according to the default_players list
2387 return mime_players
[0];
2389 js_log( 'No default player found for ' + mime_type
);
2392 userSelectFormat : function (mime_format
){
2393 this.preference
['format_prefrence'] = mime_format
;
2394 this.savePreferences();
2396 userSelectPlayer : function(player_id
, mime_type
)
2398 var selected_player
=null;
2399 for(var i
=0; i
< this.players
.length
; i
++){
2400 if(this.players
[i
].id
== player_id
)
2402 selected_player
= this.players
[i
];
2403 js_log('choosing ' + player_id
+ ' for ' + mime_type
);
2404 this.preference
[mime_type
]=player_id
;
2405 this.savePreferences();
2409 if( selected_player
)
2411 for(var i
=0; i
< $mw
.player_list
.length
; i
++)
2413 var embed
= $j('#'+$mw
.player_list
[i
]).get(0);
2414 if(embed
.media_element
.selected_source
&& (embed
.media_element
.selected_source
.mime_type
== mime_type
))
2416 embed
.selectPlayer(selected_player
);
2417 js_log('using ' + embed
.selected_player
.getName() + ' for ' + embed
.media_element
.selected_source
.getTitle());
2422 loadPreferences : function()
2424 this.preference
= new Object();
2425 // see if we have a cookie set to a clientSupported type:
2426 var cookieVal
= $j
.cookie( 'ogg_player_exp' );
2429 var pairs
= cookieVal
.split('&');
2430 for(var i
=0; i
< pairs
.length
; i
++)
2432 var name_value
= pairs
[i
].split('=');
2433 this.preference
[name_value
[0]]=name_value
[1];
2434 //js_log('load preference for ' + name_value[0] + ' is ' + name_value[1]);
2438 savePreferences : function()
2441 for(var i
in this.preference
)
2442 cookieVal
+= i
+ '='+ this.preference
[i
] + '&';
2444 cookieVal
=cookieVal
.substr(0, cookieVal
.length
-1);
2445 var week
= 7*86400*1000;
2446 $j
.cookie( 'ogg_player_exp', cookieVal
, { 'expires':week
} );
2451 * embedTypes object handles setting and getting of supported embed types:
2452 * closely mirrors OggHandler so that its easier to share efforts in this area:
2453 * http://svn.wikimedia.org/viewvc/mediawiki/trunk/extensions/OggHandler/OggPlayer.js
2460 //detect supported types
2462 this.detect_done
=true;
2464 clientSupports
: { 'thumbnail' : true },
2465 supportedMimeType: function(mimetype
) {
2466 for (var i
= navigator
.plugins
.length
; i
-- > 0; ) {
2467 var plugin
= navigator
.plugins
[i
];
2468 if (typeof plugin
[mimetype
] != "undefined")
2474 detect: function() {
2475 js_log("running detect");
2476 this.players
= new mediaPlayers();
2477 //every browser supports html rendering:
2478 this.players
.addPlayer( htmlPlayer
);
2479 // In Mozilla, navigator.javaEnabled() only tells us about preferences, we need to
2480 // search navigator.mimeTypes to see if it's installed
2481 var javaEnabled
= navigator
.javaEnabled();
2482 // In Opera, navigator.javaEnabled() is all there is
2483 var invisibleJava
= $j
.browser
.opera
;
2484 // Some browsers filter out duplicate mime types, hiding some plugins
2485 var uniqueMimesOnly
= $j
.browser
.opera
|| $j
.browser
.safari
;
2486 // Opera will switch off javaEnabled in preferences if java can't be found.
2487 // And it doesn't register an application/x-java-applet mime type like Mozilla does.
2488 if ( invisibleJava
&& javaEnabled
)
2489 this.players
.addPlayer( cortadoPlayer
);
2492 if($j
.browser
.msie
){
2494 if ( this.testActiveX( 'ShockwaveFlash.ShockwaveFlash')){
2495 //try to get the flash version for omtk include:
2497 a
= new ActiveXObject(SHOCKWAVE_FLASH_AX
+ ".7");
2498 d
= a
.GetVariable("$version"); // Will crash fp6.0.21/23/29
2500 d
= d
.split(" ")[1].split(",");
2501 //we need flash version 10 or greater:
2502 if(parseInt( d
[0]) >=10){
2503 this.players
.addPlayer( omtkPlayer
);
2509 //flowplayer has pretty good compatiablity
2510 // (but if we wanted to be fancy we would check for version of flash and update the mp4/h.264 support
2511 this.players
.addPlayer( flowPlayer
);
2514 if ( this.testActiveX( 'VideoLAN.VLCPlugin.2' ) )
2515 this.players
.addPlayer(vlcActiveXPlayer
);
2517 if ( javaEnabled
&& this.testActiveX( 'JavaWebStart.isInstalled' ) )
2518 this.players
.addPlayer(cortadoPlayer
);
2520 //if ( this.testActiveX( 'QuickTimeCheckObject.QuickTimeCheck.1' ) )
2521 // this.players.addPlayer(quicktimeActiveXPlayer);
2524 if ( typeof HTMLVideoElement
== 'object' // Firefox, Safari
2525 || typeof HTMLVideoElement
== 'function' ) // Opera
2527 //do another test for safari:
2528 if( $j
.browser
.safari
){
2530 var dummyvid
= document
.createElement("video");
2531 if (dummyvid
.canPlayType
&& dummyvid
.canPlayType("video/ogg;codecs=\"theora,vorbis\"") == "probably")
2533 this.players
.addPlayer( videoElementPlayer
);
2534 } else if(this.supportedMimeType( 'video/ogg' )) {
2535 /* older versions of safari do not support canPlayType,
2536 but xiph qt registers mimetype via quicktime plugin */
2537 this.players
.addPlayer( videoElementPlayer
);
2539 //@@todo add some user nagging to install the xiph qt
2542 js_log('could not run canPlayType in safari');
2545 this.players
.addPlayer( videoElementPlayer
);
2550 if( navigator
.mimeTypes
&& navigator
.mimeTypes
.length
> 0) {
2551 for ( var i
= 0; i
< navigator
.mimeTypes
.length
; i
++ ) {
2552 var type
= navigator
.mimeTypes
[i
].type
;
2553 var semicolonPos
= type
.indexOf( ';' );
2554 if ( semicolonPos
> -1 ) {
2555 type
= type
.substr( 0, semicolonPos
);
2557 //js_log('on type: '+type);
2558 var pluginName
= navigator
.mimeTypes
[i
].enabledPlugin
? navigator
.mimeTypes
[i
].enabledPlugin
.name
: '';
2559 if ( !pluginName
) {
2560 // In case it is null or undefined
2563 if ( pluginName
.toLowerCase() == 'vlc multimedia plugin' || pluginName
.toLowerCase() == 'vlc multimedia plug-in' ) {
2564 this.players
.addPlayer(vlcMozillaPlayer
, type
);
2568 if ( javaEnabled
&& type
== 'application/x-java-applet' ) {
2569 this.players
.addPlayer(cortadoPlayer
);
2573 if ( type
== 'application/ogg' ) {
2574 if ( pluginName
.toLowerCase() == 'vlc multimedia plugin' ){
2575 this.players
.addPlayer(vlcMozillaPlayer
, type
);
2576 //else if ( pluginName.indexOf( 'QuickTime' ) > -1 )
2577 // this.players.addPlayer(quicktimeMozillaPlayer);
2579 this.players
.addPlayer(oggPluginPlayer
);
2582 } else if ( uniqueMimesOnly
) {
2583 if ( type
== 'application/x-vlc-player' ) {
2584 this.players
.addPlayer(vlcMozillaPlayer
, type
);
2586 } else if ( type
== 'video/quicktime' ) {
2587 //this.players.addPlayer(quicktimeMozillaPlayer);
2592 /*if ( type == 'video/quicktime' ) {
2593 this.players.addPlayer(vlcMozillaPlayer, type);
2596 if(type
=='application/x-shockwave-flash'){
2597 this.players
.addPlayer( flowPlayer
);
2599 //check version to add omtk:
2600 var flashDescription
= navigator
.plugins
["Shockwave Flash"].description
;
2601 var descArray
= flashDescription
.split(" ");
2602 var tempArrayMajor
= descArray
[2].split(".");
2603 var versionMajor
= tempArrayMajor
[0];
2604 //js_log("version of flash: " + versionMajor);
2605 if(versionMajor
>= 10){
2606 this.players
.addPlayer( omtkPlayer
);
2612 //@@The xiph quicktime component does not work well with annodex streams (temporarly disable)
2613 //this.clientSupports['quicktime-mozilla'] = false;
2614 //this.clientSupports['quicktime-activex'] = false;
2615 //js_log(this.clientSupports);
2617 testActiveX : function ( name
) {
2620 // No IE, not a class called "name", it's a variable
2621 var obj
= new ActiveXObject( '' + name
);