From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.

/*

* ===============================================================

*                    FileUploadWizard.js

* Script for uploading files through a dynamic questionnaire.

* This is the code to accompany [[Wikipedia:File Upload Wizard]].

* ===============================================================

*/



var fuwTesting = false;

var fuwDefaultTextboxLength = 60;

var fuwDefaultTextareaWidth = '90%';

var fuwDefaultTextareaLines = 3;



// ================================================================

// Constructor function of global fuw (= File Upload Wizard) object

// ================================================================

function fuwGlobal() {



   // see if user is logged in, autoconfirmed, experienced etc.

   this.getUserStatus();



   fuwSetVisible('warningLoggedOut', (this.userStatus == 'anon'));

   fuwSetVisible('warningNotConfirmed', (this.userStatus == 'notAutoconfirmed'));

   this.disabled = (this.userStatus == 'anon') || (this.userStatus == 'notAutoconfirmed');

   if (this.disabled) {

      return;

   }

   fuwSetVisible('fuwStartScriptLink', false);



   // create the form element to wrap the main ScriptForm area

   // containing input elements of Step2 and Step3

   var frm = fuwGet('fuwScriptForm');

   if (! frm) {

      frm = document.createElement('form');

      frm.id = "fuwScriptForm";

      var area = fuwGet('placeholderScriptForm');

      var parent = area.parentNode;

      parent.insertBefore(frm, area);

      parent.removeChild(area);

      frm.appendChild(area);

   }

   this.ScriptForm = frm;



   // create the TargetForm element that contains the file selector

   frm = fuwGet('TargetForm');

   if (! frm) {

      frm = document.createElement('form');

      frm.id = "TargetForm";

      var area = fuwGet('placeholderTargetForm');

      var parent = area.parentNode;

      parent.insertBefore(frm, area);

      parent.removeChild(area);

      frm.appendChild(area);

   }

   this.TargetForm = frm;



   // For the testing version, create a third form that will display

   // the contents to be submitted, at the bottom of the page

   if (fuwTesting) {

      frm = fuwGet('fuwTestForm');

      if (! frm) {

         frm = document.createElement('form');

         frm.id = "fuwTestForm";

         var area = fuwGet('placeholderTestForm');

         var parent = area.parentNode;

         parent.insertBefore(frm, area);

         parent.removeChild(area);

         frm.appendChild(area);

      }

      this.TestForm = frm;

   }



   // objects to hold cached results during validation and processing

   this.opts = { };

   this.warn = { };



   // create the input filename box

   var filebox  = document.createElement('input');

   filebox.id   = 'file';

   filebox.name = 'file';

   filebox.type = 'file';

   filebox.size = fuwDefaultTextboxLength;

   filebox.onchange = fuwValidateFile;

   filebox.accept = 'image/png,image/jpeg,image/gif,image/svg+xml,image/tiff,image/x-xcf,application/pdf,image/vnd.djvu,audio/ogg,video/ogg,audio/rtp-midi,audio/mp3,image/webp,video/webm,audio/opus,video/mpeg,audio/wav,audio/flac';

   fuwAppendInput('file', filebox);



   // Default values for API call parameters

   this.UploadOptions = {

      filename : '',

      text     : '',

      comment  : '',

      ignorewarnings : 1,

      watch    : 1

   };



   if (fuwTesting) {

      fuwMakeHiddenfield('title', mw.config.get('wgPageName') + "/sandbox", 'SandboxTitle');

      fuwMakeHiddenfield('token', mw.user.tokens.get('csrfToken'), 'SandboxToken');

      fuwMakeHiddenfield('recreate', 1, 'SandboxRecreate');

   }



   if (fuwTesting) {



      // create the sandbox submit button

      btn = document.createElement('input');

      btn.id = 'SandboxButton';

      btn.value = 'Sandbox';

      btn.name  = 'Sandbox';

      btn.disabled = true;

      btn.type = 'button';

      btn.style.width = '12em';

      btn.onclick = fuwSubmitSandbox;

      fuwAppendInput('SandboxButton', btn);



   }



   // create the real submit button

   btn = document.createElement('input');

   btn.id = "SubmitButton";

   btn.value = "Upload";

   btn.name = "Upload";

   btn.disabled = true;

   btn.type = "button";

   btn.onclick = fuwSubmitUpload;

   btn.style.width = '12em';

   fuwAppendInput('SubmitButton', btn);



   // create the Commons submit button

   btn = document.createElement('input');

   btn.id = "CommonsButton";

   btn.value = "Upload on Commons";

   btn.name  = "Upload_on_Commons";

   btn.disabled = true;

   btn.type = "button";

   btn.onclick = fuwSubmitCommons;

   btn.style.width = '12em';

   fuwAppendInput('CommonsButton', btn);



   // create reset buttons

   for (i = 1; i<=2; i++) {

      btn = document.createElement('input');

      btn.id = 'ResetButton' + i;

      btn.value = "Reset form";

      btn.name  = "Reset form";

      btn.type  = "button";

      btn.onclick = fuwReset;

      btn.style.width = '12em';

      fuwAppendInput('ResetButton' + i, btn);

   }



   // names of radio button fields

   var optionRadioButtons = {

      // top-level copyright status choice

      'FreeOrNonFree' : 'OptionFree','OptionNonFree','OptionNoGood'],

      // main subsections under OptionFree

      'FreeOptions'   : 'OptionOwnWork', 'OptionThirdParty', 'OptionFreeWebsite',

                         'OptionPDOld', 'OptionPDOther'],

      // main subsections under OptionNonFree

      'NonFreeOptions': 'OptionNFSubject','OptionNF3D','OptionNFExcerpt',

                         'OptionNFCover','OptionNFLogo','OptionNFPortrait',

                         'OptionNFMisc'],

      // response options inside warningFileExists

      'FileExistsOptions': 

                        'NoOverwrite','OverwriteSame','OverwriteDifferent'],

      // choice of evidence in OptionThirdParty subsection

      'ThirdPartyEvidenceOptions' : 

                        'ThirdPartyEvidenceOptionLink',

                         'ThirdPartyEvidenceOptionOTRS',

                         'ThirdPartyEvidenceOptionOTRSForthcoming',

                         'ThirdPartyEvidenceOptionNone'],

      // choice of PD status in OptionPDOld subsection

      'PDOldOptions'  : 'PDUSExpired','PDURAA','PDFormality','PDOldOther'],

      // choice of PD status in OptionPDOther subsection

      'PDOtherOptions': 'PDOtherUSGov','PDOtherOfficial','PDOtherSimple',

                         'PDOtherOther'],

      // whether target article is wholly or only partly dedicated to discussing non-free work:

      'NFSubjectCheck': 'NFSubjectCheckDedicated','NFSubjectCheckDiscussed'],

      'NF3DCheck'     : 'NF3DCheckDedicated','NF3DCheckDiscussed'],

      // choice about copyright status of photograph in OptionNF3D

      'NF3DOptions'   : 'NF3DOptionFree','NF3DOptionSame'

   };

   for (var group in optionRadioButtons) {

      var op = optionRadioButtonsgroup];

      for (i=0; i<op.length; i++) {

         fuwMakeRadiobutton(group, opi]);

      }

   }

   this.ScriptForm.NoOverwrite.checked = true;

   

   // input fields that trigger special

   // onchange() event handlers for validation:

   fuwMakeTextfield('InputName', fuwValidateFilename);

   fuwMakeTextfield('NFArticle', fuwValidateNFArticle);



   // names of input fields that trigger normal

   // validation event handler

   var activeTextfields = 

      'Artist3D','Country3D',

      'Date','OwnWorkCreation','OwnWorkPublication',

      'Author','Source',

      'Permission','ThirdPartyOtherLicense',

      'ThirdPartyEvidenceLink','ThirdPartyOTRSTicket',

      'FreeWebsiteOtherLicense',

      'PDOldAuthorLifetime','Publication',

      'PDOldCountry','PDOldPermission',

      'PDOfficialPermission','PDOtherPermission',

      'NFSubjectPurpose', 'NF3DOrigDate', 'NF3DPurpose',

      'NF3DCreator',

      'NFPortraitDeceased',

      'EditSummary'

   ];

   for (i=0; i<activeTextfields.length; i++) {

      fuwMakeTextfield(activeTextfieldsi]);

   }



   // names of multiline textareas

   var activeTextareas = 

      'InputDesc','NF3DPermission',

      'NFCommercial','NFPurpose','NFReplaceableText',

      'NFReplaceable','NFCommercial','NFMinimality','AnyOther'

   ];

   for (i=0; i<activeTextareas.length; i++) {

      fuwMakeTextarea(activeTextareasi]);

   };



   var checkboxes = 

      'NFCoverCheckDedicated','NFLogoCheckDedicated','NFPortraitCheckDedicated'

   ];

   for (i=0; i<checkboxes.length; i++) {

      fuwMakeCheckbox(checkboxesi]);

   };



   var licenseLists = {

      'OwnWorkLicense' : 

        // array structure as expected for input to fuwMakeSelection() function.

        // any entry that is a two-element array will be turned into an option

        // (first element is the value, second element is the display string).

        // Entries that are one-element arrays will be the label of an option group.

        // Zero-element arrays mark the end of an option group.

        

        'Allow all use as long as others credit you and share it under similar conditions'],

        'self|GFDL|cc-by-sa-4.0|migration=redundant', 

         'Creative Commons Attribution-Share Alike 4.0 + GFDL (recommended)',

         true],

        'self|cc-by-sa-4.0',

         'Creative Commons Attribution-Share Alike 4.0'],

        [],

        'Allow all use as long as others credit you'],

        'self|cc-by-4.0',

         'Creative Commons Attribution 4.0'],

        [],

        'Reserve no rights'],

        'self|cc0',

         'CC0 Universal Public Domain Dedication'],

        []

        ],   

      'ThirdPartyLicense' :

        

        '', 'please select the correct license...'],

        'Freely licensed:'],

        'cc-by-sa-4.0', 'Creative Commons Attribution-Share Alike (cc-by-sa-4.0)'],

        'cc-by-4.0', 'Creative Commons Attribution (cc-by-4.0)'],

        'GFDL', 'GNU Free Documentation License (GFDL)'],

        [],

        'No rights reserved:'],

        'PD-author', 'Public domain'],

        [],

        'Other (see below)'],

        []

        ],   

      'FreeWebsiteLicense' :

        

        '', 'please select the correct license...'],

        'Freely licensed:'],

        'cc-by-sa-4.0', 'Creative Commons Attribution-Share Alike (cc-by-sa-4.0)'],

        'cc-by-4.0', 'Creative Commons Attribution (cc-by-4.0)'],

        'GFDL', 'GNU Free Documentation License (GFDL)'],

        [],

        'No rights reserved:'],

        'PD-author', 'Public domain'],

        [],

        'Other (see below)'],

        []

        ],   

      'USGovLicense' :

       

       'PD-USGov', 'US Federal Government'],

       'PD-USGov-NASA','NASA'],

       'PD-USGov-Military-Navy','US Navy'],

       'PD-USGov-NOAA','US National Oceanic and Atmospheric Administration'],

       'PD-USGov-Military-Air_Force','US Air Force'],

       'PD-USGov-Military-Army','US Army'],

       'PD-USGov-USGS','United States Geological Survey'

       ],

      'IneligibleLicense' :

       

       '', 'please select one...'],

       'PD-shape','Item consists solely of simple geometric shapes'],

       'PD-text','Item consists solely of a few individual words or letters'],

       'PD-textlogo','Logo or similar item consisting solely of letters and simple geometric shapes'],

       'PD-chem','Chemical structural formula'],

       'PD-ineligible','Other kind of item that contains no original authorship'

       ],

      'NFSubjectLicense' :

       

       '', 'please select one...'],

       'Non-free 2D art', '2-dimensional artwork (painting, drawing etc.)'], 

       'Non-free historic image', 'Unique historic photograph'], 

       'Non-free fair use in', 'something else (please describe in description field on top)'

       ],

      'NF3DLicense' :

       

       [, 'please select one...'],

       'Non-free proposed architecture', 'Architectural proposal (not yet completed)'],

       'Non-free destroyed architecture', 'Destroyed (or unrecognizably altered) architecture'],

       'Non-free 3D art', 'Other 3-dimensional creative work (sculpture etc.)'

       ],

      'NFCoverLicense' :

         

         '', 'please select one...'],

         'Non-free book cover', 'Cover page of a book'], 

         'Non-free album cover', 'Cover of a sound recording (album, single, song, CD)'], 

         'Non-free game cover', 'Cover of a video/computer game'], 

         'Non-free magazine cover', 'Cover page of a magazine'], 

         'Non-free video cover', 'Cover of a video'], 

         'Non-free software cover', 'Cover of a software product'], 

         'Non-free product cover', 'Cover of some commercial product'], 

         'Non-free title-card', 'Title screen of a TV programme'], 

         'Non-free movie poster', 'Movie poster'], 

         'Non-free poster', 'Official poster of an event'], 

         'Non-free fair use in', 'something else (please describe in description field on top)'

         ],

      'NFExcerptLicense' :

         

         '', 'please select one...'],

         'Non-free television screenshot', 'Television screenshot'], 

         'Non-free film screenshot', 'Movie screenshot'], 

         'Non-free game screenshot', 'Game screenshot'], 

         'Non-free video screenshot', 'Video screenshot'], 

         'Non-free music video screenshot', 'Music video screenshot'], 

         'Non-free software screenshot', 'Software screenshot'], 

         'Non-free web screenshot', 'Website screenshot'], 

         'Non-free speech', 'Audio excerpt from a speech'], 

         'Non-free audio sample', 'Sound sample of an audio recording'], 

         'Non-free video sample', 'Sample extract from a video'], 

         'Non-free sheet music', 'Sheet music representing a musical piece'], 

         'Non-free comic', 'Panel from a comic, graphic novel, manga etc.'], 

         'Non-free computer icon', 'Computer icon'], 

         'Non-free newspaper image', 'Page from a newspaper'], 

         'Non-free fair use in', 'something else (please describe in description field on top)'

         ],      

      'NFLogoLicense' :

         

         'Non-free logo', 'Logo of a company, organization etc.'], 

         'Non-free seal', 'Official seal, coat of arms etc'], 

         'Non-free symbol', 'Other official symbol'

         ],

      'NFMiscLicense' :

         

         'Non-free fair use in', 'something else (please describe in description field on top)'], 

         'Non-free historic image', 'Historic photograph'], 

         'Non-free 2D art', '2-dimensional artwork (painting, drawing etc.)'], 

         'Non-free currency', 'Depiction of currency (banknotes, coins etc.)'], 

         'Non-free architectural work', 'Architectural work'], 

         'Non-free 3D art', 'Other 3-dimensional creative work (sculpture etc.)'], 

         'Non-free book cover', 'Cover page of a book'], 

         'Non-free album cover', 'Cover of a sound recording(album, single, song, CD)'], 

         'Non-free game cover', 'Cover of a video/computer game'], 

         'Non-free magazine cover', 'Cover page of a magazine'], 

         'Non-free video cover', 'Cover of a video'], 

         'Non-free software cover', 'Cover of a software product'], 

         'Non-free product cover', 'Cover of some commercial product'], 

         'Non-free title-card', 'Title screen of a TV programme'], 

         'Non-free movie poster', 'Movie poster'], 

         'Non-free poster', 'Official poster of an event'], 

         'Non-free television screenshot', 'Television screenshot'], 

         'Non-free film screenshot', 'Movie screenshot'], 

         'Non-free game screenshot', 'Game screenshot'], 

         'Non-free video screenshot', 'Video screenshot'], 

         'Non-free music video screenshot', 'Music video screenshot'], 

         'Non-free software screenshot', 'Software screenshot'], 

         'Non-free web screenshot', 'Website screenshot'], 

         'Non-free speech', 'Audio excerpt from a speech'], 

         'Non-free audio sample', 'Sound sample of an audio recording'], 

         'Non-free video sample', 'Sample extract from a video'], 

         'Non-free sheet music', 'Sheet music representing a musical piece'], 

         'Non-free comic', 'Panel from a comic, graphic novel, manga etc.'], 

         'Non-free computer icon', 'Computer icon'], 

         'Non-free newspaper image', 'Page from a newspaper'], 

         'Non-free logo', 'Logo of a company, organization etc.'], 

         'Non-free seal', 'Official seal, coat of arms etc'], 

         'Non-free symbol', 'Other official symbol'], 

         'Non-free sports uniform', 'Sports uniform'], 

         'Non-free stamp', 'Stamp' 

         ],

      'NFExtraLicense' :

         

         '', 'none'],

         'Crown copyright and other governmental sources'],

         'Non-free Crown copyright', 'UK Crown Copyright'],

         'Non-free New Zealand Crown Copyright', 'NZ Crown Copyright'],

         'Non-free Canadian Crown Copyright', 'Canadian Crown Copyright'],

         'Non-free AUSPIC', 'AUSPIC (Australian Parliament image database)'],

         'Non-free Philippines government', 'Philippines government'],

         'Non-free Finnish Defence Forces', 'Finnish Defence Forces'],

         [],

         'Other individual sources'],

         'Non-free Denver Public Library image', 'Denver Public Library'],

         'Non-free ESA media', 'ESA (European Space Agency)'],

         [],

         'Possibly public domain in other countries'],

         'Non-free Old-50', 'Author died more than 50 years ago.'],

         'Non-free Old-70', 'Author died more than 70 years ago.'],

         [],

         'Some permissions granted, but not completely free'],

         'Non-free promotional', 'From promotional press kit'],

         'Non-free with NC', 'Permission granted, but only for educational and/or non-commercial purposes'],

         'Non-free with ND', 'Permission granted, but no derivative works allowed'],

         'Non-free with permission', 'Permission granted, but only for Wikipedia'],

         []

         

   };

   for (var group in licenseLists) {

      fuwMakeSelection(group, licenseListsgroup]);

   }





   this.knownCommonsLicenses = {

      'self|GFDL|cc-by-sa-all|migration=redundant' : 1,

      'self|Cc-zero' : 1,

      'PD-self' : 1,

      'self|GFDL|cc-by-sa-4.0|migration=redundant' : 1,

      'self|GFDL|cc-by-4.0|migration=redundant' : 1,

      'self|GFDL|cc-by-sa-3.0|migration=redundant' : 1,

      'self|GFDL|cc-by-3.0|migration=redundant' : 1,

      'self|cc-by-sa-4.0' : 1,

      'self|cc-by-sa-3.0' : 1,

      'cc-by-sa-4.0' : 1,

      'cc-by-sa-3.0' : 1,

      'cc-by-sa-2.5' : 1,

      'cc-by-4.0' : 1,

      'cc-by-3.0' : 1,

      'cc-by-2.5' : 1,

      'FAL' : 1,

      'PD-old-100' : 1,

      'PD-old' : 1,

      'PD-Art' : 1,

      'PD-US' : 1,

      'PD-USGov' : 1,

      'PD-USGov-NASA' : 1,

      'PD-USGov-Military-Navy' : 1,

      'PD-ineligible' : 1,

      'Attribution' : 1,

      'Copyrighted free use' : 1

   };





   // textfields that don't react directly

   // to user input and are used only for assembling stuff:

   if (fuwTesting) {

      fuwMakeTextfield('SandboxSummary', function(){void(0);});

      fuwMakeTextarea('SandboxText', function(){void(0);});

      fuwGet('SandboxSummary').disabled="disabled";

      fuwGet('SandboxText').disabled="disabled";

      fuwGet('SandboxText').rows = 12;

   }



   // set links to "_blank" target, so we don't accidentally leave the page,

   // because on some browsers that would destroy all the input the user has already entered

   $('.fuwOutLink a').each(function() {

      this.target = '_blank';

   });



   // make main area visible

   fuwSetVisible('UploadScriptArea', true);



}

