some firefogg fixes for target form selection
[lhc/web/wiklou.git] / js2 / mwEmbed / libAddMedia / mvFirefogg.js
1 /* adds firefogg support.
2 * autodetects: new upload api or old http POST.
3 */
4
5 loadGM({
6 "fogg-select_file" : "Select File",
7 "fogg-select_new_file" : "Select New File",
8 "fogg-save_local_file" : "Save Ogg",
9 "fogg-check_for_fogg" : "Checking for Firefogg <blink>...</blink>",
10 "fogg-installed" : "Firefogg is Installed",
11 "fogg-for_improved_uplods" : "For Improved uploads: ",
12 "fogg-please_install" : "<a href=\"$1\">Install Firefogg</a>. More <a href=\"http://commons.wikimedia.org/wiki/Commons:Firefogg\">about firefogg</a>",
13 "fogg-use_latest_fox" : "Please first install <a href=\"http://www.mozilla.com/en-US/firefox/upgrade.html?from=firefogg\">Firefox 3.5</a> (or later). <i>then revisit this page to install the <b>firefogg</b> extention</i>",
14 "fogg-passthrough_mode" : "Your selected file is already ogg or not a video file",
15 "fogg-transcoding" : "Encoding Video to Ogg",
16 "fogg-encoding-done" : "Encoding Done"
17 });
18
19 var firefogg_install_links = {
20 'macosx': 'http://firefogg.org/macosx/Firefogg.xpi',
21 'win32' : 'http://firefogg.org/win32/Firefogg.xpi',
22 'linux' : 'http://firefogg.org/linux/Firefogg.xpi'
23 };
24
25 var default_firefogg_options = {
26 //what to do when finished uploading
27 'upload_done_action':'redirect',
28 //if firefoog is enabled
29 'fogg_enabled':false,
30 //the api url to upload to
31 'api_url':null,
32 //the passthrough flag (enables un-modified uploads)
33 'passthrough': false,
34 //if we will be showing the encoder interface
35 'encoder_interface': false,
36 //if we want to limit the library functionality to "only firefoog" (no upload or progress bars)
37 'only_fogg': false,
38
39
40 //callbacks:
41 'new_source_cb': false, //called on source name update passes along source name
42
43 //target control container or form (can't be left null)
44 'selector' : '',
45
46 //if not rewriting a form we are encoding local.
47 'form_rewrite' : false,
48
49 //taget buttons:
50 'target_btn_select_file' : false,
51 'target_btn_select_new_file': false,
52 'target_input_file_name' : false,
53 'target_btn_save_local_file': false,
54
55
56 //target install descriptions
57 'target_check_for_fogg' : false,
58 'target_installed' : false,
59 'target_please_install' : false,
60 'target_use_latest_fox': false,
61 //status:
62 'target_passthrough_mode':false,
63
64 //if firefogg should take over the form submit action
65 'firefogg_form_action':true
66 }
67
68
69 var mvFirefogg = function(iObj){
70 return this.init( iObj );
71 }
72 mvFirefogg.prototype = { //extends mvBaseUploadInterface
73
74 min_firefogg_version : '0.9.9',
75 fogg_enabled : false, //if firefogg is enabled or not.
76 encoder_settings:{ //@@todo allow server to set this
77 'maxSize': 400,
78 'videoBitrate': 400,
79 'noUpscaling':true
80 },
81 sourceFileInfo:{},
82 ogg_extensions: ['ogg', 'ogv', 'oga'],
83 video_extensions: ['avi', 'mov', 'mp4', 'mp2', 'mpeg', 'mpeg2', 'mpeg4', 'dv', 'wmv'],
84
85 passthrough: false,
86
87 init: function( iObj ){
88 if(!iObj)
89 iObj = {};
90
91 //if we have no api_url set upload to "post"
92 if(!iObj.api_url)
93 iObj.upload_mode = 'post';
94
95 //inherit iObj properties:
96 for(var i in default_firefogg_options){
97 if(iObj[i]){
98 this[i] = iObj[i];
99 }else{
100 this[i] = default_firefogg_options[i];
101 }
102 }
103 //check if we want to limit the usage:
104 if(!this.only_fogg){
105 var myBUI = new mvBaseUploadInterface( iObj );
106
107 //standard extends code:
108 for(var i in myBUI){
109 if(this[i]){
110 this['pe_'+ i] = myBUI[i];
111 }else{
112 this[i] = myBUI[i];
113 }
114 }
115 }
116
117 if(!this.selector){
118 js_log('firefogg: missing selector ');
119 }
120 },
121 doRewrite:function( callback ){
122 var _this = this;
123 js_log('sel len: ' + this.selector + '::' + $j(this.selector).length + ' tag:'+ $j(this.selector).get(0).tagName);
124 if( $j(this.selector).length >=0 ){
125
126 if( $j(this.selector).get(0).tagName.toLowerCase() == 'input' ){
127 _this.form_rewrite = true;
128 }
129 }
130 //check if we are rewriting an input or a form:
131 if( this.form_rewrite ){
132 this.setupForm();
133 }else{
134 this.doControlHTML();
135 this.doControlBindings();
136 }
137
138 //doRewrite is done:
139 if(callback)
140 callback();
141 },
142 doControlHTML: function( ){
143 var _this = this;
144 var out = '';
145 $j.each(default_firefogg_options, function(target, na){
146 if(target.substring(0, 6)=='target'){
147 //check for the target if missing add to the output:
148 if( _this[target] === false){
149 out+= _this.getTargetHtml(target) + ' ';
150 //update the target selector
151 _this[target] = _this.selector + ' .' + target;
152 }
153 }
154 });
155 $j( this.selector ).append( out ).hide();
156 },
157 getTargetHtml:function(target){
158 if( target.substr(7,3)=='btn'){
159 return '<input style="" class="' + target + '" type="button" value="' + gM( 'fogg-' + target.substring(11)) + '"/> ';
160 }else if(target.substr(7,5)=='input'){
161 return '<input style="" class="' + target + '" type="text" value="' + gM( 'fogg-' + target.substring(11)) + '"/> ';
162 }else{
163 return '<div style="" class="' + target + '" >'+ gM('fogg-'+ target.substring(7)) + '</div> ';
164 }
165 },
166 doControlBindings: function(){
167 var _this = this;
168
169 //hide all targets:
170 var hide_target_list='';
171 var coma='';
172 $j.each(default_firefogg_options, function(target, na){
173 if(target.substring(0, 6)=='target'){
174 hide_target_list+=coma + _this[target];
175 coma=',';
176 }
177 });
178 $j( hide_target_list ).hide();
179 //now that the proper set of items has been hiiden show:
180 $j( this.selector ).show();
181
182
183 //hide all but check-for-fogg
184 //check for firefogg
185 if( _this.firefoggCheck() ){
186
187 //if rewriting the form lets keep the text input around:
188 if( _this.form_rewrite )
189 $j(this.target_input_file_name).show();
190
191 //show select file:
192 $j( this.target_btn_select_file ).unbind(
193 ).attr('disabled', false
194 ).css({'display':'inline'}
195 ).click(function(){
196 _this.selectFogg();
197 });
198 //also setup the text file display on Click to select file:
199 $j(this.target_input_file_name).unbind().attr('readonly', 'readonly').click(function(){
200 _this.selectFogg();
201 })
202
203 }else{
204 //first check firefox version:
205 if(!( $j.browser.mozilla && $j.browser.version >= '1.9.1' )) {
206 js_log( 'show use latest::' + _this.target_use_latest_fox );
207 if( _this.target_use_latest_fox ){
208 if( _this.form_rewrite )
209 $j( _this.target_use_latest_fox ).prepend( gM('fogg-for_improved_uplods') );
210
211 $j( _this.target_use_latest_fox ).show();
212 }
213 return ;
214 }
215 //they have the right version of mozilla provide install link:
216 var os_link = false;
217 if(navigator.oscpu){
218 if(navigator.oscpu.search('Linux') >= 0)
219 os_link = firefogg_install_links['linux'];
220 else if(navigator.oscpu.search('Mac') >= 0)
221 os_link = firefogg_install_links['macosx'];
222 else if(navigator.oscpu.search('Win') >= 0)
223 os_link = firefogg_install_links['win32'];
224 }
225 //if rewriting form use upload msg text
226 var upMsg = (_this.form_rewrite) ? gM('fogg-for_improved_uplods') : '';
227 $j(_this.target_please_install).html( upMsg + gM('fogg-please_install',os_link )).css('padding', '10px').show();
228 }
229 //setup the target save local file bindins:
230 $j( _this.target_btn_save_local_file ).unbind().click(function(){
231 _this.saveLocalFogg();
232 });
233 },
234 firefoggCheck:function(){
235 if(typeof(Firefogg) != 'undefined' && Firefogg().version >= this.min_firefogg_version){
236 this.fogg = new Firefogg();
237 this.fogg_enabled = true;
238 return true;
239 }else{
240 return false;
241 }
242 },
243 //assume input target
244 setupForm: function(){
245 js_log('firefogg::setupForm::');
246 //to parent form setup if we want http updates
247 if( this.form_rewrite ){
248 //do parent form setup:
249 this.pe_setupForm();
250 }
251
252 //check if we have firefogg (if not just add a link and stop proccessing)
253 if( !this.firefoggCheck() ){
254 //add some status indicators if not provided:
255 if(!this.target_please_install){
256 $j(this.selector).after ( this.getTargetHtml('target_please_install') );
257 this.target_please_install = this.selector + ' ~ .target_please_install';
258 }
259 if(!this.target_use_latest_fox){
260 $j(this.selector).after ( this.getTargetHtml('target_use_latest_fox') );
261 this.target_use_latest_fox = this.selector + ' ~ .target_use_latest_fox';
262 }
263 //update download link:
264 this.doControlBindings();
265 return ;
266 }
267
268 //change the file browser to type text: (can't directly change input from "file" to "text" so longer way:
269 var inTag = '<input ';
270 $j.each($j(this.selector).get(0).attributes, function(i, attr){
271 var val = attr.value;
272 if( attr.name == 'type')
273 val = 'text';
274 inTag += attr.name + '="' + val + '" ';
275 });
276 if(!$j(this.selector).attr('style'))
277 inTag += 'style="display:inline" ';
278
279 inTag+= '/><span id="' + $j(this.selector).attr('name') + '_fogg-control"></span>';
280
281 js_log('set input: ' + inTag);
282 $j(this.selector).replaceWith(inTag);
283
284 this.target_input_file_name = 'input[name=' + $j(this.selector).attr('name') + ']';
285 //update the selector to the control target:
286 this.selector = '#' + $j(this.selector).attr('name') + "_fogg-control";
287
288 this.doControlHTML();
289 //check for the other inline status indicator targets:
290
291 //update the bindings:
292 this.doControlBindings();
293 },
294 getEditForm:function(){
295 if( this.target_edit_from )
296 return this.pe_getEditForm();
297 //else try to get the parent "from" of the file selector:
298 return $j(this.selector).parents('form:first').get(0);
299 },
300 selectFogg:function(){
301 var _this = this;
302 if( _this.fogg.selectVideo() ) {
303 js_log('videoSelectReady');
304 //if not already hidden hide select file and show "select new":
305 $j(_this.target_btn_select_file).hide();
306 //show and setup binding for select new file:
307 $j(_this.target_btn_select_new_file).show().unbind().click(function(){
308 //create new fogg instance:
309 _this.fogg = new Firefogg();
310 _this.selectFogg();
311 });
312 //update if we are in passthrough mode or going to encode
313 if( _this.fogg.sourceInfo && _this.fogg.sourceFilename ){
314 //update the source status
315 try{
316 _this.sourceFileInfo = JSON.parse( _this.fogg.sourceInfo ) ;
317 }catch (e){
318 js_error('error could not parse fogg sourceInfo');
319 }
320
321 //now setup encoder settings based source type:
322 _this.autoEncoderSettings();
323
324 //if set to passthough update the interface:
325 if(_this.encoder_settings['passthrough']==true){
326 $j(_this.target_passthrough_mode).show();
327 }else{
328 $j(_this.target_passthrough_mode).hide();
329 //if set to encoder expose the encode button:
330 if( !_this.form_rewrite ){
331 $j(_this.target_btn_save_local_file).show();
332 }
333 }
334 //~otherwise the encoding will be triggered by the form~
335
336 //do source name update callback:
337 js_log(" should update: " + _this.target_input_file_name + ' to: ' + _this.fogg.sourceFilename );
338 $j(_this.target_input_file_name).val(_this.fogg.sourceFilename).show();
339
340 if(_this.new_source_cb){
341 if(_this.encoder_settings['passthrough']){
342 var fName = _this.fogg.sourceFilename
343 }else{
344 var oggExt = (_this.isSourceAudio())?'oga':'ogg';
345 oggExt = (_this.isSourceVideo())?'ogv':oggExt;
346 oggExt = (_this.isUnknown())?'ogg':oggExt;
347 oggName = _this.fogg.sourceFilename.substr(0,
348 _this.fogg.sourceFilename.lastIndexOf('.'));
349 var fName = oggName +'.'+ oggExt
350 }
351 _this.new_source_cb( _this.fogg.sourceFilename , fName);
352 }
353 }
354 }else{
355 //js_error("Firefogg error selecting file");
356 }
357 },
358 saveLocalFogg:function(){
359 //request target location:
360 if(this.fogg){
361 if(!this.fogg.saveVideoAs() )
362 return false;
363
364 //we have set a target now call the encode:
365 this.doEncode();
366 }
367 },
368 //simple auto encoder settings just enable passthough if file is not video or > 480 pixles tall
369 autoEncoderSettings:function(){
370 var _this = this;
371 //grab the extension:
372 var sf = _this.fogg.sourceFilename;
373 var ext = '';
374 if( sf.lastIndexOf('.') != -1){
375 ext = sf.substring( sf.lastIndexOf('.')+1 ).toLowerCase();
376 }
377
378 //set to passthrough to true by default (images, arbitrary files that we want to send with http chunks)
379 this.encoder_settings['passthrough'] = true;
380
381 //see if we have video or audio:
382 if( _this.isSourceAudio() || _this.isSourceVideo() ){
383 _this.encoder_settings['passthrough'] = false;
384 }
385
386 //special case see if we already have ogg video:
387 if( _this.isOggFormat() ){
388 _this.encoder_settings['passthrough'] = true;
389 }
390
391 js_log('base autoEncoderSettings::' + _this.sourceFileInfo.contentType + ' passthrough:' + _this.encoder_settings['passthrough']);
392 },
393 isUnknown:function(){
394 return (this.sourceFileInfo.contentType.indexOf("unknown") != -1);
395 },
396 isSourceAudio:function(){
397 return (this.sourceFileInfo.contentType.indexOf("audio/") != -1);
398 },
399 isSourceVideo:function(){
400 return (this.sourceFileInfo.contentType.indexOf("video/") != -1);
401 },
402 isOggFormat:function(){
403 return ( this.sourceFileInfo.contentType.indexOf("video/ogg") != -1 ||
404 this.sourceFileInfo.contentType.indexOf("application/ogg") != -1 );
405 },
406 getProgressTitle:function(){
407 js_log("fogg:getProgressTitle f:" + this.fogg_enabled + ' rw:' + this.form_rewrite);
408 //return the parent if we don't have fogg turned on:
409 if(! this.fogg_enabled || !this.firefogg_form_action )
410 return this.pe_getProgressTitle();
411 if( !this.form_rewrite )
412 return gM('fogg-transcoding');
413 //else return our upload+transcode msg:
414 return gM('upload-transcode-in-progress');
415 },
416 doUploadSwitch:function(){
417 var _this = this;
418 js_log("firefogg: doUploadSwitch:: " + this.fogg_enabled + ' up mode:' + _this.upload_mode);
419 //make sure firefogg is enabled otherwise do parent UploadSwich:
420 if( !this.fogg_enabled || !this.firefogg_form_action )
421 return _this.pe_doUploadSwitch();
422
423 //check what mode to use firefogg in:
424 if( _this.upload_mode == 'post' ){
425 _this.doEncode();
426 }else if( _this.upload_mode == 'api' && _this.chunks_supported){ //if api mode and chunks supported do chunkUpload
427 _this.doChunkUpload();
428 }else{
429 js_error( 'Error: unrecongized upload mode: ' + _this.upload_mode );
430 }
431 },
432 //doChunkUpload does both uploading and encoding at the same time and uploads one meg chunks as they are ready
433 doChunkUpload : function(){
434 js_log('doChunkUpload::');
435 var _this = this;
436 _this.action_done = false;
437 //extension should already be ogg but since its user editable,
438 //check again
439 //we are transcoding so we know it will be an ogg
440 //(should not be done for passthrough mode)
441 var sf = _this.formData['wpDestFile'];
442 var ext = '';
443 if( sf.lastIndexOf('.') != -1){
444 ext = sf.substring( sf.lastIndexOf('.') ).toLowerCase();
445 }
446 if(!_this.encoder_settings['passthrough'] && $j.inArray(ext.substr(1), _this.ogg_extensions) == -1 ){
447 var extreg = new RegExp(ext + '$', 'i');
448 _this.formData['wpDestFile'] = sf.replace(extreg, '.ogg');
449 }
450 //add chunk response hook to build the resultURL when uploading chunks
451
452 //build the api url:
453 var aReq ={
454 'action' : 'upload',
455 'format' : 'json',
456 'filename' : _this.formData['wpDestFile'],
457 'comment' : _this.formData['wpUploadDescription'],
458 'enablechunks' : 'true'
459 };
460 //check for editToken:
461 if(!this.etoken)
462 this.etoken = _this.formData['wpEditToken'];
463
464 if( this.etoken )
465 aReq['token'] = this.etoken;
466
467 if( _this.formData['wpWatchthis'] )
468 aReq['watch'] = _this.formData['wpWatchthis'];
469
470 if( _this.formData['wpIgnoreWarning'] )
471 aReq['ignorewarnings'] = _this.formData['wpIgnoreWarning'];
472
473 js_log('do fogg upload/encode call: '+ _this.api_url + ' :: ' + JSON.stringify( aReq ) );
474 js_log('foggEncode: '+ JSON.stringify( _this.encoder_settings ) );
475 _this.fogg.upload( JSON.stringify( _this.encoder_settings ), _this.api_url , JSON.stringify( aReq ) );
476
477 //update upload status:
478 _this.doUploadStatus();
479 },
480 //doEncode and monitor progress:
481 doEncode : function(){
482 var _this = this;
483 _this.action_done = false;
484 _this.dispProgressOverlay();
485 js_log('doEncode: with: ' + JSON.stringify( _this.encoder_settings ) );
486 _this.fogg.encode( JSON.stringify( _this.encoder_settings ) );
487
488
489 //show transcode status:
490 $j('#up-status-state').html( gM('upload-transcoded-status') );
491
492 //setup a local function for timed callback:
493 var encodingStatus = function() {
494 var status = _this.fogg.status();
495
496 //update progress bar
497 _this.updateProgress( _this.fogg.progress() );
498
499 //loop to get new status if still encoding
500 if( _this.fogg.state == 'encoding' ) {
501 setTimeout(encodingStatus, 500);
502 }else if ( _this.fogg.state == 'encoding done' ) { //encoding done, state can also be 'encoding failed
503 _this.encodeDone();
504 }else if(_this.fogg.state == 'encoding fail'){
505 //@@todo error handling:
506 js_error('encoding failed');
507 }
508 }
509 encodingStatus();
510 },
511 encodeDone:function(){
512 var _this = this;
513 js_log('::encodeDone::');
514 _this.action_done = true;
515 //send to the post url:
516 if( _this.form_rewrite && _this.upload_mode == 'post' ){
517 js_log('done with encoding do POST upload:' + _this.editForm.action);
518 // ignore warnings & set source type
519 //_this.formData[ 'wpIgnoreWarning' ]='true';
520 _this.formData[ 'wpSourceType' ] = 'upload';
521 _this.formData[ 'action' ] = 'submit';
522 //wpUploadFile is set by firefogg
523 delete _this.formData[ 'wpUploadFile' ];
524
525 _this.fogg.post( _this.editForm.action, 'wpUploadFile', JSON.stringify( _this.formData ) );
526 //update upload status:
527 _this.doUploadStatus();
528 }else{
529 js_log("done with encoding (no upload) ");
530 //set stuats to 100% for one second:
531 _this.updateProgress( 1 );
532 setTimeout(function(){
533 _this.updateProgressWin(gM('fogg-encoding-done'), gM('fogg-encoding-done'));
534 }, 1000);
535 }
536 },
537 doUploadStatus:function() {
538 var _this = this;
539 $j('#up-status-state').html( gM('uploaded-status') );
540
541 _this.oldResponseText = '';
542 //setup a local function for timed callback:
543 var uploadStatus = function(){
544 //get the response text:
545 var response_text = _this.fogg.responseText;
546 if(!response_text){
547 try{
548 var pstatus = JSON.parse( _this.fogg.uploadstatus() );
549 response_text = pstatus["responseText"];
550 }catch(e){
551 js_log("could not parse uploadstatus / could not get responseText");
552 }
553 }
554
555 if( _this.oldResponseText != response_text){
556 js_log('new result text:' + response_text + ' state:' + _this.fogg.state);
557 _this.oldResponseText = response_text;
558 //try and parse the response text and check for errors
559 try{
560 var apiResult = JSON.parse( response_text );
561 }catch(e){
562 js_log("could not parse response_text::" + response_text + ' ...for now try with eval...');
563 try{
564 var apiResult = eval( response_text );
565 }catch(e){
566 var apiResult = null;
567 }
568 }
569 if(apiResult && _this.apiUpdateErrorCheck( apiResult ) === false){
570 //stop status update we have an error
571 _this.action_done = true;
572 _this.fogg.cancel();
573 return false;
574 }
575 }
576 //update progress bar
577 _this.updateProgress( _this.fogg.progress() );
578
579 //loop to get new status if still uploading (could also be encoding if we are in chunk upload mode)
580 if( _this.fogg.state == 'encoding' || _this.fogg.state == 'uploading') {
581 setTimeout(uploadStatus, 100);
582
583 }//check upload state
584 else if( _this.fogg.state == 'upload done' ||
585 _this.fogg.state == 'done' ||
586 _this.fogg.state == 'encoding done' ) {
587 js_log( 'firefogg:upload done: ');
588 //if in "post" upload mode read the html response (should be depricated):
589 if( _this.upload_mode == 'post' && _this.api_url ) {
590 _this.procPageResponse( response_text );
591 }else if( _this.upload_mode == 'api'){
592 if( _this.fogg.resultUrl ){
593 var buttons ={};
594 buttons[gM('go-to-resource')] = function(){
595 window.location = _this.fogg.resultUrl;
596 }
597 var go_to_url_txt = gM('go-to-resource');
598 //should have an json result:
599 _this.updateProgressWin( gM('successfulupload'), gM( 'mv_upload_done', _this.fogg.resultUrl),buttons);
600 }else{
601 //done state with error? ..not really possible given how firefogg works
602 js_log(" upload done, in chunks mode, but no resultUrl!");
603 }
604 }
605 }else{
606 //upload error:
607 js_log('Error:firefogg upload error: ' + _this.fogg.state );
608 }
609 }
610 uploadStatus();
611 },
612 cancel_action:function( dlElm ){
613 if(!this.fogg_enabled){
614 return this.pe_cancel_action();
615 }
616 js_log('firefogg:cancel')
617 if( confirm( gM('mv-cancel-confim') )){
618 if(navigator.oscpu && navigator.oscpu.search('Win') >= 0){
619 alert( 'sorry we do not yet support cancel on windows' );
620 }else{
621 this.action_done = true;
622 this.fogg.cancel();
623 $j(dlElm).dialog('close');
624 }
625 } else{
626 return false;
627 }
628 },
629 /**
630 * procPageResponse should be faded out in favor of the upload api soon..
631 * its all very fragile to read the html output and guess at stuff
632 */
633 procPageResponse:function( result_page ){
634 var _this = this;
635 js_log('f:procPageResponse');
636 var sstring = 'var wgTitle = "' + this.formData['wpDestFile'].replace('_',' ');
637
638 if(wgArticlePath){
639 var result_txt = gM('mv_upload_done', wgArticlePath.replace(/\$1/, 'File:' + _this.formData['wpDestFile'] ) );
640 }else{
641 result_txt = 'File has uploaded but api "done" url was provided. Check the log for result page output';
642 }
643
644 //set the error text in case we dont' get far along in processing the response
645 _this.updateProgressWin( gM('mv_upload_completed'), result_txt );
646
647 if( result_page && result_page.toLowerCase().indexOf( sstring.toLowerCase() ) != -1){
648 js_log( 'upload done got redirect found: ' + sstring + ' r:' + _this.upload_done_action );
649 if( _this.upload_done_action == 'redirect' ){
650 $j( '#dlbox-centered' ).html( '<h3>Upload Completed:</h3>' + result_txt + '<br>' + form_txt);
651 window.location = wgArticlePath.replace( /\$1/, 'File:' + _this.formData['wpDestFile'] );
652 }else{
653 //check if the add_done_action is a callback:
654 if( typeof _this.upload_done_action == 'function' )
655 _this.upload_done_action();
656 }
657 }else{
658 //js_log( 'upload page error: did not find: ' +sstring + ' in ' + "\n" + result_page );
659 var form_txt = '';
660 if( !result_page ){
661 //@@todo fix this:
662 //the mediaWiki upload system does not have an API so we can\'t read errors
663 }else{
664 var res = grabWikiFormError( result_page );
665
666 if(res.error_txt)
667 result_txt = res.error_txt;
668
669 if(res.form_txt)
670 form_txt = res.form_txt;
671 }
672 js_log( 'error text is: ' + result_txt );
673 $j( '#dlbox-centered' ).html( '<h3>' + gM('mv_upload_completed') + '</h3>' + result_txt + '<br>' + form_txt);
674 }
675 }
676 };