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