// ====================================== 

// end of fuwGlobal constructor function

// ======================================







function fuwRadioClick(e) {

   var ev = e || event;

   var src = ev.target || ev.srcElement;

   //alert('onclick event from ' + src + ' (' + src.value + ')');

   fuwUpdateOptions();

   return true;

}



/* 

* =============================================================

* function fuwUpdateOptions

* =============================================================

* This is the onchange event handler for most of the input

* elements in the main form. It changes visibility and disabled

* status for the various sections of the input form in response

* to which options are chosen.

*/

function fuwUpdateOptions() {



   var fuw = window.fuw;

   var warn = fuw.warn;

   var opts = fuw.opts = { };

   opts.InputFilename = $('#TargetForm input#file').val();



   var widgets = fuw.ScriptForm.elements;

   for (i = 0; i < widgets.length; i++) {

      var w = widgetsi];

      if (w.type == "radio") {

         var nm = w.name;

         var id = w.id;

         var vl = w.checked && !w.disabled && fuwIsVisible(w);

         optsid = vl;

         if (vl) optsnm = id;

      }

      else {

         var id = w.id;

         var active = !w.disabled && fuwIsVisible(w);

         if (active) {

            var value = ((type == 'checkbox') ? w.checked : w.value);

            optsid = value;         

         }

      }

   };

   opts.MainOption = opts.FreeOptions || opts.NonFreeOptions;

   

   // some parts of the input form are re-used across sections

   // and must be moved into the currently active input section:



   // minimality section is shared between all NF sections

   fuwMove('NFMinimalitySection', 'detailsNFSubject', (opts.OptionNFSubject)) ||

   fuwMove('NFMinimalitySection', 'detailsNF3D', (opts.OptionNF3D)) ||

   fuwMove('NFMinimalitySection', 'detailsNFExcerpt', (opts.OptionNFExcerpt)) ||

   fuwMove('NFMinimalitySection', 'detailsNFCover', (opts.OptionNFCover)) ||

   fuwMove('NFMinimalitySection', 'detailsNFLogo', (opts.OptionNFLogo)) ||

   fuwMove('NFMinimalitySection', 'detailsNFPortrait', (opts.OptionNFPortrait)) ||

   fuwMove('NFMinimalitySection', 'detailsNFMisc', true);



   // AnyOtherInfo section is shared between all

   fuwMove('AnyOtherInfo', 'detailsOwnWork', opts.OptionOwnWork) ||

   fuwMove('AnyOtherInfo', 'detailsThirdParty', opts.OptionThirdParty) ||

   fuwMove('AnyOtherInfo', 'detailsFreeWebsite', opts.OptionFreeWebsite) ||

   fuwMove('AnyOtherInfo', 'detailsPDOld', opts.OptionPDOld) ||

   fuwMove('AnyOtherInfo', 'detailsPDOther', opts.OptionPDOther) ||

   fuwMove('AnyOtherInfo', 'detailsNFSubject', opts.OptionNFSubject) ||

   fuwMove('AnyOtherInfo', 'detailsNF3D', opts.OptionNF3D) ||

   fuwMove('AnyOtherInfo', 'detailsNFExcerpt', opts.OptionNFExcerpt) ||

   fuwMove('AnyOtherInfo', 'detailsNFCover', opts.OptionNFCover) ||

   fuwMove('AnyOtherInfo', 'detailsNFLogo', opts.OptionNFLogo) ||

   fuwMove('AnyOtherInfo', 'detailsNFPortrait', opts.OptionNFPortrait) ||

   fuwMove('AnyOtherInfo', 'detailsNFMisc', opts.OptionNFMisc);



   // author input field is shared between all sections except "Own Work".

   // (will serve for the immediate/photographic author, in those cases where there

   // are two author fields)

   fuwMove('Author', 'placeholderFreeWebsiteAuthor', (opts.OptionFreeWebsite)) ||

   fuwMove('Author', 'placeholderPDOldAuthor', (opts.OptionPDOld)) ||

   fuwMove('Author', 'placeholderPDOtherAuthor', (opts.OptionPDOther)) ||

   fuwMove('Author', 'placeholderNFSubjectAuthor', (opts.OptionNFSubject)) ||

   fuwMove('Author', 'placeholderNF3DAuthor', (opts.OptionNF3D)) ||

   fuwMove('Author', 'placeholderNFExcerptAuthor', (opts.OptionNFExcerpt)) ||

   fuwMove('Author', 'placeholderNFCoverAuthor', (opts.OptionNFCover)) ||

   fuwMove('Author', 'placeholderNFPortraitAuthor', (opts.OptionNFPortrait)) ||

   fuwMove('Author', 'placeholderNFMiscAuthor', (opts.OptionNFMisc)) ||

   fuwMove('Author', 'placeholderAuthor', true);



   // source input field is shared between all sections except "Own Work".

   // (will serve for immediate/web source, in those cases where there are two

   // source fields involved)

   fuwMove('Source', 'placeholderFreeWebsiteSource', (opts.OptionFreeWebsite)) ||

   fuwMove('Source', 'placeholderPDOldSource', (opts.OptionPDOld)) ||

   fuwMove('Source', 'placeholderPDOtherSource', (opts.OptionPDOther)) ||

   fuwMove('Source', 'placeholderNFSubjectSource', (opts.OptionNFSubject)) ||

   fuwMove('Source', 'placeholderNF3DSource', (opts.OptionNF3D)) ||

   fuwMove('Source', 'placeholderNFExcerptSource', (opts.OptionNFExcerpt)) ||

   fuwMove('Source', 'placeholderNFCoverSource', (opts.OptionNFCover)) ||

   fuwMove('Source', 'placeholderNFLogoSource', (opts.OptionNFLogo)) ||

   fuwMove('Source', 'placeholderNFPortraitSource', (opts.OptionNFPortrait)) ||

   fuwMove('Source', 'placeholderNFMiscSource', (opts.OptionNFMisc)) ||

   fuwMove('Source', 'placeholderSource', true);



   // date input field is shared between all sections except "Logo", which doesn't need it.

   // will serve for derived/photographic date in the case of 3D items

   fuwMove('Date', 'placeholderFreeWebsiteDate', (opts.OptionFreeWebsite)) ||

   fuwMove('Date', 'placeholderThirdPartyDate', (opts.OptionThirdParty)) ||

   fuwMove('Date', 'placeholderPDOldDate', (opts.OptionPDOld)) ||

   fuwMove('Date', 'placeholderPDOtherDate', (opts.OptionPDOther)) ||

   fuwMove('Date', 'placeholderNFSubjectDate', (opts.OptionNFSubject)) ||

   fuwMove('Date', 'placeholderNF3DDate', (opts.OptionNF3D)) ||

   fuwMove('Date', 'placeholderNFExcerptDate', (opts.OptionNFExcerpt)) ||

   fuwMove('Date', 'placeholderNFCoverDate', (opts.OptionNFCover)) ||

   fuwMove('Date', 'placeholderNFPortraitDate', (opts.OptionNFPortrait)) ||

   fuwMove('Date', 'placeholderNFMiscDate', (opts.OptionNFMisc)) ||

   fuwMove('Date', 'placeholderDate', true);

   

   // permission field is shared between ThirdParty and FreeWebsite sections

   fuwMove('Permission', 'placeholderFreeWebsitePermission', (opts.OptionFreeWebsite)) ||

   fuwMove('Permission', 'placeholderPermission', true);



   // publication field is shared between PDOld, NFPortrait and NFMisc

   fuwMove('Publication', 'placeholderNFPortraitPublication', (opts.OptionNFPortrait)) ||

   fuwMove('Publication', 'placeholderNFMiscPublication', (opts.OptionNFMisc)) ||

   fuwMove('Publication', 'placeholderPublication', true);



   // Purpose, Commercial, Replaceable and ReplaceableText FUR fields are shared

   // between some but not all of the non-free sections

   fuwMove('NFPurpose', 'placeholderNFExcerptPurpose', (opts.OptionNFExcerpt)) ||

   fuwMove('NFPurpose', 'placeholderNFPurpose');

   fuwMove('NFCommercial', 'placeholderNFPortraitCommercial', (opts.OptionNFPortrait)) ||

   fuwMove('NFCommercial', 'placeholderNFCommercial');

   fuwMove('NFReplaceable', 'placeholderNFPortraitReplaceable', (opts.OptionNFPortrait)) ||

   fuwMove('NFReplaceable', 'placeholderNFReplaceable');

   fuwMove('NFReplaceableText', 'placeholderNFExcerptReplaceable', (opts.OptionNFExcerpt)) ||

   fuwMove('NFReplaceableText', 'placeholderNFReplaceableText', true);



   // submit button goes to Step1 if user has chosen a plain overwrite of an existing file,

   // and to the active section of Step3 if otherwise

   fuwMove('fuwSubmit', 'UploadScriptStep1', (warn.ImageExists && opts.OverwriteSame)) ||

   fuwMove('fuwSubmit', 'detailsOwnWork', opts.OptionOwnWork) ||

   fuwMove('fuwSubmit', 'detailsThirdParty', opts.OptionThirdParty) ||

   fuwMove('fuwSubmit', 'detailsFreeWebsite', opts.OptionFreeWebsite) ||

   fuwMove('fuwSubmit', 'detailsPDOld', opts.OptionPDOld) ||

   fuwMove('fuwSubmit', 'detailsPDOther', opts.OptionPDOther) ||

   fuwMove('fuwSubmit', 'detailsNFSubject', opts.OptionNFSubject) ||

   fuwMove('fuwSubmit', 'detailsNF3D', opts.OptionNF3D) ||

   fuwMove('fuwSubmit', 'detailsNFExcerpt', opts.OptionNFExcerpt) ||

   fuwMove('fuwSubmit', 'detailsNFCover', opts.OptionNFCover) ||

   fuwMove('fuwSubmit', 'detailsNFLogo', opts.OptionNFLogo) ||

   fuwMove('fuwSubmit', 'detailsNFPortrait', opts.OptionNFPortrait) ||

   fuwMove('fuwSubmit', 'fuwSubmitHost', true);





   // Show and hide warnings:



   // filename-related warnings:

   fuwSetVisible('warningIllegalChars', warn.IllegalChars);

   fuwSetVisible('warningBadFilename',  warn.BadFilename);

   fuwSetVisible('warningImageOnCommons', warn.ImageOnCommons);

   fuwSetVisible('warningImageExists', warn.ImageExists);

   fuwMove('warningImageThumb', 'warningImageOnCommons', warn.ImageOnCommons, true) ||

   fuwMove('warningImageThumb', 'warningImageExists', true, true);





   // notices related to the top-level options:

   fuwSetVisible('warningWhyNotCommons', opts.OptionFree);

   fuwSetVisible('warningNF', opts.OptionNonFree);

   fuwSetVisible('warningNoGood', opts.OptionNoGood);



   // warnings related to non-free "used in" article

   fuwSetVisible('warningNFArticleNotFound', warn.NFArticleNotFound);

   fuwSetVisible('warningNFArticleNotMainspace', warn.NFArticleNotMainspace);

   fuwSetVisible('warningUserspaceDraft', warn.UserspaceDraft);

   fuwSetVisible('warningNFArticleDab', warn.NFArticleDab);

   fuwSetVisible('NFArticleOK', warn.NFArticleOK);



   // warnings depending on user status:

   if (fuw.userStatus.match(/problem|newbie|notAutoconfirmed/)) {

      fuwSetVisible('warningFreeWebsite', opts.OptionFreeWebsite);

      fuwSetVisible('warningOwnWork', opts.OptionOwnWork);

      fuwSetVisible('warningPDOther', opts.OptionPDOther);

      fuwSetVisible('warningNFSubject', opts.OptionNFSubject);

   }



   // hide main sections in case of intended plain overwrite:   

   fuwSetVisible('UploadScriptStep2', !(warn.ImageExists && opts.OverwriteSame));

   fuwSetVisible('UploadScriptStep3', !(warn.ImageExists && opts.OverwriteSame));



   // show/hide top-level options

   fuwSetVisible('detailsFreeStatus', opts.OptionFree);

   fuwSetVisible('sendToCommons', opts.OptionFree);



   // show/hide details sections

   fuwSetVisible('detailsNFArticle', opts.OptionNonFree);

   fuwSetVisible('detailsNFWorkType', opts.OptionNonFree);

   fuwSetVisible('detailsOwnWork', opts.OptionOwnWork);

   fuwSetVisible('detailsThirdParty', opts.OptionThirdParty);

   fuwSetVisible('detailsFreeWebsite', opts.OptionFreeWebsite);

   fuwSetVisible('detailsPDOld', opts.OptionPDOld);

   fuwSetVisible('detailsPDOther', opts.OptionPDOther);

   fuwSetVisible('detailsNFSubject', opts.OptionNFSubject);

   fuwSetVisible('detailsNF3D', opts.OptionNF3D);

   fuwSetVisible('detailsNFExcerpt', opts.OptionNFExcerpt);

   fuwSetVisible('detailsNFCover', opts.OptionNFCover);

   fuwSetVisible('detailsNFLogo', opts.OptionNFLogo);

   fuwSetVisible('detailsNFPortrait', opts.OptionNFPortrait);

   fuwSetVisible('detailsNFMisc', opts.OptionNFMisc);



   fuwSetVisible('EditSummaryDiv', opts.OverwriteSame || opts.OverwriteDifferent);



   // set enabled/disabled

   // It might be useful to adapt this to be more liberal about

   // the order of input, at least for experienced users.



   //fuwSetEnabled('Artist3D', opts.PD3D);

   //fuwSetEnabled('Country3D', opts.FOP3D);

   fuwSetEnabled('ThirdPartyEvidenceLink', opts.ThirdPartyEvidenceOptionLink);

   fuwSetEnabled('ThirdPartyOTRSTicket', opts.ThirdPartyEvidenceOptionOTRS);

   fuwSetEnabled('NFSubjectPurpose', opts.NFSubjectCheckDiscussed);

   fuwSetEnabled('NF3DPurpose', opts.NF3DCheckDiscussed);

   fuwSetEnabled('NF3DPermission', opts.NF3DOptionFree);

   fuwSetEnabled('USGovLicense', opts.PDOtherUSGov);

   fuwSetEnabled('PDOfficialPermission', opts.PDOtherOfficial);

   fuwSetEnabled('IneligibleLicense', opts.PDOtherSimple);

   fuwSetEnabled('PDOtherPermission', opts.PDOtherOther);

   fuwSetEnabled('AnyOther', true);



   // need to re-collect the remaining (non-radiobutton) input into the opts object again,

   // preparing for validation:

   for (i = 0; i < widgets.length; i++) {

      var w = widgetsi];

      var type = w.type;



      if (type != "radio") {

         var id = w.id;

         var active = !w.disabled && fuwIsVisible(w);

         if (active) {

            var value = ((type == 'checkbox') ? w.checked : w.value);

            optsid = value;         

         }

      }

   };



   // final step of validation: check if input is sufficient for

   // setting the submit buttons active

   var valid = fuw.validateInput();

   var validForCommons = valid && opts.OptionFree && !(opts.OverwriteSame || opts.OverwriteDifferent)

         && !opts.ThirdPartyEvidenceOptionNone;

   fuwSetVisible('sendToCommons', opts.OptionFree);

   fuwSetEnabled('CommonsButton', validForCommons);

   fuwGet('fuwSubmitText').innerHTML = opts.OptionFree ? 

         ("<b>No</b>, I want to upload this file here on this wiki only.<br/>" + 

          "<small>This way it can be used only on the English Wikipedia. However, somebody " +

          "else might still decide to copy it to Commons or use it elsewhere later. If you " +

          "do not want your file to be copied to Commons and deleted locally, consider adding " +

          "{{tl|Keep local}}.</small>") :

         "Upload this file.";

   fuwGet('SubmitButton').value = validForCommons ? "Upload locally" : "Upload";   

   fuwSetEnabled('EditSummary', true);

   fuwSetEnabled('SubmitButton', valid && (fuw.userStatus != 'notAutoconfirmed'));

   if (fuwTesting) {

      fuwSetEnabled('SandboxButton', valid);

   }



   // if we're in testing mode, update the Sandbox display fields

   // after each input change. In normal mode, collectInput() will

   // only be needed on submit.

   if (fuwTesting) {

      fuw.collectInput();

      fuw.formatOutput(false);

      fuwSetVisible('placeholderTestForm', true);

   }  

}



