MediaWiki:UploadForm.js

From Open Educational Resources
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

Note: After saving, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Go to Menu → Settings (Opera → Preferences on a Mac) and then to Privacy & security → Clear browsing data → Cached images and files.
// <source lang="javascript">

/*
  Upload form rewrite

  Author: [[User:Lupo]], March 2008
  License: Quadruple licensed GFDL, GPL, LGPL and Creative Commons Attribution 3.0 (CC-BY-3.0)
  
  Choose whichever license of these you like best :-)
*/

importScript( 'MediaWiki:Gadget-HotCat.js' );
importScript( 'MediaWiki:TextCleaner.js' );
importScript( 'MediaWiki:LanguageHandler.js' );
importScript( 'MediaWiki:FormRestorer.js' );
importScript( 'MediaWiki:UIElements.js' );
importScript( 'MediaWiki:Tooltips.js' );

// Guard against multiple inclusions!
if (typeof (UploadForm) == 'undefined') {

// Configuration. These can be set by a user in their monobook.js/modern.js. The typeof checks
// are not really needed when this script is globally enabled, but until then, we have to be
// careful not to overwrite a user's settings if he defines these first and then includes this
// script in his monobook.js.
if (typeof (UploadForm_forcebasic) == 'undefined')
  // If non-null, use the basic form
  var UploadForm_forcebasic     =
    (typeof (JSconfig) == 'undefined' || JSconfig.keys['UploadForm_newlayout']) ? null : true;
if (typeof (UploadForm_ownwork_author) == 'undefined')
  var UploadForm_ownwork_author = "\[\[User:" + mw.config.get('wgUserName') + "|" + mw.config.get('wgUserName') + "\]\]";
  // Change to use something else
if (typeof (UploadForm_ownwork_date) == 'undefined')
  var UploadForm_ownwork_date   = null;
  // Set to define a pre-fill value for the date field
if (typeof (UploadForm_own_language_first) == 'undefined')
  var UploadForm_own_language_first = false;
  // Set to true to have own language description on top
if (typeof (UploadForm_additional_info_height) == 'undefined')
  var UploadForm_additional_info_height = 2;
if (typeof (UploadForm_description_height) == 'undefined')
  var UploadForm_description_height = 2;
if (typeof (UploadForm_source_field_size) == 'undefined')
  var UploadForm_source_field_size = 1;
if (typeof (UploadForm_author_field_size) == 'undefined')
  var UploadForm_author_field_size = 1;
if (typeof (UploadForm_page_preview_in_tooltip) == 'undefined')
  var UploadForm_page_preview_in_tooltip = false;
if (typeof (UploadForm_description_languages) == 'undefined')
  var UploadForm_description_languages = null;
if (typeof (UploadForm_autofill) == 'undefined')
  var UploadForm_autofill = true;
  // If false, don't pre-fill description field in basic mode. May be useful
  // for people who have their own scripts pre-filling this field.

// Don't set the focus initially, it makes the page scroll, and people won't see the
// introductory texts
var EditTools_set_focus_initially = false;

var UFUtils =
{
  makeLink : function (name, url)
  {
    var link = document.createElement ('a');
    link.setAttribute ('href', url);
    link.appendChild (document.createTextNode (name));
    return link;    
  },
  
  convert_td_div : function (td)
  {
    // Replace the contents with a div, fixate the width, and give the div the id of the td
    var div = document.createElement ('div');
    var w = UFUtils.getWidth (td);
    if (w) {
      td.setAttribute ('width', "" + w);
      td.style.maxWidth = "" + w + 'px';
    }
    div.setAttribute ('width', (w ? "" + w : '100%'));
    if (w) div.style.maxWidth = "" + w + 'px';
    // Clear the warning_cell and add the div instead
    while (td.firstChild) td.removeChild (td.firstChild);
    td.appendChild (div);
    var id = td.id;
    td.id = "";
    div.id = id;
    return div;
  },
  
  getHeight : function (rows, minimum, maximum)
  {
    if (rows == null) return minimum;
    try {
      var x = rows / 2;
    } catch (ex) {
      // It's not a number
      return minimum;
    }
    if      (rows < minimum) return minimum;
    else if (rows > maximum) return maximum;
    return rows;
  },

  getWidth : function (element)
  {
    try {
      if (element.clientWidth)          // From IE, but Gecko has this, too.
        return element.clientWidth;
      else if (window.getComputedStyle) // Gecko, Opera
        return document.defaultView
                       .getComputedStyle (element, null)
                       .getPropertyValue ('width');
    } catch (ex) {
    }
    return null;
  },

  isChildOf : function (child, ancestor)
  {
    if (!ancestor) return false;
    while (child && child != ancestor) child = child.parentNode;
    return (child == ancestor);
  }

}; // end UFUtils

var UFUI =
{
  // Encapsulate all UI stuff, with checks such that it works in degraded mode
  // (built-in defaults only) if UIElements doesn't exist.

  defaultLanguage  : 'en',     // Default.
  userLanguage     : 'en',     // Sanitized wgUserLanguage.
  internalLanguage : 'en',     // Same, but with dashes replaced by underscores.
  isOwnWork        : false,    // True if uselang="*ownwork"
  isFromFlickr     : false,    // True if uselang="*fromflickr"
  isExperienced    : false,    // True if uselang="experienced"

  sanitizeUserLanguage : function ()
  {
    // Try to make sense of wgUserLanguage even if it has been hacked to have special
    // pages for particular upload sources. Also sets isOwnWork and isFromFlickr.
	var globalUserLanguage = mw.config.get('wgUserLanguage');
    if (!globalUserLanguage) return;    
    UFUI.userLanguage = globalUserLanguage;
    if (globalUserLanguage.length > 3) {
      // Special "hacked" uselang parameters...
      var hacks = ['ownwork', 'fromflickr', 'experienced', 'fromwikimedia', 'fromgov'];
      var found = false;
      for (var i = 0; i < hacks.length; i++) {
        var idx = globalUserLanguage.indexOf (hacks[i]);
        if (idx >= 0) {
          found = true;
          if (idx > 0)
            UFUI.userLanguage = globalUserLanguage.substring (0, idx);
          else
            UFUI.userLanguage = UFUI.defaultLanguage;
          if (i == 0)
            UFUI.isOwnWork = true;
          else if (i == 1)
            UFUI.isFromFlickr = true;
          else if (i == 2)
            UFUI.isExperienced = true;
          break;
        }
      }
      if (!found
          && typeof (LanguageHandler) != 'undefined'
          && typeof (LanguageHandler.getPrefix) == 'function')
      {
        // None of the "standard" hacks. Try an alternate approach.
        var lang_code_length = LanguageHandler.getPrefix (globalUserLanguage);
        if (lang_code_length > 0 && lang_code_length < globalUserLanguage.length) {
          UFUI.userLanguage = globalUserLanguage.substr (0, lang_code_length);
        }
      } 
    } // end if
    if (UFUI.userLanguage == 'en-gb') UFUI.userLanguage = 'en';
    UFUI.internalLanguage = UFUI.userLanguage.replace (/-/g, '_');
  },

  defaultLabels : {
    wpSourceUploadLbl:         'Original source:',
    wpAuthorUploadLbl:         'Author:',
    wpDateUploadLbl:           'Date:',
    wpDescUploadLbl:           'Description:',
    wpPermissionUploadLbl:     'Permission:',
    wpCategoriesUploadLbl:     'Categories:',
//    wpResourceLinkUploadLbl:  'Resource Link:',
    wpConceptNameUploadLbl:  'Name:',
    wpAltNameUploadLbl:  'Alternate Name:',
//    wpContentOrgUploadLbl:  'Content Org:',
    wpTeachesUploadLbl:  'Teaches:',
    wpBasedOnURLUploadLbl:  'Based on URL:',
    wpPreRequisitUploadLbl:  'Requires:',
    wpAssessmentUploadLbl:  'Assesses:',
    wpTimeRequiredUploadLbl:  'Time Required:',
    wpTagsTeluguUploadLbl:  'Tags Telugu:',
    wpTagsEnglishUploadLbl:  'Tags English:',
    wpTranslationOfUploadLbl:  'Translation of:',
    wpAdaptationTroerUploadLbl:  'Adaptation:',
    wpContributorsTroerUploadLbl:  'Contributors:',
    wpOtherVersionsUploadLbl:  'Other versions:',
    wpAdditionalInfoUploadLbl: 'Additional information:',
    wpPreviewLicenseUploadLbl: 'Preview the chosen license',
    wpOwnWorkUploadLbl:        'Own work',
    wpUnknownLanguageUploadLbl:'Unknown language',
    wpPreviewUploadLbl:        'Preview',
    wpOkUploadLbl:             'OK',
    wpCancelUploadLbl:         'Cancel'
  },
  
  defaultErrorMsgs : {
    wpUploadWarningError: 
      'You must provide the original source of the image, the author of the work, and a license.',
    wpNoFilenameError:
      'The target filename must not be empty.',
    wpHttpFilenameError: 
      'The target file name appears to be a URL.',
    wpNoSlashError:
      'The target file name must not contain "/".',
    wpNondescriptFilenameError:
      'Please use a more descriptive target file name.',
    wpNoExtensionError:
      'The target file name must have a file type extension (like for example ".jpg").',
    wpIllegalExtensionError:
      'Files of this type cannot be uploaded.',
    wpDoubleExtensionError:
      'Please correct the double file type in the target file name.',
    wpFlickrURLError:
      'The source must be a URL pointing to the image at Flickr.',
    wpNoDescriptionError:
      'Please give a description of the contents of the file you want to upload.',
    wpNoHelpTextError:
      'Help text not found.',
    wpPreviewOverwriteError:
      'You will upload over an already existing file. Please choose a different filename,'
     +'unless you are uploading a technically improved version of the same file.'
     +'Don\'t overwrite a file with a different image of the same topic.'
     +'If you overwrite, the information in this form will not appear on the description page.',

    wpReuploadNoSummaryError:
      'Please describe the file changes in the text box.'
  },
  
  defaultHints: {
    wpUploadFormDestFileHint:
      'Name of the file after the upload.',
    wpUploadFormSourceHint:
      'Where does this file come from?',
    wpUploadFormAuthorHint:
      'Who created this file?',
    wpUploadFormDateHint:
      'Date of creation and/or first publication of the work ("DD-MM-YYYY" format).',
    wpUploadFormAdditionalInfoHint:
      'Use for geolocation tags and other specialized information.',
    wpUploadFormCategoryHint:
      'Click (+) to add categories.'
  },

  // Do *not* use "-" here (as in "be-tarask")!! Use "_" instead: "be_tarask".
  translate: {
    en: 'translate'
  },

  labels     : null,  // Repository for form labels
  help       : null,  // Repository for help texts (and the help button)
  error_msgs : null,  // Repository for error messages
  uiElements : null,  // Repository for graphical UI elements
  hints      : null,  // Repository for brief hints

  setupRepositories : function ()
  {
    if (!UFUI.labels) {
      if (typeof (UIElements) != 'undefined') {
        UFUI.labels     = UIElements.emptyRepository ();
        UFUI.help       = UIElements.emptyRepository ();
        UFUI.error_msgs = UIElements.emptyRepository ();
        UFUI.uiElements = UIElements.emptyRepository ();
        UFUI.hints      = UIElements.emptyRepository ();

        for (var id in UFUI.defaultLabels) {
          if (id == 'wpDescUploadLbl') {
            UIElements.setEntry
              (  id
               , UFUI.labels
               , UFUtils.makeLink (  UFUI.defaultLabels[id]
                                   , '/OER/index.php/NROER Metadata')
              );
          } else {
            UIElements.setEntry
              (id, UFUI.labels, document.createTextNode (UFUI.defaultLabels[id]));
          }
        }
        for (var id in UFUI.defaultErrorMsgs) {
          UIElements.setEntry (id, UFUI.error_msgs,
                               document.createTextNode (UFUI.defaultErrorMsgs[id]));
        }
        for (var id in UFUI.defaultHints) {
          UIElements.setEntry (id, UFUI.hints,
                               document.createTextNode (UFUI.defaultHints[id]));
        }
          
        // Now try to read the localized stuff from the uploadfooter.
        UIElements.load ('wpUploadFormLabels', null, 'span', UFUI.labels);
        UIElements.load ('wpUploadFormErrorMessages', null, 'span', UFUI.error_msgs);
        UIElements.load ('wpUploadFormHints', null, 'span', UFUI.hints);
        UIElements.load ('wpUploadFormUIElements', null, 'div', UFUI.uiElements);
        UIElements.load ('wpUploadFormHelp', null, 'div', UFUI.help);
        UFUI.basic = false;
      } else {
        UFUI.labels     = UFUI.defaultLabels;
        UFUI.error_msgs = UFUI.defaultErrorMsgs;
        UFUI.hints      = UFUI.defaultHints;
        UFUI.basic      = true;
      }
    }  
  },

  getUI : function (id, repository, basic)
  {
    if (!UFUI.labels) {
      UFUI.sanitizeUserLanguage ();
      UFUI.setupRepositories ();
    }
    if (!UFUI[repository]) return null;
    var result   = null;
    var add_plea = false;
    if (UFUI.basic) {
      result = document.createTextNode (UFUI[repository][id]);
      add_plea = (UFUI.internalLanguage != UFUI.defaultLanguage);
    } else {
      result = UIElements.getEntry (id, UFUI[repository], UFUI.internalLanguage, null);
      add_plea = !result;
      if (!result) result = UIElements.getEntry (id, UFUI[repository]);
      if (!result) return null; // Hmmm... what happened here? We normally have defaults...
      result = result.cloneNode (true);
    }
    if (add_plea && !basic) {
      // Wrap it all into a span -- we can return only one element
      var span = document.createElement ('span');
      span.appendChild (result);
      span.appendChild (UFUI.plea (repository, id));
      result = span;
    }
    return result;
  },

  plea : function (what, msg_id)
  {
    var span = document.createElement ('sub');
    span.appendChild (document.createTextNode (' ('));
    span.appendChild (
      UFUtils.makeLink (
         UFUI.translate[UFUI.internalLanguage] || UFUI.translate.en
       , 'http://commons.wikimedia.org/wiki/MediaWiki_talk:UploadFormLabels/UploadFormTranslations?action=edit'
         +'&section=new' 
         +'&withJS=MediaWiki:UploadFormTranslator.js&language='
         +encodeURIComponent (UFUI.userLanguage)
         +'&uploadformurl=' + encodeURIComponent (document.URL)
         +(what ? '&uploadformitems=' + encodeURIComponent (what) : "")
         +(msg_id ? '&uploadformmsg=' + encodeURIComponent (msg_id) : "")));
    span.appendChild (document.createTextNode (')'));
    return span;
  },

  getLabel : function (id, basic)
  {
    return UFUI.getUI (id, 'labels', basic);
  },
  
  getErrorMsg : function (id, basic)
  {
    return UFUI.getUI (id, 'error_msgs', basic);
  },
  
  getHint : function (id, basic)
  {
    return UFUI.getUI (id, 'hints', basic);
  },

  getEntry : function (id, repository, lang, sel)
  {
    if (!UFUI.labels) {
      UFUI.sanitizeUserLanguage ();
      UFUI.setupRepositories ();
    }
    if (!UFUI.basic)
      return UIElements.getEntry (id, UFUI[repository], lang, sel);
    if (!UFUI[repository] || lang != UFUI.defaultLanguage || !!sel && sel != 'default')
      return null;
    return UFUI[repository][id];
  }
  
}; // end UFUI

var UFHelp =  // Collects all help-related stuff
{
  help_close_imgs : null,
  
  precreate_tooltip_closer : function ()
  {
    if (typeof (Tooltip) != 'undefined' && typeof (Buttons) != 'undefined') {
      var close_imgs =
        UFUI.getEntry ('wpUploadFormHelpCloseButton', 'uiElements', UFUI.internalLanguage);
      if (!close_imgs)
        close_imgs = UFUI.getEntry ('wpUploadFormHelpCloseButton', 'uiElements');
      if (close_imgs) close_imgs = close_imgs.getElementsByTagName ('img');
      if (!close_imgs || close_imgs.length == 0)
        close_imgs = null;
      else
        close_imgs =  Buttons.createClass (close_imgs, 'wpUploadFormHelpCloseClass');
      UFHelp.help_close_imgs = close_imgs;
    }
  },
  
  tooltip_styles : // The style for all our tooltips
   {  border          : '1px solid #8888aa'
    , backgroundColor : '#f7f8ff'
    , padding         : '0.3em'
    , fontSize        : ((mw.config.get('skin') == 'monobook' || mw.config.get('skin') == 'modern') ? '127%' : '100%')
      // Scale up to default text size
   },

  getHelp : function (help_id, with_ext) 
  {
    // This is a Tooltip callback! Sets the help texts dynamically, depending of the file
    // type the user has chosen in wpDestFile.
    var fn = null;
    if (with_ext) {
      fn = document.getElementById ('wpDestFile');
      if (fn != null) fn = fn.value;
      if (fn != null) {
        fn = fn.split ('.');
        if (fn.length >= 2) fn = fn[fn.length-1]; else fn = null;
      }
    }
    
    var add_plea   = false;
    var extensions = [fn, 'default'];
    var help_main  = null;
    for (var i = 0; i < extensions.length && !help_main; i++) {
      if (extensions[i] && extensions[i].length > 0) {
        help_main = UFUI.getEntry (help_id, 'help', UFUI.internalLanguage, extensions[i]);
        if (!help_main) {
          help_main = UFUI.getEntry (help_id, 'help', null, extensions[i]);
          add_plea  = (help_main != null);
        }
      }
    }
    var help_base = UFUI.getEntry (help_id, 'help', UFUI.internalLanguage);
    if (!help_base) {
      help_base = UFUI.getEntry (help_id, 'help');
      add_plea  = add_plea || (help_base != null);
    }
    var help = document.createElement ('div');
    if (help_base) help.appendChild (help_base);
    if (help_main) help.appendChild (help_main);
    if (!help_main && !help_base) {
      help.appendChild (UFUI.getErrorMsg ('wpNoHelpTextError'));
    } else if (add_plea) {
      help.appendChild (UFUI.plea ('help', help_id));
    }
    return help;
  },

  showHelp : function (evt, id) // Onclick handler for setup without tooltips
  {
    var e = evt || window.event;
    var node = e.target || e.srcElement;
    if (!node) {
      var error = UFUI.getErrorMsg ('wpNoHelpTextError', true);
      // We need the text contents... and IE doesn't know Node.TEXT_NODE
      while (error && error.nodeType != 3) error = error.firstChild;
      if (error) alert (error.data);
      // Otherwise what??
    } else if (!document.getElementById (id + '_Div')) {
      var help = UFHelp.getHelp (id, false);
      help.style.fontSize = 'small';
      help.style.color    = '#666666';
      // Now add a new table row after the current one
      var tr = node.parentNode;
      while (tr && tr.nodeName.toLowerCase () != 'tr') tr = tr.parentNode;
      if (!tr) {
        var error = UFUI.getErrorMsg ('wpNoHelpTextError', true);
        while (error && error.nodeType != 3) error = error.firstChild;
        if (error) alert (error.data);
      } else {
        var new_tr = document.createElement ('tr');
        var cell = document.createElement ('td');
        new_tr.appendChild (cell);
        cell = document.createElement ('td');
        cell.id = id + '_Div';
        new_tr.appendChild (cell);
        tr.parentNode.insertBefore (new_tr, tr.nextSibling);
        cell = UFUtils.convert_td_div (cell);
        cell.appendChild (help);
      }
    }
    if (!!e.stopPropagation) {
      e.stopPropagation ();
      e.preventDefault ();
    } else if (typeof (e.cancelBubble) != 'undefined') {
      e.cancelBubble = true;
    }
    return false;
  },
  
  setupHelp : function (is_reupload)
  {
    if (!UFUI.help) return; // Help not loaded
    
    function setHelp (id, imgs, lk, maximum_width, is_reupload)
    {
      // Figure out where to place the help "button"
      var field = document.getElementById (id);
      var insert_in = null, before = null;
      var help_id = id + 'Help';
      if (!UFUI.help[help_id]) return; // Don't add if we have no help at all.
      var offset = -5; // Pixels.
      switch (id) {
        case 'wpWatchthis':
        case 'wpIgnoreWarning':
          // Right of the element
          if (!field) return;
          insert_in = field.parentNode;
          // Find the label.
          {
            var lbls = insert_in.getElementsByTagName ('label');
            if (!lbls) {
              before = field.nextSibling;
            } else {
              for (var i = 0; i < lbls.length; i++) {
                if (lbls[i].htmlFor && lbls[i].htmlFor == id) {
                  before = lbls[i].nextSibling; break;
                }
              }
            }
          }
          offset = Math.abs (offset);
          break;
        case 'wpCategories':
          field = document.getElementById ('hotcatLabelTranslated');
          if (!field) return;
          insert_in = field;
          before    = null;
          if (field.firstChild) {
            field  = field.firstChild;
            offset = Math.abs (offset);
          }
          break;
        case 'wpAuthor':
        case 'wpSource':
          if (!field) return;
          field     = field.parentNode; // Because the field itself may vanish.
          insert_in = field.parentNode.cells[0];
          before    = null;
          break;
        case 'wpDestFile':
          if (!field) return;
          insert_in = field.parentNode.parentNode.cells[0];
          before    = null;
          if (is_reupload) {
            help_id = 'wpReuploadDestHelp';
            field   = null; // Field is hidden: attach the help text to the button instead
          }
          break;          
        case 'wpDesc':
          if (!field) {
            field = document.getElementById ('wpUploadDescription');
            if (field) { // Basic form
              help_id = (is_reupload ? 'wpReuploadSummaryHelp' : 'wpUploadDescriptionHelp');
            } else {
              insert_in = document.getElementById ('wpDescLabel');
              if (!insert_in) return;
              field     = insert_in;
              offset    = Math.abs (offset);
              before    = insert_in.nextSibling;
              insert_in = insert_in.parentNode;
              break;
            }
          }
          // Fall through
        default:
          if (!field) return;
          // In the table cell to the left
          insert_in = field.parentNode.parentNode.cells[0];
          before    = null;
      }
      // Create and insert the help "button"
      var button_construct = null, button = null;
      if (imgs && typeof (Buttons) != 'undefined') {
        button = Buttons.makeButton (imgs, id + '_HelpButton', '#');
        button.style.position = 'relative';
        button.style.top      = '-0.4em';
        button_construct      = button;
      } else {
        button_construct = lk.cloneNode (true);
        button = button_construct.getElementsByTagName ('a')[0];
      }
      insert_in.insertBefore (button_construct, before);
      if (typeof (Tooltip) != 'undefined') {
        // Create the tooltip
        var tooltip =
          new Tooltip
            (  button
             , function () { var hlp = help_id; return UFHelp.getHelp (hlp, true); }
             , { activate     : Tooltip.CLICK
                ,deactivate   : (UFHelp.help_close_imgs
                                 ? Tooltip.CLICK_ELEM
                                 : Tooltip.CLICK_TIP|Tooltip.CLICK_ELEM|Tooltip.LOSE_FOCUS)
                ,close_button : UFHelp.help_close_imgs
                ,mode         : Tooltip.FIXED
                ,fixed_offset : {x:10, y: offset}
                ,max_pixels   : maximum_width
                ,target       : field
                ,open_delay   : 0
                ,hide_delay   : 0
               }
             , UFHelp.tooltip_styles
            );
      } else {
        // Alternative setup without Tooltips: insert help text statically in a table field below the
        // button.
        button.onclick =
          function (evt) { var hlp = help_id; return UFHelp.showHelp (evt, hlp); };
      }
    }
  
    var button_imgs = null, button_lk   = null;
    if (typeof (Buttons) != 'undefined') {
      var button_imgs = UFUI.getEntry ('wpUploadFormHelpOpenButton', 'uiElements', UFUI.internalLanguage);
      if (!button_imgs) button_imgs = UFUI.getEntry ('wpUploadFormHelpOpenButton', 'uiElements');
      var button_lk   = null;
      if (button_imgs) button_imgs = button_imgs.getElementsByTagName ('img');
    }
    if (!button_imgs || button_imgs.length == 0) {
      // Alternative text-based "button"
      button_lk = document.createElement ('sup');
      button_lk.appendChild (document.createElement ('b'));
      button_lk.firstChild.appendChild (document.createTextNode (' ['));
      button_lk.firstChild.appendChild (UFUtils.makeLink ('?', '#'));
      button_lk.firstChild.appendChild (document.createTextNode (']'));
      button_imgs = null;
    } else {
      button_imgs = Buttons.createClass (button_imgs, 'wpUploadFormHelpOpenClass');
    }

    var widest_field = document.getElementById ('wpAdditionalInfo');
    var max_width    = 0;
    if (!widest_field) widest_field = document.getElementById ('wpUploadDescription');
    if (widest_field) {
      var w = UFUtils.getWidth (widest_field);
      try {
        max_width = Math.round (w * 0.9);
      } catch (ex) {
        max_width = 0;
      }
    }
    setHelp ('wpUploadFile', button_imgs, button_lk, max_width, is_reupload);
    setHelp ('wpDestFile', button_imgs, button_lk, max_width, is_reupload);
    setHelp ('wpSource', button_imgs, button_lk, max_width, is_reupload);
    setHelp ('wpAuthor', button_imgs, button_lk, max_width, is_reupload);
    setHelp ('wpDate', button_imgs, button_lk, max_width, is_reupload);
    setHelp ('wpDesc', button_imgs, button_lk, max_width, is_reupload);
//    setHelp ('wpResourceLink', button_imgs, button_lk, max_width, is_reupload);
    setHelp ('wpConceptName', button_imgs, button_lk, max_width, is_reupload);
    setHelp ('wpAltName', button_imgs, button_lk, max_width, is_reupload);
//    setHelp ('wpContentOrg', button_imgs, button_lk, max_width, is_reupload);
    setHelp ('wpTeaches', button_imgs, button_lk, max_width, is_reupload);
    setHelp ('wpBasedOnURL', button_imgs, button_lk, max_width, is_reupload);
    setHelp ('wpPreRequisit', button_imgs, button_lk, max_width, is_reupload);
    setHelp ('wpAssessment', button_imgs, button_lk, max_width, is_reupload);
    setHelp ('wpTimeRequired', button_imgs, button_lk, max_width, is_reupload);
    setHelp ('wpTagsTelugu', button_imgs, button_lk, max_width, is_reupload);
    setHelp ('wpTagsEnglish', button_imgs, button_lk, max_width, is_reupload);
    setHelp ('wpTranslationOf', button_imgs, button_lk, max_width, is_reupload);
    setHelp ('wpAdaptationTroer', button_imgs, button_lk, max_width, is_reupload);
    setHelp ('wpContributorsTroer', button_imgs, button_lk, max_width, is_reupload);
    setHelp ('wpPermission', button_imgs, button_lk, max_width, is_reupload);
    setHelp ('wpOtherVersions', button_imgs, button_lk, max_width, is_reupload);
    setHelp ('wpAdditionalInfo', button_imgs, button_lk, max_width, is_reupload);
    setHelp ('wpLicense', button_imgs, button_lk, max_width, is_reupload);
    setHelp ('wpCategories', button_imgs, button_lk, max_width, is_reupload);
    setHelp ('wpWatchthis', button_imgs, button_lk, max_width, is_reupload);
    setHelp ('wpIgnoreWarning', button_imgs, button_lk, max_width, is_reupload);
  }

}; // end UFHelp

var UploadFormBasic =
{
  on_error_form : false, // True iff we're on a re-sent form (error case).

  setup : function (auto_fill)
  {
    // Special setup: don't use separate input fields; just verify the filename and that the
    // description isn't empty.
    var desc = document.getElementById ('wpUploadDescription');
    var previous_form = null;
    UploadForm.previous_hotcat_state = null;
    if (!UploadForm.isReupload) {
      if (typeof (FormRestorer) != 'undefined') {
        var current_dest_file = document.getElementById ('wpDestFile');
        var original_dest_file = null;
        if (current_dest_file != null) {
          current_dest_file  = current_dest_file.value;
          original_dest_file = current_dest_file.defaultValue;
        }
        if (original_dest_file && original_dest_file.length > 0) {
          // If original_dest_file was set to something, we're not on the original upload form but
          // on the re-sent form in error cases.
          UploadFormBasic.on_error_form = true;
        } else if (current_dest_file && current_dest_file.length > 0) {
          previous_form = FormRestorer.readForm ('UploadFormBasic');
          if (!previous_form && desc && desc.value && desc.value.length > 0) {
            // Hmmm... IE sometimes cannot read the cookie (because it wasn't stored, due to some
            // strange security settings on some computers that I've been unable to track down).
            // If we're here, we have a target file name *and* a description: assume the description
            // comes from the browser's field value cache and make sure we don't overwrite it.
            auto_fill = false;
          }
        }
      }
      if (previous_form) {
        var additional_data = previous_form[0].val;
        if (additional_data) {
          additional_data    = additional_data.split ('\t');
          var previous_file  = additional_data[0];
          if (previous_file == current_dest_file) {
            if (additional_data.length >= 2) UploadForm.previous_hotcat_state = additional_data[1];
          } else {
            previous_form = null;
          }
        }
      }
    }
    UploadForm.formModified = true;
    if (document.getElementById ('wpLicense') != null)
      UploadForm.setup_license_preview ();      
    UploadForm.oldOnSubmit = UploadForm.the_form.onsubmit;
    UploadForm.the_form.onsubmit = UploadFormBasic.submit;
    if (!UploadForm.isReupload) {
      UploadForm.addPreviewButton (UploadFormBasic.preview);
    }
    if (previous_form) {
      // Restore form values.
      if (desc) {
        var prev = UploadForm.getPrevValue (previous_form, desc.id);
        if (prev) desc.value = prev;
      }
      if (UploadForm.previous_hotcat_state != null && typeof (hotcat_set_state) == 'function') {
        var input = document.getElementById ('hotcat_text');
        if (input != null) hotcat_cancel ();
        UploadForm.previous_hotcat_state = hotcat_set_state (UploadForm.previous_hotcat_state);
      }
    } else {
      if (!!UploadForm_autofill && auto_fill && !UploadForm.isReupload) {
        if (desc) desc.value = UploadForm.empty_template ('Information');
      }
    }
    if (desc && desc.value && desc.value.indexOf ('\{\{Information') >= 0) {
      // Only hide the box in the Uploadtext if there is really an inormation-template in the
      // summary!
      var infobox = document.getElementById ('Uploadtext-template-box');
      if (infobox) infobox.style.display = 'none';
    }
  },

  submit : function (evt)
  {
    var overwrite = false;
    if (!UploadForm.isReupload) overwrite = UploadForm.isOverwrite ();
    if (!UploadFormBasic.verify (overwrite)) return false;
    
    if (!UploadForm.isReupload) {
      var target_name = document.getElementById ('wpDestFile');
      if (target_name != null && target_name.value != null) // Strip whitespace
        target_name.value = target_name.value.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
      if (!UploadFormBasic.on_error_form
          && typeof (FormRestorer) != 'undefined'
          && target_name != null && target_name.value != null) {
        var hotcat_state = null;
        if (typeof (hotcat_get_state) == 'function') {
          var input = document.getElementById ('hotcat_text');
          if (input != null) hotcat_closeform ();
          hotcat_state = hotcat_get_state ();
        }
        // We already know that target_name.value is set!
        FormRestorer.saveForm
          (  'UploadFormBasic'
           , UploadForm.the_form.id
           , target_name.value
             + (hotcat_state != null ? '\t' + hotcat_state : '')
           , ';path=' + document.location.pathname
             + ';max-age=1800');
        // Expire after half an hour.
      }
    } // end if (UploadForm.isReupload)
    
    var desc = document.getElementById ('wpUploadDescription');
    var old_desc_value = desc.value;
    var do_submit = UploadForm.call_onsubmit (evt || window.event);
    if (!do_submit)
      desc.value = old_desc_value;
    else {
      desc.value = UploadForm.fixCategoryTransclusion (UploadForm.clean (desc.value));
      UploadForm.hidePreview ();
      document.getElementById ('wpDestFile').disabled = false;
    }
    return do_submit;
  },

  preview : function (evt)
  {
    var overwrite = UploadForm.isOverwrite ();
    if (!UploadFormBasic.verify (overwrite)) return false;
    
    var desc = document.getElementById ('wpUploadDescription');
    UploadForm.makePreview (UploadForm.clean (desc.value), overwrite);
    return true;
  },
  
  verify : function (overwrite)
  {
    var desc = document.getElementById ('wpUploadDescription');
    var ok   = true;

    if (UploadForm.isReupload) {
      // Only check that the description isn't empty
      if (UploadForm.errorMsgs != null) delete UploadForm.errorMsgs;
      UploadForm.errorMsgs = new Array ();
      UploadForm.warning_pushed = false;
      if (!desc.value || desc.value.search (/\S/) < 0) {
        desc.style.backgroundColor = UploadForm.errorColor;
        desc.onkeyup = UploadForm.resetBg;
        UploadForm.errorMsgs.push ('wpReuploadNoSummaryError');
        ok = false;
      }
    } else {
      if (!overwrite) {
        if (UploadForm.errorMsgs != null) delete UploadForm.errorMsgs;
        UploadForm.errorMsgs = new Array ();
        UploadForm.warning_pushed = false;
  
        if (!UploadForm.verifyMandatoryField (desc)) {
          desc.onkeyup = UploadForm.resetBg;
          ok = false;
        } else {
          // We do have a non-empty description. Try to split it up and check that the fields for
          // author, source, and description are filled in.
          var fields = UploadForm.split_description (desc.value);
          if (fields != null && fields.length == 4) {
            if (   fields[1] == null || fields[1].search (/\S/) < 0  // Author
                || fields[2] == null || fields[2].search (/\S/) < 0) // Source
            {
              desc.style.backgroundColor = UploadForm.errorColor;
              desc.onkeyup = UploadForm.resetBg;
              if (!UploadForm.warning_pushed) {
                if (UploadForm.errorMsgs != null)
                  UploadForm.errorMsgs.push ('wpUploadWarningError');
                UploadForm.warning_pushed = true;
              }
              ok = false;
            }
            if (   UploadForm.templates[fields[0]].desc_mandatory
                && (fields[3] == null || fields[3].search (/\S/) < 0)) // Description
            {
              desc.style.backgroundColor = UploadForm.errorColor;
              desc.onkeyup = UploadForm.resetBg;
              UploadForm.errorMsgs.push ('wpNoDescriptionError');
              ok = false;
            }
          }
        }
        // Try a license check
        var license = document.getElementById ('wpLicense');
        if (license == null || license.selectedIndex == 0) {
          // There must be a license somewhere in the description.
          if (!UploadForm.has_license ([desc])) {
            var d = desc.value.replace (/\{\{\s*([Ii]nformation|[Pp]ainting|[Ff]lickr)\s*\n/g,"");  
            if (d.indexOf ('\{\{') < 0) {
              // No transcludion that could provide a license either
              desc.style.backgroundColor = UploadForm.errorColor;
              desc.onkeyup = UploadForm.resetBg;
              if (!UploadForm.warning_pushed) {
                if (UploadForm.errorMsgs != null)
                  UploadForm.errorMsgs.push ('wpUploadWarningError');
                UploadForm.warning_pushed = true;
              }
              ok = false;
            } else {
              // Assume it's ok.
            }
          }
        } // end license check
        var target_name = document.getElementById ('wpDestFile');
        if (target_name != null) {
          // Trim leading and trailing whitespace
          target_name.value = target_name.value.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
          if (!UploadForm.verifyFileName (target_name.value)) {
            target_name.style.backgroundColor = UploadForm.errorColor;
            target_name.onkeyup =
              function (evt)
              {
                UploadForm.resetBg (evt);
                if (window.wgUploadWarningObj && typeof (wgUploadWarningObj.keypress) == 'function' && !UploadForm.isReupload)
                  wgUploadWarningObj.keypress();
              };
            ok = false;
          }
        }
      }
    } // end if (reupload or not)
    if (!ok) {
      UploadForm.hidePreview ();
      UploadForm.display_errors ();
    } else {
      // It's ok: hide our warning box
      var my_warning = document.getElementById ('wpUploadVerifyWarning');
      if (my_warning != null) my_warning.style.display = 'none';
    }
    return ok;
  } // end verify

}; // end UploadFormBasic

var UploadFormFull =
{
  form_type             : 0,     // 0 - single desc field; 1 - one desc field per language
  field_state           : null,  // Will be initialized in setup below.

  multi_inputs : null,
  // If we're using several description fields, this is an array of objects
    
  pushMultiInput : function (sel, text)
  {
    if (UploadFormFull.multi_inputs == null) {
      UploadFormFull.multi_inputs = [{selector : sel, textfield : text}];
    } else {
      UploadFormFull.multi_inputs[UploadFormFull.multi_inputs.length] =
        {selector : sel, textfield : text};
    }
    var idx   = UploadFormFull.multi_inputs.length;
    sel.id    = 'wpLangSel' + idx;
    sel.name  = sel.id;
    text.id   = 'wpDescText' + idx;
    text.name = text.id;  
  },
    
  addDescField : function (content, lang, idx, stored_form)
  {
    var selector = LanguageHandler.getSelect
                     (null, lang, UFUI.getLabel ('wpUnknownLanguageUploadLbl', true));
    // These style definitions are needed for IE, which otherwise creates excessively wide
    // selectors, pushing the main form to the right.
    selector.style.maxWidth = '12em';
    selector.style.width    = '12em';
    selector.style.overflow = 'hidden';
    var textfield  = document.createElement ('textarea');
    textfield.setAttribute ('rows', UFUtils.getHeight (UploadForm_description_height, 2, 6));
    textfield.style.width = '100%';
    UploadFormFull.pushMultiInput (selector, textfield);

    var new_row = content.insertRow (idx == null ? content.rows.length : idx);
    var first_cell = document.createElement ('td');
    first_cell.classList.add('mw-label');
    first_cell.setAttribute ('vAlign', 'top');
    first_cell.appendChild (selector);
    var second_cell = document.createElement ('td');
    second_cell.classList.add('mw-input');
    second_cell.setAttribute ('vAlign', 'top');
    second_cell.appendChild (textfield);
    new_row.appendChild (first_cell);
    new_row.appendChild (second_cell);

    if (stored_form != null) {
      var prev_idx = UploadForm.getPrevValue (stored_form, selector.id);
      var prev_val = UploadForm.getPrevValue (stored_form, textfield.id);
      if (prev_val != null) textfield.value = prev_val;
      if (prev_idx != null) {
        selector.options[selector.selectedIndex].selected = false;
        selector.options[prev_idx].selected = true;
      }
    }
    UploadFormFull.enable_edittools (textfield);
  },
  
  addOneDescField : function (evt) // onclick handler for the button
  {
    var button = document.getElementById ('wpUploadAddDescription');
    var table_row = button.parentNode.parentNode;
    var idx = table_row.rowIndex;
    UploadFormFull.addDescField (table_row.parentNode, null, idx, null);
  },

  addMultiDesc : function (table, idx, stored_form)
  {
    // Add en and user language, if different
    var userLang = LanguageHandler.closestLanguage (UFUI.userLanguage);

    if (userLang == 'pt-br') userLang = 'pt'; // Per request from Portuguese and Brazilians
    var first_cell = document.createElement ('td');
    first_cell.classList.add('mw-label');
    var second_cell = document.createElement ('td');
    var new_label = document.createElement ('label');
    new_label.id = 'wpDescLabel';
    new_label.appendChild (UFUI.getLabel ('wpDescUploadLbl'));
    first_cell.appendChild (new_label);

    var new_row = table.insertRow (idx);
    new_row.appendChild (first_cell);
    new_row.appendChild (second_cell);
    idx++;

    var added = false;
    if (stored_form != null) {
      // Maybe we had more... find 'wpLangSel1'
      var curr = 0;
      for (var i = 1; i < stored_form.length; i++) {
        if (stored_form[i].id == 'wpLangSel1') {
          curr = i; break;
        }
      }
      if (curr > 0) {
        while (   curr < stored_form.length
               && stored_form[curr].id.indexOf ('wpLangSel') == 0) {
          UploadFormFull.addDescField (table, null, idx++, stored_form);
          added = true; curr++;
          if (curr < stored_form.length && stored_form[curr].id.indexOf ('wpDescText') == 0)
            curr++;
        }
      }
    } // end if
    if (!added) {
      if (   UploadForm_description_languages
          && UploadForm_description_languages.constructor == Array
          && UploadForm_description_languages.length > 0) {
        for (var i = 0; i < UploadForm_description_languages.length; i++) {
          var lang = LanguageHandler.closestLanguage (UploadForm_description_languages[i]);
          UploadFormFull.addDescField (table, lang, idx++, stored_form);
        }
      } else {
        if (UploadForm_own_language_first) {
          if (userLang != null && userLang != UFUI.defaultLanguage)
            UploadFormFull.addDescField (table, userLang, idx++, stored_form);
          UploadFormFull.addDescField (table, UFUI.defaultLanguage, idx++, stored_form);
        } else {
          UploadFormFull.addDescField (table, UFUI.defaultLanguage, idx++, stored_form);
          if (userLang != null && userLang != UFUI.defaultLanguage)
            UploadFormFull.addDescField (table, userLang, idx++, stored_form);
        }
      }
    }
    // Now add a "+" button
    var additional =
      UploadForm.customFormButton
        (  'wpUploadFormAddDescButton'     // Customization ID
         , 'wpUploadAddDescription'        // ID of button
         , '+'                             // Default text
         , UploadFormFull.addOneDescField  // Event handler
        );
    
    new_row = table.insertRow (idx++);
    first_cell = document.createElement ('td');
    second_cell = document.createElement ('td');
    second_cell.classList.add('mw-input');
    second_cell.appendChild (additional);
    new_row.appendChild (first_cell);
    new_row.appendChild (second_cell);
    
    return idx;
  },

  changeField : function (field_id) // Callback for changeable field button
  {
    function get_selection (field)
    {
      // Based on code from Jonas Raoni Soares Silva at http://jsfromhell.com/forms/selection
      // License: {{tl|attribution}}
      // Warning: simplified because we apply it only to an INPUT field. For TEXTAREAs, see the
      // URL given.
      if (typeof (field.selectionStart) != 'undefined')
        return {start: field.selectionStart, end: field.selectionEnd};
      else if (!!field.createTextRange) {
        field.focus ();
        var s = document.selection.createRange();
        if (s.parentElement() != field) return {start: 0, end: 0};
        var r = field.createTextRange(); r.setEndPoint ("EndToStart", s);
        return {start: r.text.length, end: r.text.length + s.text.length};
      }
      return {start: 0, end: 0};
    }

    var field  = document.getElementById (field_id);
    if (field.disabled) return; // Don't do anything if the field isn't enabled.

    var button = document.getElementById (field_id + '_Button');
    var cell   = field.parentNode;
    if (!field || !button || !cell) return; // Error message here?
    var new_field = document.createElement ('textarea');
    var height = UFUtils.getHeight (UploadFormFull.field_state[field_id].height, 2, 4);
    new_field.setAttribute ('rows', height);
    new_field.style.width = '100%';
    new_field.value = field.value;
    var sel = get_selection (field);
    var tab_idx = field.getAttribute ('tabindex');
    cell.removeChild (button);
    cell.replaceChild (new_field, field);
    field.id = "";
    field.onfocus = null;
    new_field.id = field_id;
    new_field.setAttribute ('tabindex', tab_idx);
    UploadFormFull.enable_edittools (new_field);
    // Restore the selection
    if (!!new_field.setSelectionRange) // e.g. khtml
      new_field.setSelectionRange (sel.start, sel.end);
    else if (typeof (new_field.selectionStart) != 'undefined') {
      new_field.selectionStart = sel.start;
      new_field.selectionEnd   = sel.end;
    } else if (new_field.createTextRange) { // IE
      var new_selection = new_field.createTextRange();
      new_selection.move ("character", sel.start);
      new_selection.moveEnd ("character", sel.end - sel.start);
      new_selection.select();
    }
    new_field.focus ();
    UploadFormFull.field_state[field_id].height = height;
  },
  
  enable_edittools : function (textfield)
  {
    // To be called on each dynamically added field to ensure the edit toolbar works there
    if (typeof (EditTools) != 'undefined' && typeof (EditTools.registerTextField) == 'function') {
      // We have EditTools
      var buttons = document.getElementById('specialchars');
      if (buttons && buttons.firstChild && buttons.firstChild.nodeName.toLowerCase() == 'select') {
        // EditTools is already set up: we have to add an onfocus handler ourselves
        $(textfield).focus(EditTools.registerTextField);
      }
      // Otherwise, EditTools will be set up later, and will catch this field, so we don't have
      // to do anything.
    }
  },

  switch_intro_text : function ()
  {
    // Set up the display of [[MediaWiki:Uploadtext]]
    var long_text  = document.getElementById ('wpUploadFormLongText');
    var short_text = document.getElementById ('wpUploadFormShortText');
    if (long_text && short_text) {
      long_text.style.display = 'none';
      if (UFUtils.isChildOf (long_text, short_text)) {
        // If long_text is a child of short_text, then short_text is already shown, and
        // long_text is just a part that isn't needed for the new upload form. Hence
        // we're done.
        return;
      }
      if (UFUtils.isChildOf (short_text, long_text)) {
        // If the short_text is within the long_text, we need to take it out; otherwise
        // it won't be shown.
        short_text.parentNode.removeChild (short_text);
        long_text.parentNode.insertBefore (short_text, long_text.nextSibling);
      }
      short_text.style.display = "";
    } else {
      // Remove the redundant infobox in the uploadtext explanation. People should *not*
      // insert this template into description.
      var infobox = document.getElementById ('Uploadtext-template-box');
      if (infobox) infobox.style.display = 'none';
    }
  },

  set_hints : function ()
  {
    UploadForm.addAfterField ('wpDestFile', UFUI.getHint ('wpUploadFormDestFileHint'));
    UploadForm.addAfterField ('wpSource', UFUI.getHint ('wpUploadFormSourceHint'));
    UploadForm.addAfterField ('wpAuthor', UFUI.getHint ('wpUploadFormAuthorHint'));
    UploadForm.addAfterField ('wpDate', UFUI.getHint ('wpUploadFormDateHint'));
    UploadForm.addAfterField ('wpPermission', UFUI.getHint ('wpUploadFormPermissionHint'));
    UploadForm.addAfterField ('wpAdditionalInfo', UFUI.getHint ('wpUploadFormAdditionalInfoHint'));
    UploadForm.addAfterField ('catlinks', UFUI.getHint ('wpUploadFormCategoryHint'));
  },

  setup : function ()
  {
    function addField (table, idx, id, label, field, stored_form)
    {
      if (label == null) label = UFUI.getLabel (id + 'UploadLbl');
      var new_row = table.insertRow (idx);
      var firstCell = document.createElement ('td');
      firstCell.classList.add('mw-label');
      var new_label = document.createElement ('label');
      new_label.htmlFor = id;
      new_label.appendChild (label);
      firstCell.appendChild (new_label);
      var secondCell = document.createElement ('td');
      secondCell.classList.add('mw-input');
      field.setAttribute ('name', id);
      field.setAttribute ('id', id);
      secondCell.appendChild (field);
      new_row.appendChild (firstCell);
      new_row.appendChild (secondCell);
      var prev_value = UploadForm.getPrevValue (stored_form, id);
      if (prev_value != null) field.value = prev_value;
      UploadFormFull.enable_edittools (field);
    }

    function addInput (table, idx, id, label, width, stored_form)
    {
      var new_field = document.createElement ('input');
      new_field.setAttribute ('type', 'text');
      new_field.setAttribute ('size', '' + width);
      addField (table, idx, id, label, new_field, stored_form);
      UploadFormFull.enable_edittools (new_field);
      return new_field;
    }

    function addChangeableField (height, table, idx, id, label, width, stored_form)
    {
      var new_field = null;
      var field_id = 'wp' + id;
      if (height == 0)
        height = UFUtils.getHeight (UploadFormFull.field_state[field_id].height, 1, 4);
      if (height > 1) {
        new_field = document.createElement ('textarea');
        new_field.setAttribute ('rows', height);
        new_field.style.width = '100%';
        addField (table, idx, 'wp' + id, null, new_field, stored_form);
      } else {
        new_field  = addInput (table, idx, field_id, null, 80, stored_form);
        var button =
          UploadForm.customFormButton
            (  'wpUploadForm' + id + 'Button'
             , field_id + '_Button'
             , '...'
             , function () { UploadFormFull.changeField (field_id); }
            );
        new_field.parentNode.insertBefore (button, new_field.nextSibling);
      }
      UploadFormFull.field_state[field_id].height = height;
      UploadFormFull.enable_edittools (new_field);
    }

    function setCheckBoxes (previous_form, boxes)
    {
      if (boxes == null || boxes.length == 0 || previous_form == null) return;
      for (var i = 0; i < boxes.length; i++) {
        if (boxes[i] != null) {
          var prev_val = UploadForm.getPrevValue (previous_form, boxes[i].id);
          if (prev_val != null) boxes[i].checked = prev_val;
        }
      }
    }

    // Init the field states. Cannot be done earlier, otherwise definitions in user's
    // monobook.js (or modern.js, or ...) won't be taken aboard.
    UploadFormFull.field_state =
      { wpSource: {height: UploadForm_source_field_size}
       ,wpAuthor: {height: UploadForm_author_field_size}
      };

    var previous_form         = null;
    var previous_type         = -1; // unknown
    var previous_fields       = [0, 0];
    UploadForm.previous_hotcat_state = null;
    if (typeof (FormRestorer) != 'undefined') {
      // We know that when we arrive here originally, wpDestFile.value is empty, as is
      // wpDestFile.defaultValue. If we entered something, submitted, and then come back,
      // modern browsers restore form entries, at least for the fields in the static XHTML.
      // wpDestFile is such a static field (it isn't added by Javascript), so if we have a
      // non-empty value here, we know that the form needs to restored. (But see the caveat
      // about IE and onload handling at the bottom of the file!)
      var current_dest_file = document.getElementById ('wpDestFile');
      if (current_dest_file != null) current_dest_file = current_dest_file.value;
      if (current_dest_file != null && current_dest_file.length > 0) {
        previous_form = FormRestorer.readForm ('UploadForm');
      }
      if (previous_form) {
        var additional_data = previous_form[0].val;
        if (additional_data) {
          additional_data    = additional_data.split ('\t');
          var previous_file  = additional_data[1];
          if (previous_file == current_dest_file) {
            previous_type      = parseInt (additional_data[0], 10);
            previous_fields[0] = parseInt (additional_data[2], 10);
            previous_fields[1] = parseInt (additional_data[3], 10);          
            if (additional_data.length >= 5) UploadForm.previous_hotcat_state = additional_data[4];
          } else {
            previous_form = null;
          }
        }
      }
    }
    var original_desc = document.getElementById ('wpUploadDescription');
    var original_row  = original_desc.parentNode.parentNode;
    var table         = original_row.parentNode;
    var original_idx  = original_row.rowIndex;
    UploadForm.formModified = true;
    original_desc.setAttribute ('id', "");
    UploadForm.oldOnSubmit = UploadForm.the_form.onsubmit;
    UploadForm.the_form.onsubmit = UploadFormFull.submit;
    table.deleteRow (original_idx);
    var idx = original_idx;
    // Insert source field
    var new_field = null;
    addChangeableField (previous_fields[0], table, idx++, 'Source', null, 80, previous_form);
    addChangeableField (previous_fields[1], table, idx++, 'Author', null, 80, previous_form);
    addInput (table, idx++, 'wpDate', null, 80, previous_form);
    // Insert description field
    if (typeof (LanguageHandler) == 'undefined' || previous_type == 0) {
      // Basic setup
      new_field = document.createElement ('textarea');
      new_field.setAttribute ('rows', UFUtils.getHeight (UploadForm_description_height, 6, 12));
      new_field.style.width = '100%';
      // Do not name the new field 'wpUploadDescription', otherwise MediaWiki:Upload.js
      // might prefill it with an information template!
      addField (table, idx++, 'wpDesc', null, new_field, previous_form);
      UploadFormFull.form_type = 0;
    } else {
      idx = UploadFormFull.addMultiDesc (table, idx, previous_form);
      UploadFormFull.form_type = 1;
    }
//    addInput (table, idx++, 'wpResourceLink', null, 80, previous_form);
    addInput (table, idx++, 'wpTeaches', null, 80, previous_form);
    addInput (table, idx++, 'wpTagsTelugu', null, 80, previous_form);
    addInput (table, idx++, 'wpTagsEnglish', null, 80, previous_form);
//    addInput (table, idx++, 'wpContentOrg', null, 80, previous_form);
    addInput (table, idx++, 'wpBasedOnURL', null, 80, previous_form);
    addInput (table, idx++, 'wpPreRequisit', null, 80, previous_form);
    addInput (table, idx++, 'wpAssessment', null, 80, previous_form);
    addInput (table, idx++, 'wpTimeRequired', null, 80, previous_form);
    addInput (table, idx++, 'wpTranslationOf', null, 80, previous_form);
    addInput (table, idx++, 'wpAdaptationTroer', null, 80, previous_form);
    addInput (table, idx++, 'wpContributorsTroer', null, 80, previous_form);
    addInput (table, idx++, 'wpConceptName', null, 80, previous_form);
    addInput (table, idx++, 'wpAltName', null, 80, previous_form);
    addInput (table, idx++, 'wpOtherVersions', null, 80, previous_form);
    addInput (table, idx++, 'wpPermission', null, 80, previous_form);
    new_field = document.createElement ('textarea');
    new_field.setAttribute
      ('rows', UFUtils.getHeight (UploadForm_additional_info_height, 2, 10));
    new_field.style.width = '100%';
    // Work-around Firefox's "one additional line" bug
    addField (table, idx++, 'wpAdditionalInfo', null, new_field, previous_form);
    // Add a preview button.
    UploadForm.addPreviewButton (UploadFormFull.preview);
    // Correct tab indices.
    for (var i = 0; i < UploadForm.the_form.length; i++) {
      UploadForm.the_form.elements[i].setAttribute ('tabindex', '' + i);
    }
    var license = document.getElementById ('wpLicense');
    // Change the license previewer to not cause a table re-layout
    if (license != null) {
      // These style definitions are because long option labels result in excessively wide
      // selectors, causing also the description fields to go beyond the right border of the
      // page.
      license.style.maxWidth = '100%';
      license.style.width    = '100%';
      license.style.overflow = 'hidden';
    }
    UploadForm.setup_license_preview ();
    if (license != null) {
      var prev = UploadForm.getPrevValue (previous_form, 'wpLicense');
      if (prev != null) {
        try {
          license.options[license.selectedIndex].selected = false;
          license.options[prev].selected = true;
        } catch (ex) {
        }
      }
    }
    // Pre-fill in some cases
    if (UFUI.isOwnWork) {
      var src     = document.getElementById ('wpSource');
      var author  = document.getElementById ('wpAuthor');
      if (src != null && (src.value == null || src.value.length == 0)) {
        src.value = UploadForm.getOwnWorkSource ();
      }
      if (author != null && (author.value == null || author.value.length == 0)) {
        author.value = UploadForm.getOwnWorkAuthor ();
      }
      if (typeof (UploadForm_ownwork_date) == 'string'
          && UploadForm_ownwork_date.search (/\S/) >= 0) {
        var date = document.getElementById ('wpDate');
        if (date != null && (date.value == null || date.value.length == 0)) {
          date.value = UploadForm_ownwork_date;
        }
      }
    }
    if (previous_form != null) {
      setCheckBoxes
        (  previous_form
         , [  document.getElementById ('wpWatchthis')
            , document.getElementById ('wpIgnoreWarning')]);
    }     
    UploadFormFull.switch_intro_text ();    
    // If HotCat is present, restore its state, too.
    if (UploadForm.previous_hotcat_state != null && typeof (hotcat_set_state) == 'function') {
      var input = document.getElementById ('hotcat_text');
      if (input != null) hotcat_cancel ();
      UploadForm.previous_hotcat_state = hotcat_set_state (UploadForm.previous_hotcat_state);
    }
    UploadFormFull.set_hints ();
  }, // end setup

  get_desc_text : function (basic)
  {
    var desc_text = "";
    if (UploadFormFull.multi_inputs == null) {
      var desc = document.getElementById ('wpDesc');
      if (desc != null && !desc.disabled) desc_text = UploadForm.clean (desc.value);
    } else {
      for (var i = 0; i < UploadFormFull.multi_inputs.length; i++) {
        if (!UploadFormFull.multi_inputs[i].textfield.disabled) {
          var text     = UploadFormFull.multi_inputs[i].textfield.value;
          var selector = UploadFormFull.multi_inputs[i].selector;
          var lang     = selector.options[selector.selectedIndex].value;
          if (text != null && text.length > 0) {
            text = UploadForm.clean (text);
            if (desc_text.length > 0) desc_text = desc_text + '\n';
            if (!basic && lang != null && lang.length > 0 && lang != 'unknown') {
              // This is Commons-specific! The tl-template is already used, the template for
              // Tagalog is tgl!
              if (lang == 'tl') lang = 'tgl';
              desc_text = desc_text + '\{\{' + lang + '|1=' + text + '\}\}';
            } else
              desc_text = desc_text + text;
          }
        } // end if !disabled
      }
    }
    var more_info  = document.getElementById ('wpAdditionalInfo');
    if (!basic) {
      var src        = document.getElementById ('wpSource');
      var author     = document.getElementById ('wpAuthor');
      var date       = document.getElementById ('wpDate');
      var other      = document.getElementById ('wpPermission');
//      var resourceli  = document.getElementById ('wpResourceLink');
      var conceptname  = document.getElementById ('wpConceptName');
      var altname  = document.getElementById ('wpAltName');
//      var contentorg  = document.getElementById ('wpContentOrg');
      var teaches  = document.getElementById ('wpTeaches');
      var basedonurl  = document.getElementById ('wpBasedOnURL');
      var prerequisit  = document.getElementById ('wpPreRequisit');
      var assessment  = document.getElementById ('wpAssessment');
      var timerequir  = document.getElementById ('wpTimeRequired');
      var tagstelugu  = document.getElementById ('wpTagsTelugu');
      var tagsenglish  = document.getElementById ('wpTagsEnglish');
      var translationof  = document.getElementById ('wpTranslationOf');
      var adaptationtroer  = document.getElementById ('wpAdaptationTroer');
      var contributorstroer  = document.getElementById ('wpContributorsTroer');
      var othervers  = document.getElementById ('wpOtherVersions');
      desc_text = '\{\{Information\n'
                   + '|Description    ='+ desc_text + '\n'
                   + '|Source         =' + (!src.disabled ? UploadForm.clean (src.value) : "") + '\n'
                   + '|Author         =' + (!author.disabled ? UploadForm.clean (author.value) : "") + '\n'
                   + '|Date           =' + (!date.disabled ? UploadForm.clean (date.value) : "") + '\n'
//                   + '|Resource_link           =' + (!resourceli.disabled ? UploadForm.clean (resourceli.value) : "") + '\n'
                   + '|Name           =' + (!conceptname.disabled ? UploadForm.clean (conceptname.value) : "") + '\n'
                   + '|Alt_names           =' + (!altname.disabled ? UploadForm.clean (altname.value) : "") + '\n'
//                   + '|Content_org           =' + (!contentorg.disabled ? UploadForm.clean (contentorg.value) : "") + '\n'
                   + '|Teaches           =' + (!teaches.disabled ? UploadForm.clean (teaches.value) : "") + '\n'
                   + '|Based_on_url           =' + (!basedonurl.disabled ? UploadForm.clean (basedonurl.value) : "") + '\n'
                   + '|Requires           =' + (!prerequisit.disabled ? UploadForm.clean (prerequisit.value) : "") + '\n'
                   + '|Assessment           =' + (!assessment.disabled ? UploadForm.clean (assessment.value) : "") + '\n'
                   + '|Time_required           =' + (!timerequir.disabled ? UploadForm.clean (timerequir.value) : "") + '\n'
                   + '|Tags_Telugu           =' + (!tagstelugu.disabled ? UploadForm.clean (tagstelugu.value) : "") + '\n'
                   + '|Tags_English           =' + (!tagsenglish.disabled ? UploadForm.clean (tagsenglish.value) : "") + '\n'
                   + '|Translation_of           =' + (!translationof.disabled ? UploadForm.clean (translationof.value) : "") + '\n'
                   + '|Adaptationup           =' + (!adaptationtroer.disabled ? UploadForm.clean (adaptationtroer.value) : "") + '\n'
                   + '|Contributorup           =' + (!contributorstroer.disabled ? UploadForm.clean (contributorstroer.value) : "") + '\n'
                   + ((other && !other.disabled && other.value != null)
                      ? '|Permission     =' + UploadForm.clean (other.value) + '\n'
                      : "")
                   + ((othervers && !othervers.disabled && othervers.value != null)
                      ? '|other_versions =' + UploadForm.clean (othervers.value) +'\n'
                      : "")
                   + '\}\}\n';
    } else {
      desc_text = desc_text + '\n';
    }
    // Append the additional info, if any
    if (more_info && !more_info.disabled && more_info.value)
      desc_text = desc_text + UploadForm.clean (more_info.value);
    return desc_text;
  },
  
  submit : function (evt)
  {
    var overwrite = UploadForm.isOverwrite ();
    if (!UploadFormFull.verify (overwrite)) return false;
    
    // Now put together an information-template
    var desc_text = UploadFormFull.get_desc_text (overwrite);
    
    var do_submit = true;

    var target_name = document.getElementById ('wpDestFile');
    if (target_name != null && target_name.value != null) // Strip whitespace
      target_name.value = target_name.value.replace(/^\s\s*/, '').replace(/\s\s*$/, '');

    var dummy_desc = document.getElementById ('wpUploadDescription');
    // Sometimes, we do restore from scratch, and sometimes, the browser manages to keep everything.
    // If so, we may have a wpUploadDescription from an earlier submission. Remove it.
    if (dummy_desc != null) dummy_desc.parentNode.removeChild (dummy_desc);
    
    if (   typeof (FormRestorer) != 'undefined'
        && target_name != null && target_name.value != null) {
      var hotcat_state = null;
      if (typeof (hotcat_get_state) == 'function') {
        var input = document.getElementById ('hotcat_text');
        if (input != null) hotcat_closeform ();
        hotcat_state = hotcat_get_state ();
      }
      // We already know that target_name.value is set!
      FormRestorer.saveForm
        (  'UploadForm'
         , UploadForm.the_form.id
         , "" + UploadFormFull.form_type
           + '\t' + target_name.value
           + '\t' + UploadFormFull.field_state.wpSource.height
           + '\t' + UploadFormFull.field_state.wpAuthor.height
           + (hotcat_state != null ? '\t' + hotcat_state : '')
         , ';path=' + document.location.pathname
           + ';max-age=1800');
      // Expire after half an hour.
    }
    
    dummy_desc = document.createElement ('textarea');
    dummy_desc.setAttribute ('rows', '6');
    dummy_desc.setAttribute ('cols', '80');
    dummy_desc.style.display = 'none';
    dummy_desc.setAttribute ('name', 'wpUploadDescription');
    dummy_desc.setAttribute ('id', 'wpUploadDescription');
    UploadForm.the_form.appendChild (dummy_desc);
    dummy_desc.value = UploadForm.fixCategoryTransclusion (desc_text);

    do_submit = UploadForm.call_onsubmit (evt || window.event);
    if (!do_submit) {
      // Oops. We actually don't submit. Remove the hidden field
      UploadForm.the_form.removeChild (dummy_desc);
    } else {
      UploadForm.hidePreview ();
      document.getElementById ('wpDestFile').disabled = false;
      document.getElementById ('wpEditToken').disabled = false;
    }
    return do_submit;
  },

  preview : function (evt)
  {
    var overwrite = UploadForm.isOverwrite ();
    if (!UploadFormFull.verify (overwrite)) return false;
    
    UploadForm.makePreview (UploadFormFull.get_desc_text (overwrite), overwrite);
    return true;
  },

  verify : function (overwrite)
  {
    var src       = document.getElementById ('wpSource');
    var author    = document.getElementById ('wpAuthor');
    var date      = document.getElementById ('wpDate');
    var other     = document.getElementById ('wpPermission');
//    var resourceli = document.getElementById ('wpResourceLink');
    var conceptname = document.getElementById ('wpConceptName');
    var altname = document.getElementById ('wpAltName');
//    var contentorg = document.getElementById ('wpContentOrg');
    var teaches = document.getElementById ('wpTeaches');
    var basedonurl = document.getElementById ('wpBasedOnURL');
    var prerequisit = document.getElementById ('wpPreRequisit');
    var assessment = document.getElementById ('wpAssessment');
    var timerequir = document.getElementById ('wpTimeRequired');
    var tagstelugu = document.getElementById ('wpTagsTelugu');
    var tagsenglish = document.getElementById ('wpTagsEnglish');
    var translationof = document.getElementById ('wpTranslationOf');
    var adaptationtroer = document.getElementById ('wpAdaptationTroer');
    var contributorstroer = document.getElementById ('wpContributorsTroer');
    var othervers = document.getElementById ('wpOtherVersions');
    var moreInfo  = document.getElementById ('wpAdditionalInfo');
    var ok        = true;

    if (!overwrite) {
      if (UploadForm.errorMsgs != null) delete UploadForm.errorMsgs;
      UploadForm.errorMsgs = new Array ();
      UploadForm.warning_pushed = false;

      if (!UploadForm.verifyMandatoryField
            (src,
             function (src)
             {
                var flickr_ok = !UFUI.isFromFlickr ||
                                src.search (/https?:\/\/([^.\/]+\.)*flickr\.com/) >= 0;
                if (!flickr_ok) UploadForm.errorMsgs.push ('wpFlickrURLError');
                return flickr_ok;
             }
            )
         ) 
      {
        src.onkeyup = UploadForm.resetBg;
        ok = false;
      }
      if (!UploadForm.verifyMandatoryField (author)) {
        author.onkeyup = UploadForm.resetBg;
        ok = false;
      }
      // Piece the description(s) together
      var all_descs = "";
      if (UploadFormFull.multi_inputs == null) {
        var desc = document.getElementById ('wpDesc');
        if (desc != null) all_descs = desc.value;
      } else {
        for (var input_idx = 0; input_idx < UploadFormFull.multi_inputs.length; input_idx++) {
          all_descs = all_descs + UploadFormFull.multi_inputs[input_idx].textfield.value;
        }
      }
      // License check
      var license_field = document.getElementById ('wpLicense');
      var license_chosen = license_field == null || license_field.selectedIndex > 0;
      if (!license_chosen && !UploadForm.has_license ([all_descs, other, moreInfo])) {
        if (!UploadForm.warning_pushed) {
          UploadForm.errorMsgs.push ('wpUploadWarningError');
          UploadForm.warning_pushed = true;
        }
        ok = false;
      }
      var target_name = document.getElementById ('wpDestFile');
      if (target_name != null) {
        // Trim leading and trailing whitespace
        target_name.value = target_name.value.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
        if (!UploadForm.verifyFileName (target_name.value)) {
          target_name.style.backgroundColor = UploadForm.errorColor;
          target_name.onkeyup =
            function (evt)
            {
              UploadForm.resetBg (evt);
              if (window.wgUploadWarningObj && typeof (wgUploadWarningObj.keypress) == 'function' && !UploadForm.isReupload)
                wgUploadWarningObj.keypress();
            };
          ok = false;
        }
      }
      if (UploadForm.templates[0].desc_mandatory) {
        if (all_descs.search (/\S/) < 0) {
          if (UploadFormFull.multi_inputs == null) {
            var desc = document.getElementById ('wpDesc');
            if (desc != null) {
              desc.style.backgroundColor = UploadForm.errorColor;
              desc.onkeyup = UploadForm.resetBg;
            }
          } else {
            UploadFormFull.setMultiBg (UploadForm.errorColor, UploadFormFull.resetMultiBg);
          }
          UploadForm.errorMsgs.push ('wpNoDescriptionError');
          ok = false;
        }
      } // end description check
    } // end overwrite
    if (!ok) {
      UploadForm.hidePreview ();
      UploadForm.display_errors ();
    } else {
       // It's ok: hide our warning box
      var my_warning = document.getElementById ('wpUploadVerifyWarning');
      if (my_warning != null) my_warning.style.display = 'none';
    }
    return ok;
  },

  setMultiBg : function (color, handler)
  {
    if (UploadFormFull.multi_inputs == null) return;
    for (var i = 0; i < UploadFormFull.multi_inputs.length; i++) {
      var field = UploadFormFull.multi_inputs[i].textfield;
      field.style.backgroundColor = color;
      field.onkeyup               = handler;
    }
  },

  resetMultiBg : function (evt)
  {
    if (UploadForm.resetBg (evt)) {
      // Reset the backgrounds of all description fields
      UploadFormFull.setMultiBg ('#FFFFFF', null);
    }
  }

}; // end UploadFormFull

var UploadForm =
{
  isInstalled  : false, // Set to true when the onload hook runs

  debug        : false, // Can be set to true by adding "&debug=true" to the URL
  
  oldOnSubmit  : null,        // Possibly already existing onsubmit handler
  errorColor   : 'lightpink', // The light red from Template:Copyvio
  formModified : false,

  isReupload   : false,

  setup_hotcat_label : function ()
  {
    // If HotCat is present, translate its label if we can find it
    var hotcat_label_cell = document.getElementById ('hotcatLabel');
    if (hotcat_label_cell != null) {
      // Change its ID, just to be sure
      hotcat_label_cell.setAttribute ('id', 'hotcatLabelTranslated');
      // Assumes that the cell has only one child (which is normally the case)
      hotcat_label_cell.replaceChild
        (  UFUI.getLabel ('wpCategoriesUploadLbl')
         , hotcat_label_cell.firstChild);
    }
  },
  
  setup_error_display : function ()
  {
    var warning_cell = document.getElementById ('wpDestFile-warning');
    if (!warning_cell) return;
    var row      = warning_cell.parentNode;
    var new_cell = document.createElement ('td');
    new_cell.style.padding = '0';
    // Remove the colspan, if any, and insert a new cell to the left
    warning_cell.colspan = "";
    warning_cell.padding = '0';
    row.insertBefore (new_cell, warning_cell);
    UFUtils.convert_td_div (warning_cell);
  },

  set_fields_enabled : function (enabled, except)
  {
    // Enables or disables all named fields in the form, except those whose ids are
    // listed in except
    var skip    = except.join (' ');
    var elems   = UploadForm.the_form.elements;
    var changed = false;
    for (var i = 0; i < elems.length; i++) {
      if (elems[i].type == 'hidden') continue; // Don't fool around with hidden elements
      var id = elems[i].id;
      if (!id || id.length == 0) id = elems[i].name;
      if (id && id.length > 0) {
        if (skip.indexOf (id) < 0) {
          if (elems[i].disabled != !enabled) {
            changed = true;
            if (elems[i].type == 'text' || elems[i].type == 'textarea') {
              // Set the background. Actually, I'd like to just reset it to whatever the
              // default was, but setting it to null doesn't do anything in IE6... We
              // force a light gray for disabled fields since IE6 doesn't have a real
              // visual "disabled" indicator for input fields.
              try {
                elems[i].style.backgroundColor = (enabled ? '#FFFFFF' : '#EEEEEE');
              } catch (some_error) {
                // Swallow
              }
            }
            elems[i].disabled = !enabled;
          }
        }
      }
    }
    if (changed) {
      // Clear warning messages. If we disabled fields, they're obsolete; if we enabled fields,
      // new warnings will be generated upon submit if necessary.
      var my_warning = document.getElementById ('wpUploadVerifyWarning');
      if (my_warning != null) my_warning.style.display = 'none';
    }
  },

  previous_hotcat_state : null,

  getPrevValue : function (stored_form, element_id)
  {
    // Return a field's previous value, if known
    if (   stored_form == null || stored_form.length <= 1
        || element_id == null || element_id.length == 0)
      return null;
    for (var i = 1; i < stored_form.length; i++) {
      if (stored_form[i] != null && element_id == stored_form[i].id)
        return stored_form[i].val;
    }
    return null;
  },

  license_button          : null,
  license_button_shown    : false,
  current_license_preview : '&nbsp;',

  get_license_preview : function () // Tooltip callback
  {
    var div = document.createElement ('div');
    div.style.display = 'none';
    document.body.appendChild (div);
    div.innerHTML = UploadForm.current_license_preview;
    document.body.removeChild (div);
    div.style.fontSize = 'smaller';
    div.style.display  = "";
    var wrapper = document.createElement ('div');
    wrapper.appendChild (div);
    return wrapper;
  },

  create_license_button : function ()
  {
    // Will be called only from our rewritten wgUploadLicenseObj.showPreview, i.e.
    // we *know* that we *do* have Tooltips and Buttons here.
    var previewButton =
      UploadForm.customFormButton
        (  'wpUploadFormPreviewLicenseButton' // Customization ID
         , 'wpUploadPreviewLicense'           // ID of button
         , null                               // Default text
         , null                               // Event handler, will be set below
         , 'wpPreviewLicenseUploadLbl'        // default label ID
          );
    var tooltip =
      new Tooltip
        (  previewButton
         , UploadForm.get_license_preview
         , { activate     : Tooltip.CLICK
            ,deactivate   : (UFHelp.help_close_imgs
                             ? Tooltip.CLICK_ELEM
                             : Tooltip.CLICK_TIP|Tooltip.CLICK_ELEM|Tooltip.LOSE_FOCUS)
            ,close_button : UFHelp.help_close_imgs
            ,mode         : Tooltip.FIXED
            ,anchor       : Tooltip.TOP_LEFT
            ,fixed_offset : {x:10, y: 5, dy: -1}
            ,open_delay   : 0
            ,hide_delay   : 0
           }
         , UFHelp.tooltip_styles
        );                            
    UploadForm.license_button = previewButton;
  },

  setup_license_preview : function ()
  {
    var preview_panel = document.getElementById ('mw-license-preview');
    if (preview_panel != null) UFUtils.convert_td_div (preview_panel);
    // Change the license previewer to not overwrite our warning message, if any.
    if (   typeof (wgUploadLicenseObj) != 'undefined'
        && typeof (wgUploadLicenseObj.showPreview) != 'undefined'
        && typeof (Tooltip) != 'undefined') {

      wgUploadLicenseObj.showPreview = 
        function (preview)
        {
          var preview_panel = document.getElementById ('mw-license-preview');
          if (preview_panel == null) return;
          if (preview == UploadForm.current_license_preview) return;

          UploadForm.current_license_preview = preview;
          var contents = null;
          var new_state = false;
          if (!preview || preview.length == 0 || preview == '&nbsp;') {
            contents = document.createTextNode ('\xa0'); // a single &nbsp;
            new_state = false;
          } else {
            if (!UploadForm.license_button) {
              UploadForm.create_license_button ();
            }
            if (!UploadForm.license_button_shown)
              contents = UploadForm.license_button;
            new_state = true;
          }
          if (contents && new_state != UploadForm.license_button_shown) {
            if (preview_panel.firstChild)
              preview_panel.replaceChild (contents, preview_panel.firstChild);
            else
              preview_panel.appendChild (contents);
          }
          UploadForm.license_button_shown = new_state;
        }; // end function
    }
  },

  preview_tooltip : null, // Tooltip, if preview so configured
  do_preview      : null, // Function to call to actually generate the preview
  
  addPreviewButton : function (handler)
  {
    // If we don't have Ajax, our preview won't work anyway.
    if (!window.XMLHttpRequest && !window.ActiveXObject) return;
    
    var uploadButton = document.getElementsByName ('wpUpload')[0]; // Has no ID...
    // If we can't find the upload button, we don't know where to insert the preview button.
    if (uploadButton == null) return;

    try {
      var previewButton =
        UploadForm.customFormButton
          (  'wpUploadFormPreviewButton' // Customization ID
           , 'wpUploadPreview'           // ID of button
           , null                        // Default text
           , UploadForm.generatePreview  // Event handler
           , 'wpPreviewUploadLbl'        // default label ID
          );
      if (   UploadForm_page_preview_in_tooltip
          && typeof (Tooltip) != 'undefined') {
        UploadForm.preview_tooltip =
          new Tooltip (  previewButton
                       , UploadForm.getPreview
                       , {  activate     : Tooltip.NONE // We'll show it manually in generatePreview.
                          , deactivate   : Tooltip.CLICK_TIP
                          , close_button : UFHelp.help_close_imgs
                          , mode         : Tooltip.FIXED
                          , target       : uploadButton
                          , anchor       : Tooltip.TOP_LEFT
                          , fixed_offset : {x:0, y: 5, dy:-1}
                          , open_delay   : 0
                          , hide_delay   : 0
                         }
                       , UFHelp.tooltip_styles
                      );
      }
      UploadForm.do_preview = handler;
      previewButton.setAttribute ('style', 'margin-left: 0.5em;');
      var hotKey = 'p';
      previewButton.setAttribute ('accesskey', hotKey);
      if (mw && mw.util) {
        if (!(/\[\w+\]$/.test(previewButton.title))) previewButton.title += ' [' + hotKey + ']';
        mw.util.updateTooltipAccessKeys([previewButton]);
      }
      uploadButton.parentNode.insertBefore (previewButton, uploadButton.nextSibling);
    } catch (ex) {
    }
  },

  getOwnWorkAuthor : function ()
  {
    if (   typeof (UploadForm_ownwork_author) == 'string'
        && UploadForm_ownwork_author.search (/\S/) >= 0) {
      // It's a non-empty string
      return UploadForm_ownwork_author;
    } else {
      return '\[\[User:' + mw.config.get('wgUserName') + '|' + mw.config.get('wgUserName') + '\]\]';
    }
  },

  getOwnWorkSource : function ()
  {
    var text   = UFUI.getLabel ('wpOwnWorkUploadLbl', true);
    var result = null;
    try {
      // Must have a text node. IE6 doesn't know Node.TEXT_NODE...
      while (text && text.nodeType != 3) text = text.firstChild;
      if (text) result = text.data.replace (/^\s+/, "").replace (/\s+$/, "");
    } catch (ex) {
      result = null;
    }
    if (!result) result = 'Own work by ' + UploadForm.getOwnWorkAuthor ();
    return result;
  },

  customFormButton : function (ui_id, id, default_text, handler, default_id)
  {
    function getButtonSpan (container, idx)
    {
      if (!container) return null;
      var spans = container.getElementsByTagName ('span');
      var span  = null;
      if (!spans || spans.length <= idx) {
        // No spans... if idx is zero, try simply to take the first text node within container.
        if (idx == 0) span = container;
      } else {
        span = spans[idx];
      }
      // Ok, let's see if we have some text... IE6 doesn't know Node.TEXT_NODE!
      while (span && span.nodeType != 3) span = span.firstChild;
      if (span) return span.data.replace (/^\s+/, "").replace (/\s+$/, "");
      return null;
    }
    
    function getDefault (default_text, default_id)
    {
      if (!default_text) {
        if (default_id) {
          var default_text = UFUI.getLabel (default_id, true);
          // Must have a text node
          while (default_text && default_text.nodeType != 3) // IE6 doesn't know Node.TEXT_NODE
            default_text = default_text.firstChild;
          if (default_text)
            default_text = default_text.data.replace (/^\s+/, "").replace (/\s+$/, "");
        } else
          default_text = 'X'; // Hmmm... a serious misconfiguration
      }
      return default_text;
    }

    var button = null, imgs = null;
    button = UFUI.getEntry (ui_id, 'uiElements', UFUI.internalLanguage);
    if (!button) button = UFUI.getEntry (ui_id, 'uiElements');
    if (button) imgs = button.getElementsByTagName ('img');
    if (!imgs || imgs.length == 0 || typeof (Buttons) == 'undefined') {
      var buttonText = null;
      var buttonText = getButtonSpan (button, 0);
      if (!buttonText) buttonText = getDefault (default_text, default_id);
      var alternateText = getButtonSpan (button, 1);
      button = document.createElement ('input');
      button.setAttribute ('id', id);
      button.setAttribute ('name', id);
      button.type    = 'button';
      button.value   = buttonText;
      if (alternateText) button.title   = alternateText;
      button.onclick = handler;
    } else {
      button = Buttons.makeButton (imgs, id, handler);
    }
    return button;
  },
  
  the_form : null,

  // If a needed script that is included hasn't loaded yet, we try at most install_max_attempts
  // times install_delay. If it then still has not loaded, we install all the same, and the
  // setup routine will have to deal with it. (Note that script loading is asynchronous!)
  install_delay        : 500, // Milliseconds
  install_attempts     : 0,
  install_max_attempts : 5,   // Five times: maximum delay 2.5s

  really_install : function (force_basic)
  {
    UploadForm.install_attempts++;
    if (UploadForm.install_attempts <= UploadForm.install_max_attempts &&
        (   typeof (LanguageHandler) == 'undefined'
         || typeof (UIElements) == 'undefined'
         || typeof (Tooltips) == 'undefined')) {
      // Add needed scripts in the condition above.
      window.setTimeout (function() {
        UploadForm.really_install(force_basic);
      }, UploadForm.install_delay);
    } else {
      UFUI.sanitizeUserLanguage ();
      var use_basic = force_basic || !!UploadForm_forcebasic || UFUI.isExperienced;
      if (use_basic && !force_basic) {
        // Only for autoconfirmed users!
        var is_auto = false;
        var userGroups = mw.config.get('wgUserGroups');
        if (userGroups) {
          for (var i = 0; i < userGroups.length && !is_auto; i++) {
            is_auto = userGroups[i] == 'autoconfirmed';
          }
        }
        if (!is_auto) use_basic = false;
      }
      try {
        UFHelp.precreate_tooltip_closer ();
        UploadForm.setFileExtensions ();
        if (   use_basic
            || document.URL.indexOf ('uploadformstyle=basic') >= 0
            || document.URL.search (/uselang\=(\w|-)*fromwikimedia/) >= 0) {
          // The fromwikimedia forms are special enough to warrant a special setup.
          UploadFormBasic.setup (!force_basic);
        } else {
          UploadFormFull.setup ();
        }
        UploadForm.setup_error_display ();
        UploadForm.setup_hotcat_label ();
        UFHelp.setupHelp (UploadForm.isReupload);
        if (!UploadForm.isReupload) UFFixes.fixAutocompletion ();
        UploadForm.setupOverwriteMsg ();
        // Handle the "Upload new version" links, these have &wpDestFile=... in the URL, which
        // defeats overwrite detection. Because someone might construct such a URL manually
        // *not* actually overwriting an existing file, we still do the check:
        if (!UploadForm.isReupload) UploadForm.check_initial_dest_file (); 
      } catch (ex) {
      	if (console && console.log) {
      		console.log(ex);
      	}
      	if (mw.config.get('wgUserName') == 'Lupo') {
      		alert (ex);
      	}
        // Not good at all. Something went badly wrong. If we have already modified the form,
        // the best thing is probably to reload and make sure we don't try again:
        if (UploadForm.formModified) {
          var reload_url = document.URL;
          reload_url = reload_url
                     + ((reload_url.indexOf ('?') >= 0) ? '&' : '?')
                     + 'uploadformstyle=plain';
          window.location.href = reload_url;
        }
      }
    }
  },

  install : function ()
  {
    if (UploadForm.isInstalled) return; // Do this only once per page!
    UploadForm.isInstalled = true;

    if (document.URL.indexOf ('uploadformstyle=plain') >= 0) return; // We're disabled

    // Also don't do anything if we're not on an upload form.
    if (mw.config.get('wgCanonicalNamespace') != 'Special' || mw.config.get('wgCanonicalSpecialPageName') != 'Upload') return;         
    var form =    document.getElementById ('upload')
               || document.getElementById ('mw-upload-form');
    var original_desc = document.getElementById ('wpUploadDescription');
    if (!form || !original_desc) return; // Oops. Not good: bail out; don't do anything.

    var reupload = document.getElementById ('wpForReUpload');
    var destFile = document.getElementById ('wpDestFile');
    if (reupload)
      UploadForm.isReupload = !!reupload.value;
    else {
      UploadForm.isReupload = destFile && (destFile.disabled || destFile.readOnly);
    }
    if (destFile && !!destFile.disabled) {
      destFile.readOnly = true;
      destFile.disabled = false;
    }
    if (destFile && UploadForm.isReupload) {
      destFile.onkeyup = function (evt) {};
      destFile.onchange = function (evt) {};
    }
    // Use the basic form if the description was set *initially*, or if it's a re-upload, or if it's a special
    // form
    var use_basic =
         (original_desc.defaultValue != null && original_desc.defaultValue.length > 0)
      || UploadForm.isReupload
      || document.URL.indexOf ('uselang=nlwikilovesmonuments') > 0
    ;

    UploadForm.the_form = form;
    if (document.URL.indexOf ('debug=true') >= 0) UploadForm.debug = true;
    UploadForm.really_install (use_basic);
  },

  check_initial_dest_file : function ()
  {
    var dest_file = document.getElementById ('wpDestFile');
    if (   dest_file && dest_file.value && dest_file.value.length > 0
        && window.wgUploadWarningObj && typeof (wgUploadWarningObj.keypress) == 'function')
    {
      wgUploadWarningObj.keypress();
    }
  },

  errorMsgs      : null,
  warning_pushed : false,

  display_errors : function ()
  {
    // Give user feedback about what is not ok.      
    var my_warning = document.getElementById ('wpUploadVerifyWarning');
    if (my_warning == null) {
      // Find the upload button
      var uploadButton = document.getElementsByName ('wpUpload');
      var warningBox   = null;
      if (uploadButton) uploadButton = uploadButton[0];
      if (!uploadButton) {
        warningBox = document.getElementById ('wpDestFile-warning');
        if (!warningBox) return; // We just have the field colors to indicate errors...
      }
      my_warning = document.createElement ('div');
      my_warning.style.border = '1px #FF0000 solid';
      my_warning.style.backgroundColor = UploadForm.errorColor;
      my_warning.style.padding = '0.5em';
      my_warning.style.marginTop = '0.5em';
      my_warning.style.marginBottom = '0.5em';
      my_warning.setAttribute ('id', 'wpUploadVerifyWarning');
      my_warning.setAttribute ('width', '100%');
      my_warning.style.display = 'none';
      if (uploadButton) {
        uploadButton.parentNode.insertBefore (my_warning, uploadButton);
      } else {
        warningBox.parentNode.insertBefore (my_warning, warningBox.nextSibling);
      }
    }
    // Now collect all the error messages into one div.
    var msgs = document.createElement ('ul');
    msgs.style.paddingLeft = '1.0em';
    msgs.style.marginLeft = '0';
    for (var i = 0; i < UploadForm.errorMsgs.length; i++) {
      var msg = UFUI.getErrorMsg (UploadForm.errorMsgs[i]);
      if (msg) {
        var li = document.createElement ('li');
        li.appendChild (msg);
        msgs.appendChild (li);
      }
    }
    delete UploadForm.errorMsgs;
    UploadForm.errorMsgs = null;
    // And then display the messages
    if (my_warning.firstChild != null)
      my_warning.replaceChild (msgs, my_warning.firstChild);
    else
      my_warning.appendChild (msgs);
    my_warning.style.display = 'block';
  },

  call_onsubmit : function (evt)
  {
    var do_submit = true;
    if (UploadForm.oldOnSubmit != null) {
      if (typeof UploadForm.oldOnSubmit == 'string')
        do_submit = eval (UploadForm.oldOnSubmit);
      else if (typeof UploadForm.oldOnSubmit == 'function')
        do_submit = UploadForm.oldOnSubmit (evt);
    }
    return do_submit;
  },
  
  templates: [
   {name          : 'information',
    fields        : ['Description', 'Source', 'Date', 'Author', 'Permission', 'other_versions'],
    extract       : [3, 1, 0],    
    desc_mandatory: true,
    regexp        : null
   },
   {name          : 'painting',
    fields        : ['Artist', 'Title', 'Year', 'Technique', 'Dimensions', 'Gallery',
                     'Location', 'Notes', 'Source', 'Permission',
                     'other_versions', 'Other versions'],
    extract       : [0, 8, 7],    
    desc_mandatory: false,
    regexp        : null
   },
   {name          : 'flickr',
    fields        : ['description', 'flickr_url', 'title', 'taken', 'photographer_url',
                     'photographer', 'photographer_location', 'reviewer', 'permission'],
    extract       : [new Array (5, 4), 1, 0],    
    desc_mandatory: true,
    regexp        : null
   }
  ],

  empty_template : function (name)
  {
    if (name == null) return null;
    var test_name = name.toLowerCase ();
    for (var i = 0; i < UploadForm.templates.length; i++) {
      if (UploadForm.templates[i].name == test_name) {
        var result = '\{\{' + name;
        for (var j = 0; j < UploadForm.templates[i].fields.length; j++) {
          result = result + '\n|' + UploadForm.templates[i].fields[j] + '=';
          if (UFUI.isOwnWork && i == 0) {
            // Pre-fill some fields if we're on an own-work form and it's an
            // information-template
            switch (j) {
              case 1: // Source-field
                result = result + UploadForm.clean (UploadForm.getOwnWorkSource ());
                break;
              case 2: // Date
                if (typeof (UploadForm_ownwork_date) == 'string'
                    && UploadForm_ownwork_date.search (/\S/) >= 0) {
                  result = result + UploadForm.clean (UploadForm_ownwork_date);
                }
                break;
              case 3: // Author
                result = result + UploadForm.clean (UploadForm.getOwnWorkAuthor ());
                break;
              default:
                break;
            } // end switch
          } // end if information for ownWork
        }
        return result + '\n\}\}';
      }
    }
    return null;
  },

  extract_fields : function (desc, template_idx, list)
  {
    function get (desc, field, regexp)
    {
      var match_start = new RegExp ('\\n\\s*\\| *' + field + ' *\\=');
      var start = desc.match (match_start);
      if (start == null) return null;
      var rest = desc.substring (start.index + start[0].length);
      var end = rest.search (regexp);
      if (end < 0) return rest;
      return rest.substring (0, end);
    }

    var result  = list;
    var names   = UploadForm.templates[template_idx].fields;
    var extract = UploadForm.templates[template_idx].extract;
    if (UploadForm.templates[template_idx].regexp == null) {
      // Build the regexp
      var regexp_str = '\\n\\s*(\\| *(' + names.join ('|') + ') *\\=|\\}\\})';
      UploadForm.templates[template_idx].regexp = new RegExp (regexp_str);
    }
    for (var i = 0; i < extract.length; i++) {
      var txt = null;
      if (extract[i].constructor == Array) {
        // It's an array giving alternatives...
        var alternatives = extract[i];
        for (var j = 0; j < alternatives.length; j++) {
          txt = get (desc, names [alternatives[j]], UploadForm.templates[template_idx].regexp);
          if (txt != null && txt.search (/\S/) >= 0) break; // Non-empty: don't look further
          txt = null;
        }
      } else {
        txt = get (desc, names [extract[i]], UploadForm.templates[template_idx].regexp);
      }
      if (txt != null) result[result.length] = txt; // Push one.
      // Don't use "if (txt)", it's false if the string is != null, but empty!
    }
    return result;
  },

  split_description : function (desc)
  {
    if (desc == null || desc.length == 0) return null;
    // Returns an array containing (in that order):
    // index of template, author, source, description
    for (var i = 0; i < UploadForm.templates.length; i++) {
      var regexp = new RegExp ('\\{\\{' + UploadForm.templates[i].name + '\\s*(\\||\\n)');
      var start = desc.toLowerCase ().search (regexp);
      if (start >= 0) {
        var result = new Array (1);
        result[0] = i;
        // Now try to extract the fields:
        return UploadForm.extract_fields (desc.substring (start), i, result);
      }
    }
    return null;
  },

  generatePreview : function (evt)
  {
    if (   UploadForm.preview_tooltip
        && UploadForm.preview_tooltip.popup.style.display != 'none'
        && UploadForm.preview_tooltip.popup.style.display != null) {
      UploadForm.preview_tooltip.hide_now (null);
    } else {
      UploadForm.do_preview (evt || window.event);
    }
  },

  outerHTML : function (node)
  {
    if (!node) return "";
    if (node.nodeType == 3) return node.nodeValue; // Text node
    if (node.outerHTML) return node.outerHTML;
    var div = document.createElement ('div');
    div.style.display    = 'none';
    div.style.position   = 'absolute';
    div.appendChild (node);
    document.body.appendChild (div);
    var txt = div.innerHTML;
    document.body.removeChild (div);
    return txt;
  },

  makePreview : function (description, is_overwrite)
  {
    if (is_overwrite) {
      UploadForm.showPreview (
          '\<div style="border:1px solid red; padding:0.5em;"\>'
        + '\<div class="previewnote"\>'
        + UploadForm.outerHTML (UFUI.getErrorMsg ('wpPreviewOverwriteError'))
        + '\<\/div\>'
        + '\<\/div\>'
      );
    } else {
      var text = '\<div style="border:1px solid red; padding:0.5em;"\>\n'
               + '\<div class="previewnote"\>\n'
               + '\{\{MediaWiki:Previewnote/' + UFUI.userLanguage +'\}\}\n'
               + '\<\/div>\n';
      var license = document.getElementById ('wpLicense');
      var license_text = null;
      if (   license != null && license.selectedIndex > 0
          && license.options[license.selectedIndex].value.length > 0) {
        license_text = '\{\{' + license.options[license.selectedIndex].value + '\}\}';
      }
      if (license_text) {
        text = text
             + '\<h2\>\{\{int:filedesc\}\}\<\/h2\>\n'
             + description + '\n'
             + '\<h2\>\{\{int:license-header\}\}\<\/h2\>\n'
             + license_text;
      } else {
        text = text + description + '\n';
      }
      // Add categories
      if (typeof (hotcat_get_state) == 'function') {
        var input = document.getElementById ('hotcat_text');
        if (input != null) hotcat_closeform ();
        var hotcat_categories = hotcat_get_state ();
        if (hotcat_categories && hotcat_categories.length > 0) {
          hotcat_categories = hotcat_categories.split ('\n');
          for (var i=0; i < hotcat_categories.length; i++) {
            if (hotcat_categories[i] && hotcat_categories[i].length > 0)
              text = text + '[[Category:' + hotcat_categories[i] + ']]';
          }
        }
      }
      text = text + '\<\/div\>';

      // Make the Ajax call
      var req;
      if (window.XMLHttpRequest) req = new window.XMLHttpRequest();
      if (!req && window.ActiveXObject) {
        try {
          req = new window.ActiveXObject('Microsoft.XMLHTTP');
        } catch (any) {}
      }
      if (!req) return;
      var button = document.getElementById ('wpUploadPreview');
      var page   = document.getElementById ('wpDestFile');
      if (page) page = page.value;
      if (page != null && page.length == 0) page = null;
      if (button && typeof (injectSpinner) == 'function')
        injectSpinner (button, 'wpUploadPreviewSpinner');
      var uri  = mw.config.get('wgServer') + mw.config.get('wgScriptPath') + '/api.php';
      var args = 'action=parse&pst&text=' + encodeURIComponent (text)
               + (page ? '&title=File:' + encodeURIComponent (page.replace (/ /g, '_')) : "")
               + '&prop=text|categories&format=json';
      // "&pst" is "Pre-save transform": tilde replacement, pipe magic for links like [[foo|]].
      // Don't use a callback directly, add the function call ourselves *after* the call, since
      // the API somehow resolves tildes to an IP number instead of the username if a callback
      // is used. C.f. https://bugzilla.wikimedia.org/show_bug.cgi?id=16616
      // Apparently, that's a feature, not a bug...
      var request_length = uri.length + args.length + 1;
      if (request_length > 2000) {
        // Long URLs are problematic for GET requests
        req.open ('POST', uri, true);
        req.setRequestHeader ('Content-Type', 'application/x-www-form-urlencoded');
      } else {
        uri += '?' + args; args = null;
        req.open ('GET', uri, true);
      }
      req.setRequestHeader ('Pragma', 'cache=no');
      req.setRequestHeader ('Cache-Control', 'no-transform');
      req.onreadystatechange =
        function ()
        {
          if (req.readyState != 4) return;
          if (typeof (removeSpinner) == 'function') removeSpinner ('wpUploadPreviewSpinner');
          if (req.status != 200) return;
          // Add the "callback"...
          if (req.responseText && req.responseText.indexOf ('{') == 0)
            // Primitive sanity check. If the response text does *not* start with '{', it might have been
            // spoofed and contain a function call... of course, this simple check cannot catch more
            // elaborate spoof attempts.
            eval ('UploadForm.jsonPreview (' + req.responseText + ')');
        };
      req.send (args);
    }
  },

  jsonPreview : function (result)
  {
    if (result && result.parse && result.parse.text && result.parse.text['*'] != null) {
      var txt = result.parse.text['*'];
      var categories = result.parse.categories;
      if (categories && categories.length > 0) {
        // Add a mock-up of a category bar. We don't care about non-existing categories, and we
        // can't identify hidden categories.
        var catbar = '<div class="catlinks"><div id="mw-normal-catlinks">'
                   + UploadForm.outerHTML (UFUI.getLabel ('wpCategoriesUploadLbl'));
        categories.sort (
          function (a, b) {
            var key_a = a['*'].toLowerCase (), key_b = b['*'].toLowerCase ();
            if (key_a < key_b) return -1;
            if (key_a > key_b) return 1;
            return 0;
          }
        );
        for (var i = 0; i < categories.length; i++) {
          var catname = categories[i]['*'];
          if (catname && catname.length > 0) {
            if (i > 0) catbar += ' |';
            catbar += ' <a href="/wiki/Category:' + encodeURI (catname) + '">'
                      + catname.replace(/_/g, ' ') + '</a>';
          }
        }
        catbar += '</div></div>';
        // Now insert it into text.
        var end = txt.lastIndexOf ('</div>');
        txt = txt.substring (0, end) + catbar + '</div>';
      }
      UploadForm.showPreview (txt);
    }
  },

  showPreview : function (result)
  {
    if (UploadForm.preview_tooltip) {
      UploadForm.preview_content = result;
      UploadForm.preview_tooltip.show_tip (null, false);
    } else {
      var preview = document.getElementById ('wpUploadPreviewDisplay');
      if (preview == null) {
        var before = document.getElementById ('mw-upload-permitted');
        if (!before || UFUtils.isChildOf (before, UploadForm.the_form))
          before = UploadForm.the_form;
        if (!before) return; // Don't know where to insert preview display. Error message here?
        preview = document.createElement ('div');
        preview.setAttribute ('id', 'wpUploadPreviewDisplay');
        before.parentNode.insertBefore (preview, before);
      }
      try {
        preview.innerHTML = result;
      } catch (ex) {
        preview.innerHTML = ""; // Error message here instead?
      }
      preview.style.display = ""; // Show it 
    }
  },

  hidePreview : function ()
  {
    if (UploadForm.preview_tooltip)
      UploadForm.preview_tooltip.hide_now (null);
    else {
      var preview = document.getElementById ('wpUploadPreviewDisplay');
      if (preview) preview.style.display = 'none';
    }
  },

  getPreview : function () // Callback for the tooltip
  {
    var div = document.createElement ('div');
    div.style.display = 'none';
    document.body.appendChild (div);
    div.innerHTML = UploadForm.preview_content;
    document.body.removeChild (div);
    div.style.fontSize = 'smaller';
    div.style.display = "";
    var wrapper = document.createElement ('div');
    wrapper.appendChild (div);
    return wrapper;
  },

  licenses_regexp     :
    /\{\{(self|pd|gfdl|cc|l?gpl|fal|cecill|attribution|copyrighted free use|SOlicence|geograph|UN map|BArch-License|Apache)/i,
  user_license_regexp :
    new RegExp (  '\\{\\{[Ss]ubst:[Uu]ser:'
                + (mw.config.get('wgUserName') || "null").replace(/([\\\^\$\.\?\*\+\(\)\[\]\|\{\}])/g, "\\$1")
                + '/'),

  has_license : function (fields)
  {
    if (fields == null || fields.length == 0) return false;
    for (var i = 0; i < fields.length; i++) {
      if (fields[i] != null) {
        if (typeof (fields[i]) == 'string') {
          if (fields[i].search (UploadForm.licenses_regexp) >= 0) return true;
        } else {
          if (fields[i].value.search (UploadForm.licenses_regexp) >= 0) return true;
        }
      }
    }
    for (var j = 0; j < fields.length; j++) {
      if (fields[j] != null) {
        if (typeof (fields[j]) == 'string') {
          if (fields[j].search (UploadForm.user_license_regexp) >= 0) return true;
        } else {
          if (fields[j].value.search (UploadForm.user_license_regexp) >= 0) return true;
        }
      }
    }
    return false;
  },

  addAfterField : function (elem_id, element)
  {
    if (!element) return;
    var elem = document.getElementById (elem_id);
    if (!elem) return;
    // Find enclosing table cell.
    while (elem && elem.nodeName.toLowerCase () != 'td') elem = elem.parentNode;
    if (!elem) return;
    var container = document.createElement ('div');
    container.style.fontSize = 'smaller';
    container.appendChild (element);
    elem.appendChild (container);
  },

  old_overwrite_warning : null,

  setupOverwriteMsg : function ()
  {
    if (   typeof (wgUploadWarningObj) == 'undefined'
        || typeof (wgUploadWarningObj.setWarning) == 'undefined')
      return;
    var msg = document.createElement ('div');
    msg.id = 'wpUploadFormScriptOverwriteWarning';
    msg.style.display = 'none';
    msg.style.color = 'red';
    msg.appendChild (UFUI.getErrorMsg ('wpPreviewOverwriteError'));
    UploadForm.addAfterField ('wpDestFile', msg);
    UploadForm.old_overwrite_warning = wgUploadWarningObj.setWarning;    
    wgUploadWarningObj.setWarning = UploadForm.overwriteMsg;
  },

  overwriteMsg : function (warning)
  {
    if (!UploadForm.old_overwrite_warning || UploadForm.isReupload) return;
    // Make sure that 'this' is set to 'wgUploadWarningObj' in the call below!
    UploadForm.old_overwrite_warning.apply (wgUploadWarningObj, [warning]);
    var is_overwrite = UploadForm.isOverwrite ();
    var my_overwrite_warning = document.getElementById ('wpUploadFormScriptOverwriteWarning');
    if (my_overwrite_warning) {
      my_overwrite_warning.style.display = (is_overwrite ? "" : 'none');
    }
    UploadForm.set_fields_enabled
      (!is_overwrite
       , [  'wpUploadFile', 'wpDestFile', 'wpUploadDescription', 'wpAdditionalInfo'
          , 'wpLicense', 'wpWatchthis', 'wpIgnoreWarning', 'wpUpload']
      );
  },

  isOverwrite : function ()
  {
    if (document.getElementById ('wpUploadWarningFileexists') != null) return true;
    var destfile_warning = document.getElementById ('wpDestFile-warning');
    if (destfile_warning == null) return false;
    var dest_file = document.getElementById ('wpDestFile');
    if (dest_file == null || dest_file.value == null || dest_file.value.length == 0) return false;
    var lks = destfile_warning.getElementsByTagName ('a');
    if (lks == null || lks.length == 0) return false;

    var fn1 =
      dest_file.value.replace(/^\s\s*/, '').replace(/\s\s*$/, '').replace(/ /g, '_');
    fn1     = fn1.substr (0, 1).toUpperCase () + fn1.substring (1);
    var fn0 = 'Image:' + fn1;
    fn1     = 'File:' + fn1;
    // Trimmed, blanks replaced by underscores, first character capitalized

    for (var i = 0; i < lks.length; i++) {
      var href = lks[i].getAttribute ('href', 2);
      if ( $( lks[i] ).hasClass( 'new' ) ) continue;
      if (href.indexOf (mw.config.get('wgScript')) == 0 || href.indexOf (mw.config.get('wgServer') + mw.config.get('wgScript')) == 0) {
        var m = /[&?]title=([^&]*)/.exec (href);
        if (m && m.length > 1) href = m[1]; else href = null;
      } else {
        var prefix = mw.config.get('wgArticlePath').replace ('$1', "");
        if (href.indexOf (prefix) != 0) prefix = mw.config.get('wgServer') + prefix; // Fully expanded URL?
        if (href.indexOf (prefix) == 0) href = href.substring (prefix.length); else href = null;
      }
      if (!href) continue;
      href = decodeURIComponent (href).replace (/ /g, '_');
      if (href == fn0 || href == fn1) return true;
    }
    return false;
  },

  allowedFileTypes : null,
  forbiddenFileTypes : null,
        
  badFileNames : /^(test|image|img|bild|example|(dsc|img)?(\s|_|-)*|\d{10}(\s|_|-)[0123456789abcdef]{10}(\s|_|-)[a-z])$/i,
  // Filenames that have only components (separated by periods) that fully match this regexp
  // are considered illegal. The second-but-last one catches DSC01234, or DSC_01234, or
  // DSC_012_34 or also filenames conatining only digits and non-alphanumeric characters.
  // The last catches Flickr's raw filenames. How to relax that last expression without catching
  // too many legit file names?
  // Matching is case-insensitive.

  extractFileExtensions : function (div, list)
  {
    var list = null;
    // Get a mw-upload-permitted or mw-upload-prohibited div, extracts all extensions listed
    var txt = div;
    while (txt && txt.nodeType != 3) txt = txt.lastChild;
    if (!txt) return null;
    // Try to figure out which comma to use (localizeable through MediaWiki:Comma-separator!)
    if (txt.data.indexOf (',') >= 0) // Standard
      txt = txt.data.split (',');
    else if (txt.data.indexOf ('،') >= 0) // Arabic etc.
      txt = txt.data.split ('،');
    else if (txt.data.indexOf ('、') >= 0) // Chinese
      txt = txt.data.split ('、');
    else
      return null;
    if (!txt || txt.length == 0) return null;
    for (var i = 0; i < txt.length; i++) {
      var match = /(\w+)\W*$/.exec (txt[i]);
      if (match) {
        match = match[1].toLowerCase (); // The extension
        if (list == null) list = {};
        list[match] = true;
      }
    }
    return list;
  },

  setFileExtensions : function ()
  {
	var fileExts = mw.config.get('wgFileExtensions');
    if (fileExts) { // New as of 2009-09-17
      UploadForm.allowedFileTypes = {};
      for (var i = 0; i < fileExts.length; i++) {
        UploadForm.allowedFileTypes[fileExts[i]] = true;
      }
      UploadForm.forbiddenFileTypes = null;
      return;
    }

    UploadForm.allowedFileTypes =
      UploadForm.extractFileExtensions (document.getElementById ('mw-upload-permitted'));
    UploadForm.forbiddenFileTypes =
      UploadForm.extractFileExtensions (document.getElementById ('mw-upload-prohibited'));
    if (UploadForm.allowedFileTypes != null) {
      // Alternate OGG extensions
      if (UploadForm.allowedFileTypes.ogg) {
        if (UploadForm.forbiddenFileTypes == null || !UploadForm.forbiddenFileTypes.ogv)
          UploadForm.allowedFileTypes.ogv = true;
        if (UploadForm.forbiddenFileTypes == null || !UploadForm.forbiddenFileTypes.oga)
          UploadForm.allowedFileTypes.oga = true;
        if (UploadForm.forbiddenFileTypes == null || !UploadForm.forbiddenFileTypes.ogx)
          UploadForm.allowedFileTypes.ogx = true;
      }
      // OpenDoc extensions (are these needed?)
      if (UploadForm.forbiddenFileTypes == null || !UploadForm.forbiddenFileTypes.sxi)
        UploadForm.allowedFileTypes.sxi = true;
      if (UploadForm.forbiddenFileTypes == null || !UploadForm.forbiddenFileTypes.sxc)
        UploadForm.allowedFileTypes.sxc = true;
      if (UploadForm.forbiddenFileTypes == null || !UploadForm.forbiddenFileTypes.sxd)
        UploadForm.allowedFileTypes.sxd = true;
      if (UploadForm.forbiddenFileTypes == null || !UploadForm.forbiddenFileTypes.sxw)
        UploadForm.allowedFileTypes.sxw = true;
      // PDF (allowed, but may be hidden in the interface)
      if (UploadForm.forbiddenFileTypes == null || !UploadForm.forbiddenFileTypes.pdf)
        UploadForm.allowedFileTypes.pdf = true;
    }
  },

  checkFileExtension : function (ext, presence_only)
  {
    if (presence_only)
      return   (UploadForm.allowedFileTypes != null && UploadForm.allowedFileTypes[ext] == true)
            || (UploadForm.forbiddenFileTypes != null && UploadForm.forbiddenFileTypes[ext] == true);
    return    (UploadForm.allowedFileTypes == null || UploadForm.allowedFileTypes[ext] == true)
           && (UploadForm.forbiddenFileTypes == null || !(UploadForm.forbiddenFileTypes[ext] == true));
  },

  verifyFileName : function (filename)
  {
    if (filename == null || filename.length == 0) {
      UploadForm.errorMsgs.push ('wpNoFilenameError');
      return false;
    }
    if (filename.search (/(https?|file|ftp):\/\//i) >= 0) {
      UploadForm.errorMsgs.push ('wpHttpFilenameError');
      return false;
    }
    var ok = true;

    // Don't allow slashes
    if (filename.indexOf ('/') >= 0) {
      UploadForm.errorMsgs.push ('wpNoSlashError');
      ok = false;
    }
    // Check for double extensions
    var fn = filename.split ('.');
    if (fn.length < 2 || fn[fn.length-1].length == 0) {
      UploadForm.errorMsgs.push ('wpNoExtensionError');
      ok = false;
    }
    // Check extension
    var nof_extensions = 0;
    if (fn.length >= 2) {
      nof_extensions++;
      if (UploadForm.checkFileExtension (fn[fn.length-1].toLowerCase ())) {
        // It's ok, check for double extension
        if (fn.length > 2) {
          if (UploadForm.checkFileExtension (fn[fn.length-2].toLowerCase (), true)) {
            nof_extensions++;
            UploadForm.errorMsgs.push ('wpDoubleExtensionError');
            ok = false;
          }
        }
      } else {
        UploadForm.errorMsgs.push ('wpIllegalExtensionError');
        ok = false;
      }
    }
    // Check for allowed file name
    var one_ok = false;
    for (var i = 0; i < fn.length - nof_extensions && !one_ok; i++) {
      if (fn[i].length > 0 && fn[i].search (UploadForm.badFileNames) < 0) one_ok = true;
    }
    if (!one_ok) {
      UploadForm.errorMsgs.push ('wpNondescriptFilenameError');
      ok = false;
    }
    return ok;
  },

  cleaner : null,

  clean : function (input)
  {
    if (UploadForm.cleaner == null) {
      // Because of asynchronous script loading, we need to check whether the TextCleaner is
      // already defined. If not, just return the input.
      if (   typeof (TextCleaner) != 'undefined'
          && typeof (TextCleaner.sanitizeWikiText) == 'function')
      {
        UploadForm.cleaner = TextCleaner.sanitizeWikiText;
      }
    }
    if (UploadForm.cleaner && input && typeof (input) == 'string')
      return UploadForm.cleaner (input, true);
    else
      return input;   
  },

  resetBg : function (evt)
  {
    var e = evt || window.event; // W3C, IE
    return UploadForm.verifyMandatoryField (e.target || e.srcElement);
  },

  verifyMandatoryField: function (node, handler)
  {
    if (!node) return true;
    try {
      if (   !node.value
          || node.value.search (/\S/) < 0
          || handler && typeof (handler) == 'function' && handler.length == 1
             && !handler (node.value))
      {
        // No value set, or a handler was given and it is a function taking one parameter, and
        // it returned false
        var is_error = node.id != 'wpPermission';
        if (!is_error) {
          var license_field = document.getElementById ('wpLicense');
          // Careful here. The fromwikimedia forms appear not to have a license selector!
          is_error = license_field == null || license_field.selectedIndex == 0
        }
        if (is_error) {
          node.style.backgroundColor = UploadForm.errorColor;
          if (!UploadForm.warning_pushed) {
            if (UploadForm.errorMsgs != null)
              UploadForm.errorMsgs.push ('wpUploadWarningError');
            UploadForm.warning_pushed = true;
          }
          return false;
        }
      }
    } catch (ex) {
      // Swallow the exception  
    }
    try {
      node.style.backgroundColor = '#FFFFFF';
    } catch (some_error) {
      // Swallow.
    }
    return true;
  },

  fixCategoryTransclusion : function (str)
  {
    return str.replace (/(\{\{)\s*(:?\s*[Cc]ategory\s*:[^|}]*(\|[^}]*)?)(\}\})/g, "[[$2]]");
  }

}; // end UploadForm

var UFFixes =
{
  fixAutocompletion : function ()
  {
    // Do the overwrite check also for selections from the browser's "previous entry list"
    var dest_file = document.getElementById ('wpDestFile');
    if (dest_file && dest_file.onkeyup) {
      // For some reason, onchange doesn't fire upon autocompletion in FF and IE6. Don't use
      // onblur (recommended as a workaround on some Internet sites), it cancels button clicks
      // that cause the focus change. Unfortunately, FF also doesn't fire the DOMAttrModified
      // event upon autocompletion. Thus we're stuck for FF. At least the FF people are about
      // to correct this bug (https://bugzilla.mozilla.org/show_bug.cgi?id=388558). On IE,
      // there is a workaround.
      if (window.ActiveXObject) { // We're on IE...
        // See http://msdn2.microsoft.com/en-us/library/ms533032.aspx and
        // http://msdn2.microsoft.com/en-us/library/ms536956.aspx
        if (!dest_file.onpropertychange) {
          var previous_onkeyup_handler  = dest_file.onkeyup;
          var previous_onchange_handler = dest_file.onchange;
          var handler =
            function (evt)
            {
              var e = evt || window.event;
              if (e && e.propertyName && e.propertyName == 'value') {
                if (typeof (previous_onkeyup_handler) == 'string')
                  eval (previous_onkeyup_handler);
                else if (typeof (previous_onkeyup_handler) == 'function')
                  previous_onkeyup_handler (e);
                if (typeof (previous_onchange_handler) == 'string')
                  eval (previous_onchange_handler);
                else if (typeof (previous_onchange_handler) == 'function')
                  previous_onchange_handler (e);
              }
            };
          if (dest_file.addEventListener) {
          	dest_file.addEventListener('propertychange', handler);
          } else if (dest_file.attachEvent) {
          	dest_file.attachEvent('onpropertychange', handler);
          } else {
          	return;
          }
          dest_file.onkeyup  = null; // Otherwise, both may fire...
          dest_file.onchange = null;
        }
      } else {
      	$(dest_file).change(dest_file.onkeyup);
        // addEvent (dest_file, 'change', dest_file.onkeyup);
      }
    }
  }

}; // end UFFixes

} // end if (guard against double inclusions)


if (window.$) {
  $(UploadForm.install)
} else {
  hookEvent ('load', UploadForm.install);
}

// </source>