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