// ============================================================

// methods of the global fuw object

// ============================================================



// ============================================================

// report validation status of filename information

// ============================================================

// This is called from within fuw.validateInput(), i.e. every

// time anything in the whole form is changed. It only reports

// results that have previously been cached in the opts and warn

// objects. The actual checking is done in the event handler

// of the file input boxes.

fuwGlobal.prototype.hasValidFilename = function() {

   var opts = this.opts;

   var warn = this.warn;

   var valid =   

      opts.InputName &&

      opts.InputFilename &&

      !warn.BadFilename &&

      !warn.ImageOnCommons &&

      // if image exists on enwiki, accept only if user has confirmed overwrite:

      !(warn.ImageExists && !(opts.OverwriteSame || opts.OverwriteDifferent));

   //alert("HasValidFilename: " + valid);

   return valid;

};



// ============================================================

// validation status for common input elements for all free

// options

// ============================================================

fuwGlobal.prototype.hasValidCommonFreeInput = function() {

   var opts = this.opts;

   var warn = this.warn;

   var valid = opts.InputDesc;

   //alert("HasValidCommonFreeInput: " + valid);

   return valid;

};

// ============================================================

// validation status for common input elements for all non-free

// options

// ============================================================

fuwGlobal.prototype.hasValidCommonNFInput = function() {

   var opts = this.opts;

   var warn = this.warn;

   var valid =

      opts.OptionNonFree &&

      opts.InputDesc && 

      opts.NFArticle &&

      opts.Source &&

      opts.NFMinimality &&

      !warn.NFArticleNotFound &&

      !warn.NFArticleNotMainspace &&

      !warn.NFArticleDab;

   //alert("hasValidCommonNFInput: " + valid);

   return valid;

};

