MediaWiki:FileUploadWizard.js

/*

  • ===============================================================
  • 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 = optionRadioButtons[group];

for (i=0; i

fuwMakeRadiobutton(group, op[i]);

}

}

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

fuwMakeTextfield(activeTextfields[i]);

}

// names of multiline textareas

var activeTextareas = [

'InputDesc','NF3DPermission',

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

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

];

for (i=0; i

fuwMakeTextarea(activeTextareas[i]);

};

var checkboxes = [

'NFCoverCheckDedicated','NFLogoCheckDedicated','NFPortraitCheckDedicated'

];

for (i=0; i

fuwMakeCheckbox(checkboxes[i]);

};

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-DOC-NOAA','US National Oceanic and Atmospheric Administration'],

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

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

['PD-USGov-Interior-USGS','United States Geological Survey']

],

'IneligibleLicense' :

[

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

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

['PD-simple','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', '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 video 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 film poster', 'Film poster'],

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

['Non-free fair use', '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 video game screenshot', 'Video 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', '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 file with no commercial use license', 'Permission granted, but only for educational and/or non-commercial purposes'],

['Non-free file with no derivative works license', 'Permission granted, but no derivative works allowed'],

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

[]

]

};

for (var group in licenseLists) {

fuwMakeSelection(group, licenseLists[group]);

}

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 = widgets[i];

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

var nm = w.name;

var id = w.id;

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

opts[id] = vl;

if (vl) opts[nm] = id;

}

else {

var id = w.id;

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

if (active) {

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

opts[id] = 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 = widgets[i];

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);

opts[id] = 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 ?

("No, I want to upload this file here on this wiki only.
" +

"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}}.") :

"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.knownCommonsLicenses[simpleLicense]) {

// 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 + ' (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 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 "" + 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}}"),

"
\n",

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

var username = mw.user.getName();

descFields.Author = '' + 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,

"
\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,

"
\n",

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

]);

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

descFields.Source = fuwAppendLines([

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

"
\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): " +

"
\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),

"
\n",

fuwSurroundString("Depiction: ", opts.Author)

]);

descFields.Date = fuwAppendLines([

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

"
\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): " +

"
\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),

"
\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,

"
\nImmediate 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'],

"
\n",

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

"
\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 (descFields[fieldOrder[i]]) {

summary += "\n|" + fieldOrder[i] + " = " + descFields[fieldOrder[i]];

}

}

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 = ui[0].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 = revs[i];

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/w/index.php";

else

return "http://commons.wikimedia.org/w/index.php";

}

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

// 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 = $('')[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

var line = values[i];

var entry;

if (line.length == 0) {

current = root;

}

else if (line.length == 1) {

entry = document.createElement('optgroup');

entry.setAttribute('label', line[0]);

root.appendChild(entry);

current = entry;

}

else {

entry = document.createElement('option');

entry.setAttribute('value', line[0]);

entry.setAttribute('title', '{{' + line[0] + '}}');

entry.innerHTML = line[1];

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

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

elements[i].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 = name[0].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,'>');

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 ? found[1].toLowerCase() : "";

found = extensions.exec(name);

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

var mime = mimetypes[fileExt];

if (fileExt && mime && (mimetypes[nameExt] != 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 = foundlist[0];

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[i].setAttribute('href', descURL);

}

// insert the image itself:

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

if (thumbImg.length > 0) {

thumbImg = thumbImg[0];

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

var link = targets[i];

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

var cat = cats[i];

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.$content[0].getElementsByTagName('form');

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

forms[i].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 (parts[i]) {

if (build) build += parts[i - 1];

build += parts[i];

}

}

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 (uploads[i].getAttribute('result') == 'Success') {

success = true;

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

name = uploads[i].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 = err[0].getAttribute('info');

var details = err[0].getElementsByTagName('detail');

var add = "";

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

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

add += details[i].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 found[0];

}

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();

}

}

});

});