removed output-disable in dbms-output fetching procedure
[mediawiki.git] / js2 / mwEmbed / libAddMedia / mvAdvFirefogg.js
blobf1f88ba70c862f2c5e5720d3215c1cfa327b6cc8
1 /*
2  * Advanced Firefogg support. Lets you control many aspects of video encoding.
3  */
5 loadGM({
6         "fogg-help-sticky" : "Help (click to stick)",
7         "fogg-cg-preset" : "Preset: <strong>$1<\/strong>",
8         "fogg-cg-quality" : "Basic quality and resolution control",
9         "fogg-cg-meta" : "Metadata for the clip",
10         "fogg-cg-range" : "Encoding range",
11         "fogg-cg-advVideo" : "Advanced video encoding controls",
12         "fogg-cg-advAudio" : "Advanced audio encoding controls",
13         "fogg-preset-custom" : "Custom settings",
14         "fogg-webvideo-desc" : "Web video Theora, Vorbis 400 kbit\/s and 400px maximum width",
15         "fogg-savebandwidth-desc" : "Low bandwidth Theora, Vorbis 164 kbit\/s and 200px maximum width",
16         "fogg-highquality-desc" : "High quality Theora, Vorbis 1080px maximum width",
17         "fogg-videoQuality-title" : "Video quality",
18         "fogg-videoQuality-help" : "Used to set the <i>visual quality<\/i> of the encoded video (not used if you set bitrate in advanced controls below).",
19         "fogg-starttime-title" : "Start second",
20         "fogg-starttime-help" : "Only encode from time in seconds",
21         "fogg-endtime-title" : "End second",
22         "fogg-endtime-help" : "Only encode to time in seconds",
23         "fogg-audioQuality-title" : "Audio quality",
24         "fogg-audioQuality-help" : "Used to set the <i>acoustic quality<\/i> of the encoded audio (not used if you set bitrate in advanced controls below).",
25         "fogg-videoCodec-title" : "Video codec",
26         "fogg-videoCodec-help" : "Used to select the clip video codec. Presently only Theora is supported. More about the <a target=\"_new\" href=\"http:\/\/en.wikipedia.org\/wiki\/Theora\">Theora codec<\/a>.",
27         "fogg-audioCodec-title" : "Audio codec",
28         "fogg-audioCodec-help" : "Used to set the clip audio codec. Presently only Vorbis is supported. More about the <a target=\"_new\" href=\"http:\/\/en.wikipedia.org\/wiki\/Vorbis\">Vorbis codec<\/a>",
29         "fogg-width-title" : "Video width",
30         "fogg-width-help" : "Resize to given width.",
31         "fogg-height-title" : "Video height",
32         "fogg-height-help" : "Resize to given height.",
33         "fogg-videoBitrate-title" : "Video bitrate",
34         "fogg-videoBitrate-help" : "Video bitrate sets the encoding bitrate for video in (kb\/s)",
35         "fogg-twopass-title" : "Two pass encoding",
36         "fogg-twopass-help" : "Two pass encoding enables more constant quality by making two passes over the video file",
37         "fogg-framerate-title" : "Frame rate",
38         "fogg-framerate-help" : "The video frame rate. More about <a target=\"_new\" href=\"http:\/\/en.wikipedia.org\/wiki\/Frame_rate\">frame rate<\/a>.",
39         "fogg-aspect-title" : "Aspect ratio",
40         "fogg-aspect-help" : "The video aspect ratio can be 4:3 or 16:9. More about <a target=\"_new\" href=\"http:\/\/en.wikipedia.org\/wiki\/Aspect_ratio_%28image%29\">aspect ratios<\/a>.",
41         "fogg-keyframeInterval-title" : "Key frame interval",
42         "fogg-keyframeInterval-help" : "The keyframe interval in frames. Note: Most codecs force keyframes if the difference between frames is greater than keyframe encode size. More about <a href=\"http:\/\/en.wikipedia.org\/wiki\/I-frame\">keyframes<\/a>.",
43         "fogg-denoise-title" : "Denoise filter",
44         "fogg-denoise-help" : "Denoise input video. More about <a href=\"http:\/\/en.wikipedia.org\/wiki\/Video_denoising\">denoise<\/a>.",
45         "fogg-novideo-title" : "No video",
46         "fogg-novideo-help" : "disable video in the output",
47         "fogg-audioBitrate-title" : "Audio bitrate",
48         "fogg-samplerate-title" : "Audio sampling rate",
49         "fogg-samplerate-help" : "set output sample rate (in Hz).",
50         "fogg-noaudio-title" : "No audio",
51         "fogg-noaudio-help" : "disable audio in the output",
52         "fogg-title-title" : "Title",
53         "fogg-title-help" : "A title for your clip",
54         "fogg-artist-title" : "Creator name",
55         "fogg-artist-help" : "The creator of this clip",
56         "fogg-date-title" : "Date",
57         "fogg-date-help" : "The date the footage was created or released",
58         "fogg-location-title" : "Location",
59         "fogg-location-help" : "The location of the footage",
60         "fogg-organization-title" : "Organization",
61         "fogg-organization-help" : "Name of organization (studio)",
62         "fogg-copyright-title" : "Copyright",
63         "fogg-copyright-help" : "The copyright of the clip",
64         "fogg-license-title" : "License",
65         "fogg-license-help" : "The license of the clip (preferably a Creative Commons URL).",
66         "fogg-contact-title" : "Contact",
67         "fogg-contact-help" : "Contact link"
68 });
70 var mvAdvFirefogg = function( iObj ) {
71         return this.init( iObj );
73 var default_mvAdvFirefogg_config = {
74         // Config groups to include
75         'config_groups': [ 'preset', 'range', 'quality', 'meta', 'advVideo', 'advAudio' ],
77         // If you want to load any custom presets must follow the mvAdvFirefogg.presetConf json outline below
78         'custom_presets': {},
80         // Any Firefog config properties that may need to be excluded from options
81         'exclude_settings': [],
83         // The control container
84         'target_control_container': false
87 mvAdvFirefogg.prototype = {
88         // The configuration group names
89         config_groups: [ 'preset', 'range', 'quality', 'meta', 'advVideo', 'advAudio' ],
91         // Default configuration for this class
92         default_local_settings: {
93                 'default': 'webvideo',
94                 'type': 'select',
95                 'selectVal': ['webvideo'],
96                 'group': "preset",
97                 'presets': {
98                         'custom': {
99                                 'descKey': 'fogg-preset-custom',
100                                 'conf': {}
101                         },
102                         'webvideo': {
103                                 'desc': gM( 'fogg-webvideo-desc' ),
104                                 'conf': {
105                                         'maxSize'      : 400,
106                                         'videoBitrate' : 544,
107                                         'audioBitrate' : 96,
108                                         'noUpscaling'  : true,
109                                 }
110                         },
111                         'savebandwidth': {
112                                 'desc': gM( 'fogg-savebandwidth-desc' ),
113                                 'conf': {
114                                         'maxSize'       : 200,
115                                         'videoBitrate'  : 164,
116                                         'audioBitrate'  : 32,
117                                         'samplerate'    : 22050,
118                                         'framerate'     : 15,
119                                         'channels'      : 1,
120                                         'noUpscaling'   : true
121                                 }
122                         },
123                         'hqstream': {
124                                 'desc': gM( 'fogg-highquality-desc' ),
125                                 'conf': {
126                                         'maxSize'      : 1080,
127                                         'videoQuality' : 6,
128                                         'audioQuality' : 3,
129                                         'noUpscaling'  : true,
130                                 }
131                         },
132                 }
133         },
135         // Customised configuration hashtable
136         local_settings: {},
138         // Core Firefogg default encoder configuration
139         // See encoder options here: http://www.firefogg.org/dev/index.html
140         default_encoder_config: {
141                 // Base quality settings
142                 'videoQuality': {
143                         'default'   : 5,
144                         'range'     : { 'min': 0,'max': 10 },
145                         'type'      : 'slider',
146                         'group'     : 'quality'
147                 },
148                 'starttime': {
149                         'type'      : "float",
150                         'group'     : "range"
151                 },
152                 'endtime': {
153                         'type'      : "float",
154                         'group'     : "range"
155                 },
156                 'audioQuality': {
157                         'default'   : 1,
158                         'range'     : { 'min': -1, 'max': 10 },
159                         'type'      : 'slider',
160                         'group'     : 'quality',
161                 },
162                 'videoCodec': {
163                         'default'   : "theora",
164                         'selectVal' : [ 'theora' ],
165                         'type'      : "select",
166                         'group'     : "quality"
167                 },
168                 'audioCodec': {
169                         'default'   : "vorbis",
170                         'selectVal' : [ 'vorbis' ],
171                         'type'      : "select",
172                         'group'     : "quality"
173                 },
174                 'width': {
175                         'range'     : { 'min': 0, 'max': 1080 },
176                         'step'      : 4,
177                         'type'      : 'slider',
178                         'group'     : "quality"
179                 },
180                 'height': {
181                         'range'     : { 'min': 0, 'max' : 1080 },
182                         'step'      : 4,
183                         'type'      : "slider",
184                         'group'     : "quality"
185                 },
187                 // Advanced video control
188                 'videoBitrate': {
189                         'range'     : { 'min' : 1, 'max' : 16778 },
190                         'type'      : "slider",
191                         'group'     : "advVideo",
192                 } ,
193                 'twopass': {
194                         'type'      : "boolean",
195                         'group'     : "advVideo"
196                 },
197                 'framerate': {
198                         'default'   : '24',
199                         'selectVal' : [ '12', '16', { '24000:1001' : '23.97' }, '24', '25', 
200                                 { '30000:1001' : '29.97' }, '30' ],
201                         'type'      : "select",
202                         'group'     : "advVideo"
203                 },
204                 'aspect': {
205                         'default'   : '4:3',
206                         'type'      : "select",
207                         'selectVal' : [ '4:3', '16:9' ],
208                         'group'     : "advVideo"
209                 },
210                 'keyframeInterval': {
211                         'default'   : '64',
212                         'range'     : { 'min': 0, 'max': 65536 },
213                         'numberType': 'force keyframe every $1 frames',
214                         'type'      : 'int',
215                         'group'     : 'advVideo'
216                 },
217                 'denoise': {
218                         'type'      : "boolean",
219                         'group'     : 'advVideo'
220                 },
221                 'novideo': {
222                         'type'      : "boolean",
223                         'group'     : 'advVideo'
224                 },
226                 // Advanced audio control
227                 'audioBitrate': {
228                         'range'     : { 'min': 32, 'max': 500 },
229                         'numberType': '$1 kbs',
230                         'type'      : 'slider'
231                 },
232                 'samplerate': {
233                         'type'      : 'select',
234                         'selectVal' : [ { '22050': '22 kHz' }, { '44100': '44 khz' }, { '48000': '48 khz' } ],
235                         'formatSelect' : function( val ) {
236                                 return ( Math.round( val / 100 ) * 10 ) + ' Hz';
237                         }
238                 },
239                 'noaudio': {
240                         'type'      : 'boolean',
241                         'group'     : 'advAudio'
242                 },
244                 // Meta tags
245                 'title': {
246                         'type'      : 'string',
247                         'group'     : 'meta'
248                 },
249                 'artist': {
250                         'type'      : 'string',
251                         'group'     : 'meta'
252                 },
253                 'date': {
254                         'group'     : 'meta',
255                         'type'      : 'date'
256                 },
257                 'location': {
258                         'type'      : 'string',
259                         'group'     : 'meta'
260                 },
261                 'organization': {
262                         'type'      : 'string',
263                         'group'     : 'meta'
264                 },
265                 'copyright':  {
266                         'type'      : 'string',
267                         'group'     : 'meta'
268                 },
269                 'license': {
270                         'type'      : 'string',
271                 },
272                 'contact': {
273                         'type'      : 'string',
274                         'group'     : 'meta'
275                 }
276         },
278         /**
279          * Initialise this object
280          */
281         init: function( options ) {
282                 // Set up a supported object:
283                 for ( var key in options ) {
284                         if ( typeof default_mvAdvFirefogg_config[key] != 'undefined' ) {
285                                 this[key] = options[key];
286                         }
287                 }
288                 // Inherit the base mvFirefogg class:
289                 var baseFirefogg = new mvFirefogg( options );
290                 for ( var key in baseFirefogg ) {
291                         if ( typeof this[key] != 'undefined' ) {
292                                 this[ 'basefogg_' + key ] = baseFirefogg[ key ];
293                         } else {
294                                 this[ key ] = baseFirefogg[ key ];
295                         }
296                 }
297         },
299         setupForm: function() {
300                 basefogg_setupForm();
301                 this.createControls();
302                 this.bindControls();
303         },
305         createControls: function() {
306                 js_log( "adv createControls" );
307                 var _this = this;
308                 // Load presets from the cookie
309                 this.loadEncSettings();
311                 // Add the base control buttons
312                 this.basefogg_createControls();
314                 // Build the config group output
315                 var gdout = '';
316                 $j.each( this.config_groups, function( inx, group_key ) {
317                         gdout += '<div> ' +
318                                 '<h3><a href="#" class="gd_' + group_key + '" >' + 
319                                 gM( 'fogg-cg-' + group_key ) + '</a></h3>' +
320                                 '<div>';
321                         // Output this group's control options:
322                         gdout += '<table width="' + ( $j( _this.selector ).width() - 60 ) + '" >' + 
323                                 '<tr><td width="35%"></td><td width="65%"></td></tr>';
324                         // If this is the preset group, output the preset control
325                         if ( group_key == 'preset' ) {
326                                 gdout += _this.getPresetControlHtml();
327                         }
328                         // Output the encoder config controls
329                         for ( var configKey in _this.default_encoder_config ) {
330                                 var confEntry = _this.default_encoder_config[ configKey ];
331                                 if( confEntry.group == group_key ) {
332                                         gdout += _this.getConfigControlHtml( configKey );
333                                 }
334                         }
335                         gdout += '</table></div></div>';
336                 });
337                 // Add the control container
338                 if( !this.target_control_container ) {
339                         this.target_control_container = this.selector + ' .control_container';
340                         $j( this.selector ).append( '<p><div class="control_container"></div>' );
341                 }
342                 // Hide the container and add the output
343                 $j( this.target_control_container ).hide();
344                 $j( this.target_control_container ).html( gdout );
345         },
347         // Custom advanced target rewrites
348         getControlHtml: function( target ) {
349                 switch ( target ) {
350                         case 'target_btn_select_file':
351                         case 'target_btn_select_new_file':
352                         case 'target_btn_save_local_file':
353                                 var icon;
354                                 if ( target == 'target_btn_save_local_file' ) {
355                                         icon = 'ui-icon-video'
356                                 } else {
357                                         icon = 'ui-icon-folder-open';
358                                 }
359                                 var linkText = gM( target.replace( /^target_btn_/, 'fogg-' ) );
360                                 return '<a class="ui-state-default ui-corner-all ui-icon_link ' +
361                                                         target + '" href="#"><span class="ui-icon ' + icon + '"/>' +
362                                                         linkText +
363                                                 '</a>';
364                         case 'target_btn_select_url':
365                                 return $j.btnHtml( gM( 'fogg-select_url' ), target,  'link' );
366                         case 'target_use_latest_firefox':
367                         case 'target_please_install':
368                         case 'target_passthrough_mode':
369                                 var text = gM( target.replace( '/^target_', 'fogg-' ) );
370                                 return 
371                                         '<div ' + 
372                                                 'style="margin-top:1em;padding: 0pt 0.7em;" ' + 
373                                                 'class="ui-state-error ui-corner-all ' +
374                                                 target + '">' +
375                                         '<p>' + 
376                                         '<span style="float: left; margin-right: 0.3em;" ' + 
377                                                 'class="ui-icon ui-icon-alert"/>' +
378                                         text + 
379                                         '</p>' +
380                                         '</div>';
381                         case 'target_input_file_name':
382                                 var text = gM( 'fogg-input_file_name' );
383                                 return '<br><br><input style="" ' + 
384                                         'class="text ui-widget-content ui-corner-all ' + target + '" ' +
385                                         'type="text" value="' + text + '" size="60" /> ';
386                         default:
387                                 js_log( 'call : basefogg_getTargetHtml' );
388                                 return this.basefogg_getTargetHtml( target );
389                 }
390         },
392         getPresetControlHtml: function() {
393                 var out = '';
394                 var _this = this;
395                 js_log( 'getPresetControlHtml::' );
396                 if ( typeof this.local_settings.presets != 'undefined' ) {
397                         out += '<select class="_preset_select">';
398                         $j.each( this.local_settings.presets, function( presetKey, preset ) {
399                                 var presetDesc = preset.descKey ? gM( preset.descKey ) : preset.desc;
400                                 var sel = ( _this.local_settings['default'] == presetKey ) ? ' selected' : '';
401                                 out += '<option value="' + presetKey + '" ' + sel + '>' + presetDesc + '</option>';
402                         });
403                         out += '</select>';
404                 }
405                 return out;
406         },
408         getConfigControlHtml : function( configKey ) {
409                 var configEntry = this.default_encoder_config[configKey];
410                 var out = '';
411                 out += '<tr><td valign="top">' +
412                         '<label for="_' + configKey + '">' +
413                         gM( 'fogg-' + configKey + '-title' ) + ':' +
414                         '<span title="' + gM( 'fogg-help-sticky' ) + '" ' + 
415                                 'class="help_' + configKey + ' ui-icon ui-icon-info" style="float:left">' + 
416                         '</span>' +
417                         '</label></td><td valign="top">';
418                 // Get the default value (or an empty string if there is no default)
420                 var defaultValue = this.default_encoder_config[configKey]['default'];
421                 if ( !defaultValue ) {
422                         defaultValue = '';
423                 }
424                 var type = configEntry.type; // shortcut
426                 // Switch on the config type
427                 switch( type ) {
428                         case 'string':
429                         case 'date':
430                         case 'int':
431                         case 'float':
432                                 var size = ( type == 'string' || type == 'date' ) ? '14' : '4';
433                                 out += '<input ' + 
434                                         'size="' + size + '" ' + 
435                                         'type="text" ' + 
436                                         'class="_' + configKey + ' text ui-widget-content ui-corner-all" ' + 
437                                         'value="' + defaultValue + '" >';
438                                 break;
439                         case 'boolean':
440                                 var checked_attr = ( defaultValue === true ) ? ' checked="true"' : '';
441                                 out += '<input ' + 
442                                         'type="checkbox" ' + 
443                                         'class="_' + configKey + ' ui-widget-content ui-corner-all" ' + 
444                                         checked_attr + '>';
445                                 break;
446                         case 'slider':
447                                 var strMax = this.default_encoder_config[ configKey ].range.max + '';
448                                 maxDigits = strMax.length + 1;
449                                 out += '<input ' + 
450                                         'type="text" ' + 
451                                         'maxlength="' + maxDigits + '" ' + 
452                                         'size="' + maxDigits + '" ' +
453                                         'class="_' + configKey + ' text ui-widget-content ui-corner-all" ' + 
454                                         'style="display:inline;border:0; color:#f6931f; font-weight:bold;" ' +
455                                         'value="' + defaultValue + '" >' +
456                                         '<div class="slider_' + configKey + '"></div>';
457                                 break;
458                         case 'select':
459                                 out += '<select class="_' + configKey + '">' +
460                                                 '<option value=""> </option>';
461                                 for ( var i in configEntry.selectVal ) {
462                                         var val = configEntry.selectVal[i];
463                                         if ( typeof val == 'string' ) {
464                                                 var sel = ( configEntry.selectVal[i] == val ) ? ' selected' : '';
465                                                 out += '<option value="' + val + '"'+sel+'>' + val + '</option>';
466                                         } else if ( typeof val == 'object' ) {
467                                                 for ( var key in val ) {
468                                                         hr_val = val[key];
469                                                 }
470                                                 var sel = ( configEntry.selectVal[i] == key ) ? ' selected' : '';
472                                                 out += '<option value="' + key + '"' + sel + '>' + hr_val + '</option>';
473                                         }
474                                 }
475                                 out += '</select>';
476                                 break;
477                 }
478                 // output the help row:
479                 out += '<div class="helpRow_' + configKey + '">' +
480                                 '<span class="helpClose_' + configKey + ' ui-icon ui-icon-circle-close" ' +
481                                 'title="Close Help"' +
482                                 'style="float:left"/>' +
483                                 gM( 'fogg-'+ configKey + '-help' ) +
484                                 '</div>';
485                 out += '</td></tr><tr><td colspan="2" height="10"></td></tr>';
486                 return out;
487         },
489         /**
490          * Show a dialog box asking the user to select a source URL.
491          * FIXME: half-written, doesn't work at all. 
492          */
493         selectSourceUrl: function() {
494                 // FIXME: i18n
495                 var url = prompt( "Please enter the source media url you would like " + 
496                                 "to transcode from.", "http://" );
497                 if ( !url ) {
498                         return;
499                 }
501                 // update the mode:
502                 this.sourceMode = 'url';
503                 this.sourceUrl = url;
504                 this.clearSourceInfoCache();
505                 this.updateSourceFileUI();
506                 // update the input target
507                 $j( this.target_input_file_name ) 
508                         .unbind()
509                         .val( url )
510                         .removeAttr( 'readonly' );
511         },
513         bindControls: function() {
514                 var _this = this;
515                 _this.basefogg_bindControls();
517                 // Show the select by URL if present
518                 /*$j( this.target_btn_select_url ).unbind()
519                         .attr( 'disabled', false )
520                         .css( { 'display': 'inline' } )
521                         .click( function() {
522                                 _this.selectSourceUrl();
523                         });
524                 */
526                 // Hide the base advanced controls until a file is selected:
527                 $j( this.target_control_container ).hide();
529                 var helpState = {};
530                 // Do some display tweaks
531                 js_log( 'tw:' + $j( this.selector ).width() +
532                         ' ssf:' + $j( this.target_btn_select_new_file ).width() +
533                         ' sf:' + $j( this.target_btn_save_local_file ).width() );
535                 // Set width to 250
536                 $j( this.target_input_file_name ).width( 250 );
538                 // Special preset action
539                 $j( this.selector + ' ._preset_select' ).change( function() {
540                         _this.updatePresetSelection( $j( this ).val() );
541                 });
543                 // Bind control actions
544                 for ( var configKey in this.default_encoder_config ) {
545                         var confEntry =  this.default_encoder_config[configKey];
547                         // Initial state is hidden
548                         $j( this.selector + ' .helpRow_' + configKey ).hide();
550                         $j( this.selector + ' .help_' + configKey )
551                                 .click(
552                                         function() {
553                                                 // Get the config key (assume it's the last class)
554                                                 var configKey = _this.getClassId( this, 'help_' );
556                                                 if ( helpState[configKey] ) {
557                                                         $j( _this.selector + ' .helpRow_' + configKey ).hide( 'slow' );
558                                                         helpState[configKey] = false;
559                                                 } else {
560                                                         $j( _this.selector + ' .helpRow_' + configKey ).show( 'slow' );
561                                                         helpState[configKey] = true;
562                                                 }
563                                                 return false;
564                                         }
565                                 )
566                                 .hover(
567                                         function() {
568                                                 // get the config key (assume it's the last class)
569                                                 var configKey = _this.getClassId( this, 'help_' );
570                                                 $j( _this.selector + ' .helpRow_' + configKey ).show( 'slow' );
571                                         },
572                                         function() {
573                                                 var configKey = _this.getClassId( this, 'help_' );
574                                                 if( !helpState[configKey] )
575                                                         $j( _this.selector + ' .helpRow_' + configKey ).hide( 'slow' )
576                                         }
577                                 );
579                         $j( this.selector + ' .helpClose_' + configKey )
580                                 .click(
581                                         function() {
582                                                 js_log( "close help: " + configKey );
583                                                 // get the config key (assume it's the last class)
584                                                 var configKey = _this.getClassId( this, 'helpClose_' );
585                                                 $j( _this.selector + ' .helpRow_' + configKey ).hide( 'slow' );
586                                                 helpState[configKey] = false;
587                                                 return false;
588                                         }
589                                 )
590                                 .css( 'cursor', 'pointer' );
592                         // Set up bindings for the change events (validate input)
594                         switch ( confEntry.type ) {
595                                 case 'boolean':
596                                         $j( this.selector + ' ._' + configKey)
597                                                 .click( function() {
598                                                         _this.updateLocalValue( _this.getClassId( this ), 
599                                                                 $j( this ).is( ":checked" ) );
600                                                         _this.updatePresetSelection( 'custom' );
601                                                 });
602                                         break;
603                                 case 'select':
604                                 case 'string':
605                                 case 'int':
606                                 case 'float':
607                                         //@@check if we have a validate function on the string
608                                         $j( this.selector + ' ._' + configKey ).change( function() {
609                                                 $j( this ).val( _this.updateLocalValue(
610                                                         _this.getClassId( this ),
611                                                         $j( this ).val() ) );
612                                                 _this.updatePresetSelection( 'custom' );
613                                         })
614                                         break;
615                                 case 'date':
616                                         $j( this.selector + ' ._' + configKey ).datepicker({
617                                                         changeMonth: true,
618                                                         changeYear: true,
619                                                         dateFormat: 'd MM, yy',
620                                                         onSelect: function( dateText ) {
621                                                                 _this.updateInterfaceValue( _this.getClassId( this ), dateText );
622                                                         }
623                                         });
624                                         break;
625                                 case 'slider':
626                                         var sliderId = _this.getClassId( this, 'slider_' );
627                                         $j( this.selector + ' .slider_' + configKey ).slider({
628                                                 range: "min",
629                                                 animate: true,
630                                                 step: confEntry.step ? confEntry.step : 1,
631                                                 value: $j( this.selector + ' ._' + configKey ).val(),
632                                                 min: this.default_encoder_config[ configKey ].range.min,
633                                                 max: this.default_encoder_config[ configKey ].range.max,
634                                                 slide: function( event, ui ) {
635                                                         $j( _this.selector + ' ._' + sliderId ).val( ui.value );
637                                                         // Maintain source video aspect ratio
638                                                         if ( sliderId == 'width' ) {
639                                                                 var sourceHeight = _this.sourceFileInfo.video[0]['height'];
640                                                                 var sourceWidth = _this.sourceFileInfo.video[0]['width'];
641                                                                 var newHeight = parseInt( sourceHeight / sourceWidth * ui.value );
642                                                                 // Reject the update if the new height is above the maximum
643                                                                 if ( newHeight > _this.updateInterfaceValue( 'height', newHeight ) )
644                                                                         return false;
645                                                         }
646                                                         if ( sliderId == 'height' ) {
647                                                                 var sourceHeight = _this.sourceFileInfo.video[0]['height'];
648                                                                 var sourceWidth = _this.sourceFileInfo.video[0]['width'];
649                                                                 var newWidth = parseInt( sourceWidth / sourceHeight * ui.value );
650                                                                 // Reject the update if the new width is above the maximum
651                                                                 if ( newWidth > _this.updateInterfaceValue( 'width', wv ) )
652                                                                         return false;
653                                                         }
654                                                 },
655                                                 change: function( event, ui ) {
656                                                         _this.updateLocalValue( sliderId, ui.value );
657                                                         _this.updatePresetSelection( 'custom' );
658                                                 }
659                                         });
661                                         $j( this.selector + ' ._' + configKey ).change( function() {
662                                                 var classId = _this.getClassId( this );
663                                                 var validValue = _this.updateLocalValue( classId.substr( 1 ), 
664                                                         $j( this ).val() );
665                                                 _this.updatePresetSelection( 'custom' );
666                                                 // Change it to the validated value
667                                                 $j( this ).val( validValue );
668                                                 // update the slider
669                                                 js_log( "update: " + _this.selector + ' .slider' + classId );
670                                                 $j( _this.selector + ' .slider' + classId )
671                                                         .slider( 'option', 'value', validValue );
672                                         });
673                                         break;
674                         }
675                 }
677                 $j( this.target_control_container ).accordion({
678                         header: "h3",
679                         collapsible: true,
680                         active: false,
681                         fillSpace: true
682                 });
684                 // Do the config value updates if there are any
685                 this.updateValuesInHtml();
686         },
688         /**
689          * Update the UI due to a change in preset
690          */
691         updatePresetSelection: function( presetKey ) {
692                 // Update the local configuration
693                 this.local_settings['default'] = presetKey;
694                 // js_log( 'update preset desc: ' + presetKey );
695                 var presetDesc = '';
696                 if ( this.local_settings.presets[presetKey].desc ) {
697                         presetDesc = this.local_settings.presets[presetKey].desc;
698                 } else {
699                         presetDesc = gM( 'fogg-preset-' + presetKey );
700                 }
701                 // Update the preset title
702                 $j( this.selector + ' .gd_preset' )
703                         .html( gM( 'fogg-cg-preset', presetDesc ) );
704                 // update the selector
705                 $j( this.selector + ' ._preset_select' ).val( presetKey );
706         },
708         /*
709          * Update the interface due to a change in a particular config key
710          */
711         updateInterfaceValue: function( confKey, val ) {
712                 var _this = this;
713                 if ( !val ) {
714                         return;
715                 }
716                 // Look up the type
717                 if ( typeof this.default_encoder_config[confKey] == 'undefined' ) {
718                         js_error( 'error: missing default key: ' + confKey );
719                         return false;
720                 }
722                 // Update the local value (if it's not already up-to-date)
723                 if ( this.local_settings.presets['custom']['conf'][confKey] != val ) {
724                         val = this.updateLocalValue( confKey, val );
725                 }
726                 // Update the text field
727                 $j( _this.selector + ' ._' + confKey ).val( val );
728                 // Update the interface widget
729                 switch ( this.default_encoder_config[confKey].type ) {
730                         case 'slider':
731                                 $j( _this.selector + ' .slider_' + confKey )
732                                         .slider( 'option', 'value', $j( _this.selector + ' ._' + confKey ).val() );
733                                 break;
734                 }
735                 return val;
736         },
738         /**
739          * Validate the new config setting, fixing its type and bounding it within a 
740          * range if required. Update the configuration with the validated value and 
741          * return it.
742          */
743         updateLocalValue: function( confKey, value ) {
744                 if ( typeof this.default_encoder_config[confKey] == 'undefined' ) {
745                         js_log( "Error: could not update conf key: " + confKey )
746                         return value;
747                 }
748                 var confEntry = this.default_encoder_config[confKey];
749                 var range = confEntry.range;
750                 if ( range ) {
751                         value = parseInt( value );
752                         var min = ( range.local_min ) ? range.local_min : range.min;
753                         if ( value < min )
754                                 value = min;
755                         var max = ( range.local_max ) ? range.local_max : range.max;
756                         if (value > max )
757                                 value = max;
758                 }
759                 if ( confEntry.type == 'int' )
760                         value = parseInt( value );
762                 // step value:
763                 /* if( confEntry.step ) {
764                         if ( ( value % confEntry.step ) != 0 ) {
765                                 value = value - (value % confEntry.step);
766                         }
767                 }*/
769                 js_log( 'update:local_settings:custom:conf:' + confKey + ' = ' + value );
770                 this.local_settings.presets['custom']['conf'][confKey] = value;
772                 return value;
773         },
775         /**
776          * Get a local config value from the custom preset
777          */
778         getLocalValue: function( confKey ) {
779                 return this.local_settings.presets['custom']['conf'][confKey];
780         },
782         /**
783          * Given an element or selector, get its primary class, and strip a given 
784          * prefix from it.
785          *
786          * If no prefix is given, "_" is assumed.
787          */
788         getClassId: function( element, prefix ) {
789                 var eltClass = $j( element ).attr( "class" ).split( ' ' ).slice( 0, 1 ).toString();
791                 if ( !prefix ) {
792                         prefix = '_';
793                 }
794                 if ( eltClass.substr( 0, prefix.length ) == prefix ) {
795                         eltClass = eltClass.substr( prefix.length );
796                 }
797                 return eltClass;
798         },
800         /**
801          * Get the appropriate encoder settings for the current Firefogg object, 
802          * into which a video has already been selected. Overrides the base method.
803          */
804         getEncoderSettings: function() {
805                 if ( this.current_encoder_settings != null ) {
806                         return this.current_encoder_settings;
807                 }
809                 // Call the base function
810                 // Note that settings will be a reference and can be modified
811                 var settings = this.basefogg_getEncoderSettings();
813                 // Allow re-encoding of files that are already ogg (unlike in the base class)
814                 if ( this.isOggFormat() ) {
815                         settings['passthrough'] = false;
816                 }
817         },
819         /**
820          * Do the necessary UI updates due to the source file changing.
821          * Overrides the parent method.
822          */
823         updateSourceFileUI: function() {
824                 var _this = this;
826                 // Call the parent
827                 _this.basefogg_updateSourceFileUI();
829                 var settings = this.getEncoderSettings();
830                 var fileInfo = this.getSourceFileInfo();
832                 // In passthrough mode, hide encoder controls
833                 if ( settings['passthrough'] ) {
834                         js_log( "in passthrough mode (hide control)" );
835                         $j( this.target_control_container ).hide( 'slow' );
836                         $j( this.target_passthrough_mode ).show( 'slow' );
837                         return;
838                 }
840                 // Show encoder controls
841                 $j( this.target_control_container ).show( 'slow' );
842                 $j( this.target_passthrough_mode ).hide( 'slow' );
844                 // do set up settings based on local_settings /default_encoder_config with sourceFileInfo
845                 // see: http://firefogg.org/dev/sourceInfo_example.html
846                 var setValues = function( k, val, maxVal ) {
847                         if ( k !== false ) {
848                                 // update the value if unset:
849                                 _this.updateLocalValue( k, val );
850                         }
851                         if ( maxVal ) {
852                                 // update the local range:
853                                 if ( _this.default_encoder_config[k].range ) {
854                                         _this.default_encoder_config[k].range.local_max = maxVal;
855                                 }
856                         }
857                 }
858                 // container level settings
859                 for ( var i in fileInfo ) {
860                         var val = fileInfo[i];
861                         var k = false;
862                         var maxVal = false;
863                         switch ( i ) {
864                                 // do nothing with these:
865                                 case 'bitrate':
866                                         k = 'videoBitrate';
867                                         if ( val * 2 > this.default_encoder_config[k] ) {
868                                                 maxVal = this.default_encoder_config[k];
869                                         } else {
870                                                 maxVal = val * 2;
871                                         }
872                                         break;
873                         }
874                         setValues( k, val, maxVal );
875                 }
876                 // video stream settings
877                 for ( var i in fileInfo.video[0] ) {
878                         var val = fileInfo.video[0][i];
879                         var k = false;
880                         var maxVal= false;
881                         switch( i ) {
882                                 case 'width':
883                                 case 'height':
884                                         k = i;
885                                         maxVal = val;
886                                         break;
887                         }
888                         setValues( k, val, maxVal );
889                 }
890                 // audio stream settings, assumes for now thare is only one stream
891                 for ( var i in fileInfo.audio[0] ) {
892                         var val = fileInfo.audio[0][i];
893                         var k = false;
894                         var maxVal = false;
895                         switch ( i ) {
896                                 case 'bitrate':
897                                         k = 'audioBitrate';
898                                         if ( val * 2 > this.default_encoder_config[k] ) {
899                                                 maxVal = this.default_encoder_config[k];
900                                         } else {
901                                                 maxVal = val * 2;
902                                         }
903                                         break;
904                         }
905                         setValues( k, val, maxVal );
906                 }
908                 // set all values to new default ranges & update slider:
909                 $j.each( this.default_encoder_config, function( inx, val ) {
910                         if ( $j( _this.selector + ' ._' + inx ).length != 0 ) {
911                                 if ( typeof val.range != 'undefined' ) {
912                                         // update slider range
913                                         var new_max = (val.range.local_max) ? val.range.local_max : val.range.max
914                                         $j( _this.selector + ' .slider_' + inx ).slider( 'option', 'max', new_max );
916                                         // update slider/input value:
917                                         _this.updateInterfaceValue( inx, 
918                                                 _this.local_settings.presets['custom']['conf'][inx] );
919                                 }
920                         }
921                 });
922                 // update values
923                 this.updateValuesInHtml();
924         },
926         doEncode: function() {
927                 // update the encoder settings (from local settings)
928                 pKey = this.local_settings['default'];
929                 this.encoder_settings = this.local_settings.presets[ pKey ].conf;
930                 this.basefogg_doEncode();
931         },
933         /**
934          * Set the HTML control values to whatever is currently present in this.local_settings
935          */
936         updateValuesInHtml: function() {
937                 js_log( 'updateValuesInHtml::' );
938                 var _this = this;
939                 var pKey = this.local_settings['default'];
940                 this.updatePresetSelection( pKey );
942                 // set the actual HTML & widgets based on any local settings values:
943                 $j.each( _this.local_settings.presets['custom']['conf'], function( inx, val ) {
944                         if ( $j( _this.selector + ' ._' + inx ).length != 0 ) {
945                                 $j( _this.selector + ' ._' + inx ).val( val );
946                         }
947                 });
948         },
950         /**
951          * Restore settings from a cookie (if available)
952          */
953         loadEncSettings: function( force ) {
954                 if ( $j.cookie( 'fogg_encoder_config' ) ) {
955                         js_log( "load:fogg_encoder_config from cookie " );
956                         this.local_settings = JSON.parse( $j.cookie( 'fogg_settings' ) );
957                 }
958                 // set to default if not loaded yet:
959                 if ( this.local_settings && this.local_settings.presets 
960                         && this.local_settings.presets['custom']['conf'] ) 
961                 {
962                         js_log( 'local settings already populated' );
963                 } else {
964                         this.local_settings = this.default_local_settings;
965                 }
966         },
968         /**
969          * Clear preset settings
970          * FIXME: not called, does nothing
971          */
972         clearSettings: function( force ) {
973         },
975         /**
976          * Save the current encoder settings to a cookie.
977          */
978         saveEncSettings: function() {
979                 $j.cookie( 'fogg_settings', JSON.stringify( this.local_settings ) );
980         }