// ============================================================

// Main validation routine. Modify this to tweak which fields

// are to be considered obligatory for each case group

// ============================================================

fuwGlobal.prototype.validateInput = function() {

   var opts = this.opts;

   var warn = this.warn;

   var valid = (

      this.hasValidFilename()

      &&

      (! (opts.OverwriteDifferent && ! opts.EditSummary))

      &&

      (

       ( // overwriting is okay if there is an edit summary

        opts.OverwriteSame && opts.EditSummary

       )

       ||

       ( // free options

         this.hasValidCommonFreeInput() &&

         (

          (opts.OptionOwnWork &&

           opts.Date &&

           opts.OwnWorkLicense)

          ||

          (opts.OptionThirdParty &&

           opts.Author &&

           opts.Source &&

           opts.Permission &&

           (opts.ThirdPartyOtherLicense || opts.ThirdPartyLicense) &&

           ((opts.ThirdPartyEvidenceOptionLink && opts.ThirdPartyEvidenceLink) ||

            opts.ThirdPartyEvidenceOptionOTRS ||

            opts.ThirdPartyEvidenceOptionOTRSForthcoming ||

            opts.ThirdPartyEvidenceOptionNone))

          ||

          (opts.OptionFreeWebsite &&

           opts.Author &&

           opts.Source &&

           (opts.FreeWebsiteOtherLicense || opts.FreeWebsiteLicense) &&

           opts.Permission)

          ||

          (opts.OptionPDOld &&

           opts.Author &&

           opts.PDOldAuthorLifetime &&

           opts.Publication &&

           opts.Date &&

           opts.Source &&

           opts.PDOldOptions && 

           (! (opts.PDOldOther && ! opts.PDOldPermission)))

          ||

          (opts.OptionPDOther &&

           opts.Author &&

           opts.Source &&

           ((opts.PDOtherUSGov && opts.USGovLicense) ||

            (opts.PDOtherOfficial && opts.PDOfficialPermission) ||

            (opts.PDOtherSimple && opts.IneligibleLicense) ||

            (opts.PDOtherOther && opts.PDOtherPermission)))

        )

       ) // end of free options

       ||

       ( // non-free options

         this.hasValidCommonNFInput() &&

         (

          (opts.OptionNFSubject &&

           opts.NFSubjectLicense &&

           opts.Author &&

           (opts.NFSubjectCheckDedicated ||

           (opts.NFSubjectCheckDiscussed && opts.NFSubjectPurpose)))

          ||

          (opts.OptionNF3D &&

           opts.NF3DLicense &&

           opts.NF3DCreator &&

           opts.Author &&

           (opts.NF3DOptionSame ||

           (opts.NF3DOptionFree || opts.NF3DPermission)) && 

           (opts.NF3DCheckDedicated ||

            (opts.NF3DCheckDiscussed && opts.NF3DPurpose)))

          ||

          (opts.OptionNFExcerpt &&

           opts.NFExcerptLicense &&

           opts.Author &&

           opts.NFPurpose)

          ||

          (opts.OptionNFCover &&

           opts.NFCoverLicense &&

           opts.Author &&

           opts.NFCoverCheckDedicated 

          )

          ||

          (opts.OptionNFLogo &&

           opts.NFLogoLicense &&

           opts.NFLogoCheckDedicated

          )

          ||

          (opts.OptionNFPortrait &&

           opts.Publication &&

           opts.NFPortraitDeceased &&

           opts.Author &&

           opts.NFPortraitCheckDedicated &&

           opts.NFReplaceable &&

           opts.NFCommercial)

          ||

          (opts.OptionNFMisc &&

           opts.NFMiscLicense &&

           opts.Author &&

           opts.Publication &&

           opts.NFPurpose &&

           opts.NFReplaceableText &&

           opts.NFReplaceable &&

           opts.NFCommercial)

         )

       ) // end of non-free options

      )

   );

   return valid;

};



// =============================================================

// return which template name will be used as the main

// description template

// =============================================================

fuwGlobal.prototype.getDescriptionTemplateName = function() {

   // standard "Information" template for free files:

   if (this.opts.OptionFree) return "Information";

   // experimental new version of fair-use rationale template,

   // designed to fit the fields used in the wizard

   else if (this.opts.OptionNonFree) return "Non-free use rationale 2";

   return undefined;

};



// =============================================================

// get the license tag code from the appropriate input element

// =============================================================



fuwGlobal.prototype.getStandardLicense = function() {

   var opts = this.opts;

   

}



fuwGlobal.prototype.getLicense = function() {

   var opts = this.opts;

      // ThirdParty and FreeWebsite have alternative input fields

      // for manual entry of other licenses:

   var license = {};

   if (opts.PDOtherOther || opts.PDOldOther) {

      license.special = opts.PDOtherOther ? opts.PDOtherPermission : opts.PDOldPermission;

      if (! (license.special.match(/^\s*\{\{.+\}\}\s*$/))) {

         license.special = '{{PD-because|' + license.special + '}}';

      }

   }

   else {

      license.special = 

         opts.ThirdPartyOtherLicense || 

         opts.FreeWebsiteOtherLicense ||

         (opts.PDOtherOfficial ? ('{{PD-because|official item legally exempt from copyright in its country of origin}}') : null) ||

         (opts.OptionNFPortrait ? ('{{Non-free biog-pic|' + opts.NFArticle + '}}') : null);

   }   

   if (! license.special) {

      // standard, non-parametrized tag licenses from dropdownbox.

      var simpleLicense = (opts.OptionOwnWork ? opts.OwnWorkLicense : null) ||

          (opts.OptionThirdParty ? opts.ThirdPartyLicense : null) ||

          (opts.OptionFreeWebsite ? opts.FreeWebsiteLicense : null) ||

          (opts.OptionNFSubject ? opts.NFSubjectLicense : null) ||

          (opts.OptionNF3D ? opts.NF3DLicense : null) ||

          (opts.OptionNFExcerpt ? opts.NFExcerptLicense : null) ||

          (opts.OptionNFCover ? opts.NFCoverLicense : null) ||

          (opts.OptionNFLogo ? opts.NFLogoLicense : null) ||

          (opts.OptionNFMisc ? opts.NFMiscLicense : null) ||

          (opts.PDOtherUSGov ? opts.USGovLicense : null) ||

          (opts.PDOtherSimple ? opts.IneligibleLicense : null) ||

          (opts.PDUSExpired ? 'PD-US-expired' : null) ||

          (opts.PDURAA   ? 'PD-URAA' : null) ||

          (opts.PDFormality ? 'PD-US' : null);



       // "PD-author" needs parameter, at least on Commons

       if (simpleLicense == 'PD-author') {

          license.special = '{{PD-author|' + opts.Author + '}}';

       }

       else if (this.knownCommonsLicensessimpleLicense]) {

       // make sure we send only those licenses as "standard" licenses

       // that exist in the Commons license dropdown box

          license.standard = simpleLicense;

       }

       else {

          license.special = '\{\{' + simpleLicense + '\}\}';

       }

   }

   return license;

};



function fuwSubst(template) {

   return '{{subst:' + template + '}}';

}



// ===================================================================

// Produce code for local tracking categories

// ===================================================================

fuwGlobal.prototype.getTrackingCategory = function() {

   var opts = this.opts;

   var cat = "";

   if (opts.OptionFreeWebsite) { cat = "Files from freely licensed external sources"; }

   else if (opts.OptionThirdParty) { cat = "Files licensed by third parties"; }

   else if (opts.PDOtherOther || opts.PDOldOther) { cat = "Files with non-standard public domain statements"; }

   else if (opts.OptionNFSubject || opts.OptionNF3D) { cat = "Non-free files uploaded as object of commentary"; }

   if (cat) {

      cat = "\n\{\{Category ordered by date|" + cat + "|" + 

      fuwSubst("CURRENTYEAR") + "|" + fuwSubst("CURRENTMONTH") + "|" + fuwSubst("CURRENTDAY2") + "\}\}";

   }

   return cat;

};



