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