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