// ===================================================================

// Get or create an edit summary for the upload

// ===================================================================

// Note: if we work with the api.php interface, we can have separate

// data for the edit summary and the description page, which is far

// better than the way the index.php interface does it.

// TO DO: need to actually define an input element for a manually

// entered edit summary. Must be obligatory when overwriting files.

// In other cases we'll use an automatic edit summary.

// ===================================================================

fuwGlobal.prototype.getEditSummary = function() {

   var opts = this.opts;

   return (

      (opts.EditSummary ? (opts.EditSummary + ' ([[' + mw.config.get('wgPageName') + '|File Upload Wizard]])') : null)||

      ("Uploading " +

       (

        (opts.OptionOwnWork ? 'a self-made file ' : false) ||

        (opts.OptionThirdParty ? 'a free file from somebody else ' : false) ||

        (opts.OptionFreeWebsite ? 'a file from a free published source ' : false) ||

        (opts.OptionPDOld       ? 'an old public-domain work ' : false) ||

        (opts.OptionPDOther     ? 'a public-domain item ' : false) ||

        (opts.OptionNFSubject   ? 'a non-free work, as object of commentary ' : false) ||

        (opts.OptionNF3D        ? 'a depiction of a non-free 3D artwork ' : false) ||

        (opts.OptionNFExcerpt   ? 'an excerpt from a non-free work ' : false) ||

        (opts.OptionNFCover     ? 'a piece of non-free cover art ' : false) ||

        (opts.OptionNFLogo      ? 'a non-free logo ' : false) ||

        (opts.OptionNFPortrait    ? 'a non-free historic portrait ' : false) ||

        (opts.OptionNFMisc      ? 'a non-free file ' : "")

       )

       + 

       ("using [[" + mw.config.get('wgPageName') + "|File Upload Wizard]]")

      ));

};





function fuwPackInfo(text, forCommons) {

   if (forCommons) {

      // reformat wikilinks embedded in description fields to adapt them for Commons

      text = text.replace(/\[\[([^\]]+)\]\]/g, 

         function(str, p1, offset, s) {



            // mark File links as local

            if (p1.match(/^:(File|Image):/)) {

               return "[[:en" + p1 + "]]";

            }      

            // leave prefixed links unchanged:

            else if (p1.match(/^:[\w\-]+:/)) {

               return str;

            }

            // if the link is piped, add a prefix only

            else if (p1.match(/.+\|/)) {

               return "[[:en:" + p1 + "]]";

            }

            // introduce a pipe

            else {

               return "[[:en:" + p1 + "|" + p1 + "]]";

            }

         }

      );

      return "{{en|" + text + "}}";

   } else return text;

}



// ================================================================

// This is the main method called by the event handler for the 

// (experimental) submit button. Its main task is to collect the 

// input into a single string of wikitext for the description page.

// ================================================================

fuwGlobal.prototype.collectInput = function() {

   var opts = this.opts;



   // object representing template fields for filling in

   // the description template. Pre-loaded with some

   // standard settings:

   var descFields = this.descFields = { 

      'Description' : opts.InputDesc,

      'Author'      : opts.Author,

      'Date'        : opts.Date,

      'Source'      : opts.Source

   };

   // "other information" (outside the template)

   this.otherInfo = null;

   

   if (opts.OptionNonFree) {

      descFields.Article = opts.NFArticle;

   }

   

   // add/modify option-specific fields:

   switch (opts.MainOption) {

      case 'OptionOwnWork':

      

         // use standard "source" field for optional "how created?" and 

         // "previously published" input fields.

         descFields.Source = fuwAppendLines([

            (opts.OwnWorkCreation || "{{own}}"), 

            "<br/>\n", 

            fuwSurroundString("'''Previously published:''' ", opts.OwnWorkPublication)]);

         var username = mw.user.getName();

         descFields.Author = '[[User:' + username + '|' + username + ']]';

         break;



      case 'OptionThirdParty':

      

         // use standard "permission" field for a compilation of the

         // "permission" input field and the various "evidence" options

         var evidence = (

            opts.ThirdPartyEvidenceOptionLink ? 

               ("The license statement can be found online at: " + opts.ThirdPartyEvidenceLink) :

               (opts.ThirdPartyEvidenceOptionOTRS ? 

               ("The license agreement has been forwarded to OTRS." + 

                  fuwSurroundString(" Ticket: ", opts.ThirdPartyOTRSTicket) + "\{\{OTRS pending|year=" + fuwSubst("CURRENTYEAR") + 

                                                                              "|month=" + fuwSubst("CURRENTMONTH") + 

                                                                              "|day=" + fuwSubst("CURRENTDAY2") + "\}\}") :

               (opts.ThirdPartyEvidenceOptionOTRSForthcoming ? 

               "The license agreement will be forwarded to OTRS shortly. \{\{OTRS pending|year=" + fuwSubst("CURRENTYEAR") + 

                                                                              "|month=" + fuwSubst("CURRENTMONTH") + 

                                                                              "|day=" + fuwSubst("CURRENTDAY2") + "\}\}" :

               (opts.ThirdPartyEvidenceOptionNone ?

               "Will be provided on request." : null))));

         descFields.Permission = fuwAppendLines([

            opts.ThirdPartyPermission,

            "<br/>\n",

            fuwSurroundString("'''Evidence:''' ", evidence)]);

         break;

         

      case 'OptionFreeWebsite':

         descFields.Permission = opts.Permission;

         break;

         

      case 'OptionPDOld':

         // add "lifetime" input to "author" field

         descFields.Author = fuwAppendLines([

            opts.Author,

            "<br/>\n",

            fuwSurroundString("(Life time: ", opts.PDOldAuthorLifetime, ")")

         ]);

         

         // combine original and direct source into standard "source" field: 

         descFields.Source = fuwAppendLines([

            fuwSurroundString("'''Original publication''': ", opts.Publication),

            "<br/>\n",

            fuwSurroundString("'''Immediate source''': ", opts.Source)

         ]);

         

         // no standard tag available for "lack-of-registration" PD-US. Need

         // to put this into the "permission" field

         if (opts.PDFormality) 

            descFields.Permission = 

               "Copyright expired because the work was published without a copyright " +

               "notice and/or without the necessary copyright registration.";

      

         // add optional "explanation" input to "permission" field

         if (opts.PDOldPermission) {

            descFields.Permission = fuwAppendLines([

               descFields.Permission,

               "\n\n",

               opts.PDOldPermission

            ]);

         }

         break;

         

      case 'OptionPDOther':

         // Need "permission" field in case of "official item" option

         if (opts.PDOtherOfficial) 

            descFields.Permission = opts.PDOfficialPermission;

         break; 



      case 'OptionNFSubject':

         // most FUR elements can be automatically provided:

         descFields.Purpose = (

            opts.NFSubjectCheckDedicated ? 

             ("For visual identification of the object of the article. " +

              "The article as a whole is dedicated specifically to a discussion of this work.") :

            (opts.NFSubjectCheckDiscussed ?

             ("To support encyclopedic discussion of this work in this article. " +

              "The illustration is specifically needed to support the following point(s): " +

              "<br/>\n" + opts.NFSubjectPurpose) : null)

         );

         

         descFields.Replaceability = "Any derivative work based upon the artwork would be a copyright violation, so creation of a free image is not possible.";

         descFields.Commercial = "The use of a low resolution image of the artwork will not impact the commercial viability of the art.";

         break;



      case 'OptionNF3D':

         // complex case: we need to assemble attribution and FUR both for the 

         // original 3D work and for the photographic depiction. Both might be 

         // non-free.

         descFields.Author = fuwAppendLines([

            fuwSurroundString("'''Original work:''' ", opts.NF3DCreator),

            "<br/>\n",

            fuwSurroundString("'''Depiction:''' ", opts.Author)

         ]);

         descFields.Date = fuwAppendLines([

            fuwSurroundString("'''Original work:''' ", opts.NF3DOrigDate),

            "<br/>\n",

            fuwSurroundString("'''Depiction:''' ", opts.Date)

         ]);

         descFields.Purpose = (

            opts.NF3DCheckDedicated ? 

             ("For visual identification of the object of the article. " +

              "The article as a whole is dedicated specifically to a discussion of this work.") :

            (opts.NF3DCheckDiscussed ? 

             ("To support encyclopedic discussion of this work in this article. " +

              "The illustration is specifically needed to support the following point(s): " +

              "<br/>\n" + opts.NF3DPurpose) : null)

         );

         descFields.Replaceability = "Any derivative work based upon the artwork would be a copyright violation, so creation of a free image is not possible.";

         descFields.Commercial = "The use of a low resolution image of the artwork will not impact the commercial viability of the art.";

         descFields"Other information" = (

            opts.NF3DOptionSame ?

            ("The image was created and published by the same author who also " +

             "holds the rights to the original object, and no alternative depiction " +

             "could be suitably created.") :

            ("The author of the image has released the photographic work under a " +

             "free license, or it is in the public domain: " + opts.NF3DPermission)

         );

         break; 

         

      case 'OptionNFExcerpt':

         // FURs for screenshots etc. don't normally need to bother

         // about replaceability (with free images) and with commercial role,

         // but do need to bother about purpose and about replaceability with text.

         descFields.Purpose        = opts.NFPurpose;

         descFields.Replaceability_text = opts.NFReplaceableText;

         descFields.Replaceability = "The software or website from which the screenshot is taken is copyrighted and not released under a free license, so creation of a free image is not possible.";

         descFields.Commercial = "The use of a low resolution screenshot from software or a website will not impact the commercial viability of the software or site.";

         break;

         

      case 'OptionNFCover':

         // cover art gets standard rationales.

         descFields.Purpose = 

            "to serve as the primary means of visual identification " +

            "at the top of the article dedicated to the work in question.";

         descFields.Replaceability = "Any derivative work based upon the cover art would be a copyright violation, so creation of a free image is not possible.";

         descFields.Commercial = "The use of a low resolution image of a work's cover will not impact the commercial viability of the work.";

         break;

 

      case 'OptionNFLogo':

         // logos get standard rationales.

         descFields.Purpose = 

            "to serve as the primary means of visual identification " +

            "at the top of the article dedicated to the entity in question.";

         descFields.Replaceability = "Any derivative work based upon the logo would be a copyright violation, so creation of a free image is not possible.";

         descFields.Commercial = "The use of a low resolution image of an organization's logo in the article about that organization will not impact the commercial viability of the logo.";

         break;



      case 'OptionNFPortrait':

         // as with other historic photographs, it is useful to have both

         // original publication and direct source

         descFields.Source = fuwAppendLines([

            fuwSurroundString("'''Original publication''': ", opts.Publication),

            "<br/>\n",

            fuwSurroundString("'''Immediate source''': ", opts.Source)

         ]);   

         descFields.Purpose = 

            "for visual identification of the person in question, " +

            "at the top of their biographical article";

         descFields.Replaceability = opts.NFReplaceable;

         descFields.Commercial = opts.NFCommercial;

         descFields'Other information' = 

            "The subject of the photograph has been deceased since: " + opts.NFPortraitDeceased;

         break;

         

      case 'OptionNFMisc':

         descFields.Source = fuwAppendLines([

            fuwSurroundString(

               "'''Original publication''': ", 

               opts.Publication,

               "<br/>\n'''Immediate source:''' "),

            "",

            opts.Source

         ]);   

         descFields.Purpose = opts.NFPurpose;

         descFields.Replaceability = opts.NFReplaceable;

         descFields.Replaceability_text = opts.NFReplaceable_text;

         descFields.Commercial = opts.NFCommercial; 

         break;     

   };



   if (opts.OptionNonFree) {

      // common stuff for all non-free files:

      

      // Minimality field (same for all NF options):

      descFields.Minimality = opts.NFMinimality;

      

      // append optional "extra license" selector and "AnyOther" fields

      // to "Other information" field:

      descFields'Other information' = fuwAppendLines([

         descFields'Other information'],

         "<br/>\n",

         fuwSurroundString('\{\{', opts.NFExtraLicense, '\}\}'),

         "<br/>\n",

         opts.AnyOther

      ]);

   }

   else {

      // common stuff for all free files:

      descFields.Other_versions = ''

      this.otherInfo = fuwAppendLines([this.otherInfo, "\n\n", opts.AnyOther]);

   

   }



};



