Changes

Jump to navigation Jump to search
4,301 bytes removed ,  13:40, 16 February 2021
no edit summary
Line 1: Line 1: −
// <nowiki>
+
/**
 +
HotCat V2.43
 +
 
 +
Ajax-based simple Category manager. Allows adding/removing/changing categories on a page view.
 +
Supports multiple category changes, as well as redirect and disambiguation resolution. Also
 +
plugs into the upload form. Search engines to use for the suggestion list are configurable, and
 +
can be selected interactively.
   −
/*
+
Documentation: https://commons.wikimedia.org/wiki/Help:Gadget-HotCat
HotCat V2.34
+
List of main authors: https://commons.wikimedia.org/wiki/Help:Gadget-HotCat/Version_history
   −
Ajax-based simple Category manager. Allows adding/removing/changing categories on a page view.
+
License: Quadruple licensed GFDL, GPL, LGPL and Creative Commons Attribution 3.0 (CC-BY-3.0)
Supports multiple category changes, as well as redirect and disambiguation resolution. Also
  −
plugs into the upload form. Search engines to use for the suggestion list are configurable, and
  −
can be selected interactively.
     −
Documentation: https://commons.wikimedia.org/wiki/Help:Gadget-HotCat
+
Choose whichever license of these you like best :-)
List of main authors: https://commons.wikimedia.org/wiki/Help:Gadget-HotCat/Version_history
     −
License: Quadruple licensed GFDL, GPL, LGPL and Creative Commons Attribution 3.0 (CC-BY-3.0)
+
This code should run on any MediaWiki installation >= MW 1.27.
   −
Choose whichever license of these you like best :-)
+
For use with older versions of MediaWiki, use the archived versions below:
*/
     −
/*
+
<=1.26: https://commons.wikimedia.org/w/index.php?title=MediaWiki:Gadget-HotCat.js&oldid=211134664
This code is MW version safe. It should run on any MediaWiki installation >= MW 1.15. Note
  −
that HotCat is supposed to run with or without jQuery, and also on older installations that
  −
do not yet have window.mw. If you use any of these newer features, make sure you qualify them
  −
by checking whether they exist at all, and by providing some meaningful fallback implementation
  −
if not. To start itself, HotCat uses jQuery(document).ready(). If it doesn't exist, HotCat won't
  −
start.
   
*/
 
