Commit RELEASE-NOTES line for the wgCategories js variable I added some time ago.
[lhc/web/wiklou.git] / js2 / mwEmbed / libAddMedia / mvAdvFirefogg.js
1 /*
2 * Advanced Firefogg support. Lets you control many aspects of video encoding.
3 */
4
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 });
69
70 var mvAdvFirefogg = function( iObj ) {
71 return this.init( iObj );
72 }
73 var default_mvAdvFirefogg_config = {
74 // Config groups to include
75 'config_groups': [ 'preset', 'range', 'quality', 'meta', 'advVideo', 'advAudio' ],
76
77 // If you want to load any custom presets must follow the mvAdvFirefogg.presetConf json outline below
78 'custom_presets': {},
79
80 // Any Firefog config properties that may need to be excluded from options
81 'exclude_settings': [],
82
83 // The control container
84 'target_control_container': false
85 }
86
87 mvAdvFirefogg.prototype = {
88 // The configuration group names
89 config_groups: [ 'preset', 'range', 'quality', 'meta', 'advVideo', 'advAudio' ],
90
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 },
134
135 // Customised configuration hashtable
136 local_settings: {},
137
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 },
186
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 },
225
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 },
243
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 },
277
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 },
298
299 setupForm: function() {
300 basefogg_setupForm();
301 this.createControls();
302 this.bindControls();
303 },
304
305 createControls: function() {
306 js_log( "adv createControls" );
307 var _this = this;
308 // Load presets from the cookie
309 this.loadEncSettings();
310
311 // Add the base control buttons
312 this.basefogg_createControls();
313
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 },
346
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 },
391
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 },
407
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)
419
420 var defaultValue = this.default_encoder_config[configKey]['default'];
421 if ( !defaultValue ) {
422 defaultValue = '';
423 }
424 var type = configEntry.type; // shortcut
425
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' : '';
471
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 },
488
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 }
500
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 },
512
513 bindControls: function() {
514 var _this = this;
515 _this.basefogg_bindControls();
516
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 */
525
526 // Hide the base advanced controls until a file is selected:
527 $j( this.target_control_container ).hide();
528
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() );
534
535 // Set width to 250
536 $j( this.target_input_file_name ).width( 250 );
537
538 // Special preset action
539 $j( this.selector + ' ._preset_select' ).change( function() {
540 _this.updatePresetSelection( $j( this ).val() );
541 });
542
543 // Bind control actions
544 for ( var configKey in this.default_encoder_config ) {
545 var confEntry = this.default_encoder_config[configKey];
546
547 // Initial state is hidden
548 $j( this.selector + ' .helpRow_' + configKey ).hide();
549
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_' );
555
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 );
578
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' );
591
592 // Set up bindings for the change events (validate input)
593
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 );
636
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 });
660
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 }
676
677 $j( this.target_control_container ).accordion({
678 header: "h3",
679 collapsible: true,
680 active: false,
681 fillSpace: true
682 });
683
684 // Do the config value updates if there are any
685 this.updateValuesInHtml();
686 },
687
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 },
707
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 }
721
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 },
737
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 );
761
762 // step value:
763 /* if( confEntry.step ) {
764 if ( ( value % confEntry.step ) != 0 ) {
765 value = value - (value % confEntry.step);
766 }
767 }*/
768
769 js_log( 'update:local_settings:custom:conf:' + confKey + ' = ' + value );
770 this.local_settings.presets['custom']['conf'][confKey] = value;
771
772 return value;
773 },
774
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 },
781
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();
790
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 },
799
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 }
808
809 // Call the base function
810 // Note that settings will be a reference and can be modified
811 var settings = this.basefogg_getEncoderSettings();
812
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 },
818
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;
825
826 // Call the parent
827 _this.basefogg_updateSourceFileUI();
828
829 var settings = this.getEncoderSettings();
830 var fileInfo = this.getSourceFileInfo();
831
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 }
839
840 // Show encoder controls
841 $j( this.target_control_container ).show( 'slow' );
842 $j( this.target_passthrough_mode ).hide( 'slow' );
843
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 }
907
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 );
915
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 },
925
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 },
932
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 );
941
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 },
949
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 },
967
968 /**
969 * Clear preset settings
970 * FIXME: not called, does nothing
971 */
972 clearSettings: function( force ) {
973 },
974
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 }
981 };