fuwGlobal.prototype.formatOutput = function(forCommons) {

   var baseForm = this.ScriptForm;

   var targetForm = this.TargetForm;

   if (fuwTesting) {

      var testForm   = this.TestForm;

   }

   var opts = this.opts;

   var otherInfo = this.otherInfo;

   var descFields = this.descFields;



   var summary = "{{" + this.getDescriptionTemplateName();



   // assemble all fields into the wikitext of the description page:

   var fieldOrder = 

      'Source', 'Date', 'Author', 'Permission', 'Other_versions',

      'Article', 'Purpose', 'Replaceability', 'Replaceability_text', 

      'Minimality', 'Commercial', 'Other information'

   ];

   summary += "\n|Description = " + fuwPackInfo(descFields'Description'], forCommons);

   for (var i = 0; i < fieldOrder.length; i++) {

      if (descFieldsfieldOrderi]]) {

         summary += "\n|" + fieldOrderi + " = " + descFieldsfieldOrderi]];

      }

   }

   summary += "\n}}\n";

   if (otherInfo) {

      summary += "\n;Other information:\n" + fuwPackInfo(otherInfo, forCommons) + "\n";

   }



   var editSummary = this.getEditSummary();

   

   var license = this.getLicense();

   

   if (forCommons) {

      // pack our description info into an url pointing to the 

      // standard Commons Special:Upload

      // with pre-loaded description fields



      summary = fuwSubst("Upload marker added by en.wp UW") + "\n" + summary;

      summary = summary.replace(/\{\{OTRS pending\}\}/g, fuwSubst("OP"));



      if (license.special) {

         // manually format the whole description page including the license tag, if it

         // isn't one of the bare standard licenses in the dropdown box. Otherwise,

         // submit description summary and license as two separate url parameters.

         summary = summary + "\n\n" + license.special;

      }

      return (fuwGetCommonsURL() +

         "?title=Special:Upload" +

         "&wpUploadDescription=" +

         encodeURIComponent(summary) +

         (license.standard ? 

          ("&wpLicense=" + encodeURIComponent(license.standard)) : '') +

         "&wpDestFile=" + 

         encodeURIComponent(opts.InputName));

   }

   else {

      // pack all description into a single "text" parameter to be submitted

      // to the local api.php upload.

      summary = "==Summary==\n" + 

         summary + 

         "\n==Licensing==\n" + 

         (license.standard ? ("\{\{" + license.standard + "\}\}") : license.special) +

         this.getTrackingCategory();

         

      if (fuwTesting) {

         // Testing mode: show our data in the dummy form

         // at the bottom of the page.

         fuwGet('placeholderSandboxFilename').innerHTML = opts.InputName;

         this.TestForm.SandboxSummary.value = editSummary;

         this.TestForm.SandboxText.value = summary;

         fuwSetVisible('placeholderTestForm', true);

      }

      // Set up API call parameters

      this.UploadOptions.filename = opts.InputName;

      this.UploadOptions.text     = summary;

      this.UploadOptions.comment  = editSummary;



   }

   

};



function fuwHasUserGroup(group) {

   // workaround because old IE versions don't have array.indexOf :-(

   for (i = 0; i < mw.config.get('wgUserGroups').length; i++) {

      if (mw.config.get('wgUserGroups')[i == group) {

         return true;

      }

   }

   return false

}



fuwGlobal.prototype.getUserStatus = function() {

   // function to determine the experience status and userrights of the current user:

   // 'anon': not logged in; can't use script.

   // 'notAutoconfirmed': can't use local upload, but may use script to prepare upload for Commons

   // 'newbie': autoconfirmed but editcount < 100 

   //    (may be used in future to adapt instructions more to newbie needs)

   // 'problem': autoconfirmed but has 3 or more image-related warnings or deletion notifications among recent user talk entries

   //    (may be used in future to produce more strongly worded instructions)

   // 'autoconfirmed': regular user

   // 'sysop'



   if (mw.config.get('wgUserName')) {

      if (fuwHasUserGroup('sysop')) {

         this.userStatus = 'sysop';

      }

      else if (fuwHasUserGroup('autoconfirmed') || fuwHasUserGroup('confirmed')) {

         this.userStatus = 'autoconfirmed';

         $.ajax({

            url     : mw.util.wikiScript( 'api' ),

            type    : 'GET',

            dataType: 'xml',

            traditional : true,

            data:   {

                     format: 'xml',

                     action: 'query',

                     meta  : 'userinfo',

                     uiprop: 'editcount',

                     prop  : 'revisions',

                     titles: 'User talk' + mw.config.get('wgUserName'),

                     rvprop: 'comment|user',

                     rvlimit: 30

                    },      

            success: function(data) {

            // callback func     

               var fuw = window.fuw;

               if (data) {

                  var ui = data.getElementsByTagName('userinfo');

                  if (ui) {

                     var editcount = ui0].getAttribute('editcount');

                     if (editcount < 100) {

                        fuw.userStatus = 'newbie';

                     }

                  }

                  var revs = data.getElementsByTagName('rev');

                  var countWarn = 0;

                  for (i = 0; i < revs.length; i++) {

                     var rev = revsi];

                     var usr = rev.getAttribute('user');

                     var cmt = rev.getAttribute('comment');

                     if ((usr == 'ImageTaggingBot') ||

                         (cmt.search(/(tagging for deletion of \[\[File)|(Uploading files missing)|(File (source and )?copyright licensing problem)|(Speedy deletion nomination of \[\[File)|(Notification: listing at \[\[possibly unfree files)/) >= 0)) {

                        countWarn += 1;   

                     }

                  }

                  if (countWarn >= 3) {

                     fuw.userStatus = 'problem';

                  }

               }

            }

         });

      }

      else {

         this.userStatus = 'notAutoconfirmed';

      }

   }

   else {

      this.userStatus = 'anon';

   }

};



// =================================================================

// Convenience function for getting the regular index.php

// interface of Commons. Not very elegant.

// =================================================================

function fuwGetCommonsURL() {

   if (document.URL.match(/^https:/)) 

      return "https://commons.wikimedia.org/";

   else

      return "http://commons.wikimedia.org/";

}  



// ==================================================================

// functions for building form elements

// ==================================================================

fuwMakeRadiobutton = function(group, option, checked, event) {

   // Stupid IE7 doesn't get "value" attribute unless it's created in this convoluted way.

   // Annoying.   

   var node = $('<input type="radio" id="' + option + '" name="' + group + '" value="' + option + '"></input>')[0];

   if (checked) node.checked = true;

   node.onclick = event || fuwRadioClick;

   node.onclick = event || fuwRadioClick;

   fuwAppendInput(option, node);

};

fuwMakeTextfield = function(label, event) {

   var node  = document.createElement('input');

   node.type = 'text';

   node.name = label;

   node.size = fuwDefaultTextboxLength;

   node.onchange = event || fuwUpdateOptions;

   // only for testing:

   //node.value = label;

   fuwAppendInput(label, node);

};

fuwMakeTextarea = function(label, event) {

   var node  = document.createElement('textarea');

   node.name = label;

   node.rows = fuwDefaultTextareaLines;

   node.style.width = fuwDefaultTextareaWidth;

   node.onchange = event || fuwUpdateOptions;

   //only for testing:

   //node.innerHTML = label;

   fuwAppendInput(label, node);

};

fuwMakeCheckbox = function(label, checked, event) {

   var node  = document.createElement('input');

   node.name = label;

   node.type = 'checkbox';

   //only for testing:

   //node.title= label;

   node.checked = checked;

   node.onchange = event || fuwUpdateOptions;

   fuwAppendInput(label, node);

}

fuwMakeHiddenfield = function(name, value, id) {

   var node   = document.createElement('input');

   node.name  = name;

   node.type  = 'hidden';

   node.value = value;

   fuwAppendInput((id || name), node);

};

fuwMakeAnchor = function(label, href, content) {

   var node   = document.createElement('a');

   node.name  = label;

   node.target= "_blank";

   node.href  = href;

   node.innerHTML = content;

   fuwAppendInput(label, node);

};

fuwMakeSelection = function(name, values) {

   var root = document.createElement('select');

   var current = root;

   try {

      for (i=0; i<values.length; i++) {

         var line = valuesi];

         var entry;

         if (line.length == 0) {

            current = root;

         }

         else if (line.length == 1) {

            entry = document.createElement('optgroup');

            entry.setAttribute('label', line0]);

            root.appendChild(entry);

            current = entry;

         }

         else {

            entry = document.createElement('option');

            entry.setAttribute('value', line0]);

            entry.setAttribute('title', '{{' + line0 + '}}');

            entry.innerHTML = line1];

            if (line.length > 2) {

               entry.setAttribute('selected', 'selected');

            } 

            current.appendChild(entry);

         }

      }

   } catch (e) { alert("Name: " + name + ", i=" + i); }

   root.name = name;

   root.onchange = fuwUpdateOptions;

   fuwAppendInput(name, root);

};

function fuwMakeWikilink(place, target, redlink, display) {

   

   place = fuwGet(place);

   var id = place.id;

   var anchor;

   if (place.tagName == 'A') {

      anchor = place;

   }

   else {

      anchor = document.createElement('a');

      place.appendChild(anchor);

   }

   anchor.href = mw.util.getUrl(target);

   anchor.title = target;

   anchor.innerHTML = target;

   anchor.className = (redlink ? 'new' : null);

}



function fuwAppendInput(label, content) {

   // append a newly created input element to an existing

   // span element marked as id="placeholderXYZ"

   var node = fuwGet('placeholder' + label);

   var old  = fuwGet(label);

   if (old) {

      old.parentNode.removeChild(old);

   }

   content.id = content.id || label;

   if (node) {

      while (node.hasChildNodes()) {

         node.removeChild(node.firstChild);

      }

      node.appendChild(content);

   }

}



// ======================================================

// move an element away from its current position

// and append it to a target element if condition is true

// ======================================================

function fuwMove(mv, tg, condition, toStart) {

   if (condition) {

      move   = fuwGet(mv);

      target = fuwGet(tg);

      if (move && target) {

         var parent = move.parentNode;

         if (! (target===parent)) {

            parent.removeChild(move);

            if (toStart) {

               target.insertBefore(move, target.firstChild);

            }

            else {

               target.appendChild(move);

            }

         }

      }

      else {

         alert("Can't find elements: move=" + mv + "(" + move + "), target=" + tg + "(" + target + ")");

      } 

   }

   return condition;

}



// ===================================================

// make an element visible/invisible

// ===================================================

function fuwSetVisible(tg, condition) {

   target = fuwGet(tg);

   if (target) {

      if (condition) {

         $(target).show();

      }

      else {

         $(target).hide();

      }

   }

   else {

      alert("Element not found: " +  (tg.nodeType ? tg.id : tg));

   }

}



// ===================================================

// set enabled/disabled status for an element and/or

// all input controls contained in it.

// ===================================================

function fuwSetEnabled(tg, condition) {

   target = fuwGet(tg);

   try {

      var elements = (target.tagName.match(/^(input|textarea|select|button|a)$/i) ? 

         target :

         $('#' + target.id + ' *'));

      for (i = 0; i<elements.length; i++) {

         if (elementsi].tagName.match(/^(input|textarea|select|button|a)$/i)) {

            elementsi].disabled = (condition ? null : "disabled");

         }

      }

   } catch (e) { alert("Element not found: " +  (tg.nodeType ? tg.id : tg)); }

}