*/
/* jshint ignore:start */ // This old code uses too many coding conventions incompatible with jshint.
+
// <nowiki>
(function () {
+
/* eslint-disable vars-on-top, one-var, camelcase, no-alert, curly */
// Support: MW 1.16
+
/* global jQuery, mediaWiki, UFUI, JSconfig, UploadForm */
var conf = window.mw ? mw.config.values : window;
+
/* jslint strict:false, nonew:false, bitwise:true */
 +
( function ( $, mw ) {
 +
// Don't use mw.config.get() as that takes a copy of the config, and so doesn't
 +
// account for values changing, e.g. wgCurRevisionId after a VE edit
 +
var conf = mw.config.values;
   −
if (
+
// Guard against double inclusions (in old IE/Opera element ids become window properties)
// Guard against double inclusions (in old IE/Opera element ids become window properties)
+
if ( ( window.HotCat && !window.HotCat.nodeName ) ||
(window.HotCat && !window.HotCat.nodeName)
+
conf.wgAction === 'edit' ) // Not on edit mode
// Not on edit pages
  −
|| conf.wgAction == 'edit'
  −
 
  −
) {
   
return;
 
return;
}
     −
// Configuration stuff.
+
// Configuration stuff.
window.HotCat = {
+
var HC = window.HotCat = {
// Localize these messages to the main language of your wiki.
+
// Localize these messages to the main language of your wiki.
messages :
+
messages: {
{cat_removed : 'removed [[Category:$1]]'
+
cat_removed: 'removed [[Category:$1]]',
,template_removed : 'removed {{[[Category:$1]]}}'
+
template_removed: 'removed {{[[Category:$1]]}}',
,cat_added   : 'added [[Category:$1]]'
+
cat_added: 'added [[Category:$1]]',
,cat_keychange: 'new key for [[Category:$1]]: "$2"' // $2 is the new key
+
cat_keychange: 'new key for [[Category:$1]]: "$2"', // $2 is the new key
,cat_notFound : 'Category "$1" not found'
+
cat_notFound: 'Category "$1" not found',
,cat_exists   : 'Category "$1" already exists; not added.'
+
cat_exists: 'Category "$1" already exists; not added.',
,cat_resolved : ' (redirect [[Category:$1]] resolved)'
+
cat_resolved: ' (redirect [[Category:$1]] resolved)',
,uncat_removed: 'removed {{uncategorized}}'
+
uncat_removed: 'removed {{uncategorized}}',
,separator   : '; '
+
separator: '; ',
,prefix      : ""
+
// Some text to prefix to the edit summary.
// Some text to prefix to the edit summary.
+
prefix: '',
,using        : ' using [[Help:Gadget-HotCat|HotCat]]'
+
// Some text to append to the edit summary. Named 'using' for historical reasons. If you prefer
// Some text to append to the edit summary. Named 'using' for historical reasons. If you prefer
+
// to have a marker at the front, use prefix and set this to the empty string.
// to have a marker at the front, use prefix and set this to the empty string.
+
using: ' using [[Help:Gadget-HotCat|HotCat]]',
,multi_change : '$1 categories'
+
// $1 is replaced by a number. If your language has several plural forms (c.f. [[:en:Dual (grammatical form)]]),
// $1 is replaced by a number. If your language has several plural forms (c.f. [[:en:Dual (grammatical form)]]),
+
// you can set this to an array of strings suitable for passing to mw.language.configPlural().
// you can set this to an array of strings suitable for passing to mw.language.configPlural().
+
// If that function doesn't exist, HotCat will simply fall back to using the last
// If that function doesn't exist, HotCat will simply fall back to using the last
+
// entry in the array.
// entry in the array.
+
multi_change: '$1 categories',
,commit       : 'Save'
+
// Button text. Localize to wgContentLanguage here; localize to wgUserLanguage in a subpage,
// Button text. Localize to wgContentLanguage here; localize to wgUserLanguage in a subpage,
+
// see localization hook below.
// see localization hook below.
+
commit: 'Save',
,ok           : 'OK'
+
// Button text. Localize to wgContentLanguage here; localize to wgUserLanguage in a subpage,
// Button text. Localize to wgContentLanguage here; localize to wgUserLanguage in a subpage,
+
// see localization hook below.
// see localization hook below.
+
ok: 'OK',
,cancel       : 'Cancel'
+
// Button text. Localize to wgContentLanguage here; localize to wgUserLanguage in a subpage,
// Button text. Localize to wgContentLanguage here; localize to wgUserLanguage in a subpage,
+
// see localization hook below.
// see localization hook below.
+
cancel: 'Cancel',
,multi_error : 'Could not retrieve the page text from the server. Therefore, your category changes '
+
// Localize to wgContentLanguage here; localize to wgUserLanguage in a subpage,
+'cannot be saved. We apologize for the inconvenience.'
+
// see localization hook below.
// Localize to wgContentLanguage here; localize to wgUserLanguage in a subpage,
+
multi_error: 'Could not retrieve the page text from the server. Therefore, your category changes ' +
// see localization hook below.
+
'cannot be saved. We apologize for the inconvenience.',
,short_catchange : null
+
// Defaults to '[[' + category_canonical + ':$1]]'. Can be overridden if in the short edit summaries
// Defaults to '[[' + category_canonical + ':$1]]'. Can be overridden if in the short edit summaries
+
// not the standard category name should be used but, say, a shorter namespace alias. $1 is replaced
// not the standard category name should be used but, say, a shorter namespace alias. $1 is replaced
+
// by a category name.
// by a category name.
+
short_catchange: null
}
+
},
,category_regexp    : '[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]'
+
// Plural of category_canonical.
// Regular sub-expression matching all possible names for the category namespace. Is automatically localized
+
categories: 'Categories',
// correctly if you're running MediaWiki 1.16 or later. Otherwise, set it appropriately, e.g. at the German
+
// Any category in this category is deemed a disambiguation category; i.e., a category that should not contain
// Wikipedia, use '[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]|[Kk][Aa][Tt][Ee][Gg][Oo][Rr][Ii][Ee]', or at the
+
// any items, but that contains links to other categories where stuff should be categorized. If you don't have
// Chinese Wikipedia, use '[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]|分类|分類'. Note that namespaces are case-
+
// that concept on your wiki, set it to null. Use blanks, not underscores.
// insensitive!
+
disambig_category: 'Disambiguation',
,category_canonical : 'Category'
+
// Any category in this category is deemed a (soft) redirect to some other category defined by a link
// The standard category name on your wiki. Is automatically localized correctly if you're running
+
// to another non-blacklisted category. If your wiki doesn't have soft category redirects, set this to null.
// MediaWiki 1.16 or later; otherwise, set it to the preferred category name (e.g., "Kategorie").
+
// If a soft-redirected category contains more than one link to another non-blacklisted category, it's considered
,categories         : 'Categories'
+
// a disambiguation category instead.
// Plural of category_canonical.
+
redir_category: 'Category redirects',
,disambig_category  : 'Disambiguation'
+
// The little modification links displayed after category names. U+2212 is a minus sign; U+2193 and U+2191 are
// Any category in this category is deemed a disambiguation category; i.e., a category that should not contain
+
// downward and upward pointing arrows. Do not use ↓ and ↑ in the code!
// any items, but that contains links to other categories where stuff should be categorized. If you don't have
+
links: {
// that concept on your wiki, set it to null. Use blanks, not underscores.
+
change: '(±)',
,redir_category    : 'Category redirects'
+
remove: '(\u2212)',
// Any category in this category is deemed a (soft) redirect to some other category defined by a link
+
add: '(+)',
// to another non-blacklisted category. If your wiki doesn't have soft category redirects, set this to null.
+
restore: '(×)',
    // If a soft-redirected category contains more than one link to another non-blacklisted category, it's considered
+
undo: '(×)',
    // a disambiguation category instead.
+
down: '(\u2193)',
,links : {change: '(±)', remove: '(\u2212)', add: '(+)', restore: '(×)', undo: '(×)', down: '(\u2193)', up: '(\u2191)'}
+
up: '(\u2191)'
// The little modification links displayed after category names. U+2212 is a minus sign; U+2193 and U+2191 are
+
},
// downward and upward pointing arrows. Do not use ↓ and ↑ in the code!
+
changeTag: conf.wgUserName ? 'HotCat' : '', // if tag is missing, edit is rejected
,tooltips : {
+
// The tooltips for the above links
change: 'Modify'
+
tooltips: {
,remove: 'Remove'
+
change: 'Modify',
,add:     'Add a new category'
+
remove: 'Remove',
,restore: 'Undo changes'
+
add: 'Add a new category',
,undo:   'Undo changes'
+
restore: 'Undo changes',
,down:   'Open for modifying and display subcategories'
+
undo: 'Undo changes',
,up:     'Open for modifying and display parent categories'
+
down: 'Open for modifying and display subcategories',
  }
+
up: 'Open for modifying and display parent categories'
// The tooltips for the above links
+
},
,addmulti           : '<span>+<sup>+</sup></span>'
+
// The HTML content of the "enter multi-mode" link at the front.
// The HTML content of the "enter multi-mode" link at the front.
+
addmulti: '<span>+<sup>+</sup></span>',
,multi_tooltip     : 'Modify several categories'
+
// Tooltip for the "enter multi-mode" link
// Tooltip for the "enter multi-mode" link
+
multi_tooltip: 'Modify several categories',
,disable            :
+
// Return true to disable HotCat.
function () { // Return true to disable HotCat.
+
disable: function () {
 
var ns = conf.wgNamespaceNumber;
 
var ns = conf.wgNamespaceNumber;
 
var nsIds = conf.wgNamespaceIds;
 
var nsIds = conf.wgNamespaceIds;
return (   ns < 0     // Special pages; Special:Upload is handled differently
+
return (
|| ns === 10 // Templates
+
ns < 0 || // Special pages; Special:Upload is handled differently
|| ns === 828 // Module (Lua)
+
ns === 10 || // Templates
|| ns === 8   // MediaWiki
+
ns === 828 || // Module (Lua)
|| ns === 6 && conf.wgArticleId === 0 // Non-existing file pages
+
ns === 8 || // MediaWiki
|| ns === 2 && /\.(js|css)$/.test(conf.wgTitle) // User scripts
+
ns === 6 && !conf.wgArticleId || // Non-existing file pages
|| nsIds
+
ns === 2 && /\.(js|css)$/.test( conf.wgTitle ) || // User scripts
&& (   ns === nsIds['creator']
+
nsIds &&
|| ns === nsIds['timedtext']
+
( ns === nsIds.creator ||
|| ns === nsIds['institution']
+
ns === nsIds.timedtext ||
  )
+
ns === nsIds.institution ) );
  );
+
},
}
+
// A regexp matching a templates used to mark uncategorized pages, if your wiki does have that.
,uncat_regexp : /\{\{\s*([Uu]ncat(egori[sz]ed( image)?)?|[Nn]ocat|[Nn]eedscategory)[^}]*\}\}\s*(<\!--.*?--\>)?/g
+
// If not, set it to null.
// A regexp matching a templates used to mark uncategorized pages, if your wiki does have that.
+
uncat_regexp: /\{\{\s*[Uu]ncategorized\s*[^}]*\}\}\s*(<!--.*?-->\s*)?/g,
// If not, set it to null.
+
// The images used for the little indication icon. Should not need changing.
,existsYes   : '//upload.wikimedia.org/wikipedia/commons/thumb/b/be/P_yes.svg/20px-P_yes.svg.png'
+
existsYes: '//upload.wikimedia.org/wikipedia/commons/thumb/b/be/P_yes.svg/20px-P_yes.svg.png',
,existsNo     : '//upload.wikimedia.org/wikipedia/commons/thumb/4/42/P_no.svg/20px-P_no.svg.png'
+
existsNo: '//upload.wikimedia.org/wikipedia/commons/thumb/4/42/P_no.svg/20px-P_no.svg.png',
// The images used for the little indication icon. Should not need changing.
+
// a list of categories which can be removed by removing a template
,template_regexp    : '[Tt][Ee][Mm][Pp][Ll][Aa][Tt][Ee]'
+
// key: the category without namespace
// Regexp to recognize templates. Like "category" above; autolocalized for MW 1.16+, otherwise set manually here.
+
// value: A regexp matching the template name, again without namespace
// On the German Wikipedia, you might use '[Tt][Ee][Mm][Pp][Ll][Aa][Tt][Ee]|[Vv][Oo][Rr][Ll][Aa][Gg][Ee]'.
+
// If you don't have this at your wiki, or don't want this, set it to an empty object {}.
,template_categories : {}
+
template_categories: {},
// a list of categories which can be removed by removing a template
+
// Names for the search engines
// key: the category without namespace
+
engine_names: {
// value: A regexp matching the template name, again without namespace
+
searchindex: 'Search index',
// If you don't have this at your wiki, or don't want this, set it to an empty object {}.
+
pagelist: 'Page list',
,engine_names : {
+
combined: 'Combined search',
searchindex : 'Search index'
+
subcat: 'Subcategories',
,pagelist   : 'Page list'
+
parentcat: 'Parent categories'
,combined   : 'Combined search'
+
},
,subcat     : 'Subcategories'
+
 
,parentcat   : 'Parent categories'
+
// Override the decision of whether HotCat should help users by automatically
  }
+
// capitalising the title in the user input text if the wiki has case-sensitive page names.
// Names for the search engines
+
// Basically, this will make an API query to check the MediaWiki configuration and HotCat then sets
,capitalizePageNames : true
+
// this to true for most wikis, and to false on Wiktionary.
// Set to false if your wiki has case-sensitive page names. MediaWiki has two modes: either the first letter
+
//  
// of a page is automatically capitalized ("first-letter"; Category:aa == Category:Aa), or it isn't
+
// You can set this directly if there is a problem with it. For example, Georgian Wikipedia (kawiki),
// ("case-sensitive"; Category:aa != Category:Aa). It doesn't currently have a fully case-insensitive mode
+
// is known to have different capitalisation logic between MediaWiki PHP and JavaScript. As such, automatic
// (which would mean Category:aa == Category:Aa == Category:AA == Category:aA)
+
// case changes in JavaScript by HotCat would be wrong.
// HotCat tries to set this correctly automatically using an API query. It's still a good idea to manually
+
capitalizePageNames: null,
// configure it correctly; either directly here if you copied HotCat, or in the local configuration file
+
// If upload_disabled is true, HotCat will not be used on the Upload form.
// MediaWiki:Gadget-HotCat.js/local_defaults if you hotlink to the Commons-version, to ensure it is set even
+
upload_disabled: false,
// if that API query should fail for some strange reason.
+
// Single regular expression matching blacklisted categories that cannot be changed or
,upload_disabled : false
+
// added using HotCat. For instance /\bstubs?$/ (any category ending with the word "stub"
// If upload_disabled is true, HotCat will not be used on the Upload form.
+
// or "stubs"), or /(\bstubs?$)|\bmaintenance\b/ (stub categories and any category with the
,blacklist : null
+
// word "maintenance" in its title.
// Single regular expression matching blacklisted categories that cannot be changed or
+
blacklist: null,
// added using HotCat. For instance /\bstubs?$/ (any category ending with the word "stub"
  −
// or "stubs"), or /(\bstubs?$)|\bmaintenance\b/ (stub categories and any category with the
  −
// word "maintenance" in its title.
     −
// Stuff changeable by users:
+
// Stuff changeable by users:
,bg_changed : '#F8CCB0'
+
// Background for changed categories in multi-edit mode. Default is a very light salmon pink.
// Background for changed categories in multi-edit mode. Default is a very light salmon pink.
+
bg_changed: '#FCA',
,no_autocommit : false
+
// If true, HotCat will never automatically submit changes. HotCat will only open an edit page with
// If true, HotCat will never automatically submit changes. HotCat will only open an edit page with
+
// the changes; users must always save explicitly.
// the changes; users must always save explicitly.
+
no_autocommit: false,
,del_needs_diff : false
+
// If true, the "category deletion" link "(-)" will never save automatically but always show an
// If true, the "category deletion" link "(-)" will never save automatically but always show an
+
// edit page where the user has to save the edit manually. Is false by default because that's the
// edit page where the user has to save the edit manually. Is false by default because that's the
+
// traditional behavior. This setting overrides no_autocommit for "(-)" links.
// traditional behavior. This setting overrides no_autocommit for "(-)" links.
+
del_needs_diff: false,
,suggest_delay : 100
+
// Time, in milliseconds, that HotCat waits after a keystroke before making a request to the
// Time, in milliseconds, that HotCat waits after a keystroke before making a request to the
+
// server to get suggestions.
// server to get suggestions.
+
suggest_delay: 100,
,editbox_width : 40
+
// Default width, in characters, of the text input field.
// Default width, in characters, of the text input field.
+
editbox_width: 40,
,suggestions : 'combined'
+
// One of the engine_names above, to be used as the default suggestion engine.
// One of the engine_names above, to be used as the default suggestion engine.
+
suggestions: 'combined',
,fixed_search : false
+
// If true, always use the default engine, and never display a selector.
// If true, always use the default engine, and never display a selector.
+
fixed_search: false,
,use_up_down : true
+
// If false, do not display the "up" and "down" links
// If false, do not display the "up" and "down" links
+
use_up_down: true,
,list_size : 5
+
// Default list size
// Default list size
+
listSize: 10,
,single_minor : true
+
// If true, single category changes are marked as minor edits. If false, they're not.
// If true, single category changes are marked as minor edits. If false, they're not.
+
single_minor: true,
,dont_add_to_watchlist : false
+
// If true, never add a page to the user's watchlist. If false, pages get added to the watchlist if
// If true, never add a page to the user's watchlist. If false, pages get added to the watchlist if
+
// the user has the "Add pages I edit to my watchlist" or the "Add pages I create to my watchlist"
// the user has the "Add pages I edit to my watchlist" or the "Add pages I create to my watchlist"
+
// options in his or her preferences set.
// options in his or her preferences set.
+
dont_add_to_watchlist: false,
,shortcuts : null
+
shortcuts: null,
,addShortcuts :
+
addShortcuts: function ( map ) {
function (map) {
+
if ( !map ) return;
if (!map) return;
   
window.HotCat.shortcuts = window.HotCat.shortcuts || {};
 
window.HotCat.shortcuts = window.HotCat.shortcuts || {};
for (var k in map) {
+
for ( var k in map ) {
if (!map.hasOwnProperty (k) || typeof k != 'string') continue;
+
if ( !map.hasOwnProperty( k ) || typeof k !== 'string' ) continue;
var v = map[k];
+
 
if (typeof v != 'string') continue;
+
var v = map[ k ];
k = k.replace (/^\s+|\s+$/g, "");
+
if ( typeof v !== 'string' ) continue;
v = v.replace (/^\s+|\s+$/g, "");
+
 
if (k.length === 0 || v.length === 0) continue;
+
k = k.replace( /^\s+|\s+$/g, '' );
window.HotCat.shortcuts[k] = v;
+
v = v.replace( /^\s+|\s+$/g, '' );
 +
if ( !k.length || !v.length ) continue;
 +
 
 +
window.HotCat.shortcuts[ k ] = v;
 
}
 
}
 
}
 
}
};
+
};
    
// More backwards compatibility. We have a few places where we test for the browser: once for
 
// More backwards compatibility. We have a few places where we test for the browser: once for
// Safari < 3.0, twice for WebKit (Chrome or Safari, any versions), twice for IE <= 6, and
+
// Safari < 3.0, and twice for WebKit (Chrome or Safari, any versions)
// once for IE < 8.
   
var ua = navigator.userAgent.toLowerCase();
 
var ua = navigator.userAgent.toLowerCase();
var is_ie6 = /msie ([0-9]{1,}[\.0-9]{0,})/.exec(ua) !== null && parseFloat(RegExp.$1) <= 6.0;
+
var is_webkit = /applewebkit\/\d+/.test( ua ) && ua.indexOf( 'spoofer' ) < 0;
var is_ie_lt8 = /msie ([0-9]{1,}[\.0-9]{0,})/.exec(ua) !== null && parseFloat(RegExp.$1) < 8.0;
+
var cat_prefix = null;
var is_webkit = /applewebkit\/\d+/.test(ua) && ua.indexOf ('spoofer') < 0;
+
var noSuggestions = false;
// And even more compatbility. HotCat was developed without jQuery, and anyway current jQuery
  −
// (1.7.1) doesn't seem to support in jquery.getJSON() or jQuery.ajax() the automatic
  −
// switching from GET to POST requests if the query arguments would make the uri too long.
  −
// (IE has a hard limit of 2083 bytes, and the servers may have limits around 4 or 8kB.)
  −
//    Anyway, HotCat is supposed to run on wikis without jQuery, so we'd have to supply some
  −
// ajax routines ourselves in any case. We can't rely on the old sajax_init_object(), newer
  −
// MW versions (>= 1.19) might not have it.
  −
var getJSON = (function () {
  −
function getRequest () {
  −
var request = null;
  −
try {
  −
request = new window.XMLHttpRequest();
  −
} catch (anything) {
  −
if (window.ActiveXObject) {
  −
try {
  −
request = new window.ActiveXObject('Microsoft.XMLHTTP');
  −
} catch (any) {
  −
}
  −
} // end if IE
  −
} // end try-catch
  −
return request;
  −
}
     −
return function (settings) {
+
function LoadTrigger( needed ) {
var req = getRequest();
+
// Define methods in a closure so that self reference is available,
if (!req && settings && settings.error) settings.error (req);
+
// also allows method calls to be detached.
if (!req || !settings || !settings.uri) return req;
+
var self = this;
var uri = armorUri (settings.uri);
+
self.queue = [];
var args = settings.data || null;
+
self.needed = needed;
var method;
+
self.register = function ( callback ) {
if (args && uri.length + args.length + 1 > 2000) {
+
if ( self.needed <= 0 ) callback(); // Execute directly
// We lose caching, but at least we can make the request
+
else self.queue.push( callback );
method = 'POST';
+
};
req.setRequestHeader ('Content-Type', 'application/x-www-form-urlencoded');
+
self.loaded = function () {
} else {
+
self.needed--;
method = 'GET';
+
if ( self.needed === 0 ) {
if (args) uri += '?' + args;
+
// Run queued callbacks once
args = null;
+
for ( var i = 0; i < self.queue.length; i++ ) self.queue[ i ]();
 +
self.queue = [];
 
}
 
}
req.open (method, uri, true);
  −
req.onreadystatechange = function () {
  −
if (req.readyState != 4) return;
  −
if (req.status != 200 || !req.responseText || !(/^\s*[\{\[]/.test(req.responseText))) {
  −
if (settings.error) settings.error (req);
  −
} else {
  −
if (settings.success) settings.success (eval ('(' + req.responseText + ')'));
  −
}
  −
};
  −
req.setRequestHeader ('Pragma', 'cache=yes');
  −
req.setRequestHeader ('Cache-Control', 'no-transform');
  −
req.send (args);
  −
return req;
   
};
 
};
})();
  −
  −
function armorUri (uri) {
  −
// Avoid protocol-relative URIs, IE7 has a bug with them in Ajax calls
  −
if (uri.length >= 2 && uri.substring(0, 2) == '//') return document.location.protocol + uri;
  −
return uri;
   
}
 
}
   −
function LoadTrigger (needed) {
  −
this.queue = [];
  −
this.toLoad = needed;
  −
}
  −
LoadTrigger.prototype = {
  −
register : function (callback) {
  −
if (this.toLoad <= 0) {
  −
callback (); // Execute directly
  −
} else {
  −
this.queue[this.queue.length] = callback;
  −
}
  −
},
  −
  −
loaded : function () {
  −
if (this.toLoad > 0) {
  −
this.toLoad--;
  −
if (this.toLoad === 0) {
  −
// Run queued callbacks once
  −
for (var i = 0; i < this.queue.length; i++) this.queue[i]();
  −
this.queue = [];
  −
}
  −
}
  −
}
  −
  −
};
  −
  −
var setupCompleted = new LoadTrigger(1);
  −
// Used to run user-registered code once HotCat is fully set up and ready.
  −
HotCat.runWhenReady = function (callback) {setupCompleted.register(callback);};
  −
  −
var loadTrigger = new LoadTrigger(2);
   
// Used to delay running the HotCat setup until /local_defaults and localizations have been loaded.
 
// Used to delay running the HotCat setup until /local_defaults and localizations have been loaded.
 +
var loadTrigger = new LoadTrigger( 2 );
   −
function load (uri) {
+
function load( uri, callback ) {
var head = document.getElementsByTagName ('head')[0];
+
var s = document.createElement( 'script' );
var s = document.createElement ('script');
+
s.src = uri;
s.setAttribute ('src', armorUri(uri));
+
var called = false;
s.setAttribute ('type', 'text/javascript');
  −
var done = false;
     −
function afterLoad () {
+
s.onload = s.onerror = function () {
if (done) return;
+
if ( !called && callback ) {
done = true;
+
called = true;
s.onload = s.onreadystatechange = s.onerror = null; // Properly clean up to avoid memory leaks in IE
+
callback();
if (head && s.parentNode) head.removeChild (s);
+
}
loadTrigger.loaded();
+
if ( s.parentNode ) {
}
+
s.parentNode.removeChild( s );
 
  −
s.onload = s.onreadystatechange = function () { // onreadystatechange for IE, onload for all others
  −
if (done) return;
  −
if (!this.readyState || this.readyState === 'loaded' || this.readyState === 'complete') {
  −
afterLoad ();
   
}
 
}
 
};
 
};
s.onerror = afterLoad; // Clean up, but otherwise ignore errors
+
document.head.appendChild( s );
head.insertBefore (s, head.firstChild); // appendChild may trigger bugs in IE6 here
   
}
 
}
   −
function loadJS (page) {
+
function loadJS( page, callback ) {
load (conf.wgServer + conf.wgScript + '?title=' + encodeURIComponent (page) + '&action=raw&ctype=text/javascript');
+
load( conf.wgServer + conf.wgScript + '?title=' + encodeURIComponent( page ) + '&action=raw&ctype=text/javascript', callback );
 
}
 
}
   −
function loadURI (href) {
+
function loadURI( href, callback ) {
 
var url = href;
 
var url = href;
if (url.substring (0, 2) == '//') {
+
if ( url.substring( 0, 2 ) === '//' ) url = window.location.protocol + url; else if ( url.substring( 0, 1 ) === '/' ) url = conf.wgServer + url;
url = window.location.protocol + url;
+
 
} else if (url.substring (0, 1) == '/') {
+
load( url, callback );
url = conf.wgServer + url;
  −
}
  −
load (url);
   
}
 
}
    
// Load local configurations, overriding the pre-set default values in the HotCat object above. This is always loaded
 
// Load local configurations, overriding the pre-set default values in the HotCat object above. This is always loaded
// from the wiki where this script is executing, even if this script itself is hotlinked from the Commons. This can
+
// from the wiki where this script is executing, even if this script itself is hotlinked from Commons. This can
 
// be used to change the default settings, or to provide localized interface texts for edit summaries and so on.
 
// be used to change the default settings, or to provide localized interface texts for edit summaries and so on.
loadJS ('MediaWiki:Gadget-HotCat.js/local_defaults');
+
loadJS( 'MediaWiki:Gadget-HotCat.js/local_defaults', loadTrigger.loaded );
    
// Load localized UI texts. These are the texts that HotCat displays on the page itself. Texts shown in edit summaries
 
// Load localized UI texts. These are the texts that HotCat displays on the page itself. Texts shown in edit summaries
 
// should be localized in /local_defaults above.
 
// should be localized in /local_defaults above.
if (conf.wgUserLanguage != 'en') {
+
if ( conf.wgUserLanguage !== 'en' ) {
 
// Lupo: somebody thought it would be a good idea to add this. So the default is true, and you have to set it to false
 
// Lupo: somebody thought it would be a good idea to add this. So the default is true, and you have to set it to false
// explicitly if you're not on the Commons and don't want that.
+
// explicitly if you're not on Commons and don't want that.
if (typeof window.hotcat_translations_from_commons == 'undefined') {
+
if ( window.hotcat_translations_from_commons === undefined ) window.hotcat_translations_from_commons = true;
window.hotcat_translations_from_commons = true;
+
 
}
   
// Localization hook to localize HotCat messages, tooltips, and engine names for wgUserLanguage.
 
// Localization hook to localize HotCat messages, tooltips, and engine names for wgUserLanguage.
if (window.hotcat_translations_from_commons && conf.wgServer.indexOf('//commons') < 0) {
+
if ( window.hotcat_translations_from_commons && conf.wgServer.indexOf( '//commons' ) < 0 ) {
loadURI ('//commons.wikimedia.org/w/index.php?title='
+
loadURI( '//commons.wikimedia.org/w/index.php?title=' +
+ 'MediaWiki:Gadget-HotCat.js/' + conf.wgUserLanguage
+
'MediaWiki:Gadget-HotCat.js/' + conf.wgUserLanguage +
+ '&action=raw&ctype=text/javascript'
+
'&action=raw&ctype=text/javascript', loadTrigger.loaded );
);
   
} else {
 
} else {
 
// Load translations locally
 
// Load translations locally
loadJS ('MediaWiki:Gadget-HotCat.js/' + conf.wgUserLanguage);
+
loadJS( 'MediaWiki:Gadget-HotCat.js/' + conf.wgUserLanguage, loadTrigger.loaded );
 
}
 
}
 
} else {
 
} else {
Line 390: Line 299:     
// The following regular expression strings are used when searching for categories in wikitext.
 
// The following regular expression strings are used when searching for categories in wikitext.
var wikiTextBlank   = '[\\t _\\xA0\\u1680\\u180E\\u2000-\\u200A\\u2028\\u2029\\u202F\\u205F\\u3000]+';
+
var wikiTextBlank = '[\\t _\\xA0\\u1680\\u180E\\u2000-\\u200A\\u2028\\u2029\\u202F\\u205F\\u3000]+';
var wikiTextBlankRE = new RegExp (wikiTextBlank, 'g');
+
var wikiTextBlankRE = new RegExp( wikiTextBlank, 'g' );
 
// Regexp for handling blanks inside a category title or namespace name.
 
// Regexp for handling blanks inside a category title or namespace name.
 
// See http://svn.wikimedia.org/viewvc/mediawiki/trunk/phase3/includes/Title.php?revision=104051&view=markup#l2722
 
// See http://svn.wikimedia.org/viewvc/mediawiki/trunk/phase3/includes/Title.php?revision=104051&view=markup#l2722
Line 412: Line 321:  
var formattedNamespaces = conf.wgFormattedNamespaces;
 
var formattedNamespaces = conf.wgFormattedNamespaces;
 
var namespaceIds = conf.wgNamespaceIds;
 
var namespaceIds = conf.wgNamespaceIds;
if (formattedNamespaces) {
+
function autoLocalize( namespaceNumber, fallback ) {
function autoLocalize (namespaceNumber, fallback) {
+
function createRegexpStr( name ) {
function create_regexp_str (name)
+
if ( !name || !name.length ) return '';
{
+
 
if (!name || name.length === 0) return "";
+
var regex_name = '';
var regex_name = "";
+
for ( var i = 0; i < name.length; i++ ) {
for (var i = 0; i < name.length; i++){
+
var initial = name.charAt( i ),
var initial = name.substr (i, 1);
+
ll = initial.toLowerCase(),
var ll = initial.toLowerCase ();
+
ul = initial.toUpperCase();
var ul = initial.toUpperCase ();
+
if ( ll === ul ) regex_name += initial; else regex_name += '[' + ll + ul + ']';
if (ll == ul){
  −
regex_name += initial;
  −
} else {
  −
regex_name += '[' + ll + ul + ']';
  −
}
  −
}
  −
return regex_name
  −
.replace(/([\\\^\$\.\?\*\+\(\)])/g, '\\$1')
  −
.replace (wikiTextBlankRE, wikiTextBlank);
   
}
 
}
 +
return regex_name
 +
.replace( /([\\^$.?*+()])/g, '\\$1' )
 +
.replace( wikiTextBlankRE, wikiTextBlank );
 +
}
 +
 +
fallback = fallback.toLowerCase();
 +
var canonical = formattedNamespaces[ String( namespaceNumber ) ].toLowerCase();
 +
var regexp = createRegexpStr( canonical );
 +
if ( fallback && canonical !== fallback ) regexp += '|' + createRegexpStr( fallback );
   −
fallback = fallback.toLowerCase();
+
if ( namespaceIds ) {
var canonical  = formattedNamespaces["" + namespaceNumber].toLowerCase();
+
for ( var cat_name in namespaceIds ) {
var regexp    = create_regexp_str (canonical);
+
if (
if (fallback && canonical != fallback) regexp += '|' + create_regexp_str(fallback);
+
typeof cat_name === 'string' &&
if (namespaceIds) {
+
cat_name.toLowerCase() !== canonical &&
for (var cat_name in namespaceIds) {
+
cat_name.toLowerCase() !== fallback &&
if (   typeof cat_name == 'string'
+
namespaceIds[ cat_name ] === namespaceNumber
&& cat_name.toLowerCase() != canonical
+
) {
&& cat_name.toLowerCase() != fallback
+
regexp += '|' + createRegexpStr( cat_name );
&& namespaceIds[cat_name] == namespaceNumber)
  −
{
  −
regexp += '|' + create_regexp_str(cat_name);
  −
}
   
}
 
}
 
}
 
}
return regexp;
   
}
 
}
 +
return regexp;
 +
}
   −
if (formattedNamespaces['14']) {
+
HC.category_canonical = formattedNamespaces[ '14' ];
HotCat.category_canonical = formattedNamespaces['14'];
+
HC.category_regexp = autoLocalize( 14, 'category' );
HotCat.category_regexp = autoLocalize (14, 'category');
+
if ( formattedNamespaces[ '10' ] ) HC.template_regexp = autoLocalize( 10, 'template' );
}
  −
if (formattedNamespaces['10']) {
  −
HotCat.template_regexp = autoLocalize (10, 'template');
  −
}
  −
}
      
// Utility functions. Yes, this duplicates some functionality that also exists in other places, but
 
// Utility functions. Yes, this duplicates some functionality that also exists in other places, but
// to keep this whole stuff in a single file not depending on any other on-wiki Javascripts, we re-do
+
// to keep this whole stuff in a single file not depending on any other on-wiki JavaScripts, we re-do
 
// these few operations here.
 
// these few operations here.
function bind (func, target) {
+
function make( arg, literal ) {
var f = func, tgt = target;
+
if ( !arg ) return null;
return function () { return f.apply (tgt, arguments); };
+
 
}
+
return literal ? document.createTextNode( arg ) : document.createElement( arg );
function make (arg, literal) {
  −
if (!arg) return null;
  −
return literal ? document.createTextNode (arg) : document.createElement (arg);
   
}
 
}
function param (name, uri) {
+
function param( name, uri ) {
if (typeof uri == 'undefined' || uri === null) uri = document.location.href;
+
uri = uri || document.location.href;
var re = new RegExp ('[&?]' + name + '=([^&#]*)');
+
var re = new RegExp( '[&?]' + name + '=([^&#]*)' );
var m = re.exec (uri);
+
var m = re.exec( uri );
if (m && m.length > 1) return decodeURIComponent(m[1]);
+
if ( m && m.length > 1 ) return decodeURIComponent( m[ 1 ] );
 
return null;
 
return null;
 
}
 
}
function title (href) {
+
function title( href ) {
if (!href) return null;
+
if ( !href ) return null;
 +
 
 
var script = conf.wgScript + '?';
 
var script = conf.wgScript + '?';
if (href.indexOf (script) === 0 || href.indexOf (conf.wgServer + script) === 0 || conf.wgServer.substring(0, 2) == '//' && href.indexOf (document.location.protocol + conf.wgServer + script) === 0) {
+
if ( href.indexOf( script ) === 0 || href.indexOf( conf.wgServer + script ) === 0 || conf.wgServer.substring( 0, 2 ) === '//' && href.indexOf( document.location.protocol + conf.wgServer + script ) === 0 ) {
 
// href="/w/index.php?title=..."
 
// href="/w/index.php?title=..."
return param ('title', href);
+
return param( 'title', href );
 
} else {
 
} else {
 
// href="/wiki/..."
 
// href="/wiki/..."
var prefix = conf.wgArticlePath.replace ('$1', "");
+
var prefix = conf.wgArticlePath.replace( '$1', '' );
if (href.indexOf (prefix) !== 0) prefix = conf.wgServer + prefix; // Fully expanded URL?
+
if ( href.indexOf( prefix ) ) prefix = conf.wgServer + prefix; // Fully expanded URL?
if (href.indexOf (prefix) !== 0 && prefix.substring(0, 2) == '//') prefix = document.location.protocol + prefix; // Protocol-relative wgServer?
+
 
if (href.indexOf (prefix) === 0)
+
if ( href.indexOf( prefix ) && prefix.substring( 0, 2 ) === '//' ) prefix = document.location.protocol + prefix; // Protocol-relative wgServer?
return decodeURIComponent (href.substring (prefix.length));
+
 
 +
if ( href.indexOf( prefix ) === 0 ) return decodeURIComponent( href.substring( prefix.length ) );
 
}
 
}
 
return null;
 
return null;
 
}
 
}
function hasClass (elem, name) {
+
function hasClass( elem, name ) {
return (' ' + elem.className + ' ').indexOf (' ' + name + ' ') >= 0;
+
return ( ' ' + elem.className + ' ' ).indexOf( ' ' + name + ' ' ) >= 0;
 
}
 
}
function capitalize (str) {
+
function capitalize( str ) {
if (!str || str.length === 0) return str;
+
if ( !str || !str.length ) return str;
return str.substr(0, 1).toUpperCase() + str.substr (1);
+
 
 +
return str.substr( 0, 1 ).toUpperCase() + str.substr( 1 );
 
}
 
}
function wikiPagePath (pageName) {
+
function wikiPagePath( pageName ) {
 
// Note: do not simply use encodeURI, it doesn't encode '&', which might break if wgArticlePath actually has the $1 in
 
// Note: do not simply use encodeURI, it doesn't encode '&', which might break if wgArticlePath actually has the $1 in
 
// a query parameter.
 
// a query parameter.
return conf.wgArticlePath.replace('$1', encodeURIComponent (pageName).replace(/%3A/g, ':').replace(/%2F/g, '/'));
+
return conf.wgArticlePath.replace( '$1', encodeURIComponent( pageName ).replace( /%3A/g, ':' ).replace( /%2F/g, '/' ) );
 
}
 
}
function escapeRE(str) {
+
function escapeRE( str ) {
return str.replace(/([\\\^\$\.\?\*\+\(\)\[\]])/g, '\\$1');
+
return str.replace( /([\\^$.?*+()[\]])/g, '\\$1' );
 
}
 
}
   −
function substituteFactory (options) {
+
function substituteFactory( options ) {
 
options = options || {};
 
options = options || {};
 
var lead = options.indicator || '$';
 
var lead = options.indicator || '$';
var indicator = escapeRE (lead);
+
var indicator = escapeRE( lead );
var lbrace = escapeRE (options.lbrace || '{');
+
var lbrace = escapeRE( options.lbrace || '{' );
var rbrace = escapeRE (options.rbrace || '}');
+
var rbrace = escapeRE( options.rbrace || '}' );
 
var re;
 
var re;
    
re = new RegExp(
 
re = new RegExp(
'(?:' + indicator + '(' + indicator + '))|'                                           // $$
+
// $$
+'(?:' + indicator + '(\\d+))|'                                                       // $0, $1
+
'(?:' + indicator + '(' + indicator + '))|' +
+'(?:' + indicator + '(?:' + lbrace + '([^' + lbrace + rbrace + ']+)' + rbrace + '))|' // ${key}
+
// $0, $1
+'(?:' + indicator + '(?!(?:[' + indicator + lbrace + ']|\\d))(\\S+?)\\b)'             // $key (only if first char after $ is not $, digit, or { )
+
'(?:' + indicator + '(\\d+))|' +
,'g');
+
// ${key}
 +
'(?:' + indicator + '(?:' + lbrace + '([^' + lbrace + rbrace + ']+)' + rbrace + '))|' +
 +
// $key (only if first char after $ is not $, digit, or { )
 +
'(?:' + indicator + '(?!(?:[' + indicator + lbrace + ']|\\d))(\\S+?)\\b)',
 +
'g'
 +
);
 
// Replace $1, $2, or ${key1}, ${key2}, or $key1, $key2 by values from map. $$ is replaced by a single $.
 
// Replace $1, $2, or ${key1}, ${key2}, or $key1, $key2 by values from map. $$ is replaced by a single $.
return function (str, map) {
+
return function ( str, map ) {
if (!map) return str;
+
if ( !map ) return str;
return str.replace(re
+
 
,function (match, prefix, idx, key, alpha) {
+
return str.replace( re, function ( match, prefix, idx, key, alpha ) {
if (prefix == lead) return lead;
+
if ( prefix === lead ) return lead;
var k = alpha || key || idx;
+
 
var replacement = typeof map[k] === 'function' ? map[k](match, k) : map[k];
+
var k = alpha || key || idx;
return typeof replacement === 'string' ? replacement : (replacement || match);
+
var replacement = typeof map[ k ] === 'function' ? map[ k ]( match, k ) : map[ k ];
}
+
return typeof replacement === 'string' ? replacement : ( replacement || match );
);
+
} );
 
};
 
};
 
}
 
}
    
var substitute = substituteFactory();
 
var substitute = substituteFactory();
var replaceShortcuts = (function () {
+
var replaceShortcuts = ( function () {
var replaceHash = substituteFactory({indicator:'#',lbrace:'[',rbrace:']'});
+
var replaceHash = substituteFactory( {
return function (str, map) {
+
indicator: '#',
var s = replaceHash (str, map);
+
lbrace: '[',
return HotCat.capitalizePageNames ? capitalize(s) : s;
+
rbrace: ']'
 +
} );
 +
return function ( str, map ) {
 +
var s = replaceHash( str, map );
 +
return HC.capitalizePageNames ? capitalize( s ) : s;
 
};
 
};
})();
+
}() );
    
// Text modification
 
// Text modification
    
var findCatsRE =
 
var findCatsRE =
new RegExp ('\\[\\[' + wikiTextBlankOrBidi + '(?:' + HotCat.category_regexp + ')' + wikiTextBlankOrBidi + ':[^\\]]+\\]\\]', 'g');
+
new RegExp( '\\[\\[' + wikiTextBlankOrBidi + '(?:' + HC.category_regexp + ')' + wikiTextBlankOrBidi + ':[^\\]]+\\]\\]', 'g' );
   −
function replaceByBlanks (match) {
+
function replaceByBlanks( match ) {
return match.replace(/(\s|\S)/g, ' '); // /./ doesn't match linebreaks. /(\s|\S)/ does.
+
return match.replace( /(\s|\S)/g, ' ' ); // /./ doesn't match linebreaks. /(\s|\S)/ does.
 
}
 
}
   −
function find_category (wikitext, category, once) {
+
function find_category( wikitext, category, once ) {
 
var cat_regex = null;
 
var cat_regex = null;
if(HotCat.template_categories[category]){
+
if ( HC.template_categories[ category ] ) {
cat_regex = new RegExp ('\\{\\{' + wikiTextBlankOrBidi + '(' + HotCat.template_regexp + '(?=' + wikiTextBlankOrBidi + ':))?' + wikiTextBlankOrBidi
+
cat_regex = new RegExp(
+ '(?:' + HotCat.template_categories[category] + ')'
+
'\\{\\{' + wikiTextBlankOrBidi + '(' + HC.template_regexp + '(?=' + wikiTextBlankOrBidi + ':))?' + wikiTextBlankOrBidi +
+ wikiTextBlankOrBidi + '(\\|.*?)?\\}\\}', 'g'
+
'(?:' + HC.template_categories[ category ] + ')' +
 +
wikiTextBlankOrBidi + '(\\|.*?)?\\}\\}',
 +
'g'
 
);
 
);
 
} else {
 
} else {
var cat_name = escapeRE (category);
+
var cat_name = escapeRE( category );
var initial   = cat_name.substr (0, 1);
+
var initial = cat_name.substr( 0, 1 );
cat_regex = new RegExp ('\\[\\[' + wikiTextBlankOrBidi + '(' + HotCat.category_regexp + ')' + wikiTextBlankOrBidi + ':' + wikiTextBlankOrBidi
+
cat_regex = new RegExp(
+ (initial == '\\' || !HotCat.capitalizePageNames
+
'\\[\\[' + wikiTextBlankOrBidi + '(' + HC.category_regexp + ')' + wikiTextBlankOrBidi + ':' + wikiTextBlankOrBidi +
? initial
+
( initial === '\\' || !HC.capitalizePageNames ?
: '[' + initial.toUpperCase() + initial.toLowerCase() + ']')
+
initial :
+ cat_name.substring (1).replace (wikiTextBlankRE, wikiTextBlank)
+
'[' + initial.toUpperCase() + initial.toLowerCase() + ']' ) +
+ wikiTextBlankOrBidi + '(\\|.*?)?\\]\\]', 'g'
+
cat_name.substring( 1 ).replace( wikiTextBlankRE, wikiTextBlank ) +
 +
wikiTextBlankOrBidi + '(\\|.*?)?\\]\\]',
 +
'g'
 
);
 
);
 
}
 
}
if (once) return cat_regex.exec (wikitext);
+
if ( once ) return cat_regex.exec( wikitext );
 +
 
 
var copiedtext = wikitext
 
var copiedtext = wikitext
.replace(/<\!--(\s|\S)*?--\>/g, replaceByBlanks)
+
.replace( /<!--(\s|\S)*?-->/g, replaceByBlanks )
.replace(/<nowiki\>(\s|\S)*?<\/nowiki>/g, replaceByBlanks);
+
.replace( /<nowiki>(\s|\S)*?<\/nowiki>/g, replaceByBlanks );
 
var result = [];
 
var result = [];
 
var curr_match = null;
 
var curr_match = null;
while ((curr_match = cat_regex.exec (copiedtext)) !== null) {
+
while ( ( curr_match = cat_regex.exec( copiedtext ) ) !== null ) {
result.push ({match : curr_match});
+
result.push( {
 +
match: curr_match
 +
} );
 
}
 
}
 
result.re = cat_regex;
 
result.re = cat_regex;
return result; // An array containing all matches, with positions, in result[i].match
+
return result; // An array containing all matches, with positions, in result[ i ].match
 
}
 
}
    
var interlanguageRE = null;
 
var interlanguageRE = null;
   −
function change_category (wikitext, toRemove, toAdd, key, is_hidden) {
+
function change_category( wikitext, toRemove, toAdd, key, is_hidden ) {
   −
function find_insertionpoint (wikitext) {
+
function find_insertionpoint( wikitext ) {
 
var copiedtext = wikitext
 
var copiedtext = wikitext
.replace(/<\!--(\s|\S)*?--\>/g, replaceByBlanks)
+
.replace( /<!--(\s|\S)*?-->/g, replaceByBlanks )
.replace(/<nowiki\>(\s|\S)*?<\/nowiki>/g, replaceByBlanks);
+
.replace( /<nowiki>(\s|\S)*?<\/nowiki>/g, replaceByBlanks );
 
// Search in copiedtext to avoid that we insert inside an HTML comment or a nowiki "element".
 
// Search in copiedtext to avoid that we insert inside an HTML comment or a nowiki "element".
 
var index = -1;
 
var index = -1;
 
findCatsRE.lastIndex = 0;
 
findCatsRE.lastIndex = 0;
while (findCatsRE.exec(copiedtext) !== null) index = findCatsRE.lastIndex;
+
while ( findCatsRE.exec( copiedtext ) !== null ) index = findCatsRE.lastIndex;
if (index < 0) {
+
 
 +
if ( index < 0 ) {
 
// Find the index of the first interlanguage link...
 
// Find the index of the first interlanguage link...
 
var match = null;
 
var match = null;
if (!interlanguageRE) {
+
if ( !interlanguageRE ) {
// Approximation without API: interlanguage links start with 2 to 3 lower case letters, optionally followed by
+
// Approximation without API: interlanguage links start with 2 to 3 lower case letters, optionally followed by
// a sequence of groups consisting of a dash followed by one or more lower case letters. Exceptions are "simple"
+
// a sequence of groups consisting of a dash followed by one or more lower case letters. Exceptions are "simple"
// and "tokipona".
+
// and "tokipona".
match = /((^|\n\r?)(\[\[\s*(([a-z]{2,3}(-[a-z]+)*)|simple|tokipona)\s*:[^\]]+\]\]\s*))+$/.exec (copiedtext);
+
match = /((^|\n\r?)(\[\[\s*(([a-z]{2,3}(-[a-z]+)*)|simple|tokipona)\s*:[^\]]+\]\]\s*))+$/.exec( copiedtext );
 
} else {
 
} else {
match = interlanguageRE.exec(copiedtext);
+
match = interlanguageRE.exec( copiedtext );
 
}
 
}
if (match) index = match.index;
+
if ( match ) index = match.index;
return {idx : index, onCat : false};
+
 
 +
return {
 +
idx: index,
 +
onCat: false
 +
};
 
}
 
}
return {idx : index, onCat : index >= 0};
+
return {
 +
idx: index,
 +
onCat: index >= 0
 +
};
 
}
 
}
   −
var summary   = [];
+
var summary = [],
var nameSpace = HotCat.category_canonical;
+
nameSpace = HC.category_canonical,
var cat_point = -1; // Position of removed category;
+
cat_point = -1, // Position of removed category;
 
+
keyChange = ( toRemove && toAdd && toRemove === toAdd && toAdd.length ),
if (key) key = '|' + key;
+
matches;
var keyChange = (toRemove && toAdd && toRemove == toAdd && toAdd.length > 0);
+
if ( key ) key = '|' + key;
var matches;
+
// Remove
if (toRemove && toRemove.length > 0) {
+
if ( toRemove && toRemove.length ) {
matches = find_category (wikitext, toRemove);
+
matches = find_category( wikitext, toRemove );
if (!matches || matches.length === 0) {
+
if ( !matches || !matches.length ) {
return {text: wikitext, 'summary': summary, error: HotCat.messages.cat_notFound.replace (/\$1/g, toRemove)};
+
return {
 +
text: wikitext,
 +
summary: summary,
 +
error: HC.messages.cat_notFound.replace( /\$1/g, toRemove )
 +
};
 
} else {
 
} else {
var before = wikitext.substring (0, matches[0].match.index);
+
var before = wikitext.substring( 0, matches[ 0 ].match.index ),
var after = wikitext.substring (matches[0].match.index + matches[0].match[0].length);
+
after = wikitext.substring( matches[ 0 ].match.index + matches[ 0 ].match[ 0 ].length );
if (matches.length > 1) {
+
if ( matches.length > 1 ) {
// Remove all occurrences in after
+
// Remove all occurrences in after
 
matches.re.lastIndex = 0;
 
matches.re.lastIndex = 0;
after = after.replace (matches.re, "");
+
after = after.replace( matches.re, '' );
 
}
 
}
if (toAdd) {
+
if ( toAdd ) {
nameSpace = matches[0].match[1] || nameSpace;
+
// nameSpace = matches[ 0 ].match[ 1 ] || nameSpace; Canonical namespace should be always preferred
if (key === null) key = matches[0].match[2]; // Remember the category key, if any.
+
if ( key === null ) key = matches[ 0 ].match[ 2 ];
 +
// Remember the category key, if any.
 
}
 
}
 
// Remove whitespace (properly): strip whitespace, but only up to the next line feed.
 
// Remove whitespace (properly): strip whitespace, but only up to the next line feed.
Line 643: Line 573:  
// whitespace characters, insert a blank.
 
// whitespace characters, insert a blank.
 
var i = before.length - 1;
 
var i = before.length - 1;
while (i >= 0 && before.charAt (i) != '\n' && before.substr (i, 1).search (/\s/) >= 0) i--;
+
while ( i >= 0 && before.charAt( i ) !== '\n' && before.substr( i, 1 ).search( /\s/ ) >= 0 ) i--;
 +
 
 
var j = 0;
 
var j = 0;
while (j < after.length && after.charAt (j) != '\n' && after.substr (j, 1).search (/\s/) >= 0)
+
while ( j < after.length && after.charAt( j ) !== '\n' && after.substr( j, 1 ).search( /\s/ ) >= 0 ) j++;
j++;
+
 
if (i >= 0 && before.charAt (i) == '\n' && (after.length === 0 || j < after.length && after.charAt (j) == '\n'))
+
if ( i >= 0 && before.charAt( i ) === '\n' && ( !after.length || j < after.length && after.charAt( j ) === '\n' ) ) i--;
i--;
+
 
if (i >= 0) before = before.substring (0, i+1); else before = "";
+
if ( i >= 0 ) before = before.substring( 0, i + 1 ); else before = '';
if (j < after.length) after = after.substring (j); else after = "";
+
 
if (before.length > 0 && before.substring (before.length - 1).search (/\S/) >= 0
+
if ( j < after.length ) after = after.substring( j ); else after = '';
&& after.length > 0 && after.substr (0, 1).search (/\S/) >= 0)
+
 
 +
if (
 +
before.length && before.substring( before.length - 1 ).search( /\S/ ) >= 0 &&
 +
after.length && after.substr( 0, 1 ).search( /\S/ ) >= 0
 +
) {
 
before += ' ';
 
before += ' ';
 +
}
 +
 
cat_point = before.length;
 
cat_point = before.length;
if (cat_point === 0 && after.length > 0 && after.substr(0,1) == '\n') {
+
if ( cat_point === 0 && after.length && after.substr( 0, 1 ) === '\n' ) after = after.substr( 1 );
after = after.substr(1);
+
 
}
   
wikitext = before + after;
 
wikitext = before + after;
if (!keyChange) {
+
if ( !keyChange ) {
if(HotCat.template_categories[toRemove]) {
+
if ( HC.template_categories[ toRemove ] ) { summary.push( HC.messages.template_removed.replace( /\$1/g, toRemove ) ); } else { summary.push( HC.messages.cat_removed.replace( /\$1/g, toRemove ) ); }
summary.push (HotCat.messages.template_removed.replace (/\$1/g, toRemove));
  −
} else {
  −
summary.push (HotCat.messages.cat_removed.replace (/\$1/g, toRemove));
  −
}
   
}
 
}
 +
 
}
 
}
 
}
 
}
if (toAdd && toAdd.length > 0) {
+
// Add
matches = find_category (wikitext, toAdd);
+
if ( toAdd && toAdd.length ) {
if (matches && matches.length > 0) {
+
matches = find_category( wikitext, toAdd );
return {text: wikitext, 'summary': summary, error : HotCat.messages.cat_exists.replace (/\$1/g, toAdd)};
+
if ( matches && matches.length ) {
 +
// Already exists
 +
return {
 +
text: wikitext,
 +
summary: summary,
 +
error: HC.messages.cat_exists.replace( /\$1/g, toAdd )
 +
};
 
} else {
 
} else {
 
var onCat = false;
 
var onCat = false;
if (cat_point < 0) {
+
if ( cat_point < 0 ) {
var point = find_insertionpoint (wikitext);
+
var point = find_insertionpoint( wikitext );
 
cat_point = point.idx;
 
cat_point = point.idx;
 
onCat = point.onCat;
 
onCat = point.onCat;
Line 681: Line 620:  
onCat = true;
 
onCat = true;
 
}
 
}
var newcatstring = '[[' + nameSpace + ':' + toAdd + (key || "") + ']]';
+
var newcatstring = '[[' + nameSpace + ':' + toAdd + ( key || '' ) + ']]';
if (cat_point >= 0) {
+
if ( cat_point >= 0 ) {
var suffix = wikitext.substring (cat_point);
+
var suffix = wikitext.substring( cat_point );
wikitext = wikitext.substring (0, cat_point) + (cat_point > 0 ? '\n' : "") + newcatstring + (!onCat ? '\n' : "");
+
wikitext = wikitext.substring( 0, cat_point ) + ( cat_point > 0 ? '\n' : '' ) + newcatstring + ( !onCat ? '\n' : '' );
if (suffix.length > 0 && suffix.substr(0, 1) != '\n') {
+
if ( suffix.length && suffix.substr( 0, 1 ) !== '\n' ) wikitext += '\n' + suffix; else wikitext += suffix;
wikitext += '\n' + suffix;
  −
} else {
  −
wikitext += suffix;
  −
}
   
} else {
 
} else {
if (wikitext.length > 0 && wikitext.substr (wikitext.length - 1, 1) != '\n')
+
if ( wikitext.length && wikitext.substr( wikitext.length - 1, 1 ) !== '\n' ) wikitext += '\n';
wikitext += '\n';
+
 
wikitext += (wikitext.length > 0 ? '\n' : "") + newcatstring;
+
wikitext += ( wikitext.length ? '\n' : '' ) + newcatstring;
 
}
 
}
if (keyChange) {
+
if ( keyChange ) {
var k = key || "";
+
var k = key || '';
if (k.length > 0) k = k.substr (1);
+
if ( k.length ) k = k.substr( 1 );
summary.push (substitute (HotCat.messages.cat_keychange, [null, toAdd, k]));
+
 
 +
summary.push( substitute( HC.messages.cat_keychange, [ null, toAdd, k ] ) );
 
} else {
 
} else {
summary.push (HotCat.messages.cat_added.replace (/\$1/g, toAdd));
+
summary.push( HC.messages.cat_added.replace( /\$1/g, toAdd ) );
 
}
 
}
if (HotCat.uncat_regexp && !is_hidden) {
+
if ( HC.uncat_regexp && !is_hidden ) {
var txt = wikitext.replace (HotCat.uncat_regexp, ""); // Remove "uncat" templates
+
var txt = wikitext.replace( HC.uncat_regexp, '' ); // Remove "uncat" templates
if (txt.length != wikitext.length) {
+
if ( txt.length !== wikitext.length ) {
 
wikitext = txt;
 
wikitext = txt;
summary.push (HotCat.messages.uncat_removed);
+
summary.push( HC.messages.uncat_removed );
 
}
 
}
 
}
 
}
 
}
 
}
 
}
 
}
return {text: wikitext, 'summary': summary, error: null};
+
return {
 +
text: wikitext,
 +
summary: summary,
 +
error: null
 +
};
 
}
 
}
    
// The real HotCat UI
 
// The real HotCat UI
   −
function evtKeys (e) {
+
function evtKeys( e ) {
e = e || window.event || window.Event; // W3C, IE, Netscape
+
/* eslint-disable no-bitwise */
 
var code = 0;
 
var code = 0;
if (typeof e.ctrlKey != 'undefined') { // All modern browsers
+
if ( e.ctrlKey ) { // All modern browsers
// Ctrl-click seems to be overloaded in FF/Mac (it opens a pop-up menu), so treat cmd-click
+
// Ctrl-click seems to be overloaded in FF/Mac (it opens a pop-up menu), so treat cmd-click
// as a ctrl-click, too.
+
// as a ctrl-click, too.
if (e.ctrlKey || e.metaKey) code |= 1;
+
if ( e.ctrlKey || e.metaKey ) code |= 1;
if (e.shiftKey) code |= 2;
+
 
} else if (typeof e.modifiers != 'undefined') { // Netscape...
+
if ( e.shiftKey ) code |= 2;
if (e.modifiers & (Event.CONTROL_MASK | Event.META_MASK)) code |= 1;
  −
if (e.modifiers & Event.SHIFT_MASK) code |= 2;
   
}
 
}
 
return code;
 
return code;
 
}
 
}
function evtKill (e) {
+
function evtKill( e ) {
e = e || window.event || window.Event; // W3C, IE, Netscape
+
if ( e.preventDefault ) {
if (typeof e.preventDefault != 'undefined') {
+
e.preventDefault();
e.preventDefault ();
+
e.stopPropagation();
e.stopPropagation ();
+
} else {
} else
   
e.cancelBubble = true;
 
e.cancelBubble = true;
 +
}
 
return false;
 
return false;
}
  −
function addEvent (node, evt, f, capture) {
  −
if (window.jQuery && (!capture || !node.addEventListener)) window.jQuery (node).bind (evt, f);
  −
else if (node.addEventListener) node.addEventListener (evt, f, capture); // FF etc; IE >= 9
  −
else if (node.attachEvent) node.attachEvent ('on' + evt, f); // Older IE; Opera
  −
else node['on' + evt] = f; // Very old!
   
}
 
}
   −
var catLine     = null;
+
var catLine = null,
var onUpload     = false;
+
onUpload = false,
var editors     = [];
+
editors = [],
   −
var commitButton = null;
+
commitButton = null,
var commitForm   = null;
+
commitForm = null,
var multiSpan   = null;
+
multiSpan = null,
   −
var pageText     = null;
+
pageText = null,
var pageTime     = null;
+
pageTime = null,
var pageWatched = false;
+
pageWatched = false,
var watchCreate = false;
+
watchCreate = false,
var watchEdit   = false;
+
watchEdit = false,
var minorEdits   = false;
+
minorEdits = false,
var editToken   = null;
+
editToken = null,
   −
var is_rtl       = false;
+
is_rtl = false,
var serverTime   = null;
+
serverTime = null,
var lastRevId   = null;
+
lastRevId = null,
var pageTextRevId = null;
+
pageTextRevId = null,
var conflictingUser = null;
+
conflictingUser = null,
   −
var newDOM       = false; // true if MediaWiki serves the new UL-LI DOM for categories
+
newDOM = false; // true if MediaWiki serves the new UL-LI DOM for categories
   −
function setMultiInput () {
+
function CategoryEditor() {
if (commitButton || onUpload) return;
+
this.initialize.apply( this, arguments );
commitButton = make ('input');
  −
commitButton.type  = 'button';
  −
commitButton.value = HotCat.messages.commit;
  −
commitButton.onclick = multiSubmit;
  −
if (multiSpan) {
  −
multiSpan.parentNode.replaceChild (commitButton, multiSpan);
  −
} else {
  −
catLine.appendChild (commitButton);
  −
}
   
}
 
}
   −
function checkMultiInput () {
+
function setPage( json ) {
if (!commitButton) return;
+
var startTime = null;
var has_changes = false;
+
if ( json && json.query ) {
for (var i = 0; i < editors.length; i++) {
+
if ( json.query.pages ) {
if (editors[i].state != CategoryEditor.UNCHANGED) {
+
var page = json.query.pages[ !conf.wgArticleId ? '-1' : String( conf.wgArticleId ) ];
has_changes = true;
+
if ( page ) {
break;
+
if ( page.revisions && page.revisions.length ) {
 +
// Revisions are sorted by revision ID, hence [ 0 ] is the one we asked for, and possibly there's a [ 1 ] if we're
 +
// not on the latest revision (edit conflicts and such).
 +
pageText = page.revisions[ 0 ][ '*' ];
 +
if ( page.revisions[ 0 ].timestamp ) pageTime = page.revisions[ 0 ].timestamp.replace( /\D/g, '' );
 +
if ( page.revisions[ 0 ].revid ) pageTextRevId = page.revisions[ 0 ].revid;
 +
if ( page.revisions.length > 1 ) conflictingUser = page.revisions[ 1 ].user;
 +
}
 +
if ( page.lastrevid ) lastRevId = page.lastrevid;
 +
if ( page.starttimestamp ) startTime = page.starttimestamp.replace( /\D/g, '' );
 +
pageWatched = typeof page.watched === 'string';
 +
editToken = page.edittoken;
 +
if ( page.langlinks && ( !json[ 'query-continue' ] || !json[ 'query-continue' ].langlinks ) ) {
 +
// We have interlanguage links, and we got them all.
 +
var re = '';
 +
for ( var i = 0; i < page.langlinks.length; i++ ) re += ( i > 0 ? '|' : '' ) + page.langlinks[ i ].lang.replace( /([\\^$.?*+()])/g, '\\$1' );
 +
if ( re.length ) interlanguageRE = new RegExp( '((^|\\n\\r?)(\\[\\[\\s*(' + re + ')\\s*:[^\\]]+\\]\\]\\s*))+$' );
 +
}
 +
}
 +
}
 +
// Siteinfo
 +
if ( json.query.general ) {
 +
if ( json.query.general.time && !startTime ) startTime = json.query.general.time.replace( /\D/g, '' );
 +
 
 +
if ( HC.capitalizePageNames === null ) {
 +
// ResourceLoader's JSParser doesn't like .case, so override eslint.
 +
// eslint-disable-next-line dot-notation
 +
HC.capitalizePageNames = ( json.query.general[ 'case' ] === 'first-letter' );
 +
}
 +
}
 +
serverTime = startTime;
 +
// Userinfo
 +
if ( json.query.userinfo && json.query.userinfo.options ) {
 +
watchCreate = !HC.dont_add_to_watchlist && json.query.userinfo.options.watchcreations === '1';
 +
watchEdit = !HC.dont_add_to_watchlist && json.query.userinfo.options.watchdefault === '1';
 +
minorEdits = json.query.userinfo.options.minordefault === 1;
 +
// If the user has the "All edits are minor" preference enabled, we should honor that
 +
// for single category changes, no matter what the site configuration is.
 +
if ( minorEdits ) HC.single_minor = true;
 
}
 
}
 
}
 
}
commitButton.disabled = !has_changes;
  −
}
  −
  −
function currentTimestamp () {
  −
var now = new Date();
  −
var ts  = "" + now.getUTCFullYear();
  −
function two (s) { return s.substr (s.length - 2); }
  −
ts = ts
  −
+ two ('0' + (now.getUTCMonth() + 1))
  −
+ two ('0' + now.getUTCDate())
  −
+ two ('00' + now.getUTCHours())
  −
+ two ('00' + now.getUTCMinutes())
  −
+ two ('00' + now.getUTCSeconds());
  −
return ts;
   
}
 
}
    
var saveInProgress = false;
 
var saveInProgress = false;
function initiateEdit (doEdit, failure) {
+
function initiateEdit( doEdit, failure ) {
if (saveInProgress) return;
+
if ( saveInProgress ) return;
 
saveInProgress = true;
 
saveInProgress = true;
 
var oldButtonState;
 
var oldButtonState;
if (commitButton) {
+
if ( commitButton ) {
 
oldButtonState = commitButton.disabled;
 
oldButtonState = commitButton.disabled;
 
commitButton.disabled = true;
 
commitButton.disabled = true;
Line 820: Line 767:  
function fail() {
 
function fail() {
 
saveInProgress = false;
 
saveInProgress = false;
if (commitButton) commitButton.disabled = oldButtonState;
+
if ( commitButton ) commitButton.disabled = oldButtonState;
failure.apply(this, arguments);
+
failure.apply( this, arguments );
 
}
 
}
    
// Must use Ajax here to get the user options and the edit token.
 
// Must use Ajax here to get the user options and the edit token.
 +
$.getJSON(
 +
conf.wgServer + conf.wgScriptPath + '/api.php?' +
 +
'format=json&action=query&rawcontinue=&titles=' + encodeURIComponent( conf.wgPageName ) +
 +
'&prop=info%7Crevisions%7Clanglinks&inprop=watched&intoken=edit&rvprop=content%7Ctimestamp%7Cids%7Cuser&lllimit=500' +
 +
'&rvlimit=2&rvdir=newer&rvstartid=' + conf.wgCurRevisionId + '&meta=siteinfo%7Cuserinfo&uiprop=options',
 +
function ( json ) {
 +
setPage( json );
 +
doEdit( fail );
 +
}
 +
).fail( function ( req ) {
 +
fail( req.status + ' ' + req.statusText );
 +
} );
 +
}
 +
 +
function multiChangeMsg( count ) {
 +
var msg = HC.messages.multi_change;
 +
if ( typeof msg !== 'string' && msg.length )
 +
if ( mw.language && mw.language.convertPlural ) { msg = mw.language.convertPlural( count, msg ); } else { msg = msg[ msg.length - 1 ]; }
   −
getJSON ({
+
return substitute( msg, [ null, String( count ) ] );
uri : conf.wgServer + conf.wgScriptPath + '/api.php'
  −
,data : 'format=json&action=query&rawcontinue=&titles=' + encodeURIComponent (conf.wgPageName)
  −
+ '&prop=info%7Crevisions%7Clanglinks&inprop=watched&intoken=edit&rvprop=content%7Ctimestamp%7Cids%7Cuser&lllimit=500'
  −
+ '&rvlimit=2&rvdir=newer&rvstartid=' + conf.wgCurRevisionId
  −
+ '&meta=siteinfo%7Cuserinfo&uiprop=options'
  −
,success : function (json) { setPage(json); doEdit(fail); }
  −
,error : function (req) { fail(req.status + ' ' + req.statusText); }
  −
});
   
}
 
}
   −
function multiChangeMsg (count) {
+
function currentTimestamp() {
var msg = HotCat.messages.multi_change;
+
var now = new Date();
if (typeof msg != 'string' && msg.length) {
+
var ts = String( now.getUTCFullYear() );
if (window.mw && mw.language && mw.language.convertPlural) {
+
function two( s ) {
msg = mw.language.convertPlural (count, msg);
+
return s.substr( s.length - 2 );
} else {
  −
msg = msg[msg.length-1];
  −
}
   
}
 
}
return substitute (msg, [null, "" + count]);
+
ts +=
 +
two( '0' + ( now.getUTCMonth() + 1 ) ) +
 +
two( '0' + now.getUTCDate() ) +
 +
two( '00' + now.getUTCHours() ) +
 +
two( '00' + now.getUTCMinutes() ) +
 +
two( '00' + now.getUTCSeconds() );
 +
return ts;
 
}
 
}
   −
function performChanges (failure, singleEditor) {
+
function performChanges( failure, singleEditor ) {
if (pageText === null) {
+
if ( pageText === null ) {
failure (HotCat.messages.multi_error);
+
failure( HC.messages.multi_error );
 
return;
 
return;
 
}
 
}
 
// Backwards compatibility after message change (added $2 to cat_keychange)
 
// Backwards compatibility after message change (added $2 to cat_keychange)
if (HotCat.messages.cat_keychange.indexOf ('$2') < 0) HotCat.messages.cat_keychange += '"$2"';
+
if ( HC.messages.cat_keychange.indexOf( '$2' ) < 0 ) HC.messages.cat_keychange += '"$2"';
 +
 
 
// More backwards-compatibility with earlier HotCat versions:
 
// More backwards-compatibility with earlier HotCat versions:
if (!HotCat.messages.short_catchange) HotCat.messages.short_catchange = '[[' + HotCat.category_canonical + ':$1]]';
+
if ( !HC.messages.short_catchange ) HC.messages.short_catchange = '[[' + HC.category_canonical + ':$1]]';
 +
 
 
// Create a form and submit it. We don't use the edit API (api.php?action=edit) because
 
// Create a form and submit it. We don't use the edit API (api.php?action=edit) because
 
// (a) sensibly reporting back errors like edit conflicts is always a hassle, and
 
// (a) sensibly reporting back errors like edit conflicts is always a hassle, and
Line 871: Line 833:  
// current user, then we set the "oldid" value and switch to diff, which gives the "you are editing an old version;
 
// current user, then we set the "oldid" value and switch to diff, which gives the "you are editing an old version;
 
// if you save, any more recent changes will be lost" screen.
 
// if you save, any more recent changes will be lost" screen.
var editingOldVersion = lastRevId !== null && lastRevId != conf.wgCurRevisionId || pageTextRevId !== null && pageTextRevId != conf.wgCurRevisionId;
+
var selfEditConflict = ( lastRevId !== null && lastRevId !== conf.wgCurRevisionId || pageTextRevId !== null &&
var selfEditConflict = editingOldVersion && conflictingUser && conflictingUser == conf.wgUserName;
+
pageTextRevId !== conf.wgCurRevisionId ) && conflictingUser && conflictingUser === conf.wgUserName;
if (singleEditor && !singleEditor.noCommit && !HotCat.no_autocommit && editToken && !selfEditConflict) {
+
if ( singleEditor && !singleEditor.noCommit && !HC.no_autocommit && editToken && !selfEditConflict ) {
 
// If we do have an edit conflict, but not with ourself, that's no reason not to attempt to save: the server side may actually be able to
 
// If we do have an edit conflict, but not with ourself, that's no reason not to attempt to save: the server side may actually be able to
 
// merge the changes. We just need to make sure that we do present a diff view if it's a self edit conflict.
 
// merge the changes. We just need to make sure that we do present a diff view if it's a self edit conflict.
 
commitForm.wpEditToken.value = editToken;
 
commitForm.wpEditToken.value = editToken;
 
action = commitForm.wpDiff;
 
action = commitForm.wpDiff;
if (action) action.name = action.value = 'wpSave';
+
if ( action ) action.name = action.value = 'wpSave';
 
} else {
 
} else {
 
action = commitForm.wpSave;
 
action = commitForm.wpSave;
if (action) action.name = action.value = 'wpDiff';
+
if ( action ) action.name = action.value = 'wpDiff';
 
}
 
}
var result = { text : pageText };
+
var result = {
var changed = [], added = [], deleted = [], changes = 0;
+
text: pageText
var toEdit = !!singleEditor ? [singleEditor] : editors;
+
},
var error = null;
+
changed = [],
var i;
+
added = [],
for (i=0; i < toEdit.length; i++) {
+
deleted = [],
if (toEdit[i].state == CategoryEditor.CHANGED) {
+
changes = 0,
result = change_category (
+
toEdit = singleEditor ? [ singleEditor ] : editors,
result.text
+
error = null,
, toEdit[i].originalCategory
+
edit,
, toEdit[i].currentCategory
+
i;
, toEdit[i].currentKey
+
for ( i = 0; i < toEdit.length; i++ ) {
, toEdit[i].currentHidden
+
edit = toEdit[ i ];
);
+
if ( edit.state === CategoryEditor.CHANGED ) {
if (!result.error) {
+
result = change_category(
 +
result.text,
 +
edit.originalCategory,
 +
edit.currentCategory,
 +
edit.currentKey,
 +
edit.currentHidden );
 +
if ( !result.error ) {
 
changes++;
 
changes++;
if (!toEdit[i].originalCategory || toEdit[i].originalCategory.length === 0) {
+
if ( !edit.originalCategory || !edit.originalCategory.length ) {
added.push (toEdit[i].currentCategory);
+
added.push( edit.currentCategory );
 
} else {
 
} else {
changed.push ({from : toEdit[i].originalCategory, to : toEdit[i].currentCategory});
+
changed.push( {
 +
from: edit.originalCategory,
 +
to: edit.currentCategory
 +
} );
 
}
 
}
} else if (error === null) {
+
} else if ( error === null ) {
 
error = result.error;
 
error = result.error;
 
}
 
}
} else if (   toEdit[i].state == CategoryEditor.DELETED
+
} else if (
  && toEdit[i].originalCategory
+
edit.state === CategoryEditor.DELETED && edit.originalCategory && edit.originalCategory.length ) {
  && toEdit[i].originalCategory.length > 0)
+
result = change_category(
{
+
result.text,
result = change_category (result.text, toEdit[i].originalCategory, null, null, false);
+
edit.originalCategory,
if (!result.error) {
+
null, null, false );
 +
if ( !result.error ) {
 
changes++;
 
changes++;
deleted.push (toEdit[i].originalCategory);
+
deleted.push( edit.originalCategory );
} else if (error === null) {
+
} else if ( error === null ) {
 
error = result.error;
 
error = result.error;
 
}
 
}
 
}
 
}
 
}
 
}
if (error !== null) { // Do not commit if there were errors
+
if ( error !== null ) { // Do not commit if there were errors
 
action = commitForm.wpSave;
 
action = commitForm.wpSave;
if (action) action.name = action.value = 'wpDiff';
+
if ( action ) action.name = action.value = 'wpDiff';
 
}
 
}
 
// Fill in the form and submit it
 
// Fill in the form and submit it
commitForm.wpAutoSummary.value = 'd41d8cd98f00b204e9800998ecf8427e'; // MD5 hash of the empty string
   
commitForm.wpMinoredit.checked = minorEdits;
 
commitForm.wpMinoredit.checked = minorEdits;
commitForm.wpWatchthis.checked = conf.wgArticleId === 0 && watchCreate || watchEdit || pageWatched;
+
commitForm.wpWatchthis.checked = !conf.wgArticleId && watchCreate || watchEdit || pageWatched;
if (conf.wgArticleId > 0 || !!singleEditor) {
+
if ( conf.wgArticleId || !!singleEditor ) {
if (changes == 1) {
+
// Prepare change-tag save
if (result.summary && result.summary.length > 0)
+
if ( action && action.value === 'wpSave' ) {
commitForm.wpSummary.value = HotCat.messages.prefix + result.summary.join (HotCat.messages.separator) + HotCat.messages.using;
+
if ( HC.changeTag ) {
commitForm.wpMinoredit.checked = HotCat.single_minor || minorEdits;
+
commitForm.wpChangeTags.value = HC.changeTag;
} else if (changes > 1) {
+
HC.messages.using = '';
 +
HC.messages.prefix = '';
 +
}
 +
} else {
 +
commitForm.wpAutoSummary.value = HC.changeTag;
 +
}
 +
if ( changes === 1 ) {
 +
if ( result.summary && result.summary.length ) commitForm.wpSummary.value = HC.messages.prefix + result.summary.join( HC.messages.separator ) + HC.messages.using;
 +
commitForm.wpMinoredit.checked = HC.single_minor || minorEdits;
 +
} else if ( changes ) {
 
var summary = [];
 
var summary = [];
 
var shortSummary = [];
 
var shortSummary = [];
 
// Deleted
 
// Deleted
for (i = 0; i < deleted.length; i++) {
+
for ( i = 0; i < deleted.length; i++ ) summary.push( '' + substitute( HC.messages.short_catchange, [ null, deleted[ i ] ] ) );
summary.push ('-' + substitute (HotCat.messages.short_catchange, [null, deleted[i]]));
+
 
}
+
if ( deleted.length === 1 ) shortSummary.push( '' + substitute( HC.messages.short_catchange, [ null, deleted[ 0 ] ] ) ); else if ( deleted.length ) shortSummary.push( '' + multiChangeMsg( deleted.length ) );
if (deleted.length == 1)
+
 
shortSummary.push ('-' + substitute (HotCat.messages.short_catchange, [null, deleted[0]]));
  −
else if (deleted.length > 1)
  −
shortSummary.push ('- ' + multiChangeMsg (deleted.length));
   
// Added
 
// Added
for (i = 0; i < added.length; i++) {
+
for ( i = 0; i < added.length; i++ ) summary.push( '+' + substitute( HC.messages.short_catchange, [ null, added[ i ] ] ) );
summary.push ('+' + substitute (HotCat.messages.short_catchange, [null, added[i]]));
+
 
}
+
if ( added.length === 1 ) shortSummary.push( '+' + substitute( HC.messages.short_catchange, [ null, added[ 0 ] ] ) ); else if ( added.length ) shortSummary.push( '+ ' + multiChangeMsg( added.length ) );
if (added.length == 1)
+
 
shortSummary.push ('+' + substitute (HotCat.messages.short_catchange, [null, added[0]]));
  −
else if (added.length > 1)
  −
shortSummary.push ('+ ' + multiChangeMsg (added.length));
   
// Changed
 
// Changed
 
var arrow = is_rtl ? '\u2190' : '\u2192'; // left and right arrows. Don't use ← and → in the code.
 
var arrow = is_rtl ? '\u2190' : '\u2192'; // left and right arrows. Don't use ← and → in the code.
for (i = 0; i < changed.length; i++) {
+
for ( i = 0; i < changed.length; i++ ) {
if (changed[i].from != changed[i].to) {
+
if ( changed[ i ].from !== changed[ i ].to ) {
summary.push ('±' + substitute (HotCat.messages.short_catchange, [null, changed[i].from]) + arrow
+
summary.push(
+ substitute (HotCat.messages.short_catchange, [null, changed[i].to]));
+
'±' + substitute( HC.messages.short_catchange, [ null, changed[ i ].from ] ) + arrow +
 +
substitute( HC.messages.short_catchange, [ null, changed[ i ].to ] )
 +
);
 
} else {
 
} else {
summary.push ('±' + substitute (HotCat.messages.short_catchange, [null, changed[i].from]));
+
summary.push( '±' + substitute( HC.messages.short_catchange, [ null, changed[ i ].from ] ) );
 
}
 
}
 
}
 
}
if (changed.length == 1) {
+
if ( changed.length === 1 ) {
if (changed[0].from != changed[0].to) {
+
if ( changed[ 0 ].from !== changed[ 0 ].to ) {
shortSummary.push ('±' + substitute (HotCat.messages.short_catchange, [null, changed[0].from]) + arrow
+
shortSummary.push(
+ substitute (HotCat.messages.short_catchange, [null, changed[0].to]));
+
'±' + substitute( HC.messages.short_catchange, [ null, changed[ 0 ].from ] ) + arrow +
 +
substitute( HC.messages.short_catchange, [ null, changed[ 0 ].to ] )
 +
);
 
} else {
 
} else {
shortSummary.push ('±' + substitute (HotCat.messages.short_catchange, [null, changed[0].from]));
+
shortSummary.push( '±' + substitute( HC.messages.short_catchange, [ null, changed[ 0 ].from ] ) );
 
}
 
}
} else if (changed.length > 1) {
+
} else if ( changed.length ) {
shortSummary.push ('± ' + multiChangeMsg (changed.length));
+
shortSummary.push( '± ' + multiChangeMsg( changed.length ) );
 
}
 
}
if (summary.length > 0) {
+
if ( summary.length ) {
summary = summary.join (HotCat.messages.separator);
+
summary = summary.join( HC.messages.separator );
if (summary.length > 200 - HotCat.messages.prefix.length - HotCat.messages.using.length) {
+
if ( summary.length > 200 - HC.messages.prefix.length - HC.messages.using.length ) summary = shortSummary.join( HC.messages.separator );
summary = shortSummary.join (HotCat.messages.separator);
+
 
}
+
commitForm.wpSummary.value = HC.messages.prefix + summary + HC.messages.using;
commitForm.wpSummary.value = HotCat.messages.prefix + summary + HotCat.messages.using;
   
}
 
}
 
}
 
}
 
}
 
}
 +
 
commitForm.wpTextbox1.value = result.text;
 
commitForm.wpTextbox1.value = result.text;
commitForm.wpStarttime.value = serverTime || currentTimestamp ();
+
commitForm.wpStarttime.value = serverTime || currentTimestamp();
 
commitForm.wpEdittime.value = pageTime || commitForm.wpStarttime.value;
 
commitForm.wpEdittime.value = pageTime || commitForm.wpStarttime.value;
if (selfEditConflict) commitForm.oldid.value = "" + (pageTextRevId || conf.wgCurRevisionId);
+
if ( selfEditConflict ) commitForm.oldid.value = String( pageTextRevId || conf.wgCurRevisionId );
 +
 
 
// Submit the form in a way that triggers onsubmit events: commitForm.submit() doesn't.
 
// Submit the form in a way that triggers onsubmit events: commitForm.submit() doesn't.
 
commitForm.hcCommit.click();
 
commitForm.hcCommit.click();
 
}
 
}
   −
function resolveMulti (toResolve, callback) {
+
function resolveOne( page, toResolve ) {
var i;
+
var cats = page.categories,
for (i = 0; i < toResolve.length; i++) {
+
lks = page.links,
toResolve[i].dab = null;
+
is_dab = false,
toResolve[i].dabInput = toResolve[i].lastInput;
+
is_redir = typeof page.redirect === 'string', // Hard redirect?
}
+
is_hidden = page.categoryinfo && typeof page.categoryinfo.hidden === 'string',
if (noSuggestions) {
+
is_missing = typeof page.missing === 'string',
callback (toResolve);
+
i;
return;
+
for ( i = 0; i < toResolve.length; i++ ) {
}
+
if ( i && toResolve[ i ].dabInputCleaned !== page.title.substring( page.title.indexOf( ':' ) + 1 ) ) continue;
// Use %7C instead of |, otherwise Konqueror insists on re-encoding the arguments, resulting in doubly encoded
  −
// category names. (That is a bug in Konqueror. Other browsers don't have this problem.)
  −
var args = 'action=query&prop=info%7Clinks%7Ccategories%7Ccategoryinfo&plnamespace=14'
  −
+ '&pllimit=' + (toResolve.length * 10)
  −
+ '&cllimit=' + (toResolve.length * 10)
  −
+ '&format=json&titles=';
  −
for (i = 0; i < toResolve.length; i++) {
  −
var v = toResolve[i].dabInput;
  −
v = replaceShortcuts (v, HotCat.shortcuts);
  −
toResolve[i].dabInputCleaned = v;
  −
args += encodeURIComponent ('Category:' + v);
  −
if (i+1 < toResolve.length) args += '%7C';
  −
}
  −
getJSON({
  −
uri : conf.wgServer + conf.wgScriptPath + '/api.php'
  −
,data : args
  −
,success: function (json) { resolveRedirects (toResolve, json); callback (toResolve); }
  −
,error: function (req) { if (!req) noSuggestions = true; callback (toResolve); }
  −
});
  −
}
  −
 
  −
function resolveOne (page, toResolve) {
  −
var cats     = page.categories;
  −
var lks     = page.links;
  −
var is_dab   = false;
  −
var is_redir = typeof page.redirect == 'string'; // Hard redirect?
  −
var is_hidden = page.categoryinfo && typeof page.categoryinfo.hidden == 'string';
  −
var is_missing = typeof page.missing == 'string';
  −
var i;
  −
for (i = 0; i < toResolve.length; i++) {
  −
if (toResolve.length > 1 && toResolve[i].dabInputCleaned != page.title.substring (page.title.indexOf (':') + 1)) continue;
   
// Note: the server returns in page an NFC normalized Unicode title. If our input was not NFC normalized, we may not find
 
// Note: the server returns in page an NFC normalized Unicode title. If our input was not NFC normalized, we may not find
 
// any entry here. If we have only one editor to resolve (the most common case, I presume), we may simply skip the check.
 
// any entry here. If we have only one editor to resolve (the most common case, I presume), we may simply skip the check.
toResolve[i].currentHidden = is_hidden;
+
toResolve[ i ].currentHidden = is_hidden;
toResolve[i].inputExists = !is_missing;
+
toResolve[ i ].inputExists = !is_missing;
toResolve[i].icon.src = armorUri(is_missing ? HotCat.existsNo : HotCat.existsYes);
+
toResolve[ i ].icon.src = ( is_missing ? HC.existsNo : HC.existsYes );
 
}
 
}
if (is_missing) return;
+
if ( is_missing ) return;
if (!is_redir && cats && (HotCat.disambig_category || HotCat.redir_category)) {
+
if ( !is_redir && cats && ( HC.disambig_category || HC.redir_category ) ) {
for (var c = 0; c < cats.length; c++) {
+
for ( var c = 0; c < cats.length; c++ ) {
var cat = cats[c]['title'];
+
var cat = cats[ c ].title;
 
// Strip namespace prefix
 
// Strip namespace prefix
if (cat) {
+
if ( cat ) {
cat = cat.substring (cat.indexOf (':') + 1).replace(/_/g, ' ');
+
cat = cat.substring( cat.indexOf( ':' ) + 1 ).replace( /_/g, ' ' );
if (cat == HotCat.disambig_category) {
+
if ( cat === HC.disambig_category ) {
is_dab = true; break;
+
is_dab = true;
} else if (cat == HotCat.redir_category) {
+
break;
is_redir = true; break;
+
} else if ( cat === HC.redir_category ) {
 +
is_redir = true;
 +
break;
 
}
 
}
 
}
 
}
 
}
 
}
 
}
 
}
if (!is_redir && !is_dab) return;
+
if ( !is_redir && !is_dab ) return;
if (!lks || lks.length === 0) return;
+
if ( !lks || !lks.length ) return;
 
var titles = [];
 
var titles = [];
for (i = 0; i < lks.length; i++) {
+
for ( i = 0; i < lks.length; i++ ) {
if (   lks[i]['ns'] == 14                            // Category namespace -- always true since we ask only for the category links
+
if (
&& lks[i]['title'] && lks[i]['title'].length > 0) // Name not empty
+
// Category namespace -- always true since we ask only for the category links
{
+
lks[ i ].ns === 14 &&
 +
// Name not empty
 +
lks[ i ].title && lks[ i ].title.length
 +
) {
 
// Internal link to existing thingy. Extract the page name and remove the namespace.
 
// Internal link to existing thingy. Extract the page name and remove the namespace.
var match = lks[i]['title'];
+
var match = lks[ i ].title;
match = match.substring (match.indexOf (':') + 1);
+
match = match.substring( match.indexOf( ':' ) + 1 );
 
// Exclude blacklisted categories.
 
// Exclude blacklisted categories.
if (!HotCat.blacklist || !HotCat.blacklist.test (match)) {
+
if ( !HC.blacklist || !HC.blacklist.test( match ) ) titles.push( match );
titles.push (match);
+
}
}
+
}
 +
if ( !titles.length ) return;
 +
for ( i = 0; i < toResolve.length; i++ ) {
 +
if ( i && toResolve[ i ].dabInputCleaned !== page.title.substring( page.title.indexOf( ':' ) + 1 ) ) continue;
 +
toResolve[ i ].inputExists = true; // Might actually be wrong if it's a redirect pointing to a non-existing category
 +
toResolve[ i ].icon.src = HC.existsYes;
 +
if ( titles.length > 1 ) {
 +
toResolve[ i ].dab = titles;
 +
} else {
 +
toResolve[ i ].text.value =
 +
titles[ 0 ] + ( toResolve[ i ].currentKey !== null ? '|' + toResolve[ i ].currentKey : '' );
 
}
 
}
 
}
 
}
if (titles.length === 0) {
+
}
 +
 
 +
function resolveRedirects( toResolve, params ) {
 +
if ( !params || !params.query || !params.query.pages ) return;
 +
for ( var p in params.query.pages ) resolveOne( params.query.pages[ p ], toResolve );
 +
}
 +
 
 +
function resolveMulti( toResolve, callback ) {
 +
var i;
 +
for ( i = 0; i < toResolve.length; i++ ) {
 +
toResolve[ i ].dab = null;
 +
toResolve[ i ].dabInput = toResolve[ i ].lastInput;
 +
}
 +
if ( noSuggestions ) {
 +
callback( toResolve );
 
return;
 
return;
 
}
 
}
for (i = 0; i < toResolve.length; i++) {
+
// Use %7C instead of |, otherwise Konqueror insists on re-encoding the arguments, resulting in doubly encoded
if (toResolve.length > 1 && toResolve[i].dabInputCleaned != page.title.substring (page.title.indexOf (':') + 1)) continue;
+
// category names. (That is a bug in Konqueror. Other browsers don't have this problem.)
toResolve[i].inputExists = true; // Might actually be wrong if it's a redirect pointing to a non-existing category
+
var args = 'action=query&prop=info%7Clinks%7Ccategories%7Ccategoryinfo&plnamespace=14' +
toResolve[i].icon.src = armorUri(HotCat.existsYes);
+
'&pllimit=' + ( toResolve.length * 10 ) +
if (titles.length > 1) {
+
'&cllimit=' + ( toResolve.length * 10 ) +
toResolve[i].dab = titles;
+
'&format=json&titles=';
} else {
+
for ( i = 0; i < toResolve.length; i++ ) {
toResolve[i].text.value =
+
var v = toResolve[ i ].dabInput;
titles[0] + (toResolve[i].currentKey !== null ? '|' + toResolve[i].currentKey : "");
+
v = replaceShortcuts( v, HC.shortcuts );
 +
toResolve[ i ].dabInputCleaned = v;
 +
args += encodeURIComponent( 'Category:' + v );
 +
if ( i + 1 < toResolve.length ) args += '%7C';
 +
}
 +
$.getJSON( conf.wgServer + conf.wgScriptPath + '/api.php?' + args,
 +
function ( json ) {
 +
resolveRedirects( toResolve, json );
 +
callback( toResolve );
 +
} ).fail( function ( req ) {
 +
if ( !req ) noSuggestions = true;
 +
callback( toResolve );
 +
} );
 +
}
 +
 
 +
function makeActive( which ) {
 +
if ( which.is_active ) return;
 +
for ( var i = 0; i < editors.length; i++ )
 +
if ( editors[ i ] !== which ) editors[ i ].inactivate();
 +
 
 +
which.is_active = true;
 +
if ( which.dab ) {
 +
// eslint-disable-next-line no-use-before-define
 +
showDab( which );
 +
} else {
 +
// Check for programmatic value changes.
 +
var expectedInput = which.lastRealInput || which.lastInput || '';
 +
var actualValue = which.text.value || '';
 +
if ( !expectedInput.length && actualValue.length || expectedInput.length && actualValue.indexOf( expectedInput ) ) {
 +
// Somehow the field's value appears to have changed, and which.lastSelection therefore is no longer valid. Try to set the
 +
// cursor at the end of the category, and do not display the old suggestion list.
 +
which.showsList = false;
 +
var v = actualValue.split( '|' );
 +
which.lastRealInput = which.lastInput = v[ 0 ];
 +
if ( v.length > 1 ) which.currentKey = v[ 1 ];
 +
 
 +
if ( which.lastSelection ) {
 +
which.lastSelection = {
 +
start: v[ 0 ].length,
 +
end: v[ 0 ].length
 +
};
 +
}
 +
}
 +
if ( which.showsList ) which.displayList();
 +
 
 +
if ( which.lastSelection ) {
 +
if ( is_webkit ) {
 +
// WebKit (Safari, Chrome) has problems selecting inside focus()
 +
// See http://code.google.com/p/chromium/issues/detail?id=32865#c6
 +
window.setTimeout(
 +
function () {
 +
which.setSelection( which.lastSelection.start, which.lastSelection.end );
 +
},
 +
1 );
 +
} else {
 +
which.setSelection( which.lastSelection.start, which.lastSelection.end );
 +
}
 
}
 
}
 
}
 
}
 
}
 
}
   −
function resolveRedirects (toResolve, params) {
+
function showDab( which ) {
if (!params || !params.query || !params.query.pages) return;
+
if ( !which.is_active ) {
for (var p in params.query.pages) resolveOne (params.query.pages[p], toResolve);
+
makeActive( which );
 +
} else {
 +
which.showSuggestions( which.dab, false, null, null ); // do autocompletion, no key, no engine selector
 +
which.dab = null;
 +
}
 
}
 
}
   −
function multiSubmit () {
+
function multiSubmit() {
 
var toResolve = [];
 
var toResolve = [];
for (var i = 0; i < editors.length; i++) {
+
for ( var i = 0; i < editors.length; i++ )
if (editors[i].state == CategoryEditor.CHANGE_PENDING || editors[i].state == CategoryEditor.OPEN)
+
if ( editors[ i ].state === CategoryEditor.CHANGE_PENDING || editors[ i ].state === CategoryEditor.OPEN ) toResolve.push( editors[ i ] );
toResolve.push (editors[i]);
+
 
}
+
if ( !toResolve.length ) {
if (toResolve.length === 0) {
+
initiateEdit( function ( failure ) {
initiateEdit (function (failure) {performChanges (failure);}, function (msg) {alert (msg);});
+
performChanges( failure );
 +
}, function ( msg ) {
 +
alert( msg );
 +
} );
 
return;
 
return;
 
}
 
}
resolveMulti (
+
resolveMulti( toResolve, function ( resolved ) {
  toResolve
+
var firstDab = null;
, function (resolved) {
+
var dontChange = false;
var firstDab = null;
+
for ( var i = 0; i < resolved.length; i++ ) {
var dontChange = false;
+
if ( resolved[ i ].lastInput !== resolved[ i ].dabInput ) {
for (var i = 0; i < resolved.length; i++) {
+
// We didn't disable all the open editors, but we did asynchronous calls. It is
if (resolved[i].lastInput != resolved[i].dabInput) {
+
// theoretically possible that the user changed something...
// We didn't disable all the open editors, but we did asynchronous calls. It is
+
dontChange = true;
// theoretically possible that the user changed something...
+
} else {
dontChange = true;
+
if ( resolved[ i ].dab ) {
} else {
+
if ( !firstDab ) firstDab = resolved[ i ];
if (resolved[i].dab) {
+
} else {
if (!firstDab) firstDab = resolved[i];
+
if ( resolved[ i ].acceptCheck( true ) ) resolved[ i ].commit();
} else {
  −
if (resolved[i].acceptCheck(true)) resolved[i].commit();
  −
}
  −
}
  −
}
  −
if (firstDab) {
  −
showDab (firstDab);
  −
} else if (!dontChange) {
  −
initiateEdit (function (failure) {performChanges (failure);}, function (msg) {alert (msg);});
   
}
 
}
 
}
 
}
);
+
}
 +
if ( firstDab ) {
 +
showDab( firstDab );
 +
} else if ( !dontChange ) {
 +
initiateEdit( function ( failure ) {
 +
performChanges( failure );
 +
}, function ( msg ) {
 +
alert( msg );
 +
} );
 +
}
 +
} );
 +
}
 +
 
 +
function setMultiInput() {
 +
if ( commitButton || onUpload ) return;
 +
commitButton = make( 'input' );
 +
commitButton.type = 'button';
 +
commitButton.value = HC.messages.commit;
 +
commitButton.onclick = multiSubmit;
 +
if ( multiSpan ) multiSpan.parentNode.replaceChild( commitButton, multiSpan ); else catLine.appendChild( commitButton );
 +
}
 +
 
 +
function checkMultiInput() {
 +
if ( !commitButton ) return;
 +
var hasChanges = false;
 +
for ( var i = 0; i < editors.length; i++ ) {
 +
if ( editors[ i ].state !== CategoryEditor.UNCHANGED ) {
 +
hasChanges = true;
 +
break;
 +
}
 +
}
 +
commitButton.disabled = !hasChanges;
 
}
 
}
   −
var cat_prefix = null;
  −
var noSuggestions = false;
   
var suggestionEngines = {
 
var suggestionEngines = {
opensearch :
+
opensearch: {
{ uri     : '/api.php?format=json&action=opensearch&namespace=14&limit=30&search=Category:$1' // $1 = search term
+
uri: '/api.php?format=json&action=opensearch&namespace=14&limit=30&search=Category:$1', // $1 = search term
,handler : // Function to convert result of uri into an array of category names
+
// Function to convert result of uri into an array of category names
function (queryResult, queryKey) {
+
handler: function ( queryResult, queryKey ) {
if (queryResult && queryResult.length >= 2) {
+
if ( queryResult && queryResult.length >= 2 ) {
var key = queryResult[0].substring(queryResult[0].indexOf(':') + 1);
+
var key = queryResult[ 0 ].substring( queryResult[ 0 ].indexOf( ':' ) + 1 );
var titles = queryResult[1];
+
var titles = queryResult[ 1 ];
var exists = false;
+
var exists = false;
if (!cat_prefix) cat_prefix = new RegExp ('^(' + HotCat.category_regexp + ':)');
+
if ( !cat_prefix ) cat_prefix = new RegExp( '^(' + HC.category_regexp + '):' );
for (var i = 0; i < titles.length; i++) {
+
 
cat_prefix.lastIndex = 0;
+
for ( var i = 0; i < titles.length; i++ ) {
var m = cat_prefix.exec (titles[i]);
+
cat_prefix.lastIndex = 0;
if (m && m.length > 1) {
+
var m = cat_prefix.exec( titles[ i ] );
titles[i] = titles[i].substring (titles[i].indexOf (':') + 1); // rm namespace
+
if ( m && m.length > 1 ) {
if (key == titles[i]) exists = true;
+
titles[ i ] = titles[ i ].substring( titles[ i ].indexOf( ':' ) + 1 ); // rm namespace
} else {
+
if ( key === titles[ i ] ) exists = true;
titles.splice (i, 1); // Nope, it's not a category after all.
+
} else {
i--;
+
titles.splice( i, 1 ); // Nope, it's not a category after all.
}
+
i--;
 
}
 
}
titles.exists = exists;
  −
if (queryKey != key) titles.normalized = key; // Remember the NFC normalized key we got back from the server
  −
return titles;
   
}
 
}
return null;
+
titles.exists = exists;
 +
if ( queryKey !== key ) titles.normalized = key;
 +
// Remember the NFC normalized key we got back from the server
 +
return titles;
 
}
 
}
 +
return null;
 
}
 
}
,internalsearch :
+
},
{ uri     : '/api.php?format=json&action=query&list=allpages&apnamespace=14&aplimit=30&apfrom=$1&apprefix=$1'
+
internalsearch: {
,handler :
+
uri: '/api.php?format=json&action=query&list=allpages&apnamespace=14&aplimit=30&apfrom=$1&apprefix=$1',
function (queryResult, queryKey) {
+
handler: function ( queryResult ) {
if (queryResult && queryResult.query && queryResult.query.allpages) {
+
if ( queryResult && queryResult.query && queryResult.query.allpages ) {
var titles = queryResult.query.allpages;
+
var titles = queryResult.query.allpages;
for (var i = 0; i < titles.length; i++) {
+
for ( var i = 0; i < titles.length; i++ ) titles[ i ] = titles[ i ].title.substring( titles[ i ].title.indexOf( ':' ) + 1 ); // rm namespace
titles[i] = titles[i].title.substring (titles[i].title.indexOf (':') + 1); // rm namespace
+
 
}
+
return titles;
return titles;
  −
}
  −
return null;
   
}
 
}
 +
return null;
 
}
 
}
,exists :
+
},
{ uri     : '/api.php?format=json&action=query&prop=info&titles=Category:$1'
+
exists: {
,handler :
+
uri: '/api.php?format=json&action=query&prop=info&titles=Category:$1',
function (queryResult, queryKey) {
+
handler: function ( queryResult, queryKey ) {
if (queryResult && queryResult.query && queryResult.query.pages && !queryResult.query.pages[-1]) {
+
if ( queryResult && queryResult.query && queryResult.query.pages && !queryResult.query.pages[ -1 ] ) {
// Should have exactly 1
+
// Should have exactly 1
for (var p in queryResult.query.pages) {
+
for ( var p in queryResult.query.pages ) {
var title = queryResult.query.pages[p].title;
+
var title = queryResult.query.pages[ p ].title;
title = title.substring (title.indexOf (':') + 1);
+
title = title.substring( title.indexOf( ':' ) + 1 );
var titles = [title];
+
var titles = [ title ];
titles.exists = true;
+
titles.exists = true;
if (queryKey != title) titles.normalized = title; // NFC
+
if ( queryKey !== title ) titles.normalized = title;
return titles;
+
// NFC
}
+
return titles;
 
}
 
}
return null;
   
}
 
}
 +
return null;
 
}
 
}
,subcategories :
+
},
// I don't understand why they didn't map cmnamespace=14 automatically to cmtype=subcat,
+
subcategories: {
// which gives better results and is faster.
+
uri: '/api.php?format=json&action=query&list=categorymembers&cmtype=subcat&cmlimit=max&cmtitle=Category:$1',
{ uri     : '/api.php?format=json&action=query&list=categorymembers'
+
handler: function ( queryResult ) {
+(function (version) {
+
if ( queryResult && queryResult.query && queryResult.query.categorymembers ) {
var m = version.match(/^(\d+)\.(\d+)/);
+
var titles = queryResult.query.categorymembers;
var major = 0, minor = 0;
+
for ( var i = 0; i < titles.length; i++ ) titles[ i ] = titles[ i ].title.substring( titles[ i ].title.indexOf( ':' ) + 1 ); // rm namespace
if (m && m.length > 1) {
+
 
major = parseInt (m[1], 10);
+
return titles;
minor = (m.length > 2 ? parseInt (m[2], 10) : 0);
  −
}
  −
if (major > 1 || major === 1 && minor > 17) return '&cmtype=subcat'; // Since MW1.18
  −
return '&cmnamespace=14';
  −
  }
  −
)(conf.wgVersion)
  −
+'&cmlimit=max&cmtitle=Category:$1'
  −
,handler :
  −
function (queryResult, queryKey) {
  −
if (queryResult && queryResult.query && queryResult.query.categorymembers) {
  −
var titles = queryResult.query.categorymembers;
  −
for (var i = 0; i < titles.length; i++) {
  −
titles[i] = titles[i].title.substring (titles[i].title.indexOf (':') + 1); // rm namespace
  −
}
  −
return titles;
  −
}
  −
return null;
   
}
 
}
 +
return null;
 
}
 
}
,parentcategories :
+
},
{ uri     : '/api.php?format=json&action=query&prop=categories&titles=Category:$1&cllimit=max'
+
parentcategories: {
,handler :
+
uri: '/api.php?format=json&action=query&prop=categories&titles=Category:$1&cllimit=max',
function (queryResult, queryKey) {
+
handler: function ( queryResult ) {
if (queryResult && queryResult.query && queryResult.query.pages) {
+
if ( queryResult && queryResult.query && queryResult.query.pages ) {
for (var p in queryResult.query.pages) {
+
for ( var p in queryResult.query.pages ) {
if (queryResult.query.pages[p].categories) {
+
if ( queryResult.query.pages[ p ].categories ) {
var titles = queryResult.query.pages[p].categories;
+
var titles = queryResult.query.pages[ p ].categories;
for (var i = 0; i < titles.length; i++) {
+
for ( var i = 0; i < titles.length; i++ ) titles[ i ] = titles[ i ].title.substring( titles[ i ].title.indexOf( ':' ) + 1 ); // rm namespace
titles[i] = titles[i].title.substring (titles[i].title.indexOf (':') + 1); // rm namespace
+
 
}
+
return titles;
return titles;
  −
}
   
}
 
}
 
}
 
}
return null;
   
}
 
}
 +
return null;
 
}
 
}
 +
}
 
};
 
};
    
var suggestionConfigs = {
 
var suggestionConfigs = {
searchindex : {name: 'Search index', engines: ['opensearch'], cache: {}, show: true, temp: false, noCompletion : false}
+
searchindex: {
,pagelist   : {name: 'Page list', engines: ['internalsearch', 'exists'], cache: {}, show: true, temp: false, noCompletion : false}
+
name: 'Search index',
,combined   : {name: 'Combined search', engines: ['opensearch', 'internalsearch'], cache: {}, show: true, temp: false, noCompletion : false}
+
engines: [ 'opensearch' ],
,subcat     : {name: 'Subcategories', engines: ['subcategories'], cache: {}, show: true, temp: true, noCompletion : true}
+
cache: {},
,parentcat   : {name: 'Parent categories', engines: ['parentcategories'], cache: {}, show: true, temp: true, noCompletion : true}
+
show: true,
 +
temp: false,
 +
noCompletion: false
 +
},
 +
pagelist: {
 +
name: 'Page list',
 +
engines: [ 'internalsearch', 'exists' ],
 +
cache: {},
 +
show: true,
 +
temp: false,
 +
noCompletion: false
 +
},
 +
combined: {
 +
name: 'Combined search',
 +
engines: [ 'opensearch', 'internalsearch' ],
 +
cache: {},
 +
show: true,
 +
temp: false,
 +
noCompletion: false
 +
},
 +
subcat: {
 +
name: 'Subcategories',
 +
engines: [ 'subcategories' ],
 +
cache: {},
 +
show: true,
 +
temp: true,
 +
noCompletion: true
 +
},
 +
parentcat: {
 +
name: 'Parent categories',
 +
engines: [ 'parentcategories' ],
 +
cache: {},
 +
show: true,
 +
temp: true,
 +
noCompletion: true
 +
}
 
};
 
};
   −
function CategoryEditor () { this.initialize.apply (this, arguments); }
+
CategoryEditor.UNCHANGED = 0;
CategoryEditor.UNCHANGED     = 0;
+
CategoryEditor.OPEN = 1; // Open, but no input yet
CategoryEditor.OPEN           = 1; // Open, but no input yet
   
CategoryEditor.CHANGE_PENDING = 2; // Open, some input made
 
CategoryEditor.CHANGE_PENDING = 2; // Open, some input made
CategoryEditor.CHANGED       = 3;
+
CategoryEditor.CHANGED = 3;
CategoryEditor.DELETED       = 4;
+
CategoryEditor.DELETED = 4;
    +
// Support: IE6
 
// IE6 sometimes forgets to redraw the list when editors are opened or closed.
 
// IE6 sometimes forgets to redraw the list when editors are opened or closed.
 
// Adding/removing a dummy element helps, at least when opening editors.
 
// Adding/removing a dummy element helps, at least when opening editors.
var dummyElement = make ('\xa0', true);
+
var dummyElement = make( '\xa0', true );
   −
function forceRedraw () {
+
function forceRedraw() {
if (!is_ie6) return;
+
if ( dummyElement.parentNode ) document.body.removeChild( dummyElement ); else document.body.appendChild( dummyElement );
if (dummyElement.parentNode) {
  −
document.body.removeChild (dummyElement);
  −
} else {
  −
document.body.appendChild (dummyElement);
  −
}
   
}
 
}
    
// Event keyCodes that we handle in the text input field/suggestion list.
 
// Event keyCodes that we handle in the text input field/suggestion list.
var BS = 8, TAB = 9, RET = 13, ESC = 27, SPACE = 32, PGUP = 33, PGDOWN = 34, UP = 38, DOWN = 40, DEL = 46, IME = 229;
+
var BS = 8,
+
TAB = 9,
function makeActive (which) {
+
RET = 13,
if (which.is_active) return;
+
ESC = 27,
for (var i = 0; i < editors.length; i++) {
+
SPACE = 32,
if (editors[i] !== which) editors[i].inactivate ();
+
PGUP = 33,
}
+
PGDOWN = 34,
which.is_active = true;
+
UP = 38,
if (which.dab) {
+
DOWN = 40,
showDab (which);
+
DEL = 46,
} else {
+
IME = 229;
// Check for programmatic value changes.
  −
var expectedInput = which.lastRealInput || which.lastInput || "";
  −
var actualValue = which.text.value || "";
  −
if (expectedInput.length === 0 && actualValue.length > 0 || expectedInput.length > 0 && actualValue.indexOf (expectedInput) !== 0) {
  −
// Somehow the field's value appears to have changed, and which.lastSelection therefore is no longer valid. Try to set the
  −
// cursor at the end of the category, and do not display the old suggestion list.
  −
which.showsList = false;
  −
var v = actualValue.split('|');
  −
which.lastRealInput = which.lastInput = v[0];
  −
if (v.length > 1) which.currentKey = v[1];
  −
if (which.lastSelection) which.lastSelection = {start: v[0].length, end: v[0].length};
  −
}
  −
if (which.showsList) which.displayList();
  −
if (which.lastSelection) {
  −
if (is_webkit) {
  −
// WebKit (Safari, Chrome) has problems selecting inside focus()
  −
// See http://code.google.com/p/chromium/issues/detail?id=32865#c6
  −
window.setTimeout (
  −
function () { which.setSelection (which.lastSelection.start, which.lastSelection.end); }
  −
,1
  −
);
  −
} else {
  −
which.setSelection (which.lastSelection.start, which.lastSelection.end);
  −
}
  −
}
  −
}
  −
}
  −
 
  −
function showDab (which) {
  −
if (!which.is_active) {
  −
makeActive(which);
  −
} else {
  −
which.showSuggestions (which.dab, false, null, null); // do autocompletion, no key, no engine selector
  −
which.dab = null;
  −
}
  −
}
      
CategoryEditor.prototype = {
 
CategoryEditor.prototype = {
   −
initialize : function (line, span, after, key, is_hidden) {
+
initialize: function ( line, span, after, key, is_hidden ) {
 
// If a span is given, 'after' is the category title, otherwise it may be an element after which to
 
// If a span is given, 'after' is the category title, otherwise it may be an element after which to
 
// insert the new span. 'key' is likewise overloaded; if a span is given, it is the category key (if
 
// insert the new span. 'key' is likewise overloaded; if a span is given, it is the category key (if
 
// known), otherwise it is a boolean indicating whether a bar shall be prepended.
 
// known), otherwise it is a boolean indicating whether a bar shall be prepended.
if (!span) {
+
if ( !span ) {
 
this.isAddCategory = true;
 
this.isAddCategory = true;
 
// Create add span and append to catLinks
 
// Create add span and append to catLinks
this.originalCategory = "";
+
this.originalCategory = '';
 
this.originalKey = null;
 
this.originalKey = null;
this.originalExists   = false;
+
this.originalExists = false;
if (!newDOM) {
+
if ( !newDOM ) {
span = make ('span');
+
span = make( 'span' );
 
span.className = 'noprint';
 
span.className = 'noprint';
if (key) {
+
if ( key ) {
span.appendChild (make (' | ', true));
+
span.appendChild( make( ' | ', true ) );
if (after) {
+
if ( after ) {
after.parentNode.insertBefore (span, after.nextSibling);
+
after.parentNode.insertBefore( span, after.nextSibling );
 
after = after.nextSibling;
 
after = after.nextSibling;
} else {
+
} else if (line) {
line.appendChild (span);
+
line.appendChild( span );
 
}
 
}
} else if (line.firstChild) {
+
} else if ( line && line.firstChild ) {
span.appendChild (make (' ', true));
+
span.appendChild( make( ' ', true ) );
line.appendChild (span);
+
line.appendChild( span );
 
}
 
}
 
}
 
}
this.linkSpan = make ('span');
+
this.linkSpan = make( 'span' );
 
this.linkSpan.className = 'noprint nopopups hotcatlink';
 
this.linkSpan.className = 'noprint nopopups hotcatlink';
var lk = make ('a'); lk.href = '#catlinks'; lk.onclick = bind (this.open, this);
+
var lk = make( 'a' );
lk.appendChild (make (HotCat.links.add, true)); lk.title = HotCat.tooltips.add;
+
lk.href = '#catlinks';
this.linkSpan.appendChild (lk);
+
lk.onclick = this.open.bind( this );
span = make (newDOM ? 'li' : 'span');
+
lk.appendChild( make( HC.links.add, true ) );
 +
lk.title = HC.tooltips.add;
 +
this.linkSpan.appendChild( lk );
 +
span = make( newDOM ? 'li' : 'span' );
 
span.className = 'noprint';
 
span.className = 'noprint';
if (is_rtl) span.dir = 'rtl';
+
if ( is_rtl ) span.dir = 'rtl';
span.appendChild (this.linkSpan);
+
 
if (after)
+
span.appendChild( this.linkSpan );
after.parentNode.insertBefore (span, after.nextSibling);
+
if ( after ) {
else
+
after.parentNode.insertBefore( span, after.nextSibling );
line.appendChild (span);
+
} else if ( line ) {
 +
line.appendChild( span );
 +
}
 +
 
 
this.normalLinks = null;
 
this.normalLinks = null;
 
this.undelLink = null;
 
this.undelLink = null;
 
this.catLink = null;
 
this.catLink = null;
 
} else {
 
} else {
if (is_rtl) span.dir = 'rtl';
+
if ( is_rtl ) span.dir = 'rtl';
 +
 
 
this.isAddCategory = false;
 
this.isAddCategory = false;
 
this.catLink = span.firstChild;
 
this.catLink = span.firstChild;
 
this.originalCategory = after;
 
this.originalCategory = after;
this.originalKey = (key && key.length > 1) ? key.substr(1) : null; // > 1 because it includes the leading bar
+
this.originalKey = ( key && key.length > 1 ) ? key.substr( 1 ) : null; // > 1 because it includes the leading bar
this.originalExists   = !hasClass (this.catLink, 'new');
+
this.originalExists = !hasClass( this.catLink, 'new' );
 
// Create change and del links
 
// Create change and del links
this.makeLinkSpan ();
+
this.makeLinkSpan();
if (!this.originalExists && this.upDownLinks) this.upDownLinks.style.display = 'none';
+
if ( !this.originalExists && this.upDownLinks ) this.upDownLinks.style.display = 'none';
span.appendChild (this.linkSpan);
+
 
 +
span.appendChild( this.linkSpan );
 
}
 
}
this.originalHidden     = is_hidden;
+
this.originalHidden = is_hidden;
this.line               = line;
+
this.line = line;
this.engine             = HotCat.suggestions;
+
this.engine = HC.suggestions;
this.span               = span;
+
this.span = span;
this.currentCategory   = this.originalCategory;
+
this.currentCategory = this.originalCategory;
this.currentExists     = this.originalExists;
+
this.currentExists = this.originalExists;
this.currentHidden     = this.originalHidden;
+
this.currentHidden = this.originalHidden;
this.currentKey         = this.originalKey;
+
this.currentKey = this.originalKey;
this.state             = CategoryEditor.UNCHANGED;
+
this.state = CategoryEditor.UNCHANGED;
this.lastSavedState     = CategoryEditor.UNCHANGED;
+
this.lastSavedState = CategoryEditor.UNCHANGED;
this.lastSavedCategory = this.originalCategory;
+
this.lastSavedCategory = this.originalCategory;
this.lastSavedKey       = this.originalKey;
+
this.lastSavedKey = this.originalKey;
this.lastSavedExists   = this.originalExists;
+
this.lastSavedExists = this.originalExists;
this.lastSavedHidden   = this.originalHidden;
+
this.lastSavedHidden = this.originalHidden;
if (this.catLink && this.currentKey) {
+
if ( this.catLink && this.currentKey ) this.catLink.title = this.currentKey;
this.catLink.title = this.currentKey;
+
 
}
+
editors[ editors.length ] = this;
editors[editors.length] = this;
   
},
 
},
   −
makeLinkSpan : function () {
+
makeLinkSpan: function () {
this.normalLinks = make ('span');
+
this.normalLinks = make( 'span' );
 
var lk = null;
 
var lk = null;
if (this.originalCategory && this.originalCategory.length > 0) {
+
if ( this.originalCategory && this.originalCategory.length ) {
lk = make ('a'); lk.href = '#catlinks'; lk.onclick = bind (this.remove, this);
+
lk = make( 'a' );
lk.appendChild (make (HotCat.links.remove, true)); lk.title = HotCat.tooltips.remove;
+
lk.href = '#catlinks';
this.normalLinks.appendChild (make (' ', true));
+
lk.onclick = this.remove.bind( this );
this.normalLinks.appendChild (lk);
+
lk.appendChild( make( HC.links.remove, true ) );
 +
lk.title = HC.tooltips.remove;
 +
this.normalLinks.appendChild( make( ' ', true ) );
 +
this.normalLinks.appendChild( lk );
 
}
 
}
if (!HotCat.template_categories[this.originalCategory]) {
+
if ( !HC.template_categories[ this.originalCategory ] ) {
lk = make ('a'); lk.href = '#catlinks'; lk.onclick = bind (this.open, this);
+
lk = make( 'a' );
lk.appendChild (make (HotCat.links.change, true)); lk.title = HotCat.tooltips.change;
+
lk.href = '#catlinks';
this.normalLinks.appendChild (make (' ', true));
+
lk.onclick = this.open.bind( this );
this.normalLinks.appendChild (lk);
+
lk.appendChild( make( HC.links.change, true ) );
if (!noSuggestions && HotCat.use_up_down) {
+
lk.title = HC.tooltips.change;
this.upDownLinks = make ('span');
+
this.normalLinks.appendChild( make( ' ', true ) );
lk = make ('a'); lk.href = '#catlinks'; lk.onclick = bind (this.down, this);
+
this.normalLinks.appendChild( lk );
lk.appendChild (make (HotCat.links.down, true)); lk.title = HotCat.tooltips.down;
+
if ( !noSuggestions && HC.use_up_down ) {
this.upDownLinks.appendChild (make (' ', true));
+
this.upDownLinks = make( 'span' );
this.upDownLinks.appendChild (lk);
+
lk = make( 'a' );
lk = make ('a'); lk.href = '#catlinks'; lk.onclick = bind (this.up, this);
+
lk.href = '#catlinks';
lk.appendChild (make (HotCat.links.up, true)); lk.title = HotCat.tooltips.up;
+
lk.onclick = this.down.bind( this );
this.upDownLinks.appendChild (make (' ', true));
+
lk.appendChild( make( HC.links.down, true ) );
this.upDownLinks.appendChild (lk);
+
lk.title = HC.tooltips.down;
this.normalLinks.appendChild (this.upDownLinks);
+
this.upDownLinks.appendChild( make( ' ', true ) );
 +
this.upDownLinks.appendChild( lk );
 +
lk = make( 'a' );
 +
lk.href = '#catlinks';
 +
lk.onclick = this.up.bind( this );
 +
lk.appendChild( make( HC.links.up, true ) );
 +
lk.title = HC.tooltips.up;
 +
this.upDownLinks.appendChild( make( ' ', true ) );
 +
this.upDownLinks.appendChild( lk );
 +
this.normalLinks.appendChild( this.upDownLinks );
 
}
 
}
 
}
 
}
this.linkSpan = make ('span');
+
this.linkSpan = make( 'span' );
 
this.linkSpan.className = 'noprint nopopups hotcatlink';
 
this.linkSpan.className = 'noprint nopopups hotcatlink';
this.linkSpan.appendChild (this.normalLinks);
+
this.linkSpan.appendChild( this.normalLinks );
this.undelLink = make ('span');
+
this.undelLink = make( 'span' );
 
this.undelLink.className = 'nopopups hotcatlink';
 
this.undelLink.className = 'nopopups hotcatlink';
 
this.undelLink.style.display = 'none';
 
this.undelLink.style.display = 'none';
lk = make ('a'); lk.href = '#catlinks'; lk.onclick = bind (this.restore, this);
+
lk = make( 'a' );
lk.appendChild (make (HotCat.links.restore, true)); lk.title = HotCat.tooltips.restore;
+
lk.href = '#catlinks';
this.undelLink.appendChild (make (' ', true));
+
lk.onclick = this.restore.bind( this );
this.undelLink.appendChild (lk);
+
lk.appendChild( make( HC.links.restore, true ) );
this.linkSpan.appendChild (this.undelLink);
+
lk.title = HC.tooltips.restore;
 +
this.undelLink.appendChild( make( ' ', true ) );
 +
this.undelLink.appendChild( lk );
 +
this.linkSpan.appendChild( this.undelLink );
 
},
 
},
   −
invokeSuggestions : function (dont_autocomplete) {
+
invokeSuggestions: function ( dont_autocomplete ) {
if (this.engine && suggestionConfigs[this.engine] && suggestionConfigs[this.engine].temp && !dont_autocomplete) {
+
if ( this.engine && suggestionConfigs[ this.engine ] && suggestionConfigs[ this.engine ].temp && !dont_autocomplete ) this.engine = HC.suggestions; // Reset to a search upon input
this.engine = HotCat.suggestions; // Reset to a search upon input
+
 
}
   
this.state = CategoryEditor.CHANGE_PENDING;
 
this.state = CategoryEditor.CHANGE_PENDING;
 
var self = this;
 
var self = this;
window.setTimeout (function () {self.textchange (dont_autocomplete);}, HotCat.suggest_delay);
+
window.setTimeout( function () {
 +
self.textchange( dont_autocomplete );
 +
}, HC.suggest_delay );
 
},
 
},
   −
makeForm : function () {
+
makeForm: function () {
var form = make ('form');
+
var form = make( 'form' );
form.method = 'POST'; form.onsubmit = bind (this.accept, this);
+
form.method = 'POST';
 +
form.onsubmit = this.accept.bind( this );
 
this.form = form;
 
this.form = form;
 
var self = this;
 
var self = this;
var text = make ('input'); text.type = 'text'; text.size = HotCat.editbox_width;
+
var text = make( 'input' );
if (!noSuggestions) {
+
text.type = 'text';
 +
text.size = HC.editbox_width;
 +
if ( !noSuggestions ) {
 
// Be careful here to handle IME input. This is browser/OS/IME dependent, but basically there are two mechanisms:
 
// Be careful here to handle IME input. This is browser/OS/IME dependent, but basically there are two mechanisms:
 
// - Modern (DOM Level 3) browsers use compositionstart/compositionend events to signal composition; if the
 
// - Modern (DOM Level 3) browsers use compositionstart/compositionend events to signal composition; if the
Line 1,453: Line 1,515:  
// - Older browsers signal composition by keyDown === IME for the first and subsequent keys for a composition. The
 
// - Older browsers signal composition by keyDown === IME for the first and subsequent keys for a composition. The
 
//  first keyDown !== IME is certainly after the end of the composition. Typically, composition end can also be
 
//  first keyDown !== IME is certainly after the end of the composition. Typically, composition end can also be
//  detected by a keyDown IME with a keyUp of space, tab, escape, or return. (Example: IE8)
+
//  detected by a keyDown IME with a keyUp of space, tab, escape, or return.
text.onkeyup =
+
text.onkeyup = function ( evt ) {
function (evt) {
+
var key = evt.keyCode || 0;
evt = evt || window.event || window.Event; // W3C, IE, Netscape
+
if ( self.ime && self.lastKey === IME && !self.usesComposition && ( key === TAB || key === RET || key === ESC || key === SPACE ) ) self.ime = false;
var key = evt.keyCode || 0;
+
 
if (self.ime && self.lastKey === IME && !self.usesComposition && (key === TAB || key === RET || key == ESC || key === SPACE)) self.ime = false;
+
if ( self.ime ) return true;
if (self.ime) return true;
+
 
if (key === UP || key === DOWN || key === PGUP || key === PGDOWN) {
+
if ( key === UP || key === DOWN || key === PGUP || key === PGDOWN ) {
// In case a browser doesn't generate keypress events for arrow keys...
+
// In case a browser doesn't generate keypress events for arrow keys...
if (self.keyCount === 0) return self.processKey (evt);
+
if ( self.keyCount === 0 ) return self.processKey( evt );
} else {
+
} else {
if (key === ESC && self.lastKey !== IME) {
+
if ( key === ESC && self.lastKey !== IME ) {
if (!self.resetKeySelection ()) {
+
if ( !self.resetKeySelection() ) {
// No undo of key selection: treat ESC as "cancel".
+
// No undo of key selection: treat ESC as "cancel".
self.cancel ();
+
self.cancel();
return;
+
return;
}
   
}
 
}
// Also do this for ESC as a workaround for Firefox bug 524360
  −
// https://bugzilla.mozilla.org/show_bug.cgi?id=524360
  −
self.invokeSuggestions (key === BS || key === DEL || key === ESC);
   
}
 
}
return true;
+
// Also do this for ESC as a workaround for Firefox bug 524360
};
+
// https://bugzilla.mozilla.org/show_bug.cgi?id=524360
text.onkeydown =
+
self.invokeSuggestions( key === BS || key === DEL || key === ESC );
function (evt) {
+
}
evt = evt || window.event || window.Event; // W3C, IE, Netscape
+
return true;
var key = evt.keyCode || 0;
+
};
self.lastKey = key;
+
text.onkeydown = function ( evt ) {
self.keyCount = 0;
+
var key = evt.keyCode || 0;
// DOM Level < 3 IME input
+
self.lastKey = key;
if (!self.ime && key === IME && !self.usesComposition) {
+
self.keyCount = 0;
// self.usesComposition catches browsers that may emit spurious keydown IME after a composition has ended
+
// DOM Level < 3 IME input
self.ime = true;
+
if ( !self.ime && key === IME && !self.usesComposition ) {
} else if (self.ime && key !== IME && !(key >= 16 && key <= 20 || key >= 91 && key <= 93 || key === 144)) {
+
// self.usesComposition catches browsers that may emit spurious keydown IME after a composition has ended
// Ignore control keys: ctrl, shift, alt, alt gr, caps lock, windows/apple cmd keys, num lock. Only the windows keys
+
self.ime = true;
// terminate IME (apple cmd doesn't), but they also cause a blur, so it's OK to ignore them here.
+
} else if ( self.ime && key !== IME && !( key >= 16 && key <= 20 || key >= 91 && key <= 93 || key === 144 ) ) {
// Note: Safari 4 (530.17) propagates ESC out of an IME composition (observed at least on Win XP).
+
// Ignore control keys: ctrl, shift, alt, alt gr, caps lock, windows/apple cmd keys, num lock. Only the windows keys
self.ime = false;
+
// terminate IME (apple cmd doesn't), but they also cause a blur, so it's OK to ignore them here.
}
+
// Note: Safari 4 (530.17) propagates ESC out of an IME composition (observed at least on Win XP).
if (self.ime) return true;
+
self.ime = false;
// Handle return explicitly, to override the default form submission to be able to check for ctrl
+
}
if (key === RET) return self.accept (evt);
+
if ( self.ime ) return true;
// Inhibit default behavior of ESC (revert to last real input in FF: we do that ourselves)
+
 
return (key === ESC) ? evtKill(evt) : true;
+
// Handle return explicitly, to override the default form submission to be able to check for ctrl
};
+
if ( key === RET ) return self.accept( evt );
 +
 
 +
// Inhibit default behavior of ESC (revert to last real input in FF: we do that ourselves)
 +
return ( key === ESC ) ? evtKill( evt ) : true;
 +
};
 
// And handle continued pressing of arrow keys
 
// And handle continued pressing of arrow keys
text.onkeypress = function (evt) {self.keyCount++; return self.processKey (evt);};
+
text.onkeypress = function ( evt ) {
addEvent (text, 'focus', function () { makeActive(self); });
+
self.keyCount++;
 +
return self.processKey( evt );
 +
};
 +
$( text ).on( 'focus', function () {
 +
makeActive( self );
 +
} );
 
// On IE, blur events are asynchronous, and may thus arrive after the element has lost the focus. Since IE
 
// On IE, blur events are asynchronous, and may thus arrive after the element has lost the focus. Since IE
 
// can get the selection only while the element is active (has the focus), we may not always get the selection.
 
// can get the selection only while the element is active (has the focus), we may not always get the selection.
 
// Therefore, use an IE-specific synchronous event on IE...
 
// Therefore, use an IE-specific synchronous event on IE...
// Don't test for text.selectionStart being defined; FF3.6.4 raises an exception when trying to access that
+
// Don't test for text.selectionStart being defined;
// property while the element is not being displayed.
+
$( text ).on(
addEvent (text
+
( text.onbeforedeactivate !== undefined && text.createTextRange ) ? 'beforedeactivate' : 'blur',
, (typeof text.onbeforedeactivate != 'undefined' && text.createTextRange) ? 'beforedeactivate' : 'blur'
+
this.saveView.bind( this ) );
, bind (this.saveView, this)
  −
);
   
// DOM Level 3 IME handling
 
// DOM Level 3 IME handling
 
try {
 
try {
 
// Setting lastKey = IME provides a fake keyDown for Gecko's single keyUp after a cmposition. If we didn't do this,
 
// Setting lastKey = IME provides a fake keyDown for Gecko's single keyUp after a cmposition. If we didn't do this,
 
// cancelling a composition via ESC would also cancel and close the whole category input editor.
 
// cancelling a composition via ESC would also cancel and close the whole category input editor.
addEvent(text, 'compositionstart', function (evt) { self.lastKey = IME; self.usesComposition = true; self.ime = true; });
+
$( text ).on( 'compositionstart', function () {
addEvent(text, 'compositionend', function (evt) { self.lastKey = IME; self.usesComposition = true; self.ime = false; });
+
self.lastKey = IME;
addEvent(text, 'textInput', function (evt) { self.ime = false; self.invokeSuggestions(false); });
+
self.usesComposition = true;
} catch (any) {
+
self.ime = true;
 +
} );
 +
$( text ).on( 'compositionend', function () {
 +
self.lastKey = IME;
 +
self.usesComposition = true;
 +
self.ime = false;
 +
} );
 +
$( text ).on( 'textInput', function () {
 +
self.ime = false;
 +
self.invokeSuggestions( false );
 +
} );
 +
} catch ( any ) {
 
// Just in case some browsers might produce exceptions with these DOM Level 3 events
 
// Just in case some browsers might produce exceptions with these DOM Level 3 events
 
}
 
}
addEvent(text, 'blur', function (evt) { self.usesComposition = false; self.ime = false; });
+
$( text ).on( 'blur', function () {
 +
self.usesComposition = false;
 +
self.ime = false;
 +
} );
 
}
 
}
 
this.text = text;
 
this.text = text;
   −
this.icon = make ('img');
+
this.icon = make( 'img' );
    
var list = null;
 
var list = null;
if (!noSuggestions) {
+
if ( !noSuggestions ) {
list = make ('select');
+
list = make( 'select' );
list.onclick   = function (e) { if (self.highlightSuggestion(0)) self.textchange (false, true); };
+
list.onclick = function () {
list.ondblclick = function (e) { if (self.highlightSuggestion(0)) self.accept (e); };
+
if ( self.highlightSuggestion( 0 ) ) self.textchange( false, true );
list.onchange = function (e) { self.highlightSuggestion(0); self.text.focus(); };
+
};
list.onkeyup =
+
list.ondblclick = function ( e ) {
function (evt) {
+
if ( self.highlightSuggestion( 0 ) ) self.accept( e );
evt = evt || window.event || window.Event; // W3C, IE, Netscape
+
};
if (evt.keyCode === ESC) {
+
list.onchange = function () {
self.resetKeySelection ();
+
self.highlightSuggestion( 0 );
self.text.focus();
+
self.text.focus();
window.setTimeout (function () {self.textchange (true);}, HotCat.suggest_delay);
+
};
} else if (evt.keyCode === RET) {
+
list.onkeyup = function ( evt ) {
self.accept (evt);
+
if ( evt.keyCode === ESC ) {
}
+
self.resetKeySelection();
};
+
self.text.focus();
if (!HotCat.fixed_search) {
+
window.setTimeout( function () {
var engineSelector = make ('select');
+
self.textchange( true );
for (var key in suggestionConfigs) {
+
}, HC.suggest_delay );
if (suggestionConfigs[key].show) {
+
} else if ( evt.keyCode === RET ) {
var opt = make ('option');
+
self.accept( evt );
 +
}
 +
};
 +
if ( !HC.fixed_search ) {
 +
var engineSelector = make( 'select' );
 +
for ( var key in suggestionConfigs ) {
 +
if ( suggestionConfigs[ key ].show ) {
 +
var opt = make( 'option' );
 
opt.value = key;
 
opt.value = key;
if (key == this.engine) opt.selected = true;
+
if ( key === this.engine ) opt.selected = true;
opt.appendChild (make (suggestionConfigs[key].name, true));
+
 
engineSelector.appendChild (opt);
+
opt.appendChild( make( suggestionConfigs[ key ].name, true ) );
 +
engineSelector.appendChild( opt );
 
}
 
}
 
}
 
}
engineSelector.onchange =
+
engineSelector.onchange = function () {
function () {
+
self.engine = self.engineSelector.options[ self.engineSelector.selectedIndex ].value;
self.engine = self.engineSelector.options[self.engineSelector.selectedIndex].value;
+
self.text.focus();
self.text.focus();
+
self.textchange( true, true ); // Don't autocomplete, force re-display of list
self.textchange (true, true); // Don't autocomplete, force re-display of list
+
};
};
   
this.engineSelector = engineSelector;
 
this.engineSelector = engineSelector;
 
}
 
}
Line 1,566: Line 1,652:  
this.list = list;
 
this.list = list;
   −
function button_label (id, defaultText) {
+
function button_label( id, defaultText ) {
 
var label = null;
 
var label = null;
if (   onUpload
+
if (
&& typeof UFUI != 'undefined'
+
onUpload &&
&& typeof UIElements != 'undefined'
+
window.UFUI !== undefined &&
&& typeof UFUI.getLabel == 'function')
+
window.UIElements !== undefined &&
{
+
UFUI.getLabel instanceof Function
 +
) {
 
try {
 
try {
label = UFUI.getLabel (id, true);
+
label = UFUI.getLabel( id, true );
 
// Extract the plain text. IE doesn't know that Node.TEXT_NODE === 3
 
// Extract the plain text. IE doesn't know that Node.TEXT_NODE === 3
while (label && label.nodeType != 3) label = label.firstChild;
+
while ( label && label.nodeType !== 3 ) label = label.firstChild;
} catch (ex) {
+
} catch ( ex ) {
 
label = null;
 
label = null;
 
}
 
}
 
}
 
}
if (!label || !label.data) return defaultText;
+
if ( !label || !label.data ) return defaultText;
 +
 
 
return label.data;
 
return label.data;
 
}
 
}
    
// Do not use type 'submit'; we cannot detect modifier keys if we do
 
// Do not use type 'submit'; we cannot detect modifier keys if we do
var OK = make ('input'); OK.type = 'button';
+
var OK = make( 'input' );
OK.value = button_label ('wpOkUploadLbl', HotCat.messages.ok);
+
OK.type = 'button';
OK.onclick = bind (this.accept, this);
+
OK.value = button_label( 'wpOkUploadLbl', HC.messages.ok );
 +
OK.onclick = this.accept.bind( this );
 
this.ok = OK;
 
this.ok = OK;
   −
var cancel = make ('input'); cancel.type = 'button';
+
var cancel = make( 'input' );
cancel.value = button_label ('wpCancelUploadLbl', HotCat.messages.cancel);
+
cancel.type = 'button';
cancel.onclick = bind (this.cancel, this);
+
cancel.value = button_label( 'wpCancelUploadLbl', HC.messages.cancel );
 +
cancel.onclick = this.cancel.bind( this );
 
this.cancelButton = cancel;
 
this.cancelButton = cancel;
   −
var span = make ('span');
+
var span = make( 'span' );
 
span.className = 'hotcatinput';
 
span.className = 'hotcatinput';
 
span.style.position = 'relative';
 
span.style.position = 'relative';
// FF3.6: add the input field first, then the two absolutely positioned elements. Otherwise, FF3.6 may leave the
+
span.appendChild( text );
// suggestions and the selector at the right edge of the screen if display of the input field causes a re-layout
  −
// moving the form to the front of the next line.
  −
span.appendChild (text);
     −
// IE8/IE9: put some text into this span (a0 is nbsp) and make sure it always stays on the
+
// Support: IE8, IE9
// same line as the input field, otherwise, IE8/9 miscalculates the height of the span and
+
// Put some text into this span (a0 is nbsp) and make sure it always stays on the same
 +
// line as the input field, otherwise, IE8/9 miscalculates the height of the span and
 
// then the engine selector may overlap the input field.
 
// then the engine selector may overlap the input field.
span.appendChild (make ('\xa0', true));
+
span.appendChild( make( '\xa0', true ) );
 
span.style.whiteSpace = 'nowrap';
 
span.style.whiteSpace = 'nowrap';
   −
if (list) span.appendChild (list);
+
if ( list ) span.appendChild( list );
if (this.engineSelector) span.appendChild (this.engineSelector);
+
 
if (!noSuggestions) span.appendChild (this.icon);
+
if ( this.engineSelector ) span.appendChild( this.engineSelector );
span.appendChild (OK);
+
 
span.appendChild (cancel);
+
if ( !noSuggestions ) span.appendChild( this.icon );
form.appendChild(span);
+
 
 +
span.appendChild( OK );
 +
span.appendChild( cancel );
 +
form.appendChild( span );
 
form.style.display = 'none';
 
form.style.display = 'none';
this.span.appendChild (form);
+
this.span.appendChild( form );
 
},
 
},
   −
display : function (evt) {
+
display: function ( evt ) {
if (this.isAddCategory && !onUpload) {
+
if ( this.isAddCategory && !onUpload && this.line ) {
var newAdder = new CategoryEditor (this.line, null, this.span, true); // Create a new one
+
// eslint-disable-next-line no-new
 +
new CategoryEditor( this.line, null, this.span, true ); // Create a new one
 
}
 
}
if (!commitButton && !onUpload) {
+
if ( !commitButton && !onUpload ) {
for (var i = 0; i < editors.length; i++) {
+
for ( var i = 0; i < editors.length; i++ ) {
if (editors[i].state != CategoryEditor.UNCHANGED) {
+
if ( editors[ i ].state !== CategoryEditor.UNCHANGED ) {
 
setMultiInput();
 
setMultiInput();
 
break;
 
break;
Line 1,632: Line 1,724:  
}
 
}
 
}
 
}
if (!this.form) {
+
if ( !this.form ) this.makeForm();
this.makeForm ();
+
 
}
+
if ( this.list ) this.list.style.display = 'none';
if (this.list) this.list.style.display = 'none';
+
 
if (this.engineSelector) this.engineSelector.style.display = 'none';
+
if ( this.engineSelector ) this.engineSelector.style.display = 'none';
 +
 
 
this.currentCategory = this.lastSavedCategory;
 
this.currentCategory = this.lastSavedCategory;
this.currentExists   = this.lastSavedExists;
+
this.currentExists = this.lastSavedExists;
this.currentHidden   = this.lastSavedHidden;
+
this.currentHidden = this.lastSavedHidden;
this.currentKey     = this.lastSavedKey;
+
this.currentKey = this.lastSavedKey;
this.icon.src = armorUri(this.currentExists ? HotCat.existsYes : HotCat.existsNo);
+
this.icon.src = ( this.currentExists ? HC.existsYes : HC.existsNo );
this.text.value = this.currentCategory + (this.currentKey !== null ? '|' + this.currentKey : "");
+
this.text.value = this.currentCategory + ( this.currentKey !== null ? '|' + this.currentKey : '' );
 
this.originalState = this.state;
 
this.originalState = this.state;
this.lastInput     = this.currentCategory;
+
this.lastInput = this.currentCategory;
this.inputExists   = this.currentExists;
+
this.inputExists = this.currentExists;
this.state         = this.state == CategoryEditor.UNCHANGED ? CategoryEditor.OPEN : CategoryEditor.CHANGE_PENDING;
+
this.state = this.state === CategoryEditor.UNCHANGED ? CategoryEditor.OPEN : CategoryEditor.CHANGE_PENDING;
this.lastSelection = {start: this.currentCategory.length, end: this.currentCategory.length};
+
this.lastSelection = {
 +
start: this.currentCategory.length,
 +
end: this.currentCategory.length
 +
};
 
this.showsList = false;
 
this.showsList = false;
 
// Display the form
 
// Display the form
if (this.catLink) this.catLink.style.display = 'none';
+
if ( this.catLink ) this.catLink.style.display = 'none';
 +
 
 
this.linkSpan.style.display = 'none';
 
this.linkSpan.style.display = 'none';
 
this.form.style.display = 'inline';
 
this.form.style.display = 'inline';
 
this.ok.disabled = false;
 
this.ok.disabled = false;
 
// Kill the event before focussing, otherwise IE will kill the onfocus event!
 
// Kill the event before focussing, otherwise IE will kill the onfocus event!
var result = evtKill (evt);
+
var result = evtKill( evt );
 
this.text.focus();
 
this.text.focus();
 
this.text.readOnly = false;
 
this.text.readOnly = false;
checkMultiInput ();
+
checkMultiInput();
 
return result;
 
return result;
 
},
 
},
   −
show : function (evt, engine, readOnly) {
+
show: function ( evt, engine, readOnly ) {
var result = this.display (evt);
+
var result = this.display( evt );
 
var v = this.lastSavedCategory;
 
var v = this.lastSavedCategory;
if (v.length === 0) return result;
+
if ( !v.length ) return result;
 +
 
 
this.text.readOnly = !!readOnly;
 
this.text.readOnly = !!readOnly;
 
this.engine = engine;
 
this.engine = engine;
this.textchange (false, true); // do autocompletion, force display of suggestions
+
this.textchange( false, true ); // do autocompletion, force display of suggestions
forceRedraw ();
+
forceRedraw();
 
return result;
 
return result;
 
},
 
},
   −
open : function (evt) {
+
open: function ( evt ) {
return this.show (evt, (this.engine && suggestionConfigs[this.engine].temp) ? HotCat.suggestions : this.engine);
+
return this.show( evt, ( this.engine && suggestionConfigs[ this.engine ].temp ) ? HC.suggestions : this.engine );
 
},
 
},
   −
down : function (evt) {
+
down: function ( evt ) {
return this.show (evt, 'subcat', true);
+
return this.show( evt, 'subcat', true );
 
},
 
},
   −
up : function (evt) {
+
up: function ( evt ) {
return this.show (evt, 'parentcat');
+
return this.show( evt, 'parentcat' );
 
},
 
},
   −
cancel : function () {
+
cancel: function () {
if (this.isAddCategory && !onUpload) {
+
if ( this.isAddCategory && !onUpload ) {
 
this.removeEditor(); // We added a new adder when opening
 
this.removeEditor(); // We added a new adder when opening
 
return;
 
return;
Line 1,693: Line 1,791:  
this.inactivate();
 
this.inactivate();
 
this.form.style.display = 'none';
 
this.form.style.display = 'none';
if (this.catLink) this.catLink.style.display = "";
+
if ( this.catLink ) this.catLink.style.display = '';
this.linkSpan.style.display = "";
+
 
 +
this.linkSpan.style.display = '';
 
this.state = this.originalState;
 
this.state = this.originalState;
 
this.currentCategory = this.lastSavedCategory;
 
this.currentCategory = this.lastSavedCategory;
this.currentKey     = this.lastSavedKey;
+
this.currentKey = this.lastSavedKey;
this.currentExists   = this.lastSavedExists;
+
this.currentExists = this.lastSavedExists;
this.currentHidden   = this.lastSavedHidden;
+
this.currentHidden = this.lastSavedHidden;
if (this.catLink) {
+
if ( this.catLink )
if (this.currentKey && this.currentKey.length > 0) {
+
if ( this.currentKey && this.currentKey.length ) { this.catLink.title = this.currentKey; } else { this.catLink.title = ''; }
this.catLink.title = this.currentKey;
+
 
} else {
+
if ( this.state === CategoryEditor.UNCHANGED ) {
this.catLink.title = "";
+
if ( this.catLink ) this.catLink.style.backgroundColor = 'transparent';
}
  −
}
  −
if (this.state == CategoryEditor.UNCHANGED) {
  −
if (this.catLink) this.catLink.style.backgroundColor = 'transparent';
   
} else {
 
} else {
if (!onUpload) {
+
if ( !onUpload ) {
 
try {
 
try {
this.catLink.style.backgroundColor = HotCat.bg_changed;
+
this.catLink.style.backgroundColor = HC.bg_changed;
} catch (ex) {}
+
} catch ( ex ) {}
 
}
 
}
 
}
 
}
checkMultiInput ();
+
checkMultiInput();
forceRedraw ();
+
forceRedraw();
 
},
 
},
   −
removeEditor : function () {
+
removeEditor: function () {
if (!newDOM) {
+
if ( !newDOM ) {
 
var next = this.span.nextSibling;
 
var next = this.span.nextSibling;
if (next) next.parentNode.removeChild (next);
+
if ( next ) next.parentNode.removeChild( next );
 
}
 
}
this.span.parentNode.removeChild (this.span);
+
this.span.parentNode.removeChild( this.span );
for (var i = 0; i < editors.length; i++) {
+
for ( var i = 0; i < editors.length; i++ ) {
if (editors[i] == this) {
+
if ( editors[ i ] === this ) {
editors.splice (i, 1);
+
editors.splice( i, 1 );
 
break;
 
break;
 
}
 
}
 
}
 
}
checkMultiInput ();
+
checkMultiInput();
var self = this;
  −
window.setTimeout (function () {delete self;}, 10);
   
},
 
},
   −
rollback : function (evt) {
+
rollback: function ( evt ) {
this.undoLink.parentNode.removeChild (this.undoLink);
+
this.undoLink.parentNode.removeChild( this.undoLink );
 
this.undoLink = null;
 
this.undoLink = null;
 
this.currentCategory = this.originalCategory;
 
this.currentCategory = this.originalCategory;
Line 1,749: Line 1,842:  
this.lastSavedHidden = this.originalHidden;
 
this.lastSavedHidden = this.originalHidden;
 
this.state = CategoryEditor.UNCHANGED;
 
this.state = CategoryEditor.UNCHANGED;
if (!this.currentCategory || this.currentCategory.length === 0) {
+
if ( !this.currentCategory || !this.currentCategory.length ) {
 
// It was a newly added category. Remove the whole editor.
 
// It was a newly added category. Remove the whole editor.
 
this.removeEditor();
 
this.removeEditor();
 
} else {
 
} else {
 
// Redisplay the link...
 
// Redisplay the link...
this.catLink.removeChild (this.catLink.firstChild);
+
this.catLink.removeChild( this.catLink.firstChild );
this.catLink.appendChild (make (this.currentCategory, true));
+
this.catLink.appendChild( make( this.currentCategory, true ) );
this.catLink.href = wikiPagePath (HotCat.category_canonical + ':' + this.currentCategory);
+
this.catLink.href = wikiPagePath( HC.category_canonical + ':' + this.currentCategory );
this.catLink.title = this.currentKey || "";
+
this.catLink.title = this.currentKey || '';
this.catLink.className = this.currentExists ? "" : 'new';
+
this.catLink.className = this.currentExists ? '' : 'new';
 
this.catLink.style.backgroundColor = 'transparent';
 
this.catLink.style.backgroundColor = 'transparent';
if (this.upDownLinks) this.upDownLinks.style.display = this.currentExists ? "" : 'none';
+
if ( this.upDownLinks ) this.upDownLinks.style.display = this.currentExists ? '' : 'none';
checkMultiInput ();
+
 
 +
checkMultiInput();
 
}
 
}
return evtKill (evt);
+
return evtKill( evt );
 
},
 
},
   −
inactivate : function () {
+
inactivate: function () {
if (this.list) this.list.style.display = 'none';
+
if ( this.list ) this.list.style.display = 'none';
if (this.engineSelector) this.engineSelector.style.display = 'none';
+
 
 +
if ( this.engineSelector ) this.engineSelector.style.display = 'none';
 +
 
 
this.is_active = false;
 
this.is_active = false;
 
},
 
},
   −
acceptCheck : function (dontCheck) {
+
acceptCheck: function ( dontCheck ) {
this.sanitizeInput ();
+
this.sanitizeInput();
var value = this.text.value.split('|');
+
var value = this.text.value.split( '|' );
var key   = null;
+
var key = null;
if (value.length > 1) key = value[1];
+
if ( value.length > 1 ) key = value[ 1 ];
var v = value[0].replace(/_/g, ' ').replace(/^\s+|\s+$/g, "");
+
 
if (HotCat.capitalizePageNames) v = capitalize (v);
+
var v = value[ 0 ].replace( /_/g, ' ' ).replace( /^\s+|\s+$/g, '' );
 +
if ( HC.capitalizePageNames ) v = capitalize( v );
 +
 
 
this.lastInput = v;
 
this.lastInput = v;
v = replaceShortcuts(v, HotCat.shortcuts);
+
v = replaceShortcuts( v, HC.shortcuts );
if (v.length === 0) {
+
if ( !v.length ) {
this.cancel ();
+
this.cancel();
 
return false;
 
return false;
 
}
 
}
if (!dontCheck
+
if ( !dontCheck && (
&& (   conf.wgNamespaceNumber === 14 && v == conf.wgTitle
+
conf.wgNamespaceNumber === 14 && v === conf.wgTitle || HC.blacklist && HC.blacklist.test( v ) ) ) {
|| HotCat.blacklist && HotCat.blacklist.test(v))
+
this.cancel();
  )
  −
{
  −
this.cancel ();
   
return false;
 
return false;
 
}
 
}
Line 1,799: Line 1,894:  
},
 
},
   −
accept : function (evt) {
+
accept: function ( evt ) {
this.noCommit = (evtKeys (evt) & 1) !== 0;
+
// eslint-disable-next-line no-bitwise
var result = evtKill (evt);
+
this.noCommit = ( evtKeys( evt ) & 1 ) !== 0;
if (this.acceptCheck ()) {
+
var result = evtKill( evt );
var toResolve = [this];
+
if ( this.acceptCheck() ) {
var original = this.currentCategory;
+
var toResolve = [ this ];
resolveMulti (
+
var original = this.currentCategory;
toResolve
+
resolveMulti( toResolve, function ( resolved ) {
,function (resolved) {
+
if ( resolved[ 0 ].dab ) {
if (resolved[0].dab) {
+
showDab( resolved[ 0 ] );
showDab (resolved[0]);
+
} else {
} else {
+
if ( resolved[ 0 ].acceptCheck( true ) ) {
if (resolved[0].acceptCheck(true)) {
+
resolved[ 0 ].commit(
resolved[0].commit (
+
( resolved[ 0 ].currentCategory !== original ) ?
(resolved[0].currentCategory != original)
+
HC.messages.cat_resolved.replace( /\$1/g, original ) :
? HotCat.messages.cat_resolved.replace (/\$1/g, original)
+
null );
: null
  −
);
  −
}
   
}
 
}
}
+
}
);
+
} );
 
}
 
}
 
return result;
 
return result;
 
},
 
},
   −
close : function () {
+
close: function () {
if (!this.catLink) {
+
if ( !this.catLink ) {
 
// Create a catLink
 
// Create a catLink
this.catLink = make ('a');
+
this.catLink = make( 'a' );
this.catLink.appendChild (make ('foo', true));
+
this.catLink.appendChild( make( 'foo', true ) );
 
this.catLink.style.display = 'none';
 
this.catLink.style.display = 'none';
this.span.insertBefore (this.catLink, this.span.firstChild.nextSibling);
+
this.span.insertBefore( this.catLink, this.span.firstChild.nextSibling );
 
}
 
}
this.catLink.removeChild (this.catLink.firstChild);
+
this.catLink.removeChild( this.catLink.firstChild );
this.catLink.appendChild (make (this.currentCategory, true));
+
this.catLink.appendChild( make( this.currentCategory, true ) );
this.catLink.href = wikiPagePath (HotCat.category_canonical + ':' + this.currentCategory);
+
this.catLink.href = wikiPagePath( HC.category_canonical + ':' + this.currentCategory );
this.catLink.className = this.currentExists ? "" : 'new';
+
this.catLink.className = this.currentExists ? '' : 'new';
 
this.lastSavedCategory = this.currentCategory;
 
this.lastSavedCategory = this.currentCategory;
this.lastSavedKey     = this.currentKey;
+
this.lastSavedKey = this.currentKey;
this.lastSavedExists   = this.currentExists;
+
this.lastSavedExists = this.currentExists;
this.lastSavedHidden   = this.currentHidden;
+
this.lastSavedHidden = this.currentHidden;
 
// Close form and redisplay category
 
// Close form and redisplay category
 
this.inactivate();
 
this.inactivate();
 
this.form.style.display = 'none';
 
this.form.style.display = 'none';
this.catLink.title = this.currentKey || "";
+
this.catLink.title = this.currentKey || '';
this.catLink.style.display = "";
+
this.catLink.style.display = '';
if (this.isAddCategory) {
+
if ( this.isAddCategory ) {
if (onUpload) {
+
if ( onUpload && this.line ) {
var newAdder = new CategoryEditor (this.line, null, this.span, true); // Create a new one
+
// eslint-disable-next-line no-new
 +
new CategoryEditor( this.line, null, this.span, true ); // Create a new one
 
}
 
}
 
this.isAddCategory = false;
 
this.isAddCategory = false;
this.linkSpan.parentNode.removeChild (this.linkSpan);
+
this.linkSpan.parentNode.removeChild( this.linkSpan );
this.makeLinkSpan ();
+
this.makeLinkSpan();
this.span.appendChild (this.linkSpan);
+
this.span.appendChild( this.linkSpan );
 
}
 
}
if (!this.undoLink) {
+
if ( !this.undoLink ) {
 
// Append an undo link.
 
// Append an undo link.
var span = make ('span');
+
var span = make( 'span' );
var lk = make ('a'); lk.href = '#catlinks'; lk.onclick = bind (this.rollback, this);
+
var lk = make( 'a' );
lk.appendChild (make (HotCat.links.undo, true)); lk.title = HotCat.tooltips.undo;
+
lk.href = '#catlinks';
span.appendChild (make (' ', true));
+
lk.onclick = this.rollback.bind( this );
span.appendChild (lk);
+
lk.appendChild( make( HC.links.undo, true ) );
this.normalLinks.appendChild (span);
+
lk.title = HC.tooltips.undo;
 +
span.appendChild( make( ' ', true ) );
 +
span.appendChild( lk );
 +
this.normalLinks.appendChild( span );
 
this.undoLink = span;
 
this.undoLink = span;
if (!onUpload) {
+
if ( !onUpload ) {
 
try {
 
try {
this.catLink.style.backgroundColor = HotCat.bg_changed;
+
this.catLink.style.backgroundColor = HC.bg_changed;
} catch (ex) {}
+
} catch ( ex ) {}
 
}
 
}
 
}
 
}
if (this.upDownLinks) this.upDownLinks.style.display = this.lastSavedExists ? "" : 'none';
+
if ( this.upDownLinks ) this.upDownLinks.style.display = this.lastSavedExists ? '' : 'none';
this.linkSpan.style.display = "";
+
 
 +
this.linkSpan.style.display = '';
 
this.state = CategoryEditor.CHANGED;
 
this.state = CategoryEditor.CHANGED;
checkMultiInput ();
+
checkMultiInput();
forceRedraw ();
+
forceRedraw();
 
},
 
},
   −
commit : function (comment) {
+
commit: function () {
 
// Check again to catch problem cases after redirect resolution
 
// Check again to catch problem cases after redirect resolution
if (   (   this.currentCategory == this.originalCategory
+
if (
&& (this.currentKey == this.originalKey
+
(
|| this.currentKey === null && this.originalKey.length === 0
+
this.currentCategory === this.originalCategory &&
  )
+
(
  )
+
this.currentKey === this.originalKey ||
|| conf.wgNamespaceNumber == 14 && this.currentCategory == conf.wgTitle
+
this.currentKey === null && !this.originalKey.length
|| HotCat.blacklist && HotCat.blacklist.test (this.currentCategory)
+
)
  )
+
) ||
{
+
conf.wgNamespaceNumber === 14 && this.currentCategory === conf.wgTitle ||
this.cancel ();
+
HC.blacklist && HC.blacklist.test( this.currentCategory )
 +
) {
 +
this.cancel();
 
return;
 
return;
 
}
 
}
if (commitButton || onUpload) {
+
this.close();
this.close ();
+
if ( !commitButton && !onUpload ) {
} else {
  −
this.close ();
   
var self = this;
 
var self = this;
initiateEdit (function (failure) {performChanges (failure, self);}, function (msg) {alert (msg);});
+
initiateEdit( function ( failure ) {
 +
performChanges( failure, self );
 +
}, function ( msg ) {
 +
alert( msg );
 +
} );
 
}
 
}
 
},
 
},
   −
remove : function (evt) {
+
remove: function ( evt ) {
this.doRemove (evtKeys (evt) & 1);
+
// eslint-disable-next-line no-bitwise
return evtKill (evt);
+
this.doRemove( evtKeys( evt ) & 1 );
 +
return evtKill( evt );
 
},
 
},
   −
doRemove : function (noCommit) {
+
doRemove: function ( noCommit ) {
if (this.isAddCategory) { // Empty input on adding a new category
+
if ( this.isAddCategory ) { // Empty input on adding a new category
this.cancel ();
+
this.cancel();
 
return;
 
return;
 
}
 
}
if (!commitButton && !onUpload) {
+
if ( !commitButton && !onUpload ) {
for (var i = 0; i < editors.length; i++) {
+
for ( var i = 0; i < editors.length; i++ ) {
if (editors[i].state != CategoryEditor.UNCHANGED) {
+
if ( editors[ i ].state !== CategoryEditor.UNCHANGED ) {
 
setMultiInput();
 
setMultiInput();
 
break;
 
break;
Line 1,918: Line 2,020:  
}
 
}
 
}
 
}
if (commitButton) {
+
if ( commitButton ) {
this.catLink.title = "";
+
this.catLink.title = '';
 
this.catLink.style.cssText += '; text-decoration : line-through !important;';
 
this.catLink.style.cssText += '; text-decoration : line-through !important;';
 
try {
 
try {
this.catLink.style.backgroundColor = HotCat.bg_changed;
+
this.catLink.style.backgroundColor = HC.bg_changed;
} catch (ex) {}
+
} catch ( ex ) {}
 
this.originalState = this.state;
 
this.originalState = this.state;
 
this.state = CategoryEditor.DELETED;
 
this.state = CategoryEditor.DELETED;
 
this.normalLinks.style.display = 'none';
 
this.normalLinks.style.display = 'none';
this.undelLink.style.display = "";
+
this.undelLink.style.display = '';
checkMultiInput ();
+
checkMultiInput();
 
} else {
 
} else {
if (onUpload) {
+
if ( onUpload ) {
 
// Remove this editor completely
 
// Remove this editor completely
this.removeEditor ();
+
this.removeEditor();
 
} else {
 
} else {
 
this.originalState = this.state;
 
this.originalState = this.state;
 
this.state = CategoryEditor.DELETED;
 
this.state = CategoryEditor.DELETED;
this.noCommit = noCommit || HotCat.del_needs_diff;
+
this.noCommit = noCommit || HC.del_needs_diff;
 
var self = this;
 
var self = this;
initiateEdit (function (failure) {performChanges (failure, self);}, function (msg) {self.state = self.originalState; alert (msg);});
+
initiateEdit(
 +
function ( failure ) {
 +
performChanges( failure, self );
 +
},
 +
function ( msg ) {
 +
self.state = self.originalState;
 +
alert( msg );
 +
} );
 
}
 
}
 
}
 
}
 
},
 
},
   −
restore : function (evt) {
+
restore: function ( evt ) {
 
// Can occur only if we do have a commit button and are not on the upload form
 
// Can occur only if we do have a commit button and are not on the upload form
this.catLink.title = this.currentKey || "";
+
this.catLink.title = this.currentKey || '';
this.catLink.style.textDecoration = "";
+
this.catLink.style.textDecoration = '';
 
this.state = this.originalState;
 
this.state = this.originalState;
if (this.state == CategoryEditor.UNCHANGED) {
+
if ( this.state === CategoryEditor.UNCHANGED ) {
 
this.catLink.style.backgroundColor = 'transparent';
 
this.catLink.style.backgroundColor = 'transparent';
 
} else {
 
} else {
 
try {
 
try {
this.catLink.style.backgroundColor = HotCat.bg_changed;
+
this.catLink.style.backgroundColor = HC.bg_changed;
} catch (ex) {}
+
} catch ( ex ) {}
 
}
 
}
this.normalLinks.style.display = "";
+
this.normalLinks.style.display = '';
 
this.undelLink.style.display = 'none';
 
this.undelLink.style.display = 'none';
checkMultiInput ();
+
checkMultiInput();
return evtKill (evt);
+
return evtKill( evt );
 
},
 
},
    
// Internal operations
 
// Internal operations
   −
selectEngine : function (engineName) {
+
selectEngine: function ( engineName ) {
if (!this.engineSelector) return;
+
if ( !this.engineSelector ) return;
for (var i = 0; i < this.engineSelector.options.length; i++) {
+
for ( var i = 0; i < this.engineSelector.options.length; i++ ) this.engineSelector.options[ i ].selected = this.engineSelector.options[ i ].value === engineName;
this.engineSelector.options[i].selected = this.engineSelector.options[i].value == engineName;
  −
}
   
},
 
},
   −
sanitizeInput : function () {
+
sanitizeInput: function () {
var v = this.text.value || "";
+
var v = this.text.value || '';
v = v.replace(/^(\s|_)+/, ""); // Trim leading blanks and underscores
+
v = v.replace( /^(\s|_)+/, '' ); // Trim leading blanks and underscores
var re = new RegExp ('^(' + HotCat.category_regexp + '):');
+
var re = new RegExp( '^(' + HC.category_regexp + '):' );
if (re.test (v)) {
+
if ( re.test( v ) ) v = v.substring( v.indexOf( ':' ) + 1 ).replace( /^(\s|_)+/, '' );
v = v.substring (v.indexOf (':') + 1).replace(/^(\s|_)+/, "");
+
v = v.replace(/\u200E$/, ''); // Trim ending left-to-right mark
}
+
if ( HC.capitalizePageNames ) v = capitalize( v );
if (HotCat.capitalizePageNames) v = capitalize (v);
+
 
// Only update the input field if there is a difference. IE8 appears to reset the selection
+
// Only update the input field if there is a difference. Various browsers otherwise
// and place the cursor at the front upon reset, which makes our autocompletetion become a
+
// reset the selection and cursor position after each value re-assignment.
// nuisance. FF and IE6 don't seem to have this problem.
+
if ( this.text.value !== null && this.text.value !== v ) this.text.value = v;
if (this.text.value !== null && this.text.value != v)
  −
this.text.value = v;
   
},
 
},
   −
makeCall : function (url, callbackObj, engine, queryKey, cleanKey) {
+
makeCall: function ( url, callbackObj, engine, queryKey, cleanKey ) {
var cb = callbackObj;
+
var cb = callbackObj,
var e = engine;
+
e = engine,
var v = queryKey;
+
v = queryKey,
var z = cleanKey;
+
z = cleanKey,
var thisObj = this;
+
thisObj = this;
   −
function done () {
+
function done() {
 
cb.callsMade++;
 
cb.callsMade++;
if (cb.callsMade === cb.nofCalls) {
+
if ( cb.callsMade === cb.nofCalls ) {
if (cb.exists) cb.allTitles.exists = true;
+
if ( cb.exists ) cb.allTitles.exists = true;
if (cb.normalized) cb.allTitles.normalized = cb.normalized;
+
 
if (!cb.dontCache && !suggestionConfigs[cb.engineName].cache[z]) {
+
if ( cb.normalized ) cb.allTitles.normalized = cb.normalized;
suggestionConfigs[cb.engineName].cache[z] = cb.allTitles;
+
 
}
+
if ( !cb.dontCache && !suggestionConfigs[ cb.engineName ].cache[ z ] ) suggestionConfigs[ cb.engineName ].cache[ z ] = cb.allTitles;
 +
 
 
thisObj.text.readOnly = false;
 
thisObj.text.readOnly = false;
if (!cb.cancelled) thisObj.showSuggestions (cb.allTitles, cb.noCompletion, v, cb.engineName);
+
if ( !cb.cancelled ) thisObj.showSuggestions( cb.allTitles, cb.noCompletion, v, cb.engineName );
if (cb === thisObj.callbackObj) thisObj.callbackObj = null;
+
 
delete cb;
+
if ( cb === thisObj.callbackObj ) thisObj.callbackObj = null;
 +
 
 +
cb = undefined;
 
}
 
}
 
}
 
}
   −
getJSON ({
+
$.getJSON( url, function ( json ) {
  uri : url
+
var titles = e.handler( json, z );
,success : function (json) {
+
if ( titles && titles.length ) {
var titles = e.handler (json, z);
+
if ( cb.allTitles === null ) cb.allTitles = titles; else cb.allTitles = cb.allTitles.concat( titles );
if (titles && titles.length > 0) {
+
if ( titles.exists ) cb.exists = true;
if (cb.allTitles === null) {
+
if ( titles.normalized ) cb.normalized = titles.normalized;
cb.allTitles = titles;
  −
} else {
  −
cb.allTitles = cb.allTitles.concat (titles);
  −
}
  −
if (titles.exists) cb.exists = true;
  −
if (titles.normalized) cb.normalized = titles.normalized;
   
}
 
}
 
done();
 
done();
  }
+
} ).fail( function ( req ) {
,error : function (req) {if (!req) noSuggestions = true; cb.dontCache = true; done(); }
+
if ( !req ) noSuggestions = true;
});
+
cb.dontCache = true;
 +
done();
 +
} );
 
},
 
},
   −
callbackObj : null,
+
callbackObj: null,
   −
textchange : function (dont_autocomplete, force) {
+
textchange: function ( dont_autocomplete, force ) {
 
// Hide all other lists
 
// Hide all other lists
makeActive (this);
+
makeActive( this );
 
// Get input value, omit sort key, if any
 
// Get input value, omit sort key, if any
this.sanitizeInput ();
+
this.sanitizeInput();
 
var v = this.text.value;
 
var v = this.text.value;
 
// Disregard anything after a pipe.
 
// Disregard anything after a pipe.
var pipe = v.indexOf ('|');
+
var pipe = v.indexOf( '|' );
if (pipe >= 0) {
+
if ( pipe >= 0 ) {
this.currentKey = v.substring (pipe+1);
+
this.currentKey = v.substring( pipe + 1 );
v = v.substring (0, pipe);
+
v = v.substring( 0, pipe );
 
} else {
 
} else {
 
this.currentKey = null;
 
this.currentKey = null;
 
}
 
}
if (this.lastInput == v && !force) return; // No change
+
if ( this.lastInput === v && !force ) return; // No change
if (this.lastInput != v) checkMultiInput ();
+
if ( this.lastInput !== v ) checkMultiInput();
 +
 
 
this.lastInput = v;
 
this.lastInput = v;
 
this.lastRealInput = v;
 
this.lastRealInput = v;
    
// Mark blacklisted inputs.
 
// Mark blacklisted inputs.
this.ok.disabled = v.length > 0 && HotCat.blacklist && HotCat.blacklist.test (v);
+
this.ok.disabled = v.length && HC.blacklist && HC.blacklist.test( v );
 +
 
 +
if ( noSuggestions ) {
 +
// No Ajax: just make sure the list is hidden
 +
if ( this.list ) this.list.style.display = 'none';
 +
if ( this.engineSelector ) this.engineSelector.style.display = 'none';
 +
if ( this.icon ) this.icon.style.display = 'none';
 +
return;
 +
}
   −
if (noSuggestions) {
+
if ( !v.length ) {
// No Ajax: just make sure the list is hidden
+
this.showSuggestions( [] );
if (this.list) this.list.style.display = 'none';
+
return;
if (this.engineSelector) this.engineSelector.style.display = 'none';
+
}
if (this.icon) this.icon.style.display = 'none';
+
var cleanKey = v.replace( /[\u200E\u200F\u202A-\u202E]/g, '' ).replace( wikiTextBlankRE, ' ' );
 +
cleanKey = replaceShortcuts( cleanKey, HC.shortcuts );
 +
cleanKey = cleanKey.replace( /^\s+|\s+$/g, '' );
 +
if ( !cleanKey.length ) {
 +
this.showSuggestions( [] );
 
return;
 
return;
 
}
 
}
   −
if (v.length === 0) { this.showSuggestions([]); return; }
+
if ( this.callbackObj ) this.callbackObj.cancelled = true;
var cleanKey = v.replace(/[\u200E\u200F\u202A-\u202E]/g, "").replace(wikiTextBlankRE, ' ');
  −
cleanKey = replaceShortcuts(cleanKey, HotCat.shortcuts);
  −
cleanKey = cleanKey.replace(/^\s+|\s+$/g, '');
  −
if (cleanKey.length === 0) { this.showSuggestions([]); return; }
     −
if (this.callbackObj) this.callbackObj.cancelled = true;
+
var engineName = suggestionConfigs[ this.engine ] ? this.engine : 'combined';
var engineName = suggestionConfigs[this.engine] ? this.engine : 'combined';
     −
dont_autocomplete = dont_autocomplete || suggestionConfigs[engineName].noCompletion;
+
dont_autocomplete = dont_autocomplete || suggestionConfigs[ engineName ].noCompletion;
if (suggestionConfigs[engineName].cache[cleanKey]) {
+
if ( suggestionConfigs[ engineName ].cache[ cleanKey ] ) {
this.showSuggestions (suggestionConfigs[engineName].cache[cleanKey], dont_autocomplete, v, engineName);
+
this.showSuggestions( suggestionConfigs[ engineName ].cache[ cleanKey ], dont_autocomplete, v, engineName );
 
return;
 
return;
 
}
 
}
   −
var engines = suggestionConfigs[engineName].engines;
+
var engines = suggestionConfigs[ engineName ].engines;
this.callbackObj =
+
this.callbackObj = {
{allTitles: null, callsMade: 0, nofCalls: engines.length, noCompletion: dont_autocomplete, engineName: engineName};
+
allTitles: null,
this.makeCalls (engines, this.callbackObj, v, cleanKey);
+
callsMade: 0,
 +
nofCalls: engines.length,
 +
noCompletion: dont_autocomplete,
 +
engineName: engineName
 +
};
 +
this.makeCalls( engines, this.callbackObj, v, cleanKey );
 
},
 
},
   −
makeCalls : function (engines, cb, v, cleanKey) {
+
makeCalls: function ( engines, cb, v, cleanKey ) {
for (var j = 0; j < engines.length; j++) {
+
for ( var j = 0; j < engines.length; j++ ) {
var engine = suggestionEngines[engines[j]];
+
var engine = suggestionEngines[ engines[ j ] ];
var url = conf.wgServer + conf.wgScriptPath + engine.uri.replace (/\$1/g, encodeURIComponent (cleanKey));
+
var url = conf.wgServer + conf.wgScriptPath + engine.uri.replace( /\$1/g, encodeURIComponent( cleanKey ) );
this.makeCall (url, cb, engine, v, cleanKey);
+
this.makeCall( url, cb, engine, v, cleanKey );
 
}
 
}
 
},
 
},
   −
showSuggestions : function (titles, dontAutocomplete, queryKey, engineName) {
+
showSuggestions: function ( titles, dontAutocomplete, queryKey, engineName ) {
 
this.text.readOnly = false;
 
this.text.readOnly = false;
 
this.dab = null;
 
this.dab = null;
 
this.showsList = false;
 
this.showsList = false;
if (!this.list) return;
+
if ( !this.list ) return;
if (noSuggestions) {
+
if ( noSuggestions ) {
if (this.list) this.list.style.display = 'none';
+
if ( this.list ) this.list.style.display = 'none';
if (this.engineSelector) this.engineSelector.style.display = 'none';
+
 
if (this.icon) this.icon.style.display = 'none';
+
if ( this.engineSelector ) this.engineSelector.style.display = 'none';
 +
 
 +
if ( this.icon ) this.icon.style.display = 'none';
 +
 
 
this.inputExists = true; // Default...
 
this.inputExists = true; // Default...
 
return;
 
return;
 
}
 
}
 
this.engineName = engineName;
 
this.engineName = engineName;
if (engineName) {
+
if ( engineName ) {
if (!this.engineSelector) this.engineName = null;
+
if ( !this.engineSelector ) this.engineName = null;
 
} else {
 
} else {
if (this.engineSelector) this.engineSelector.style.display = 'none';
+
if ( this.engineSelector ) this.engineSelector.style.display = 'none';
 
}
 
}
if (queryKey) {
+
if ( queryKey ) {
if (this.lastInput.indexOf (queryKey) !== 0) return;
+
if ( this.lastInput.indexOf( queryKey ) ) return;
if (this.lastQuery && this.lastInput.indexOf (this.lastQuery) === 0 && this.lastQuery.length > queryKey.length)
+
if ( this.lastQuery && this.lastInput.indexOf( this.lastQuery ) === 0 && this.lastQuery.length > queryKey.length ) return;
return;
   
}
 
}
 
this.lastQuery = queryKey;
 
this.lastQuery = queryKey;
    
// Get current input text
 
// Get current input text
var v = this.text.value.split('|');
+
var v = this.text.value.split( '|' );
var key = v.length > 1 ? '|' + v[1] : "";
+
var key = v.length > 1 ? '|' + v[ 1 ] : '';
v = (HotCat.capitalizePageNames ? capitalize (v[0]) : v[0]);
+
v = ( HC.capitalizePageNames ? capitalize( v[ 0 ] ) : v[ 0 ] );
 
var vNormalized = v;
 
var vNormalized = v;
 
var knownToExist = titles && titles.exists;
 
var knownToExist = titles && titles.exists;
 
var i;
 
var i;
if (titles) {
+
if ( titles ) {
if (titles.normalized && v.indexOf(queryKey) === 0) {
+
if ( titles.normalized && v.indexOf( queryKey ) === 0 ) {
// We got back a different normalization than what is in the input field
+
// We got back a different normalization than what is in the input field
vNormalized = titles.normalized + v.substring(queryKey.length);
+
vNormalized = titles.normalized + v.substring( queryKey.length );
 
}
 
}
var vLow = vNormalized.toLowerCase ();
+
var vLow = vNormalized.toLowerCase();
 
// Strip blacklisted categories
 
// Strip blacklisted categories
if (HotCat.blacklist) {
+
if ( HC.blacklist ) {
for (i = 0; i < titles.length; i++) {
+
for ( i = 0; i < titles.length; i++ ) {
if (HotCat.blacklist.test (titles[i])) {
+
if ( HC.blacklist.test( titles[ i ] ) ) {
titles.splice(i, 1);
+
titles.splice( i, 1 );
 
i--;
 
i--;
 
}
 
}
 
}
 
}
 
}
 
}
titles.sort (
+
titles.sort(
function (a, b) {
+
function ( a, b ) {
if (a == b) return 0;
+
if ( a === b ) return 0;
if (a.indexOf (b) === 0) return 1; // a begins with b: a > b
+
 
if (b.indexOf (a) === 0) return -1; // b begins with a: a < b
+
if ( a.indexOf( b ) === 0 ) return 1;
 +
// a begins with b: a > b
 +
if ( b.indexOf( a ) === 0 ) return -1;
 +
// b begins with a: a < b
 
// Opensearch may return stuff not beginning with the search prefix!
 
// Opensearch may return stuff not beginning with the search prefix!
var prefixMatchA = (a.indexOf (vNormalized) === 0 ? 1 : 0);
+
var prefixMatchA = ( a.indexOf( vNormalized ) === 0 ? 1 : 0 );
var prefixMatchB = (b.indexOf (vNormalized) === 0 ? 1 : 0);
+
var prefixMatchB = ( b.indexOf( vNormalized ) === 0 ? 1 : 0 );
if (prefixMatchA != prefixMatchB) return prefixMatchB - prefixMatchA;
+
if ( prefixMatchA !== prefixMatchB ) return prefixMatchB - prefixMatchA;
 +
 
 
// Case-insensitive prefix match!
 
// Case-insensitive prefix match!
var aLow = a.toLowerCase(), bLow = b.toLowerCase();
+
var aLow = a.toLowerCase(),
prefixMatchA = (aLow.indexOf (vLow) === 0 ? 1 : 0);
+
bLow = b.toLowerCase();
prefixMatchB = (bLow.indexOf (vLow) === 0 ? 1 : 0);
+
prefixMatchA = ( aLow.indexOf( vLow ) === 0 ? 1 : 0 );
if (prefixMatchA != prefixMatchB) return prefixMatchB - prefixMatchA;
+
prefixMatchB = ( bLow.indexOf( vLow ) === 0 ? 1 : 0 );
if (a < b) return -1;
+
if ( prefixMatchA !== prefixMatchB ) return prefixMatchB - prefixMatchA;
if (b < a) return 1;
+
 
 +
if ( a < b ) return -1;
 +
 
 +
if ( b < a ) return 1;
 +
 
 
return 0;
 
return 0;
}
+
} );
);
   
// Remove duplicates and self-references
 
// Remove duplicates and self-references
for (i = 0; i < titles.length; i++) {
+
for ( i = 0; i < titles.length; i++ ) {
if (   i+1 < titles.length && titles[i] == titles[i+1]
+
if (
|| conf.wgNamespaceNumber == 14 && titles[i] == conf.wgTitle
+
i + 1 < titles.length && titles[ i ] === titles[ i + 1 ] ||
  )
+
conf.wgNamespaceNumber === 14 && titles[ i ] === conf.wgTitle
{
+
) {
titles.splice (i, 1);
+
titles.splice( i, 1 );
 
i--;
 
i--;
 
}
 
}
 
}
 
}
 
}
 
}
if (!titles || titles.length === 0) {
+
if ( !titles || !titles.length ) {
if (this.list) this.list.style.display = 'none';
+
if ( this.list ) this.list.style.display = 'none';
if (this.engineSelector) this.engineSelector.style.display = 'none';
+
 
if (engineName && suggestionConfigs[engineName] && !suggestionConfigs[engineName].temp) {
+
if ( this.engineSelector ) this.engineSelector.style.display = 'none';
if (this.icon) this.icon.src = armorUri(HotCat.existsNo);
+
 
 +
if ( engineName && suggestionConfigs[ engineName ] && !suggestionConfigs[ engineName ].temp ) {
 +
if ( this.icon ) this.icon.src = HC.existsNo;
 +
 
 
this.inputExists = false;
 
this.inputExists = false;
 
}
 
}
Line 2,174: Line 2,303:  
}
 
}
   −
var firstTitle = titles[0];
+
var firstTitle = titles[ 0 ];
var completed = this.autoComplete (firstTitle, v, vNormalized, key, dontAutocomplete);
+
var completed = this.autoComplete( firstTitle, v, vNormalized, key, dontAutocomplete );
var existing = completed || knownToExist || firstTitle == replaceShortcuts(v, HotCat.shortcuts);
+
var existing = completed || knownToExist || firstTitle === replaceShortcuts( v, HC.shortcuts );
if (engineName && suggestionConfigs[engineName] && !suggestionConfigs[engineName].temp) {
+
if ( engineName && suggestionConfigs[ engineName ] && !suggestionConfigs[ engineName ].temp ) {
this.icon.src = armorUri(existing ? HotCat.existsYes : HotCat.existsNo);
+
this.icon.src = ( existing ? HC.existsYes : HC.existsNo );
 
this.inputExists = existing;
 
this.inputExists = existing;
 
}
 
}
if (completed) {
+
if ( completed ) {
 
this.lastInput = firstTitle;
 
this.lastInput = firstTitle;
if (titles.length === 1) {
+
if ( titles.length === 1 ) {
 
this.list.style.display = 'none';
 
this.list.style.display = 'none';
if (this.engineSelector) this.engineSelector.style.display = 'none';
+
if ( this.engineSelector ) this.engineSelector.style.display = 'none';
 +
 
 
return;
 
return;
 
}
 
}
 
}
 
}
 
// (Re-)fill the list
 
// (Re-)fill the list
while (this.list.firstChild) this.list.removeChild (this.list.firstChild);
+
while ( this.list.firstChild ) this.list.removeChild( this.list.firstChild );
for (i = 0 ; i < titles.length ; i++) {
+
 
var opt = make ('option') ;
+
for ( i = 0; i < titles.length; i++ ) {
opt.appendChild (make (titles[i], true));
+
var opt = make( 'option' );
opt.selected = completed && (i === 0);
+
opt.appendChild( make( titles[ i ], true ) );
this.list.appendChild (opt);
+
opt.selected = completed && ( i === 0 );
 +
this.list.appendChild( opt );
 
}
 
}
 
this.displayList();
 
this.displayList();
 
},
 
},
   −
displayList : function () {
+
displayList: function () {
 
this.showsList = true;
 
this.showsList = true;
if (!this.is_active) {
+
if ( !this.is_active ) {
 
this.list.style.display = 'none';
 
this.list.style.display = 'none';
if (this.engineSelector) this.engineSelector.style.display = 'none';
+
if ( this.engineSelector ) this.engineSelector.style.display = 'none';
 +
 
 
return;
 
return;
 
}
 
}
var nofItems = (this.list.options.length > HotCat.list_size ? HotCat.list_size : this.list.options.length);
+
var nofItems = ( this.list.options.length > HC.listSize ? HC.listSize : this.list.options.length );
if (nofItems <= 1) nofItems = 2;
+
if ( nofItems <= 1 ) nofItems = 2;
 +
 
 
this.list.size = nofItems;
 
this.list.size = nofItems;
this.list.style.align   = is_rtl ? 'right' : 'left';
+
this.list.style.align = is_rtl ? 'right' : 'left';
this.list.style.zIndex   = 5;
+
this.list.style.zIndex = 5;
 
this.list.style.position = 'absolute';
 
this.list.style.position = 'absolute';
 
// Compute initial list position. First the height.
 
// Compute initial list position. First the height.
 
var anchor = is_rtl ? 'right' : 'left';
 
var anchor = is_rtl ? 'right' : 'left';
 
var listh = 0;
 
var listh = 0;
if (this.list.style.display == 'none') {
+
if ( this.list.style.display === 'none' ) {
 
// Off-screen display to get the height
 
// Off-screen display to get the height
 
this.list.style.top = this.text.offsetTop + 'px';
 
this.list.style.top = this.text.offsetTop + 'px';
this.list.style[anchor] = '-10000px';
+
this.list.style[ anchor ] = '-10000px';
this.list.style.display = "";
+
this.list.style.display = '';
 
listh = this.list.offsetHeight;
 
listh = this.list.offsetHeight;
 
this.list.style.display = 'none';
 
this.list.style.display = 'none';
Line 2,228: Line 2,361:  
// Approximate calculation of maximum list size
 
// Approximate calculation of maximum list size
 
var maxListHeight = listh;
 
var maxListHeight = listh;
if (nofItems < HotCat.list_size) maxListHeight = (listh / nofItems) * HotCat.list_size;
+
if ( nofItems < HC.listSize ) maxListHeight = ( listh / nofItems ) * HC.listSize;
   −
function viewport (what) {
+
function viewport( what ) {
if (is_webkit && !document.evaluate)
+
if ( is_webkit && !document.evaluate ) {
return window['inner' + what]; // Safari < 3.0
+
// Safari < 3.0
 +
return window[ 'inner' + what ];
 +
}
 
var s = 'client' + what;
 
var s = 'client' + what;
if (window.opera) return document.body[s];
+
if ( window.opera ) return document.body[ s ];
return (document.documentElement ? document.documentElement[s] : 0)
+
 
|| document.body[s] || 0;
+
return ( document.documentElement ? document.documentElement[ s ] : 0 ) || document.body[ s ] || 0;
 
}
 
}
function scroll_offset (what) {
+
function scroll_offset( what ) {
 
var s = 'scroll' + what;
 
var s = 'scroll' + what;
var result = (document.documentElement ? document.documentElement[s] : 0)
+
var result = ( document.documentElement ? document.documentElement[ s ] : 0 ) || document.body[ s ] || 0;
|| document.body[s] || 0;
+
if ( is_rtl && what === 'Left' ) {
if (is_rtl && what == 'Left') {
   
// RTL inconsistencies.
 
// RTL inconsistencies.
 
// FF: 0 at the far right, then increasingly negative values.
 
// FF: 0 at the far right, then increasingly negative values.
 
// IE >= 8: 0 at the far right, then increasingly positive values.
 
// IE >= 8: 0 at the far right, then increasingly positive values.
 
// Webkit: scrollWidth - clientWidth at the far right, then down to zero.
 
// Webkit: scrollWidth - clientWidth at the far right, then down to zero.
// IE 7: like webkit; IE6: disabled in RTL anyway since too many problems.
   
// Opera: don't know...
 
// Opera: don't know...
if (result < 0) result = - result;
+
if ( result < 0 ) result = -result;
if (!is_webkit && !is_ie_lt8) {
+
 
result = scroll_offset('Width') - viewport('Width') - result;
+
if ( !is_webkit ) result = scroll_offset( 'Width' ) - viewport( 'Width' ) - result;
}
+
 
 
// Now all have webkit behavior, i.e. zero if at the leftmost edge.
 
// Now all have webkit behavior, i.e. zero if at the leftmost edge.
 
}
 
}
 
return result;
 
return result;
 
}
 
}
function position (node) {
+
function position( node ) {
 
// Stripped-down simplified position function. It's good enough for our purposes.
 
// Stripped-down simplified position function. It's good enough for our purposes.
if (node.getBoundingClientRect) {
+
if ( node.getBoundingClientRect ) {
var box   = node.getBoundingClientRect ();
+
var box = node.getBoundingClientRect();
return { x : Math.round (box.left + scroll_offset ('Left'))
+
return {
,y : Math.round (box.top + scroll_offset ('Top'))
+
x: Math.round( box.left + scroll_offset( 'Left' ) ),
  };
+
y: Math.round( box.top + scroll_offset( 'Top' ) )
 +
};
 
}
 
}
var t = 0, l = 0;
+
var t = 0,
 +
l = 0;
 
do {
 
do {
t = t + (node.offsetTop || 0);
+
t += ( node.offsetTop || 0 );
l = l + (node.offsetLeft || 0);
+
l += ( node.offsetLeft || 0 );
 
node = node.offsetParent;
 
node = node.offsetParent;
} while (node);
+
} while ( node );
return {x : l, y : t};
+
return {
 +
x: l,
 +
y: t
 +
};
 
}
 
}
   −
var textPos = position (this.text);
+
var textPos = position( this.text ),
var nl = 0;
+
nl = 0,
var nt = 0;
+
nt = 0,
var offset = 0;
+
offset = 0,
// Opera 9.5 somehow has offsetWidth = 0 here?? Use the next best value...
+
// Opera 9.5 somehow has offsetWidth = 0 here?? Use the next best value...
var textBoxWidth = this.text.offsetWidth || this.text.clientWidth;
+
textBoxWidth = this.text.offsetWidth || this.text.clientWidth;
if (this.engineName) {
+
if ( this.engineName ) {
 
this.engineSelector.style.zIndex = 5;
 
this.engineSelector.style.zIndex = 5;
 
this.engineSelector.style.position = 'absolute';
 
this.engineSelector.style.position = 'absolute';
 
this.engineSelector.style.width = textBoxWidth + 'px';
 
this.engineSelector.style.width = textBoxWidth + 'px';
 
// Figure out the height of this selector: display it off-screen, then hide it again.
 
// Figure out the height of this selector: display it off-screen, then hide it again.
if (this.engineSelector.style.display == 'none') {
+
if ( this.engineSelector.style.display === 'none' ) {
this.engineSelector.style[anchor] = '-10000px';
+
this.engineSelector.style[ anchor ] = '-10000px';
this.engineSelector.style.top = '0px';
+
this.engineSelector.style.top = '0';
this.engineSelector.style.display = "";
+
this.engineSelector.style.display = '';
 
offset = this.engineSelector.offsetHeight;
 
offset = this.engineSelector.offsetHeight;
 
this.engineSelector.style.display = 'none';
 
this.engineSelector.style.display = 'none';
Line 2,294: Line 2,432:  
offset = this.engineSelector.offsetHeight;
 
offset = this.engineSelector.offsetHeight;
 
}
 
}
this.engineSelector.style[anchor] = nl + 'px';
+
this.engineSelector.style[ anchor ] = nl + 'px';
 
}
 
}
if (textPos.y < maxListHeight + offset + 1) {
+
if ( textPos.y < maxListHeight + offset + 1 ) {
// The list might extend beyond the upper border of the page. Let's avoid that by placing it
+
// The list might extend beyond the upper border of the page. Let's avoid that by placing it
// below the input text field.
+
// below the input text field.
 
nt = this.text.offsetHeight + offset + 1;
 
nt = this.text.offsetHeight + offset + 1;
if (this.engineName) this.engineSelector.style.top = this.text.offsetHeight + 'px';
+
if ( this.engineName ) this.engineSelector.style.top = this.text.offsetHeight + 'px';
 
} else {
 
} else {
nt = - listh - offset - 1;
+
nt = -listh - offset - 1;
if (this.engineName) this.engineSelector.style.top = - (offset + 1) + 'px';
+
if ( this.engineName ) this.engineSelector.style.top = -( offset + 1 ) + 'px';
 
}
 
}
 
this.list.style.top = nt + 'px';
 
this.list.style.top = nt + 'px';
this.list.style.width = ""; // No fixed width (yet)
+
this.list.style.width = ''; // No fixed width (yet)
this.list.style[anchor] = nl + 'px';
+
this.list.style[ anchor ] = nl + 'px';
if (this.engineName) {
+
if ( this.engineName ) {
this.selectEngine (this.engineName);
+
this.selectEngine( this.engineName );
this.engineSelector.style.display = "";
+
this.engineSelector.style.display = '';
 
}
 
}
 
this.list.style.display = 'block';
 
this.list.style.display = 'block';
 
// Set the width of the list
 
// Set the width of the list
if (this.list.offsetWidth < textBoxWidth ) {
+
if ( this.list.offsetWidth < textBoxWidth ) {
 
this.list.style.width = textBoxWidth + 'px';
 
this.list.style.width = textBoxWidth + 'px';
 
return;
 
return;
 
}
 
}
 
// If the list is wider than the textbox: make sure it fits horizontally into the browser window
 
// If the list is wider than the textbox: make sure it fits horizontally into the browser window
var scroll = scroll_offset ('Left');
+
var scroll = scroll_offset( 'Left' );
var view_w = viewport ('Width');
+
var view_w = viewport( 'Width' );
var w     = this.list.offsetWidth;
+
var w = this.list.offsetWidth;
var l_pos = position (this.list);
+
var l_pos = position( this.list );
var left   = l_pos.x;
+
var left = l_pos.x;
var right = left + w;
+
var right = left + w;
if (left < scroll || right > scroll + view_w) {
+
if ( left < scroll || right > scroll + view_w ) {
if (w > view_w) {
+
if ( w > view_w ) {
 
w = view_w;
 
w = view_w;
 
this.list.style.width = w + 'px';
 
this.list.style.width = w + 'px';
if (is_rtl) {
+
if ( is_rtl ) left = right - w; else right = left + w;
left = right - w;
  −
} else {
  −
right = left + w;
  −
}
   
}
 
}
 
var relative_offset = 0;
 
var relative_offset = 0;
if (left < scroll) {
+
if ( left < scroll ) relative_offset = scroll - left; else if ( right > scroll + view_w ) relative_offset = -( right - scroll - view_w );
relative_offset = scroll - left;
+
 
} else if (right > scroll + view_w) {
+
if ( is_rtl ) relative_offset = -relative_offset;
relative_offset = - (right - scroll - view_w);
+
 
}
+
if ( relative_offset ) this.list.style[ anchor ] = ( nl + relative_offset ) + 'px';
if (is_rtl) relative_offset = - relative_offset;
  −
if (relative_offset !== 0) {
  −
this.list.style[anchor] = (nl + relative_offset) + 'px';
  −
}
   
}
 
}
 
},
 
},
   −
autoComplete : function (newVal, actVal, normalizedActVal, key, dontModify) {
+
autoComplete: function ( newVal, actVal, normalizedActVal, key, dontModify ) {
if (newVal == actVal) return true;
+
if ( newVal === actVal ) return true;
if (dontModify || this.ime || !this.canSelect()) return false;
+
 
 +
if ( dontModify || this.ime || !this.canSelect() ) return false;
 +
 
 
// If we can't select properly or an IME composition is ongoing, autocompletion would be a major annoyance to the user.
 
// If we can't select properly or an IME composition is ongoing, autocompletion would be a major annoyance to the user.
if (newVal.indexOf (actVal) !== 0) {
+
if ( newVal.indexOf( actVal ) ) {
 
// Maybe it'll work with the normalized value (NFC)?
 
// Maybe it'll work with the normalized value (NFC)?
if (normalizedActVal && newVal.indexOf(normalizedActVal) === 0) {
+
if ( normalizedActVal && newVal.indexOf( normalizedActVal ) === 0 ) {
if (this.lastRealInput == actVal) this.lastRealInput = normalizedActVal;
+
if ( this.lastRealInput === actVal ) this.lastRealInput = normalizedActVal;
 +
 
 
actVal = normalizedActVal;
 
actVal = normalizedActVal;
 
} else {
 
} else {
Line 2,365: Line 2,498:  
this.text.focus();
 
this.text.focus();
 
this.text.value = newVal + key;
 
this.text.value = newVal + key;
this.setSelection (actVal.length, newVal.length);
+
this.setSelection( actVal.length, newVal.length );
 
return true;
 
return true;
 
},
 
},
   −
canSelect : function () {
+
canSelect: function () {
return this.text.setSelectionRange
+
return this.text.setSelectionRange ||
|| this.text.createTextRange
+
this.text.createTextRange ||
||   typeof this.text.selectionStart != 'undefined'
+
this.text.selectionStart !== undefined &&
  && typeof this.text.selectionEnd != 'undefined';
+
this.text.selectionEnd !== undefined;
 
},
 
},
   −
setSelection : function (from, to) {
+
setSelection: function ( from, to ) {
 
// this.text must be focused (at least on IE)
 
// this.text must be focused (at least on IE)
if (!this.text.value) return;
+
if ( !this.text.value ) return;
if (this.text.setSelectionRange) {   // e.g. khtml
+
if ( this.text.setSelectionRange ) { // e.g. khtml
this.text.setSelectionRange (from, to);
+
this.text.setSelectionRange( from, to );
} else if (typeof this.text.selectionStart != 'undefined') {
+
} else if ( this.text.selectionStart !== undefined ) {
if (from > this.text.selectionStart) {
+
if ( from > this.text.selectionStart ) {
this.text.selectionEnd   = to;
+
this.text.selectionEnd = to;
 
this.text.selectionStart = from;
 
this.text.selectionStart = from;
 
} else {
 
} else {
 
this.text.selectionStart = from;
 
this.text.selectionStart = from;
this.text.selectionEnd   = to;
+
this.text.selectionEnd = to;
 
}
 
}
} else if (this.text.createTextRange) { // IE
+
} else if ( this.text.createTextRange ) { // IE
 
var new_selection = this.text.createTextRange();
 
var new_selection = this.text.createTextRange();
new_selection.move ('character', from);
+
new_selection.move( 'character', from );
new_selection.moveEnd ('character', to - from);
+
new_selection.moveEnd( 'character', to - from );
 
new_selection.select();
 
new_selection.select();
 
}
 
}
 
},
 
},
   −
getSelection : function () {
+
getSelection: function () {
var from = 0, to = 0;
+
var from = 0,
 +
to = 0;
 
// this.text must be focused (at least on IE)
 
// this.text must be focused (at least on IE)
if (!this.text.value) {
+
if ( !this.text.value ) {
 
// No text.
 
// No text.
} else if (typeof this.text.selectionStart != 'undefined') {
+
} else if ( this.text.selectionStart !== undefined ) {
 
from = this.text.selectionStart;
 
from = this.text.selectionStart;
to   = this.text.selectionEnd;
+
to = this.text.selectionEnd;
} else if (document.selection && document.selection.createRange) { // IE
+
} else if ( document.selection && document.selection.createRange ) { // IE
 
var rng = document.selection.createRange().duplicate();
 
var rng = document.selection.createRange().duplicate();
if (rng.parentElement() === this.text) {
+
if ( rng.parentElement() === this.text ) {
 
try {
 
try {
 
var textRng = this.text.createTextRange();
 
var textRng = this.text.createTextRange();
textRng.move('character', 0);
+
textRng.move( 'character', 0 );
textRng.setEndPoint('EndToEnd', rng);
+
textRng.setEndPoint( 'EndToEnd', rng );
 
// We're in a single-line input box: no need to care about IE's strange
 
// We're in a single-line input box: no need to care about IE's strange
 
// handling of line ends
 
// handling of line ends
 
to = textRng.text.length;
 
to = textRng.text.length;
textRng.setEndPoint('EndToStart', rng);
+
textRng.setEndPoint( 'EndToStart', rng );
 
from = textRng.text.length;
 
from = textRng.text.length;
} catch (notFocused) {
+
} catch ( notFocused ) {
from = this.text.value.length; to = from; // At end of text
+
from = this.text.value.length;
 +
to = from; // At end of text
 
}
 
}
 
}
 
}
 
}
 
}
return {start: from, end: to};
+
return {
 +
start: from,
 +
end: to
 +
};
 
},
 
},
   −
saveView : function (evt) {
+
saveView: function () {
this.lastSelection = this.getSelection ();
+
this.lastSelection = this.getSelection();
 
},
 
},
   −
processKey : function (evt) {
+
processKey: function ( evt ) {
 
var dir = 0;
 
var dir = 0;
switch (this.lastKey) {
+
switch ( this.lastKey ) {
case UP: dir = -1;
+
case UP:
case DOWN: if (dir === 0) dir = 1;  
+
dir = -1;
case PGUP: if (dir === 0) dir = -HotCat.list_size;
+
break;
case PGDOWN: if (dir === 0) dir = HotCat.list_size;
+
case DOWN:
if (this.list.style.display != 'none') {
+
dir = 1;
// List is visible, so there are suggestions
+
break;
this.highlightSuggestion (dir);
+
case PGUP:
// Kill the event, otherwise some browsers (e.g., Firefox) may additionally treat an up-arrow
+
dir = -HC.listSize;
// as "place the text cursor at the front", which we don't want here.
+
break;
return evtKill (evt);
+
case PGDOWN:
} else if (  this.keyCount <= 1
+
dir = HC.listSize;
  && (!this.callbackObj || this.callbackObj.callsMade == this.callbackObj.nofCalls)
  −
  )
  −
{
  −
// If no suggestions displayed, get them, unless we're already getting them.
  −
this.textchange ();
  −
}
   
break;
 
break;
 
case ESC: // Inhibit default behavior (revert to last real input in FF: we do that ourselves)
 
case ESC: // Inhibit default behavior (revert to last real input in FF: we do that ourselves)
return evtKill (evt);
+
return evtKill( evt );
 +
}
 +
if ( dir ) {
 +
if ( this.list.style.display !== 'none' ) {
 +
// List is visible, so there are suggestions
 +
this.highlightSuggestion( dir );
 +
// Kill the event, otherwise some browsers (e.g., Firefox) may additionally treat an up-arrow
 +
// as "place the text cursor at the front", which we don't want here.
 +
return evtKill( evt );
 +
} else if (
 +
this.keyCount <= 1 &&
 +
( !this.callbackObj || this.callbackObj.callsMade === this.callbackObj.nofCalls )
 +
) {
 +
// If no suggestions displayed, get them, unless we're already getting them.
 +
this.textchange();
 +
}
 
}
 
}
 
return true;
 
return true;
 
},
 
},
   −
highlightSuggestion : function (dir) {
+
highlightSuggestion: function ( dir ) {
if (noSuggestions || !this.list || this.list.style.display == 'none') return false;
+
if ( noSuggestions || !this.list || this.list.style.display === 'none' ) return false;
 +
 
 
var curr = this.list.selectedIndex;
 
var curr = this.list.selectedIndex;
var tgt = -1;
+
var tgt = -1;
if (dir === 0) {
+
if ( dir === 0 ) {
if (curr < 0 || curr >= this.list.options.length) return false;
+
if ( curr < 0 || curr >= this.list.options.length ) return false;
 +
 
 
tgt = curr;
 
tgt = curr;
 
} else {
 
} else {
 
tgt = curr < 0 ? 0 : curr + dir;
 
tgt = curr < 0 ? 0 : curr + dir;
 
tgt = tgt < 0 ? 0 : tgt;
 
tgt = tgt < 0 ? 0 : tgt;
if (tgt >= this.list.options.length) tgt = this.list.options.length - 1;
+
if ( tgt >= this.list.options.length ) tgt = this.list.options.length - 1;
 
}
 
}
if (tgt != curr || dir === 0) {
+
if ( tgt !== curr || dir === 0 ) {
if (curr >= 0 && curr < this.list.options.length && dir !== 0) this.list.options[curr].selected = false;
+
if ( curr >= 0 && curr < this.list.options.length && dir !== 0 ) this.list.options[ curr ].selected = false;
this.list.options[tgt].selected = true;
+
 
 +
this.list.options[ tgt ].selected = true;
 
// Get current input text
 
// Get current input text
var v = this.text.value.split('|');
+
var v = this.text.value.split( '|' );
var key = v.length > 1 ? '|' + v[1] : "";
+
var key = v.length > 1 ? '|' + v[ 1 ] : '';
var completed = this.autoComplete (this.list.options[tgt].text, this.lastRealInput, null, key, false);
+
var completed = this.autoComplete( this.list.options[ tgt ].text, this.lastRealInput, null, key, false );
if (!completed || this.list.options[tgt].text == this.lastRealInput) {
+
if ( !completed || this.list.options[ tgt ].text === this.lastRealInput ) {
this.text.value = this.list.options[tgt].text + key;
+
this.text.value = this.list.options[ tgt ].text + key;
if (this.canSelect()) this.setSelection (this.list.options[tgt].text.length, this.list.options[tgt].text.length);
+
if ( this.canSelect() ) this.setSelection( this.list.options[ tgt ].text.length, this.list.options[ tgt ].text.length );
 
}
 
}
this.lastInput = this.list.options[tgt].text;
+
this.lastInput = this.list.options[ tgt ].text;
 
this.inputExists = true; // Might be wrong if from a dab list...
 
this.inputExists = true; // Might be wrong if from a dab list...
if (this.icon) this.icon.src = armorUri(HotCat.existsYes);
+
if ( this.icon ) this.icon.src = HC.existsYes;
 +
 
 
this.state = CategoryEditor.CHANGE_PENDING;
 
this.state = CategoryEditor.CHANGE_PENDING;
 
}
 
}
Line 2,487: Line 2,638:  
},
 
},
   −
resetKeySelection : function () {
+
resetKeySelection: function () {
if (noSuggestions || !this.list || this.list.style.display == 'none') return false;
+
if ( noSuggestions || !this.list || this.list.style.display === 'none' ) return false;
 +
 
 
var curr = this.list.selectedIndex;
 
var curr = this.list.selectedIndex;
if (curr >= 0 && curr < this.list.options.length) {
+
if ( curr >= 0 && curr < this.list.options.length ) {
this.list.options[curr].selected = false;
+
this.list.options[ curr ].selected = false;
 
// Get current input text
 
// Get current input text
var v = this.text.value.split('|');
+
var v = this.text.value.split( '|' );
var key = v.length > 1 ? '|' + v[1] : "";
+
var key = v.length > 1 ? '|' + v[ 1 ] : '';
 
// ESC is handled strangely by some browsers (e.g., FF); somehow it resets the input value before
 
// ESC is handled strangely by some browsers (e.g., FF); somehow it resets the input value before
 
// our event handlers ever get a chance to run.
 
// our event handlers ever get a chance to run.
var result = v[0] != this.lastInput;
+
var result = v[ 0 ] !== this.lastInput;
if (v[0] != this.lastRealInput) {
+
if ( v[ 0 ] !== this.lastRealInput ) {
 
this.text.value = this.lastRealInput + key;
 
this.text.value = this.lastRealInput + key;
 
result = true;
 
result = true;
Line 2,507: Line 2,659:  
return false;
 
return false;
 
}
 
}
   
}; // end CategoryEditor.prototype
 
}; // end CategoryEditor.prototype
   −
function initialize () {
+
function initialize() {
// User configurations. Do this here, called from the onload handler, so that users can
+
// User configurations: Do this here, called from the onload handler, so that users can
 
// override it easily in their own user script files by just declaring variables. JSconfig
 
// override it easily in their own user script files by just declaring variables. JSconfig
 
// is some feature used at Wikimedia Commons.
 
// is some feature used at Wikimedia Commons.
var config = (typeof JSconfig != 'undefined' && JSconfig.keys) ? JSconfig.keys : {};
+
var config = ( window.JSconfig !== undefined && JSconfig.keys ) ? JSconfig.keys : {};
HotCat.dont_add_to_watchlist =
+
HC.dont_add_to_watchlist = ( window.hotcat_dont_add_to_watchlist !== undefined ?
(typeof window.hotcat_dont_add_to_watchlist != 'undefined'
+
!!window.hotcat_dont_add_to_watchlist :
? !!window.hotcat_dont_add_to_watchlist
+
( config.HotCatDontAddToWatchlist !== undefined ? config.HotCatDontAddToWatchlist :
: (typeof config.HotCatDontAddToWatchlist != 'undefined'
+
HC.dont_add_to_watchlist ) );
? config.HotCatDontAddToWatchlist
+
HC.no_autocommit = ( window.hotcat_no_autocommit !== undefined ?
: HotCat.dont_add_to_watchlist
+
!!window.hotcat_no_autocommit : ( config.HotCatNoAutoCommit !== undefined ?
  )
+
config.HotCatNoAutoCommit :
);
+
// On talk namespace default autocommit off
HotCat.no_autocommit =
+
( conf.wgNamespaceNumber % 2 ?
(typeof window.hotcat_no_autocommit != 'undefined'
+
true : HC.no_autocommit ) ) );
? !!window.hotcat_no_autocommit
+
HC.del_needs_diff = ( window.hotcat_del_needs_diff !== undefined ?
: (typeof config.HotCatNoAutoCommit != 'undefined'
+
!!window.hotcat_del_needs_diff :
? config.HotCatNoAutoCommit
+
( config.HotCatDelNeedsDiff !== undefined ?
: HotCat.no_autocommit
+
config.HotCatDelNeedsDiff :
  )
+
HC.del_needs_diff ) );
);
+
HC.suggest_delay = window.hotcat_suggestion_delay || config.HotCatSuggestionDelay || HC.suggest_delay;
HotCat.del_needs_diff =
+
HC.editbox_width = window.hotcat_editbox_width || config.HotCatEditBoxWidth || HC.editbox_width;
(typeof window.hotcat_del_needs_diff != 'undefined'
+
HC.suggestions = window.hotcat_suggestions || config.HotCatSuggestions || HC.suggestions;
? !!window.hotcat_del_needs_diff
+
if ( typeof HC.suggestions !== 'string' || !suggestionConfigs[ HC.suggestions ] ) HC.suggestions = 'combined';
: (typeof config.HotCatDelNeedsDiff != 'undefined'
+
 
? config.HotCatDelNeedsDiff
+
HC.fixed_search = ( window.hotcat_suggestions_fixed !== undefined ?
: HotCat.del_needs_diff
+
!!window.hotcat_suggestions_fixed : ( config.HotCatFixedSuggestions !== undefined ?
  )
+
config.HotCatFixedSuggestions : HC.fixed_search ) );
);
+
HC.single_minor = ( window.hotcat_single_changes_are_minor !== undefined ?
HotCat.suggest_delay = window.hotcat_suggestion_delay
+
!!window.hotcat_single_changes_are_minor :
|| config['HotCatSuggestionDelay']
+
( config.HotCatMinorSingleChanges !== undefined ?
|| HotCat.suggest_delay;
+
config.HotCatMinorSingleChanges :
HotCat.editbox_width = window.hotcat_editbox_width
+
HC.single_minor ) );
|| config['HotCatEditBoxWidth']
+
HC.bg_changed = window.hotcat_changed_background || config.HotCatChangedBackground || HC.bg_changed;
|| HotCat.editbox_width;
+
HC.use_up_down = ( window.hotcat_use_category_links !== undefined ?
HotCat.suggestions   = window.hotcat_suggestions
+
!!window.hotcat_use_category_links :
|| config['HotCatSuggestions']
+
( config.HotCatUseCategoryLinks !== undefined ?
|| HotCat.suggestions;
+
config.HotCatUseCategoryLinks :
if (typeof HotCat.suggestions != 'string' || !suggestionConfigs[HotCat.suggestions])
+
HC.use_up_down ) );
HotCat.suggestions = 'combined';
+
HC.listSize = window.hotcat_list_size || config.HotCatListSize || HC.listSize;
HotCat.fixed_search =
+
if ( conf.wgDBname !== 'commonswiki' ) HC.changeTag = config.HotCatChangeTag || '';
(typeof window.hotcat_suggestions_fixed != 'undefined'
+
 
? !!window.hotcat_suggestions_fixed
+
// The next whole shebang is needed, because manual tags get not submitted except of save
: (typeof config.HotCatFixedSuggestions != 'undefined'
+
if ( HC.changeTag ) {
? config.HotCatFixedSuggestions
+
var eForm = document.editform,
: HotCat.fixed_search
+
catRegExp = new RegExp( '^\\[\\[(' + HC.category_regexp + '):' ),
  )
+
oldTxt;
);
+
// Returns true if minor change
HotCat.single_minor =
+
var isMinorChange = function () {
(typeof window.hotcat_single_changes_are_minor != 'undefined'
+
var newTxt = eForm.wpTextbox1;
? !!window.hotcat_single_changes_are_minor
+
if ( !newTxt ) return;
: (typeof config.HotCatMinorSingleChanges != 'undefined'
+
newTxt = newTxt.value;
? config.HotCatMinorSingleChanges
+
var oldLines = oldTxt.match( /^.*$/gm ),
: HotCat.single_minor
+
newLines = newTxt.match( /^.*$/gm ),
  )
+
cArr; // changes
);
+
var except = function ( aArr, bArr ) {
HotCat.bg_changed = window.hotcat_changed_background
+
var result = [],
|| config.HotCatChangedBackground
+
lArr, // larger
|| HotCat.bg_changed;
+
sArr; // smaller
HotCat.use_up_down =
+
if ( aArr.length < bArr.length ) {
(typeof window.hotcat_use_category_links != 'undefined'
+
lArr = bArr;
? !!window.hotcat_use_category_links
+
sArr = aArr;
: (typeof config.HotCatUseCategoryLinks != 'undefined'
+
} else {
? config.HotCatUseCategoryLinks
+
lArr = aArr;
: HotCat.use_up_down
+
sArr = bArr;
  )
+
}
);
+
for ( var i = 0; i < lArr.length; i++ ) {
HotCat.list_size = window.hotcat_list_size
+
var item = lArr[ i ];
|| config.HotCatListSize
+
var ind = $.inArray( item, sArr );
|| HotCat.list_size;
+
if ( ind === -1 ) result.push( item );
 +
else sArr.splice( ind, 1 ); // don't check this item again
 +
}
 +
return result.concat( sArr );
 +
};
 +
cArr = except( oldLines, newLines );
 +
if ( cArr.length ) {
 +
cArr = $.grep( cArr, function ( c ) {
 +
c = $.trim( c );
 +
return ( c && !catRegExp.test( c ) );
 +
} );
 +
}
 +
if ( !cArr.length ) {
 +
oldTxt = newTxt;
 +
return true;
 +
}
 +
};
 +
 
 +
if ( conf.wgAction === 'submit' && conf.wgArticleId && eForm && eForm.wpSummary && document.getElementById( 'wikiDiff' ) ) {
 +
var sum = eForm.wpSummary,
 +
sumA = eForm.wpAutoSummary;
 +
if ( sum.value && sumA.value === HC.changeTag ) { // HotCat diff
 +
// MD5 hash of the empty string, as HotCat edit is based on empty sum
 +
sumA.value = sumA.value.replace( HC.changeTag, 'd41d8cd98f00b204e9800998ecf8427e' );
 +
// Attr creation and event handling is not same in all (old) browsers so use $
 +
var $ct = $( '<input type="hidden" name="wpChangeTags">' ).val( HC.changeTag );
 +
$( eForm ).append( $ct );
 +
oldTxt = eForm.wpTextbox1.value;
 +
$( '#wpSave' ).one( 'click', function () {
 +
if ( $ct.val() )
 +
sum.value = sum.value.replace( ( HC.messages.using || HC.messages.prefix ), '' );
 +
 
 +
} );
 +
var removeChangeTag = function () {
 +
$( eForm.wpTextbox1 ).add( sum ).one( 'input', function () {
 +
window.setTimeout( function () {
 +
if ( !isMinorChange() ) $ct.val( '' );
 +
else removeChangeTag();
 +
}, 500 );
 +
} );
 +
};
 +
removeChangeTag();
 +
}
 +
}
 +
}
 
// Numeric input, make sure we have a numeric value
 
// Numeric input, make sure we have a numeric value
HotCat.list_size = parseInt (HotCat.list_size, 10);
+
HC.listSize = parseInt( HC.listSize, 10 );
if (isNaN (HotCat.list_size) || HotCat.list_size < 5) HotCat.list_size = 5;
+
if ( isNaN( HC.listSize ) || HC.listSize < 5 ) HC.listSize = 5;
if (HotCat.list_size > 15) HotCat.list_size = 15;
+
 
 +
HC.listSize = Math.min( HC.listSize, 30 ); // Max size
 +
 
 
// Localize search engine names
 
// Localize search engine names
if (HotCat.engine_names) {
+
if ( HC.engine_names ) {
for (var key in HotCat.engine_names) {
+
for ( var key in HC.engine_names )
if (suggestionConfigs[key] && HotCat.engine_names[key]) {
+
if ( suggestionConfigs[ key ] && HC.engine_names[ key ] ) suggestionConfigs[ key ].name = HC.engine_names[ key ];
suggestionConfigs[key].name = HotCat.engine_names[key];
+
 
}
  −
}
   
}
 
}
 
// Catch both native RTL and "faked" RTL through [[MediaWiki:Rtl.js]]
 
// Catch both native RTL and "faked" RTL through [[MediaWiki:Rtl.js]]
is_rtl = hasClass (document.body, 'rtl');
+
is_rtl = hasClass( document.body, 'rtl' );
if (!is_rtl) {
+
if ( !is_rtl ) {
if (document.defaultView && document.defaultView.getComputedStyle) { // Gecko etc.
+
if ( document.defaultView && document.defaultView.getComputedStyle ) { // Gecko etc.
is_rtl = document.defaultView.getComputedStyle (document.body, null).getPropertyValue ('direction');
+
is_rtl = document.defaultView.getComputedStyle( document.body, null ).getPropertyValue( 'direction' );
} else if (document.body.currentStyle) { // IE, has subtle differences to getComputedStyle
+
} else if ( document.body.currentStyle ) { // IE, has subtle differences to getComputedStyle
is_rtl = document.body.currentStyle['direction'];
+
is_rtl = document.body.currentStyle.direction;
 
} else { // Not exactly right, but best effort
 
} else { // Not exactly right, but best effort
is_rtl = document.body.style['direction'];
+
is_rtl = document.body.style.direction;
 
}
 
}
is_rtl = (is_rtl == 'rtl');
+
is_rtl = ( is_rtl === 'rtl' );
 
}
 
}
 
}
 
}
   −
function can_edit () {
+
function can_edit() {
 
var container = null;
 
var container = null;
switch (mw.config.get('skin')) {
+
switch ( mw.config.get( 'skin' ) ) {
 
case 'cologneblue':
 
case 'cologneblue':
container = document.getElementById ('quickbar');
+
container = document.getElementById( 'quickbar' );
// Fall through
+
/* fall through */
 
case 'standard':
 
case 'standard':
 
case 'nostalgia':
 
case 'nostalgia':
if (!container) container = document.getElementById ('topbar');
+
if ( !container ) container = document.getElementById( 'topbar' );
var lks = container.getElementsByTagName ('a');
+
var lks = container.getElementsByTagName( 'a' );
for (var i = 0; i < lks.length; i++) {
+
for ( var i = 0; i < lks.length; i++ ) {
if (   param ('title', lks[i].href) == conf.wgPageName
+
if (
&& param ('action', lks[i].href) == 'edit')
+
param( 'title', lks[ i ].href ) === conf.wgPageName &&
 +
param( 'action', lks[ i ].href ) === 'edit'
 +
) {
 
return true;
 
return true;
 +
}
 
}
 
}
 
return false;
 
return false;
 
default:
 
default:
 
// all modern skins:
 
// all modern skins:
return document.getElementById ('ca-edit') !== null;
+
return document.getElementById( 'ca-edit' ) !== null;
 +
}
 +
}
 +
 
 +
// Legacy stuff
 +
function closeForm() {
 +
// Close all open editors without redirect resolution and other asynchronous stuff.
 +
for ( var i = 0; i < editors.length; i++ ) {
 +
var edit = editors[ i ];
 +
if ( edit.state === CategoryEditor.OPEN ) {
 +
edit.cancel();
 +
} else if ( edit.state === CategoryEditor.CHANGE_PENDING ) {
 +
edit.sanitizeInput();
 +
var value = edit.text.value.split( '|' );
 +
var key = null;
 +
if ( value.length > 1 ) key = value[ 1 ];
 +
var v = value[ 0 ].replace( /_/g, ' ' ).replace( /^\s+|\s+$/g, '' );
 +
if ( !v.length ) {
 +
edit.cancel();
 +
} else {
 +
edit.currentCategory = v;
 +
edit.currentKey = key;
 +
edit.currentExists = this.inputExists;
 +
edit.close();
 +
}
 +
}
 
}
 
}
return false;
   
}
 
}
   −
function setup_upload () {
+
function setup_upload() {
 
onUpload = true;
 
onUpload = true;
 
// Add an empty category bar at the end of the table containing the description, and change the onsubmit handler.
 
// Add an empty category bar at the end of the table containing the description, and change the onsubmit handler.
var ip = document.getElementById ('mw-htmlform-description') || document.getElementById ('wpDestFile');
+
var ip = document.getElementById( 'mw-htmlform-description' ) || document.getElementById( 'wpDestFile' );
if (!ip) {
+
if ( !ip ) {
ip = document.getElementById ('wpDestFile');
+
ip = document.getElementById( 'wpDestFile' );
while (ip && ip.nodeName.toLowerCase() != 'table') ip = ip.parentNode;
+
while ( ip && ip.nodeName.toLowerCase() !== 'table' ) ip = ip.parentNode;
 
}
 
}
if (!ip) return;
+
if ( !ip ) return;
var reupload = document.getElementById ('wpForReUpload');
+
var reupload = document.getElementById( 'wpForReUpload' );
var destFile = document.getElementById ('wpDestFile');
+
var destFile = document.getElementById( 'wpDestFile' );
if (   (reupload && !!reupload.value)
+
if (
|| (destFile && (destFile.disabled || destFile.readOnly)))
+
( reupload && !!reupload.value ) ||
 +
( destFile && ( destFile.disabled || destFile.readOnly ) )
 +
) {
 
return; // re-upload form...
 
return; // re-upload form...
 +
}
 
// Insert a table row with two fields (label and empty category bar)
 
// Insert a table row with two fields (label and empty category bar)
var labelCell = make ('td');
+
var labelCell = make( 'td' );
var lineCell = make ('td');
+
var lineCell = make( 'td' );
 
// Create the category line
 
// Create the category line
catLine = make ('div');
+
catLine = make( 'div' );
 
catLine.className = 'catlinks';
 
catLine.className = 'catlinks';
 
catLine.id = 'catlinks';
 
catLine.id = 'catlinks';
Line 2,654: Line 2,879:  
catLine.style.margin = '0';
 
catLine.style.margin = '0';
 
catLine.style.border = 'none';
 
catLine.style.border = 'none';
lineCell.appendChild (catLine);
+
lineCell.appendChild( catLine );
 
// Create the label
 
// Create the label
 
var label = null;
 
var label = null;
if (   typeof UFUI != 'undefined'
+
if ( window.UFUI && window.UIElements && UFUI.getLabel instanceof Function ) {
&& typeof UIElements != 'undefined'
  −
&& typeof UFUI.getLabel == 'function'
  −
  )
  −
{
   
try {
 
try {
label = UFUI.getLabel('wpCategoriesUploadLbl');
+
label = UFUI.getLabel( 'wpCategoriesUploadLbl' );
} catch (ex) {
+
} catch ( ex ) {
 
label = null;
 
label = null;
 
}
 
}
 
}
 
}
if (!label) {
+
if ( !label ) {
 
labelCell.id = 'hotcatLabel';
 
labelCell.id = 'hotcatLabel';
labelCell.appendChild (make (HotCat.categories, true));
+
labelCell.appendChild( make( HC.categories, true ) );
 
} else {
 
} else {
 
labelCell.id = 'hotcatLabelTranslated';
 
labelCell.id = 'hotcatLabelTranslated';
labelCell.appendChild (label);
+
labelCell.appendChild( label );
 
}
 
}
labelCell.className           = 'mw-label';
+
labelCell.className = 'mw-label';
labelCell.style.textAlign     = 'right';
+
labelCell.style.textAlign = 'right';
 
labelCell.style.verticalAlign = 'middle';
 
labelCell.style.verticalAlign = 'middle';
 
// Change the onsubmit handler
 
// Change the onsubmit handler
var form = document.getElementById('upload') || document.getElementById('mw-upload-form');
+
var form = document.getElementById( 'upload' ) || document.getElementById( 'mw-upload-form' );
if (form) {
+
if ( form ) {
var newRow = ip.insertRow (-1);
+
var newRow = ip.insertRow( -1 );
newRow.appendChild (labelCell);
+
newRow.appendChild( labelCell );
newRow.appendChild (lineCell);
+
newRow.appendChild( lineCell );
form.onsubmit = (function (oldSubmit) {
+
form.onsubmit = ( function ( oldSubmit ) {
 
return function () {
 
return function () {
 
var do_submit = true;
 
var do_submit = true;
if (oldSubmit) {
+
if ( oldSubmit ) {
if (typeof oldSubmit == 'string')
+
if ( typeof oldSubmit === 'string' ) {
do_submit = eval (oldSubmit);
+
// eslint-disable-next-line no-eval
else if (typeof oldSubmit == 'function')
+
do_submit = eval( oldSubmit );
do_submit = oldSubmit.apply (form, arguments);
+
} else if ( oldSubmit instanceof Function ) {
 +
do_submit = oldSubmit.apply( form, arguments );
 +
}
 
}
 
}
if (!do_submit) return false;
+
if ( !do_submit ) return false;
closeForm ();
+
closeForm();
 
// Copy the categories
 
// Copy the categories
var eb = document.getElementById ('wpUploadDescription')
+
var eb = document.getElementById( 'wpUploadDescription' ) || document.getElementById( 'wpDesc' );
|| document.getElementById ('wpDesc');
   
var addedOne = false;
 
var addedOne = false;
for (var i = 0; i < editors.length; i++) {
+
for ( var i = 0; i < editors.length; i++ ) {
var t = editors[i].currentCategory;
+
var t = editors[ i ].currentCategory;
if (!t) continue ;
+
if ( !t ) continue;
var key = editors[i].currentKey;
+
var key = editors[ i ].currentKey;
var new_cat = '[[' + HotCat.category_canonical + ':' + t + (key ? '|' + key : "") + ']]';
+
var new_cat = '[[' + HC.category_canonical + ':' + t + ( key ? '|' + key : '' ) + ']]';
 
// Only add if not already present
 
// Only add if not already present
 
var cleanedText = eb.value
 
var cleanedText = eb.value
.replace(/<\!--(\s|\S)*?--\>/g, "")
+
.replace( /<!--(\s|\S)*?-->/g, '' )
.replace(/<nowiki\>(\s|\S)*?<\/nowiki>/g, "");
+
.replace( /<nowiki>(\s|\S)*?<\/nowiki>/g, '' );
if (!find_category (cleanedText, t, true)) {
+
if ( !find_category( cleanedText, t, true ) ) {
 
eb.value += '\n' + new_cat;
 
eb.value += '\n' + new_cat;
 
addedOne = true;
 
addedOne = true;
 
}
 
}
 
}
 
}
if (addedOne) {
+
if ( addedOne ) {
// Remove "subst:unc" added by Flinfo if it didn't find categories
+
// Remove "subst:unc" added by Flinfo if it didn't find categories
eb.value = eb.value.replace(/\{\{subst:unc\}\}/g, "");
+
eb.value = eb.value.replace( /\{\{subst:unc\}\}/g, '' );
 
}
 
}
 
return true;
 
return true;
 
};
 
};
}) (form.onsubmit);
+
}( form.onsubmit ) );
 
}
 
}
 
}
 
}
Line 2,725: Line 2,947:  
var cleanedText = null;
 
var cleanedText = null;
   −
function isOnPage (span) {
+
function isOnPage( span ) {
var catTitle = title (span.firstChild.getAttribute ('href', 2));
+
if ( span.firstChild.nodeType !== Node.ELEMENT_NODE ) return null;
if (!catTitle) return null;
+
 
catTitle = catTitle.substr (catTitle.indexOf (':') + 1).replace (/_/g, ' ');
+
var catTitle = title( span.firstChild.getAttribute( 'href' ) );
if (HotCat.blacklist && HotCat.blacklist.test (catTitle)) return null;
+
if ( !catTitle ) return null;
var result = { title : catTitle, match : ["", "", ""] };
+
 
if (pageText === null) return result;
+
catTitle = catTitle.substr( catTitle.indexOf( ':' ) + 1 ).replace( /_/g, ' ' );
if (cleanedText === null) {
+
if ( HC.blacklist && HC.blacklist.test( catTitle ) ) return null;
 +
 
 +
var result = {
 +
title: catTitle,
 +
match: [ '', '', '' ]
 +
};
 +
if ( pageText === null ) return result;
 +
 
 +
if ( cleanedText === null ) {
 
cleanedText = pageText
 
cleanedText = pageText
.replace(/<\!--(\s|\S)*?--\>/g, "")
+
.replace( /<!--(\s|\S)*?-->/g, '' )
.replace(/<nowiki\>(\s|\S)*?<\/nowiki>/g, "");
+
.replace( /<nowiki>(\s|\S)*?<\/nowiki>/g, '' );
 
}
 
}
result.match = find_category (cleanedText, catTitle, true);
+
result.match = find_category( cleanedText, catTitle, true );
 
return result;
 
return result;
 
}
 
}
Line 2,744: Line 2,974:  
var setupTimeout = null;
 
var setupTimeout = null;
   −
function findByClass (scope, tag, className) {
+
function findByClass( scope, tag, className ) {
// Compatibility routine. Uses jQuery if available, otherwise works with older getElementsByClassName
+
var result = $( scope ).find( tag + '.' + className );
var result;
+
return ( result && result.length ) ? result[ 0 ] : null;
if (window.jQuery) {
  −
result = window.jQuery(scope).find(tag + '.' + className);
  −
} else {
  −
result = getElementsByClassName(scope, tag, className);
  −
}
  −
return (result && result.length) ? result[0] : null;
   
}
 
}
   −
function setup (additionalWork) {
+
function setup( additionalWork ) {
if (initialized) return;
+
if ( initialized ) return;
 
initialized = true;
 
initialized = true;
if (setupTimeout) {
+
if ( setupTimeout ) {
window.clearTimeout (setupTimeout);
+
window.clearTimeout( setupTimeout );
 
setupTimeout = null;
 
setupTimeout = null;
 
}
 
}
 
// Find the category bar, or create an empty one if there isn't one. Then add -/+- links after
 
// Find the category bar, or create an empty one if there isn't one. Then add -/+- links after
 
// each category, and add the + link.
 
// each category, and add the + link.
catLine = catLine                                        // Special:Upload
+
catLine =
|| document.getElementById ('mw-normal-catlinks') // MW >= 1.13alpha
+
// Special:Upload
|| findByClass (document , 'p' , 'catlinks');     // MW < 1.13
+
catLine ||
var hiddenCats = document.getElementById ('mw-hidden-catlinks');
+
document.getElementById( 'mw-normal-catlinks' );
if (!catLine) {
+
var hiddenCats = document.getElementById( 'mw-hidden-catlinks' );
 +
if ( !catLine ) {
 
var footer = null;
 
var footer = null;
if (!hiddenCats) {
+
if ( !hiddenCats ) {
footer = findByClass (document , 'div' , 'printfooter');
+
footer = findByClass( document, 'div', 'printfooter' );
if (!footer) return; // Don't know where to insert the category line
+
if ( !footer ) return; // Don't know where to insert the category line
 
}
 
}
catLine = make ('div');
+
catLine = make( 'div' );
 
catLine.id = 'mw-normal-catlinks';
 
catLine.id = 'mw-normal-catlinks';
 
catLine.style.textAlign = is_rtl ? 'right' : 'left';
 
catLine.style.textAlign = is_rtl ? 'right' : 'left';
 
// Add a label
 
// Add a label
var label = make ('a');
+
var label = make( 'a' );
label.href = conf.wgArticlePath.replace ('$1', 'Special:Categories');
+
label.href = conf.wgArticlePath.replace( '$1', 'Special:Categories' );
label.title = HotCat.categories;
+
label.title = HC.categories;
label.appendChild (make (HotCat.categories, true));
+
label.appendChild( make( HC.categories, true ) );
catLine.appendChild (label);
+
catLine.appendChild( label );
catLine.appendChild (make (':', true));
+
catLine.appendChild( make( ':', true ) );
 
// Insert the new category line
 
// Insert the new category line
var container = (hiddenCats ? hiddenCats.parentNode : document.getElementById ('catlinks'));
+
var container = ( hiddenCats ? hiddenCats.parentNode : document.getElementById( 'catlinks' ) );
if (!container) {
+
if ( !container ) {
container = make ('div');
+
container = make( 'div' );
 
container.id = 'catlinks';
 
container.id = 'catlinks';
footer.parentNode.insertBefore (container, footer.nextSibling);
+
footer.parentNode.insertBefore( container, footer.nextSibling );
 
}
 
}
 
container.className = 'catlinks noprint';
 
container.className = 'catlinks noprint';
container.style.display = "";
+
container.style.display = '';
if (!hiddenCats) {
+
if ( !hiddenCats ) container.appendChild( catLine ); else container.insertBefore( catLine, hiddenCats );
container.appendChild (catLine);
  −
} else {
  −
container.insertBefore (catLine, hiddenCats);
  −
}
   
} // end if catLine exists
 
} // end if catLine exists
if (is_rtl) catLine.dir = 'rtl';
+
if ( is_rtl ) catLine.dir = 'rtl';
    
// Create editors for all existing categories
 
// Create editors for all existing categories
   −
function createEditors (line, is_hidden) {
+
function createEditors( line, is_hidden ) {
 
var i;
 
var i;
var cats = line.getElementsByTagName ('li');
+
var cats = line.getElementsByTagName( 'li' );
if (cats.length > 0) {
+
if ( cats.length ) {
newDOM = true; line = cats[0].parentNode;
+
newDOM = true;
 +
line = cats[ 0 ].parentNode;
 
} else {
 
} else {
cats = line.getElementsByTagName ('span');
+
cats = line.getElementsByTagName( 'span' );
 
}
 
}
 
// Copy cats, otherwise it'll also magically contain our added spans as it is a live collection!
 
// Copy cats, otherwise it'll also magically contain our added spans as it is a live collection!
var copyCats = new Array (cats.length);
+
var copyCats = new Array( cats.length );
for (i = 0; i < cats.length; i++) copyCats[i] = cats[i];
+
for ( i = 0; i < cats.length; i++ ) copyCats[ i ] = cats[ i ];
var editor = null;
+
for ( i = 0; i < copyCats.length; i++ ) {
for (i = 0; i < copyCats.length; i++) {
+
var test = isOnPage( copyCats[ i ] );
var test = isOnPage (copyCats[i]);
+
if ( test !== null && test.match !== null && line ) {
if (test !== null && test.match !== null) {
+
// eslint-disable-next-line no-new
editor = new CategoryEditor (line, copyCats[i], test.title, test.match[2], is_hidden);
+
new CategoryEditor( line, copyCats[ i ], test.title, test.match[ 2 ], is_hidden );
 
}
 
}
 
}
 
}
return copyCats.length > 0 ? copyCats[copyCats.length-1] : null;
+
return copyCats.length ? copyCats[ copyCats.length - 1 ] : null;
 
}
 
}
   −
var lastSpan = createEditors (catLine, false);
+
var lastSpan = createEditors( catLine, false );
 
// Create one to add a new category
 
// Create one to add a new category
var editor = new CategoryEditor(newDOM ? catLine.getElementsByTagName('ul')[0] : catLine, null, null, lastSpan !== null, false);
+
// eslint-disable-next-line no-new
if (!onUpload) {
+
new CategoryEditor( newDOM ? catLine.getElementsByTagName( 'ul' )[ 0 ] : catLine, null, null, lastSpan !== null, false );
if (pageText !== null && hiddenCats) {
+
if ( !onUpload ) {
if (is_rtl) hiddenCats.dir = 'rtl';
+
if ( pageText !== null && hiddenCats ) {
createEditors (hiddenCats, true);
+
if ( is_rtl ) hiddenCats.dir = 'rtl';
 +
createEditors( hiddenCats, true );
 
}
 
}
 
// And finally add the "multi-mode" span. (Do this at the end, otherwise it ends up in the list above.)
 
// And finally add the "multi-mode" span. (Do this at the end, otherwise it ends up in the list above.)
var enableMulti = make ('span');
+
var enableMulti = make( 'span' );
 
enableMulti.className = 'noprint';
 
enableMulti.className = 'noprint';
if (is_rtl) enableMulti.dir = 'rtl';
+
if ( is_rtl ) enableMulti.dir = 'rtl';
catLine.insertBefore (enableMulti, catLine.firstChild.nextSibling);
+
catLine.insertBefore( enableMulti, catLine.firstChild.nextSibling );
enableMulti.appendChild (make ('\xa0', true)); // nbsp
+
enableMulti.appendChild( make( '\xa0', true ) ); // nbsp
multiSpan = make ('span');
+
multiSpan = make( 'span' );
enableMulti.appendChild (multiSpan);
+
enableMulti.appendChild( multiSpan );
multiSpan.innerHTML = '(<a>' + HotCat.addmulti + '</a>)';
+
multiSpan.innerHTML = '(<a>' + HC.addmulti + '</a>)';
var lk = multiSpan.getElementsByTagName ('a')[0];
+
var lk = multiSpan.getElementsByTagName( 'a' )[ 0 ];
lk.onclick = function (evt) {setMultiInput (); checkMultiInput (); return evtKill (evt);};
+
lk.onclick = function ( evt ) {
lk.title = HotCat.multi_tooltip;
+
setMultiInput();
 +
checkMultiInput();
 +
return evtKill( evt );
 +
};
 +
lk.title = HC.multi_tooltip;
 
lk.style.cursor = 'pointer';
 
lk.style.cursor = 'pointer';
 
}
 
}
 
cleanedText = null;
 
cleanedText = null;
if (typeof additionalWork == 'function') additionalWork();
+
if ( additionalWork instanceof Function ) additionalWork();
setupCompleted.loaded(); // Trigger signal; execute registered functions
+
mw.hook( 'hotcat.ready' ).fire(); // Execute registered callback functions
if (window.jQuery) jQuery('body').trigger('hotcatSetupCompleted');
+
$( 'body' ).trigger( 'hotcatSetupCompleted' );
}
  −
 
  −
function setPage (json) {
  −
var startTime = null;
  −
if (json && json.query) {
  −
if (json.query.pages) {
  −
var page = json.query.pages[conf.wgArticleId === 0 ? "-1" : "" + conf.wgArticleId];
  −
if (page) {
  −
if (page.revisions && page.revisions.length > 0) {
  −
// Revisions are sorted by revision ID, hence [0] is the one we asked for, and possibly there's a [1] if we're
  −
// not on the latest revision (edit conflicts and such).
  −
pageText = page.revisions[0]['*'];
  −
if (page.revisions[0].timestamp) pageTime = page.revisions[0].timestamp.replace (/\D/g, "");
  −
if (page.revisions[0].revid) pageTextRevId = page.revisions[0].revid;
  −
if (page.revisions.length > 1) conflictingUser = page.revisions[1].user;
  −
}
  −
if (page.lastrevid) lastRevId = page.lastrevid;
  −
if (page.starttimestamp) startTime = page.starttimestamp.replace (/\D/g, "");
  −
pageWatched = typeof page.watched == 'string';
  −
editToken = page.edittoken;
  −
if (page.langlinks && (!json['query-continue'] || !json['query-continue'].langlinks)) {
  −
// We have interlanguage links, and we got them all.
  −
var re = "";
  −
for (var i = 0; i < page.langlinks.length; i++) {
  −
re += (i > 0 ? '|' : "") + page.langlinks[i].lang.replace(/([\\\^\$\.\?\*\+\(\)])/g, '\\$1');
  −
}
  −
if (re.length > 0) {
  −
interlanguageRE = new RegExp ('((^|\\n\\r?)(\\[\\[\\s*(' + re + ')\\s*:[^\\]]+\\]\\]\\s*))+$');
  −
}
  −
}
  −
 
  −
}
  −
}
  −
// Siteinfo
  −
if (json.query.general) {
  −
HotCat.capitalizePageNames = (json.query.general['case'] == 'first-letter');
  −
if (json.query.general.time && !startTime) startTime = json.query.general.time.replace (/\D/g, "");
  −
}
  −
serverTime = startTime;
  −
// Userinfo
  −
if (json.query.userinfo && json.query.userinfo.options) {
  −
watchCreate = !HotCat.dont_add_to_watchlist && json.query.userinfo.options.watchcreations == '1';
  −
watchEdit  = !HotCat.dont_add_to_watchlist && json.query.userinfo.options.watchdefault == '1';
  −
minorEdits  = json.query.userinfo.options.minordefault == 1;
  −
// If the user has the "All edits are minor" preference enabled, we should honor that
  −
// for single category changes, no matter what the site configuration is.
  −
if (minorEdits) HotCat.single_minor = true;
  −
}
  −
}
   
}
 
}
   −
function createCommitForm () {
+
function createCommitForm() {
if (commitForm) return;
+
if ( commitForm ) return;
var formContainer = make ('div');
+
var formContainer = make( 'div' );
 
formContainer.style.display = 'none';
 
formContainer.style.display = 'none';
document.body.appendChild (formContainer);
+
document.body.appendChild( formContainer );
 
formContainer.innerHTML =
 
formContainer.innerHTML =
'<form id="hotcatCommitForm" method="post" enctype="multipart/form-data" action="'
+
'<form id="hotcatCommitForm" method="post" enctype="multipart/form-data" action="' +
+ conf.wgScript + '?title=' + encodeURIComponent (conf.wgPageName)
+
conf.wgScript + '?title=' + encodeURIComponent( conf.wgPageName ) + '&action=submit">' +
+ '&action=edit">'
+
'<input type="hidden" name="wpTextbox1">' +
+ '<input type="hidden" name="wpTextbox1" />'
+
'<input type="hidden" name="model" value="' + conf.wgPageContentModel + '">' +
+ '<input type="hidden" name="model" value="wikitext" />'
+
'<input type="hidden" name="format" value="text/x-wiki">' +
+ '<input type="hidden" name="format" value="text/x-wiki" />'
+
'<input type="hidden" name="wpSummary" value="">' +
+ '<input type="hidden" name="wpSummary" value="" />'
+
'<input type="checkbox" name="wpMinoredit" value="1">' +
+ '<input type="checkbox" name="wpMinoredit" value="1" />'
+
'<input type="checkbox" name="wpWatchthis" value="1">' +
+ '<input type="checkbox" name="wpWatchthis" value="1" />'
+
'<input type="hidden" name="wpAutoSummary" value="d41d8cd98f00b204e9800998ecf8427e">' +
+ '<input type="hidden" name="wpAutoSummary" value="" />'
+
'<input type="hidden" name="wpEdittime">' +
+ '<input type="hidden" name="wpEdittime" />'
+
'<input type="hidden" name="wpStarttime">' +
+ '<input type="hidden" name="wpStarttime" />'
+
'<input type="hidden" name="wpDiff" value="wpDiff">' +
+ '<input type="hidden" name="wpDiff" value="wpDiff" />'
+
'<input type="hidden" name="oldid" value="0">' +
+ '<input type="hidden" name="oldid" value="0" />'
+
'<input type="submit" name="hcCommit" value="hcCommit">' +
+ '<input type="submit" name="hcCommit" value="hcCommit" />'
+
'<input type="hidden" name="wpEditToken">' +
+ '<input type="hidden" name="wpEditToken" />'
+
'<input type="hidden" name="wpUltimateParam" value="1">' +
+ '<input type="hidden" name="wpUltimateParam" value="1" />'
+
'<input type="hidden" name="wpChangeTags">' +
+ '</form>';
+
'<input type="hidden" value="ℳ𝒲♥𝓊𝓃𝒾𝒸ℴ𝒹ℯ" name="wpUnicodeCheck">' +
commitForm = document.getElementById ('hotcatCommitForm');
+
'</form>';
 +
commitForm = document.getElementById( 'hotcatCommitForm' );
 
}
 
}
   −
function getPage () {
+
function getPage() {
 
// We know we have an article here.
 
// We know we have an article here.
if (conf.wgArticleId === 0) {
+
if ( !conf.wgArticleId ) {
// Doesn't exist yet.
+
// Doesn't exist yet. Disable on non-existing User pages -- might be a global user page.
if (conf.wgNamespaceNumber === 2) {
+
if ( conf.wgNamespaceNumber === 2 ) return;
// Disable on non-existing User pages -- might be a global user page.
+
pageText = '';
return;
  −
}
  −
pageText = "";
   
pageTime = null;
 
pageTime = null;
setup (createCommitForm);
+
setup( createCommitForm );
 
} else {
 
} else {
var url = conf.wgServer + conf.wgScriptPath + '/api.php?format=json&callback=HotCat.start&action=query&rawcontinue=&titles='
+
var url = conf.wgServer + conf.wgScriptPath + '/api.php?format=json&callback=HotCat.start&action=query&rawcontinue=&titles=' +
+ encodeURIComponent (conf.wgPageName)
+
encodeURIComponent( conf.wgPageName ) +
+ '&prop=info%7Crevisions&rvprop=content%7Ctimestamp%7Cids&meta=siteinfo&rvlimit=1&rvstartid='
+
'&prop=info%7Crevisions&rvprop=content%7Ctimestamp%7Cids&meta=siteinfo&rvlimit=1&rvstartid=' +
+ conf.wgCurRevisionId;
+
conf.wgCurRevisionId;
var s = make ('script');
+
var s = make( 'script' );
s.src = armorUri(url);
+
s.src = url;
s.type = 'text/javascript';
+
HC.start = function ( json ) {
HotCat.start = function (json) { setPage (json); setup (createCommitForm); };
+
setPage( json );
document.getElementsByTagName ('head')[0].appendChild (s);
+
setup( createCommitForm );
setupTimeout = window.setTimeout (function () {setup (createCommitForm);}, 4000); // 4 sec, just in case getting the wikitext takes longer.
+
};
 +
document.getElementsByTagName( 'head' )[ 0 ].appendChild( s );
 +
setupTimeout = window.setTimeout( function () {
 +
setup( createCommitForm );
 +
}, 4000 ); // 4 sec, just in case getting the wikitext takes longer.
 
}
 
}
 
}
 
}
   −
function run () {
+
function setState( state ) {
if (HotCat.started) return;
+
var cats = state.split( '\n' );
HotCat.started = true;
+
if ( !cats.length ) return null;
loadTrigger.register(really_run);
  −
}
     −
function really_run () {
+
if ( initialized && editors.length === 1 && editors[ 0 ].isAddCategory ) {
initialize ();
+
// Insert new spans and create new editors for them.
 +
var newSpans = [];
 +
var before = editors.length === 1 ? editors[ 0 ].span : null;
 +
var i;
 +
for ( i = 0; i < cats.length; i++ ) {
 +
if ( !cats[ i ].length ) continue;
 +
var cat = cats[ i ].split( '|' );
 +
var key = cat.length > 1 ? cat[ 1 ] : null;
 +
cat = cat[ 0 ];
 +
var lk = make( 'a' );
 +
lk.href = wikiPagePath( HC.category_canonical + ':' + cat );
 +
lk.appendChild( make( cat, true ) );
 +
lk.title = cat;
 +
var span = make( 'span' );
 +
span.appendChild( lk );
 +
if ( !i ) catLine.insertBefore( make( ' ', true ), before );
   −
if (is_rtl && is_ie6) return; // Disabled! IE6 with RTL is just too broken...
+
catLine.insertBefore( span, before );
if (!HotCat.upload_disabled && conf.wgNamespaceNumber === -1 && conf.wgCanonicalSpecialPageName == 'Upload' && conf.wgUserName) {
+
if ( before && i + 1 < cats.length ) parent.insertBefore( make( ' | ', true ), before );
setup_upload ();
  −
setup (function () {
  −
// Check for state restoration once the setup is done otherwise, but before signalling setup completion
  −
if (   typeof UploadForm != 'undefined'
  −
&& typeof UploadForm.previous_hotcat_state != 'undefined'
  −
&& UploadForm.previous_hotcat_state !== null)
  −
{
  −
UploadForm.previous_hotcat_state = setState (UploadForm.previous_hotcat_state);
  −
}
  −
});
  −
} else {
  −
if (!conf.wgIsArticle || conf.wgAction != 'view' || param('diff') !== null || param('oldid') !== null || !can_edit() || HotCat.disable()) return;
  −
getPage ();
  −
}
  −
}
     −
// Legacy stuff
+
newSpans.push( {
 +
element: span,
 +
title: cat,
 +
key: key
 +
} );
 +
}
 +
// And change the last one...
 +
if ( before ) before.parentNode.insertBefore( make( ' | ', true ), before );
   −
function closeForm () {
+
for ( i = 0; i < newSpans.length; i++ ) {
// Close all open editors without redirect resolution and other asynchronous stuff.
+
// eslint-disable-next-line no-new
for (var i = 0; i < editors.length; i++) {
+
new CategoryEditor( catLine, newSpans[ i ].element, newSpans[ i ].title, newSpans[ i ].key );
if (editors[i].state == CategoryEditor.OPEN) {
  −
editors[i].cancel();
  −
} else if (editors[i].state == CategoryEditor.CHANGE_PENDING) {
  −
editors[i].sanitizeInput ();
  −
var value = editors[i].text.value.split('|');
  −
var key  = null;
  −
if (value.length > 1) key = value[1];
  −
var v = value[0].replace(/_/g, ' ').replace(/^\s+|\s+$/g, "");
  −
if (v.length === 0) {
  −
editors[i].cancel ();
  −
} else {
  −
editors[i].currentCategory = v;
  −
editors[i].currentKey = key;
  −
editors[i].currentExists = this.inputExists;
  −
editors[i].close ();
  −
}
   
}
 
}
 
}
 
}
 +
return null;
 
}
 
}
   −
function getState () {
+
function getState() {
 
var result = null;
 
var result = null;
for (var i = 0; i < editors.length; i++) {
+
for ( var i = 0; i < editors.length; i++ ) {
var text = editors[i].currentCategory;
+
var text = editors[ i ].currentCategory;
var key = editors[i].currentKey;
+
var key = editors[ i ].currentKey;
if (text && text.length > 0) {
+
if ( text && text.length ) {
if (key !== null) text += '|' + key;
+
if ( key !== null ) text += '|' + key;
if (result === null)
+
if ( result === null ) result = text; else result += '\n' + text;
result = text;
  −
else
  −
result = result + '\n' + text;
   
}
 
}
 
}
 
}
Line 3,021: Line 3,188:  
}
 
}
   −
function setState (state) {
+
function really_run() {
var cats = state.split ('\n');
+
initialize();
if (cats.length === 0) return null;
+
 
if (initialized && editors.length == 1 && editors[0].isAddCategory) {
+
if ( !HC.upload_disabled && conf.wgNamespaceNumber === -1 && conf.wgCanonicalSpecialPageName === 'Upload' && conf.wgUserName ) {
// Insert new spans and create new editors for them.
+
setup_upload();
var newSpans = [];
+
setup( function () {
var before = editors.length == 1 ? editors[0].span : null;
+
// Check for state restoration once the setup is done otherwise, but before signalling setup completion
var i;
+
if ( window.UploadForm && UploadForm.previous_hotcat_state ) UploadForm.previous_hotcat_state = setState( UploadForm.previous_hotcat_state );
for (i = 0; i < cats.length; i++) {
+
} );
if (cats[i].length === 0) continue;
+
} else {
var cat = cats[i].split ('|');
+
if ( !conf.wgIsArticle || conf.wgAction !== 'view' || param( 'diff' ) !== null || param( 'oldid' ) !== null || !can_edit() || HC.disable() ) return;
var key = cat.length > 1 ? cat[1] : null;
+
getPage();
cat = cat[0];
  −
var lk = make ('a'); lk.href = wikiPagePath (HotCat.category_canonical + ':' + cat);
  −
lk.appendChild (make (cat, true));
  −
lk.title = cat;
  −
var span = make ('span');
  −
span.appendChild (lk);
  −
if (i === 0) catLine.insertBefore (make (' ', true), before);
  −
catLine.insertBefore (span, before);
  −
if (before && i+1 < cats.length) parent.insertBefore (make (' | ', true), before);
  −
newSpans.push ({element: span, title: cat, 'key': key});
  −
}
  −
// And change the last one...
  −
if (before) {
  −
before.parentNode.insertBefore (make (' | ', true), before);
  −
}
  −
var editor = null;
  −
for (i = 0; i < newSpans.length; i++) {
  −
editor = new CategoryEditor (catLine, newSpans[i].element, newSpans[i].title, newSpans[i].key);
  −
}
   
}
 
}
return null;
+
}
 +
 
 +
function run() {
 +
if ( HC.started ) return;
 +
HC.started = true;
 +
loadTrigger.register( really_run );
 
}
 
}
    
// Export legacy functions
 
// Export legacy functions
window.hotcat_get_state = function () { return getState(); };
+
window.hotcat_get_state = function () {
window.hotcat_set_state = function (state) { return setState (state); };
+
return getState();
window.hotcat_close_form = function () { closeForm (); };
+
};
 +
window.hotcat_set_state = function ( state ) {
 +
return setState( state );
 +
};
 +
window.hotcat_close_form = function () {
 +
closeForm();
 +
};
 +
HC.runWhenReady = function ( callback ) {
 +
// run user-registered code once HotCat is fully set up and ready.
 +
mw.hook( 'hotcat.ready' ).add( callback );
 +
};
 +
 
 +
// Make sure we don't get conflicts with AjaxCategories (core development that should one day
 +
// replace HotCat).
 +
mw.config.set( 'disableAJAXCategories', true );
   −
if (window.mw) {
  −
// Make sure we don't get conflicts with AjaxCategories (core development that should one day
  −
// replace HotCat).
  −
mw.config.set('disableAJAXCategories', true);
  −
}
   
// Run as soon as possible. This varies depending on MediaWiki version;
 
// Run as soon as possible. This varies depending on MediaWiki version;
 
// window's 'load' event is always safe, but usually we can do better than that.
 
// window's 'load' event is always safe, but usually we can do better than that.
   −
// Check for version to avoid MediaWiki bug 32537.
+
if ( conf.wgCanonicalSpecialPageName !== 'Upload' ) {
var mwVersion = conf.wgVersion.split('.');
+
// Reload HotCat after (VE) edits (bug T103285)
if (mwVersion[0] >= 1 && parseFloat(mwVersion[1]) > 20) {
+
mw.hook( 'postEdit' ).add( function () {
if (parseFloat(mwVersion[1]) > 21 && conf.wgCanonicalSpecialPageName !== 'Upload') {
+
// Reset HotCat in case this is a soft reload (VE edit)
// Use wikipage.content hook so that HotCat reloads after VE edits (bug T103285)
+
catLine = null;
var startHotCat = function() {
+
editors = [];
mw.hook('wikipage.content').add( function() {
+
initialized = false;
// Reset HotCat in case this is a soft reload (VE edit)
+
HC.started = false;
catLine = null;
+
run();
editors = [];
+
} );
initialized = false;
  −
HotCat.started = false;
  −
run ();
  −
} );
  −
};
  −
} else {
  −
// We are using MediaWiki 1.21, which doesn't support mw.hook. Fall back to dom-ready.
  −
// OR: We're running on Special:Upload, where the 'wikipage.content' hook is fired for
  −
// various previewed wikitext snippets, which shouldn't reload HotCat interface.
  −
var startHotCat = function() {
  −
jQuery(document).ready(run);
  −
};
  −
}
  −
// We can safely trigger just after user configuration is loaded. Also start HotCat if the user module fails to load.
  −
// Avoid using Promise methods of mw.loader.using as those aren't supported in older
  −
// MediaWiki versions.
  −
mw.loader.using('user', startHotCat, startHotCat);
  −
} else {
  −
// mw.loader.using('user', ...) could have unintended side-effects on MW <= 1.20. Fall back to dom-ready.
  −
jQuery(document).ready(run);
   
}
 
}
})();
      +
// We can safely trigger just after user configuration is loaded.
 +
// Use always() instead of then() to also start HotCat if the user module has problems.
 +
$.when( mw.loader.using( 'user' ), $.ready ).always( run );
 +
}( jQuery, mediaWiki ) );
 
// </nowiki>
 
// </nowiki>
1,823

edits

Navigation menu