// ===================================================

// convenience function to check whether a given

// element is currenly visible. Needs to check display

// property of the element and its ancestors

// ===================================================

function fuwIsVisible(el) {

   element = fuwGet(el);

   if (!element) return false;

   el = element.id;

   

   var visible = true;

   while (! (element === document.body)) {

      if (element.style.display == "none") {

         visible = false;

         break;

      }

      element = element.parentNode;

   }

   return visible;

}



// ===================================================

// cleanup filename

// ===================================================

function fuwCleanFilename() {

   var nameBox = window.fuw.ScriptForm.InputName;

   var oldname = name = $.trim(nameBox.value);



   if (name) {

      // strip accidentally added [[   ]] or [[:  ]] brackets

      name = name.replace(/(^\[\[:?)|(\]\]$)/g, "");

      // strip accidentally added "File:" prefix

      name = name.replace(/^(File|Image):/, "");

      // replace underscores with spaces

      name = name.replace(/_/g, " ");

      // uppercase first letter

      name = name0].toUpperCase() + name.slice(1);

   }

   if (oldname != name) {

      nameBox.value = name;

   }

   // always return true so the next validation step will proceed:

   return true;

}





// ==================================================

// check filename for technically illegal 

// characters, trying to fix them automatically

// ==================================================

function fuwCheckLegalFilename() {

   var nameBox = window.fuw.ScriptForm.InputName;

   var oldname = name = $.trim(nameBox.value);



   if (name) {

      // resolve accidentally entered html entities and URI-encoded %XX character codes

      name = name.replace(/\&[a-z]+;/g, fuwHtmlEntityDecode);

      name = name.replace(/(\%[A-F0-9]{2,2})/g, decodeURI);

      // remove illegal characters # < > [ ] | { } /:

      // using a best guess for an acceptable replacement

      name = name.replace(/[<\[\{]/g, "(");

      name = name.replace(/[>\]\}]/g, ")");

      name = name.replace(/[#:\|]/g,  ",");

      name = name.replace(/\//g, "-");

      // remove sequences of tildes

      name = name.replace(/\~{3,}/g, "---");

      // remove initial slash

      name = name.replace(/^\//, "");

   }

   

   if (oldname != name) {

      window.fuw.warn.IllegalChars = true;

      nameBox.value = name;

      return false;

   }

   else {

      window.fuw.warn.IllegalChars = false;

      return true;

   }

}

function fuwHtmlEntityDecode(str) {

   // hack to translate accidentally entered html entity code

   // into actual characters

   var ta=document.createElement('textarea');

   ta.innerHTML=str.replace(/</g,'&lt;').replace(/>/g,'&gt;');

   return ta.value;

}



// =======================================================

// Check against various common patterns of poorly chosen

// filenames (too short / too generic)

// =======================================================

function fuwCheckPoorFilename() {

   var nameBox = window.fuw.ScriptForm.InputName;

   var name = $.trim(nameBox.value);

	name = name.replace(/\.(png|gif|jpg|jpeg|xcf|pdf|mid|ogg|ogv|svg|djvu|tiff|tif|oga|mp3|webp|webm|opus|mpg|mpeg|wav|wave|flac)$/i, "");



   // name should be at least 10 characters long, excluding file type extension

   var tooShort = (name.length < 10);

   

   // common generic filename patterns: 

   // IMG......jpg

   // Image....jpg

   // DSC......jpg

   // Picture......jpg

   // Pic..........jpg

   // anything that has fewer than 3 alphabetic letters and then just numbers

   var pattern = /^(img|image|dsc|picture|pic)?(\\s*|\\_*|[a-z]{,3})?\\d+$/i;

   var auto = name.match(pattern);



   window.fuw.warn.BadFilename = (tooShort || auto);

   return !tooShort && !auto;

}



// =======================================================

// check if file extensions match between local filename

// and target filename input box. Automatically append 

// appropriate extension to target filename if they don't.

// =======================================================

function fuwCheckFileExtension() {

   var nameBox = window.fuw.ScriptForm.InputName;

   var name = $.trim(nameBox.value);

   var fileBox = window.fuw.TargetForm.file;

   var file = fileBox.value;

   

   // cancel check if no filename has been provided yet

   if (!file || !name) return true;

   

   var extensions = /.+\.(png|gif|jpg|jpeg|xcf|pdf|mid|ogg|ogv|svg|djvu|tiff|tif|oga|mp3|webp|webm|opus|mpg|mpeg|wav|wave|flac)$/i;

   var mimetypes = {

      "png"  : "image/png",

      "gif"  : "image/gif",

      "jpg"  : "image/jpeg",

      "jpeg" : "image/jpeg",

      "xcf"  : "image/x-xcf",

      "webp" : "image/webp",

      "pdf"  : "application/pdf",

      "mid"  : "audio/rtp-midi",

      "ogg"  : "audio/ogg",

      "mp3"	 : "audio/mp3",

      "opus" : "audio/opus",

      "wav"  : "audio/wav",

      "wave" : "audio/wav",

      "flac" : "audio/flac",

      "ogv"  : "video/ogg",

      "svg"  : "image/svg+xml",

      "djvu" : "image/vnd.djvu",

      "tiff" : "image/tiff",

      "tif"  : "image/tiff",

      "oga"  : "video/ogg",

      "webm" : "video/webm",

      "mpg"  : "video/mpeg",

      "mpeg" : "video/mpeg"

   };   



   var found = extensions.exec(file);

   var fileExt = found ? found1].toLowerCase() : "";

   found = extensions.exec(name);

   var nameExt = found ? found1].toLowerCase() : "";

   var mime = mimetypesfileExt]; 

   

   if (fileExt && mime && (mimetypesnameExt != mime)) {

      nameBox.value = name.replace(/\.?$/, ('.' + fileExt));

   }

   return true;

}



// ============================================================

// Check if a file under the chosen name already exists,

// either locally or on Commons.

// Store results in the fuw.warn object, so warnings will

// be displayed on the next fuwUpdateOptions() call

// ============================================================

function fuwCheckFileExists() {

   // this is an asynchronous AJAX function.

   // results won't yet be present when this function returns.



   var nameBox = window.fuw.ScriptForm.InputName;

   var name = $.trim(nameBox.value);



   // using the jQuery wrapper for the Ajax functionality:

   $.ajax({

      url     : mw.util.wikiScript( 'api' ),

      type    : 'GET',

      dataType: 'xml',

      traditional : true,

      data:   {

               format: 'xml',

               action: 'query',

               titles: 'File:' + name,

               prop  : 'imageinfo',

               iiprop: 'url|user',

               iiurlwidth: 120

              },      

      success: function(resp) {

      // callback function, called when API query has succeeded:

         // see if the request has returned info from an existing image:

         var foundlist = resp.getElementsByTagName('ii');

         var exists = (foundlist.length >= 1);

         var isCommons = false;

         if (exists) {



            // extract description data from http response.

            // see https://www.mediawiki.org/wiki/API:Properties#imageinfo_.2F_ii 

            // for structure of API response

            var foundImg = foundlist0];

            isCommons = (foundImg.parentNode.parentNode.getAttribute('imagerepository')=='shared');



            // need this data for creating our own image thumb link

            var width = foundImg.getAttribute('thumbwidth');

            var height = foundImg.getAttribute('thumbheight');

            var thumbURL = foundImg.getAttribute('thumburl');

            var lastUser = foundImg.getAttribute('user');

            var descURL = foundImg.getAttribute('descriptionurl');



            // API returns link to local description page even for Commons images.

            // However, we want a direct link to Commons.

            if (isCommons) {

               descURL = descURL.replace(/en\.wikipedia\.org/, "commons.wikimedia.org");

               descURL = descURL.replace(/\/\/secure\.wikimedia\.org\/wikipedia\/en/, "commons.wikimedia.org");

            }



            // build the image info into the warning section of our page:

            thumbDiv = fuwGet('warningImageThumb');

            if (thumbDiv) {

            

               // make all links point to description page:

               var thumbA = thumbDiv.getElementsByTagName('a');

               for (i = 0; i<thumbA.length; i++) {

                  thumbAi].setAttribute('href', descURL);

               }

               // insert the image itself:

               var thumbImg = thumbDiv.getElementsByTagName('img');

               if (thumbImg.length > 0) {

                  thumbImg = thumbImg0];

                  thumbImg.setAttribute('src', thumbURL);

                  thumbImg.setAttribute('width', width);

                  thumbImg.setAttribute('height', height);

               }

               // insert the name of the last uploader:

               var thumbSpan = fuwGet('existingImageUploader');

               // TO DO: turn this into a proper link

               if (thumbSpan) thumbSpan.innerHTML = lastUser;

            }



            

         }

         warn = window.fuw.warn;

         warn.ImageOnCommons = exists && isCommons;

         warn.ImageExists    = exists && !isCommons;



         fuwUpdateOptions();

      }

   });

}



// ===========================================================

// onchange event handler for the local filename box

// ===========================================================

fuwValidateFile = function() {

   fuwCheckFileExtension();

   fuwUpdateOptions();

}



// ===========================================================

// onchange event handler for the name input box

// ===========================================================

fuwValidateFilename = function() {

   fuwCleanFilename();

   if (

      fuwCheckLegalFilename() &&

      fuwCheckPoorFilename() &&

      fuwCheckFileExtension()) {

      // after fuwCheckFileExists(),

      // fuwUpdateOptions will be triggered

      // by the callback function after Ajax completion

      fuwCheckFileExists();

   }

   else {

      // if there's been no Ajax call.

      fuwUpdateOptions();

   }

};



// ==========================================================

// function fuwValidateNFArticle()

// ==========================================================

// This is the validation routine for the obligatory

// article-to-be-used-in field for non-free files. It queries

// api.php about the target article through an Ajax call.

// It will store error info in the fuw.warn object,

// triggering the following error on the next updateOptions():

// * warningNFArticleNotFound : target page doesn't exist.

// * warningNFArticleNotMainspace : target is not an article. 

// * warningNFArticleDab : target is a disambiguation page.

// Redirects will automatically be substituted.

// ==========================================================

fuwValidateNFArticle = function() {

   

   var nameBox = window.fuw.ScriptForm.NFArticle;

   oldname = name = nameBox.value;

   

   // cleanup article name:

   // automatically fix accidentally added [[ ... ]] and

   // regularize underscores

   name = $.trim(name);

   name = name.replace(/(^\[\[)|(\]\]$)/g, "");

   // automatically fix article names entered as full urls:

   name = name.replace(/^https?:\/\/en\.wikipedia\.org\/wiki\//, "");

   name = name.replace(/^https?:\/\/en\.wikipedia\.org\/w\/index\.php\?title=/, "");

   name = name.replace(/_/g, " ");

   if (name != oldname) nameBox.value = name;

   

   // do nothing more if field was blank

   if (!name) return;



   // using the jQuery wrapper for the Ajax functionality:

   $.ajax({

      url     : mw.util.wikiScript( 'api' ),

      type    : 'GET',

      dataType: 'xml',

      traditional : true,

      data:   {

               format: 'xml',

               action: 'query',

               titles: name,

               prop  : 'info|categories|links'

              },      

      success: function(resp) {

      // callback function, called when API query has succeeded:

         var errorType = 0;

         var pg = resp.getElementsByTagName('page')[0];

         var title = pg.getAttribute('title');

         var target = title;

         if (pg.getAttribute('missing') != null) {

            // no page found under this title.

            errorType = 1;

         }           

         else {

            var userspace = false;

            var ns = pg.getAttribute('ns');

            var rd = pg.getAttribute('redirect');

            if (ns != 0) {

               // not a mainspace page!

               errorType = 2;



               // try to detect if the target might be a user space draft:               

               if (title.match(new RegExp("User( talk)?:" + mw.config.get('wgUserName')))) {

                  userspace = true;

               }

            }

            else if (rd != null) {

               // redirect page

               // API returns an empty redirect="" attribute if

               // the page is a redirect

               var targets = pg.getElementsByTagName('pl');

               for (i=0; i<targets.length; i++) {

                  var link = targetsi];

                  if (link.getAttribute('ns')==0) {

                     target = link.getAttribute('title');

                     errorType = 3;

                     break;

                  }

               }

            }

            else {

               // check for disambiguation categories

               var cats = pg.getElementsByTagName('cl');

               for (i=0; i<cats.length; i++) {

                  var cat = catsi];

                  if (cat.getAttribute('title') == "Category:All disambiguation pages") {

                     errorType = 4;

                     break;

                  }

               }  

            }

         }

         warn = window.fuw.warn;

         warn.NFArticleNotFound = (errorType==1);

         warn.NFArticleNotMainspace = (errorType==2);

         warn.UserspaceDraft = ((errorType==2) && userspace);

         warn.NFArticleDab = (errorType==4);

         warn.NFArticleOK  = (errorType==0);



         // fix links in error messages:

         if (warn.NFArticleNotFound) {

            fuwMakeWikilink(fuwGet('warningNFArticleNotFound').getElementsByTagName('A')[0], target, true);

         }

         else if (warn.NFArticleNotMainspace) {

            fuwMakeWikilink(fuwGet('warningNFArticleNotMainspace').getElementsByTagName('A')[0], target);

         }

         else if (warn.NFArticleDab) {

            fuwMakeWikilink(fuwGet('warningNFArticleDab').getElementsByTagName('A')[0], target);

         }

         else if (warn.NFArticleOK) {

            fuwMakeWikilink(fuwGet('NFArticleOK').getElementsByTagName('A')[0], target);

         }

                       

         if (errorType==3) {

            // automatically replace title with redirect target

            window.fuw.ScriptForm.NFArticle.value = target;

            // need to recursively call validation again now

            //if (confirm(name + " is a redirect. Follow it to " + target + "?")) {

               fuwValidateNFArticle();

            //}

         }

         else {

            fuwUpdateOptions();

         }          

      }

   });

};



// ================================================

// manually reload script (just for testing)

// ================================================

function fuwReload() {

   mw.loader.load( 'http://localhost/script/uploadscript.js' );

   fuwReset();

}



// ================================================

// reset forms

// TO DO: add a button that actually triggers this.

// ================================================

function fuwReset() {

   var forms = mw.util.$content0].getElementsByTagName('form');

   for (i = 0; i < forms.length; i++) {

      formsi].reset();

      window.fuw.warn = { };

      window.fuw.opts = { };

   }

   fuwSetVisible('UploadScriptArea', true);

   fuwSetVisible('fuwSuccess', false);

   fuwSetVisible('fuwWaiting', false);

   fuwUpdateOptions();

}



// ===============================================

// convenience functions for string handling

// ===============================================

function fuwAppendLines(parts) {

   // assemble a string from an array of strings.

   // treat every second element as a conditional

   // separator that will be included only if 

   // surrounding elements are non-empty.

   var build = "";

   for (var i = 0; i < parts.length; i += 2) {

      if (partsi]) {

         if (build) build += partsi - 1];

         build += partsi];

      }

   }

   return build;

}

function fuwSurroundString(prefix, content, suffix) {

   // put a prefix and a suffix on a string, 

   // if the input string is non-empty.

   if (content) 

      return (prefix ? prefix : "") + content + (suffix ? suffix : ""); 

   else return "";

}



// ========================================================

// handler for the API response.

// TO DO: expand stub to add real notification of success,

// link to new file page, instructions about how to include

// file in articles, etc.

// ========================================================

function fuwUploadCompleted(doc) {

   if (doc) {

      //alert(doc);

      fuwSetVisible('successThumb', false);



      var fuw = window.fuw;

      var name = fuw.opts.InputName;



      var uploads = doc.getElementsByTagName('upload');

      var success = false;

      for (i = uploads.length-1; i>=0; i--) {

         if (uploadsi].getAttribute('result') == 'Success') {

            success = true;

            // need to get the real resulting filename here; might be different from the requested one in some cases.

            name = uploadsi].getAttribute('filename');

            break;

         }

      }

      if (success) {



         // need another ajax call to check the file is actually there,

         // and to retrieve its direct thumb img url:

         $.ajax({

            url     : mw.util.wikiScript( 'api' ),

            type    : 'GET',

            dataType: 'xml',

            traditional : true,

            data:   {

                     format: 'xml',

                     action: 'query',

                     titles: 'File:' + name,

                     prop  : 'imageinfo',

                     iiprop: 'url',

                     iiurlwidth: 120

                    },      

            success: function(resp) {

               // callback function, called when API query has succeeded:

               // see if the request has returned info from an existing image:



               var foundImg = resp.getElementsByTagName('ii')[0];

               if (foundImg) {



                  // need this data for creating our own image thumb link

                  var width = foundImg.getAttribute('thumbwidth');

                  var height = foundImg.getAttribute('thumbheight');

                  var thumbURL = foundImg.getAttribute('thumburl');

                  var lastUser = foundImg.getAttribute('user');

                  var descURL = foundImg.getAttribute('descriptionurl');



                  // build the thumbnail in the success message:

                  thumbDiv = fuwGet('successThumb');

                  

                  // make link point to description page:

                  var thumbA = thumbDiv.getElementsByTagName('a')[0];

                  thumbA.href = descURL;



                  // insert the image itself:

                  var thumbImg = thumbDiv.getElementsByTagName('img')[0];

                  thumbImg.setAttribute('src', thumbURL);

                  thumbImg.setAttribute('width', width);

                  thumbImg.setAttribute('height', height);

                  

                  fuwSetVisible(thumbDiv, true);

               }

            }

         });

         fuwMakeWikilink(

            fuwGet('fuwSuccessLink2').getElementsByTagName('a')[0],

            'File:' + name);

         fuwGet('placeholderExFilename1').innerHTML = name;

         fuwGet('placeholderExFilename2').innerHTML = name;

         fuwSetVisible('fuwSuccess', true);

         fuwSetVisible('fuwWaiting', false);

      }

      else {

         var err = doc.getElementsByTagName('error');

         if (err) {

            var info = err0].getAttribute('info');

            var details = err0].getElementsByTagName('detail');

            var add = "";

            for (i = 0; i < details.length; i++) {

               if (add.length > 0) add += ", ";

               add += detailsi].textContent;

            }

            if (add) {

               info = info + " (" + add + ")";

            }

            alert("Upload failed: " + info);

         }

         else {

            alert("Unknown error: upload may have failed.");

         }

      }

   }

}



// ========================================================

// Event handler for the real submit button

// ========================================================

function fuwSubmitUpload() {

   

   var fuw = window.fuw;



   fuw.collectInput();

   fuw.formatOutput(false);



   if (fuwTesting) {

      fuwSetVisible('placeholderTestForm', false);

   }

   fuwSetVisible('UploadScriptArea', false);



   fuwMakeWikilink(

     fuwGet('fuwSuccessLink').getElementsByTagName('a')[0], 'File:' + fuw.opts.InputName);

   fuwSetVisible('fuwWaiting', true);



   // Upload the file, then add success notification etc.

   // Note that mw.Api doesn't have special support for the XML format, so even when the API

   // returns an error, this won't be detected and it will call the success handler.

   var api = new mw.Api({

      parameters : { format   : 'xml' },

      ajax       : { dataType : 'xml' }

   });

   api.upload(fuw.TargetForm.file, fuw.UploadOptions).then(fuwUploadCompleted);



   var opts = window.fuw.opts;

   // the API won't overwrite the description page text while overwriting

   // a file, which is really, really, really annoying and stupid.

   // So in the opts.OverwriteDifferent scenario, we need to edit

   // the description page through a separate ajax call. Dang.

   if (opts.OverwriteDifferent) {

      $.ajax({

         url   : mw.util.wikiScript('api'),

         type  : 'POST',

         dataType : 'xml',

         data  : {

                  format : 'xml',

                  action : 'edit',

                  title  : 'File:' + opts.InputName,

                  token  : mw.user.tokens.get('csrfToken'),

                  summary : opts.EditSummary,

                  text   : fuw.UploadOptions.text



                 }

      });

   }

}



// =======================================================

// Event handler for the Commons submit button

// =======================================================

function fuwSubmitCommons() {

   var fuw = window.fuw;

   fuw.collectInput();

   var url = fuw.formatOutput(true);

   alert("You will now be redirected to Commons. \nPlease use the Commons upload form to add categories to your file description, and then complete the upload.");

   window.location = url;

}



// =======================================================

// Event handler for the test submit button

// (write description string to sandbox only)

// =======================================================

function fuwSubmitSandbox() {

   var frm = window.fuw.TestForm;

   $.ajax({

      url     : mw.util.wikiScript( 'api' ),

      type    : 'POST',

      dataType: 'xml',

      data:   {

               format: 'xml',

               action: 'edit',

               title : mw.config.get('wgPageName') + "/sandbox",

               token : mw.user.tokens.get('csrfToken'),

               recreate : 1,

               summary  : frm.SandboxSummary.value,

               text     : frm.SandboxText.value

              },      

      success: function(resp) {

         alert("Sandbox page edited!");

      }

   });

}







// ========================================================

// convenience wrapper function to replace calls to

// document.getElementById() 

// to avoid browser incompatibility

// ========================================================

function fuwGet(target) {

   if (target && target.nodeType) return target;

   else {

      var found = $('#' + target);

      if (found) return found0];

   }

   return undefined;

}



// ========================================================

// onload hook function, loading this script

// ========================================================

mw.loader.using(['mediawiki.api']).then(function() {

   $(function() {

      if (fuwGet('UploadScriptArea')) {

         window.fuw = new fuwGlobal();

         if (! window.fuw.disabled) {

            fuwUpdateOptions();

         }

      }

   });

});