14,091
edits
(Trying 2.41) |
(Add) |
||
(One intermediate revision by the same user not shown) | |||
Line 1: | Line 1: | ||
/ | // <nowiki> | ||
HotCat V2. | /* | ||
HotCat V2.37 | |||
Ajax-based simple Category manager. Allows adding/removing/changing categories on a page view. | 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 | 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 | plugs into the upload form. Search engines to use for the suggestion list are configurable, and | ||
can be selected interactively. | can be selected interactively. | ||
Documentation: https://commons.wikimedia.org/wiki/Help:Gadget-HotCat | Documentation: https://commons.wikimedia.org/wiki/Help:Gadget-HotCat | ||
List of main authors: https://commons.wikimedia.org/wiki/Help:Gadget-HotCat/Version_history | 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) | License: Quadruple licensed GFDL, GPL, LGPL and Creative Commons Attribution 3.0 (CC-BY-3.0) | ||
Choose whichever license of these you like best :-) | Choose whichever license of these you like best :-) | ||
*/ | |||
This code should run on any MediaWiki installation >= MW 1.27. | /* | ||
This code should run on any MediaWiki installation >= MW 1.27. | |||
For use with older versions of MediaWiki, use the archived versions below: | 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 | <=1.26: https://commons.wikimedia.org/w/index.php?title=MediaWiki:Gadget-HotCat.js&oldid=211134664 | ||
*/ | */ | ||
/* eslint-disable vars-on-top, one-var, camelcase, no-use-before-define, no-alert */ | |||
/* eslint-disable vars-on-top, one-var, camelcase, no-alert | /* global HotCat, mediaWiki, UFUI, JSconfig, UploadForm */ | ||
/* global | |||
( function ( $, mw ) { | ( function ( $, mw ) { | ||
// Don't use mw.config.get() as that takes a copy of the config, and so doesn't | // Don't use mw.config.get() as that takes a copy of the config, and so doesn't | ||
Line 29: | Line 30: | ||
var conf = mw.config.values; | var conf = mw.config.values; | ||
// Guard against double inclusions (in old IE/Opera element ids become window properties) | if ( | ||
// Guard against double inclusions (in old IE/Opera element ids become window properties) | |||
conf.wgAction === 'edit' ) | ( window.HotCat && !window.HotCat.nodeName ) || | ||
// Not on edit pages | |||
conf.wgAction === 'edit' | |||
) { | |||
return; | return; | ||
} | |||
// Configuration stuff. | // Configuration stuff. | ||
window.HotCat = { | |||
// Localize these messages to the main language of your wiki. | |||
messages: { | messages: { | ||
cat_removed: 'removed [[Category:$1]]', | cat_removed: 'removed [[Category:$1]]', | ||
Line 69: | Line 75: | ||
// see localization hook below. | // see localization hook below. | ||
multi_error: 'Could not retrieve the page text from the server. Therefore, your category changes ' + | multi_error: 'Could not retrieve the page text from the server. Therefore, your category changes ' + | ||
'cannot be saved. We apologize for the inconvenience.', | |||
// 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 | ||
Line 88: | Line 94: | ||
// The little modification links displayed after category names. U+2212 is a minus sign; U+2193 and U+2191 are | // 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! | // downward and upward pointing arrows. Do not use ↓ and ↑ in the code! | ||
links: { | links: { change: '(±)', remove: '(\u2212)', add: '(+)', restore: '(×)', undo: '(×)', down: '(\u2193)', up: '(\u2191)' }, | ||
// The tooltips for the above links | // The tooltips for the above links | ||
tooltips: { | tooltips: { | ||
Line 118: | Line 115: | ||
return ( | return ( | ||
ns < 0 || // Special pages; Special:Upload is handled differently | ns < 0 || // Special pages; Special:Upload is handled differently | ||
ns === 10 || // Templates | |||
ns === 828 || // Module (Lua) | |||
ns === 8 || // MediaWiki | |||
ns === 6 && conf.wgArticleId === 0 || // Non-existing file pages | |||
ns === 2 && /\.(js|css)$/.test( conf.wgTitle ) || // User scripts | |||
nsIds && | |||
( ns === nsIds.creator || | |||
ns === nsIds.timedtext || | |||
ns === nsIds.institution | |||
) | |||
); | |||
}, | }, | ||
// A regexp matching a templates used to mark uncategorized pages, if your wiki does have that. | // A regexp matching a templates used to mark uncategorized pages, if your wiki does have that. | ||
// If not, set it to null. | // If not, set it to null. | ||
uncat_regexp: /\{\{\s*[Uu] | uncat_regexp: /\{\{\s*([Uu]ncat(egori[sz]ed( image)?)?|[Nn]ocat|[Nn]eedscategory)[^}]*\}\}\s*(<!--.*?-->\s*)?/g, | ||
// The images used for the little indication icon. Should not need changing. | // 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', | ||
Line 147: | Line 146: | ||
parentcat: 'Parent categories' | parentcat: 'Parent categories' | ||
}, | }, | ||
// Set to false if your wiki has case-sensitive page names. MediaWiki has two modes: either the // | // 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 | |||
// ("case-sensitive"; Category:aa !== Category:Aa). It doesn't currently have a fully case-insensitive mode | // ("case-sensitive"; Category:aa !== Category:Aa). It doesn't currently have a fully case-insensitive mode | ||
// (which would mean Category:aa === Category:Aa === Category:AA === Category:aA) | // (which would mean Category:aa === Category:Aa === Category:AA === Category:aA) | ||
Line 166: | Line 165: | ||
// Stuff changeable by users: | // Stuff changeable by users: | ||
// 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: '# | bg_changed: '#F8CCB0', | ||
// 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. | ||
Line 186: | Line 185: | ||
use_up_down: true, | use_up_down: true, | ||
// Default list size | // Default list size | ||
list_size: 5, | |||
// 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, | single_minor: true, | ||
Line 195: | Line 194: | ||
shortcuts: null, | shortcuts: null, | ||
addShortcuts: function ( map ) { | addShortcuts: 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 ]; | var v = map[ k ]; | ||
if ( typeof v !== 'string' ) continue; | if ( typeof v !== 'string' ) { continue; } | ||
k = k.replace( /^\s+|\s+$/g, '' ); | k = k.replace( /^\s+|\s+$/g, '' ); | ||
v = v.replace( /^\s+|\s+$/g, '' ); | v = v.replace( /^\s+|\s+$/g, '' ); | ||
if ( | if ( k.length === 0 || v.length === 0 ) { continue; } | ||
window.HotCat.shortcuts[ k ] = v; | window.HotCat.shortcuts[ k ] = v; | ||
} | } | ||
Line 216: | Line 212: | ||
var ua = navigator.userAgent.toLowerCase(); | var ua = navigator.userAgent.toLowerCase(); | ||
var is_webkit = /applewebkit\/\d+/.test( ua ) && ua.indexOf( 'spoofer' ) < 0; | var is_webkit = /applewebkit\/\d+/.test( ua ) && ua.indexOf( 'spoofer' ) < 0; | ||
var | // 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 ) { | |||
var req = getRequest(); | |||
if ( !req && settings && settings.error ) { settings.error( req ); } | |||
if ( !req || !settings || !settings.uri ) { return req; } | |||
// eslint-disable-next-line no-use-before-define | |||
var uri = armorUri( settings.uri ); | |||
var args = settings.data || null; | |||
if ( | var method; | ||
if ( args && uri.length + args.length + 1 > 2000 ) { | |||
// We lose caching, but at least we can make the request | |||
method = 'POST'; | |||
req.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded' ); | |||
if ( | } else { | ||
// | method = 'GET'; | ||
if ( args ) { uri += '?' + args; } | |||
args = null; | |||
} | } | ||
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 { | |||
// eslint-disable-next-line no-eval | |||
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; | |||
} | } | ||
// Used to delay running the HotCat setup until /local_defaults and localizations have been loaded. | 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. | |||
function load( uri | function load( uri ) { | ||
var head = document.getElementsByTagName( 'head' )[ 0 ]; | |||
var s = document.createElement( 'script' ); | var s = document.createElement( 'script' ); | ||
s.src | s.setAttribute( 'src', armorUri( uri ) ); | ||
var | s.setAttribute( 'type', 'text/javascript' ); | ||
var done = false; | |||
function afterLoad() { | |||
if ( done ) { return; } | |||
done = true; | |||
s.onload = s.onreadystatechange = s.onerror = null; // Properly clean up to avoid memory leaks in IE | |||
if ( head && s.parentNode ) { head.removeChild( s ); } | |||
loadTrigger.loaded(); | |||
} | |||
s.onload = s. | s.onload = s.onreadystatechange = function () { // onreadystatechange for IE, onload for all others | ||
if ( | if ( done ) { return; } | ||
if ( !this.readyState || this.readyState === 'loaded' || this.readyState === 'complete' ) { | |||
afterLoad(); | |||
if ( | |||
} | } | ||
}; | }; | ||
s.onerror = afterLoad; // Clean up, but otherwise ignore errors | |||
head.insertBefore( s, head.firstChild ); // appendChild may trigger bugs in IE6 here | |||
} | } | ||
function loadJS( page | function loadJS( page ) { | ||
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' ); | ||
} | } | ||
function loadURI( href | function loadURI( href ) { | ||
var url = href; | var url = href; | ||
if ( url.substring( 0, 2 ) === '//' ) url = window.location.protocol + url; else if ( url.substring( 0, 1 ) === '/' ) url = conf.wgServer + url; | if ( url.substring( 0, 2 ) === '//' ) { | ||
url = window.location.protocol + url; | |||
load( url | } else if ( url.substring( 0, 1 ) === '/' ) { | ||
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 Commons. This can | // from the wiki where this script is executing, even if this script itself is hotlinked from the 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' ); | ||
// 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 | ||
Line 279: | Line 356: | ||
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 Commons and don't want that. | // explicitly if you're not on the Commons and don't want that. | ||
if ( window.hotcat_translations_from_commons === undefined ) window.hotcat_translations_from_commons = true; | if ( typeof window.hotcat_translations_from_commons === 'undefined' ) { | ||
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 + | |||
'&action=raw&ctype=text/javascript' | |||
); | |||
} else { | } else { | ||
// Load translations locally | // Load translations locally | ||
loadJS( 'MediaWiki:Gadget-HotCat.js/' + conf.wgUserLanguage | loadJS( 'MediaWiki:Gadget-HotCat.js/' + conf.wgUserLanguage ); | ||
} | } | ||
} else { | } else { | ||
Line 321: | Line 400: | ||
var namespaceIds = conf.wgNamespaceIds; | var namespaceIds = conf.wgNamespaceIds; | ||
function autoLocalize( namespaceNumber, fallback ) { | function autoLocalize( namespaceNumber, fallback ) { | ||
function | function create_regexp_str( name ) { | ||
if ( !name || | 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. | var initial = name.substr( i, 1 ); | ||
var ll = initial.toLowerCase(); | |||
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 | return regex_name | ||
Line 338: | Line 420: | ||
fallback = fallback.toLowerCase(); | fallback = fallback.toLowerCase(); | ||
var canonical = formattedNamespaces[ String( namespaceNumber ) ].toLowerCase(); | var canonical = formattedNamespaces[ String( namespaceNumber ) ].toLowerCase(); | ||
var regexp = | var regexp = create_regexp_str( canonical ); | ||
if ( fallback && canonical !== fallback ) regexp += '|' + | if ( fallback && canonical !== fallback ) { regexp += '|' + create_regexp_str( fallback ); } | ||
if ( namespaceIds ) { | if ( namespaceIds ) { | ||
for ( var cat_name in namespaceIds ) { | for ( var cat_name in namespaceIds ) { | ||
Line 349: | Line 430: | ||
namespaceIds[ cat_name ] === namespaceNumber | namespaceIds[ cat_name ] === namespaceNumber | ||
) { | ) { | ||
regexp += '|' + | regexp += '|' + create_regexp_str( cat_name ); | ||
} | } | ||
} | } | ||
Line 356: | Line 437: | ||
} | } | ||
HotCat.category_canonical = formattedNamespaces[ '14' ]; | |||
HotCat.category_regexp = autoLocalize( 14, 'category' ); | |||
if ( formattedNamespaces[ '10' ] ) | 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 | // 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 make( arg, literal ) { | function make( arg, literal ) { | ||
if ( !arg ) return null; | if ( !arg ) { return null; } | ||
return literal ? document.createTextNode( arg ) : document.createElement( arg ); | return literal ? document.createTextNode( arg ) : document.createElement( arg ); | ||
} | } | ||
function param( name, uri ) { | function param( name, uri ) { | ||
uri = | if ( typeof uri === 'undefined' || uri === null ) { 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 ) { | ||
Line 385: | Line 466: | ||
// href="/wiki/..." | // href="/wiki/..." | ||
var prefix = conf.wgArticlePath.replace( '$1', '' ); | var prefix = conf.wgArticlePath.replace( '$1', '' ); | ||
if ( href.indexOf( prefix ) ) prefix = conf.wgServer + prefix; // Fully expanded URL? | if ( href.indexOf( prefix ) !== 0 ) { | ||
prefix = conf.wgServer + prefix; // Fully expanded URL? | |||
if ( href.indexOf( prefix ) && prefix.substring( 0, 2 ) === '//' ) prefix = document.location.protocol + prefix; // Protocol-relative wgServer? | } | ||
if ( href.indexOf( prefix ) !== 0 && prefix.substring( 0, 2 ) === '//' ) { | |||
if ( href.indexOf( prefix ) === 0 ) return decodeURIComponent( href.substring( prefix.length ) ); | prefix = document.location.protocol + prefix; // Protocol-relative wgServer? | ||
} | |||
if ( href.indexOf( prefix ) === 0 ) { | |||
return decodeURIComponent( href.substring( prefix.length ) ); | |||
} | |||
} | } | ||
return null; | return null; | ||
Line 397: | Line 482: | ||
} | } | ||
function capitalize( str ) { | function capitalize( str ) { | ||
if ( !str || | if ( !str || str.length === 0 ) { return str; } | ||
return str.substr( 0, 1 ).toUpperCase() + str.substr( 1 ); | return str.substr( 0, 1 ).toUpperCase() + str.substr( 1 ); | ||
} | } | ||
Line 431: | Line 515: | ||
// 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 k = alpha || key || idx; | ||
var replacement = typeof map[ k ] === 'function' ? map[ k ]( match, k ) : map[ k ]; | var replacement = typeof map[ k ] === 'function' ? map[ k ]( match, k ) : map[ k ]; | ||
Line 445: | Line 529: | ||
var substitute = substituteFactory(); | var substitute = substituteFactory(); | ||
var replaceShortcuts = ( function () { | var replaceShortcuts = ( function () { | ||
var replaceHash = substituteFactory( { | var replaceHash = substituteFactory( { indicator: '#', lbrace: '[', rbrace: ']' } ); | ||
return function ( str, map ) { | return function ( str, map ) { | ||
var s = replaceHash( str, map ); | var s = replaceHash( str, map ); | ||
return | return HotCat.capitalizePageNames ? capitalize( s ) : s; | ||
}; | }; | ||
}() ); | }() ); | ||
Line 459: | Line 539: | ||
var findCatsRE = | var findCatsRE = | ||
new RegExp( '\\[\\[' + wikiTextBlankOrBidi + '(?:' + HotCat.category_regexp + ')' + wikiTextBlankOrBidi + ':[^\\]]+\\]\\]', 'g' ); | |||
function replaceByBlanks( match ) { | function replaceByBlanks( match ) { | ||
Line 467: | Line 547: | ||
function find_category( wikitext, category, once ) { | function find_category( wikitext, category, once ) { | ||
var cat_regex = null; | var cat_regex = null; | ||
if ( | if ( HotCat.template_categories[ category ] ) { | ||
cat_regex = new RegExp( | cat_regex = new RegExp( | ||
'\\{\\{' + wikiTextBlankOrBidi + '(' + | '\\{\\{' + wikiTextBlankOrBidi + '(' + HotCat.template_regexp + '(?=' + wikiTextBlankOrBidi + ':))?' + wikiTextBlankOrBidi + | ||
'(?:' + | '(?:' + HotCat.template_categories[ category ] + ')' + | ||
wikiTextBlankOrBidi + '(\\|.*?)?\\}\\}', | wikiTextBlankOrBidi + '(\\|.*?)?\\}\\}', | ||
'g' | 'g' | ||
Line 478: | Line 558: | ||
var initial = cat_name.substr( 0, 1 ); | var initial = cat_name.substr( 0, 1 ); | ||
cat_regex = new RegExp( | cat_regex = new RegExp( | ||
'\\[\\[' + wikiTextBlankOrBidi + '(' + | '\\[\\[' + wikiTextBlankOrBidi + '(' + HotCat.category_regexp + ')' + wikiTextBlankOrBidi + ':' + wikiTextBlankOrBidi + | ||
( initial === '\\' || ! | ( initial === '\\' || !HotCat.capitalizePageNames ? | ||
initial : | initial : | ||
'[' + initial.toUpperCase() + initial.toLowerCase() + ']' ) + | '[' + initial.toUpperCase() + initial.toLowerCase() + ']' | ||
) + | |||
cat_name.substring( 1 ).replace( wikiTextBlankRE, wikiTextBlank ) + | cat_name.substring( 1 ).replace( wikiTextBlankRE, wikiTextBlank ) + | ||
wikiTextBlankOrBidi + '(\\|.*?)?\\]\\]', | wikiTextBlankOrBidi + '(\\|.*?)?\\]\\]', | ||
Line 487: | Line 568: | ||
); | ); | ||
} | } | ||
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 ) | ||
Line 495: | Line 575: | ||
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( { | result.push( { match: curr_match } ); | ||
} | } | ||
result.re = cat_regex; | result.re = cat_regex; | ||
Line 514: | Line 592: | ||
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 | |||
// a sequence of groups consisting of a dash followed by one or more lower case letters. Exceptions are "simple" | |||
// 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 { | |||
} | } | ||
return { | return { idx: index, onCat: index >= 0 }; | ||
} | } | ||
var summary = [] | var summary = []; | ||
var nameSpace = HotCat.category_canonical; | |||
var cat_point = -1; // Position of removed category; | |||
if ( key ) { key = '|' + key; } | |||
var keyChange = ( toRemove && toAdd && toRemove === toAdd && toAdd.length > 0 ); | |||
var matches; | |||
if ( toRemove && toRemove.length ) { | if ( toRemove && toRemove.length > 0 ) { | ||
matches = find_category( wikitext, toRemove ); | matches = find_category( wikitext, toRemove ); | ||
if ( !matches || | if ( !matches || matches.length === 0 ) { | ||
return { | return { text: wikitext, summary: summary, error: HotCat.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 ); | |||
if ( matches.length > 1 ) { | if ( matches.length > 1 ) { | ||
// 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; | |||
if ( key === null ) key = matches[ 0 ].match[ 2 ]; | 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 572: | Line 637: | ||
// 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 ) j++; | while ( j < after.length && after.charAt( j ) !== '\n' && after.substr( j, 1 ).search( /\s/ ) >= 0 ) { j++; } | ||
if ( i >= 0 && before.charAt( i ) === '\n' && ( after.length === 0 || j < after.length && after.charAt( j ) === '\n' ) ) { i--; } | |||
if ( i >= 0 && before.charAt( i ) === '\n' && ( | if ( i >= 0 ) { | ||
before = before.substring( 0, i + 1 ); | |||
if ( i >= 0 ) before = before.substring( 0, i + 1 ); else before = ''; | } else { | ||
before = ''; | |||
if ( j < after.length ) after = after.substring( j ); else after = ''; | } | ||
if ( j < after.length ) { | |||
after = after.substring( j ); | |||
} else { | |||
after = ''; | |||
} | |||
if ( | if ( | ||
before.length && before.substring( before.length - 1 ).search( /\S/ ) >= 0 && | before.length > 0 && before.substring( before.length - 1 ).search( /\S/ ) >= 0 && | ||
after.length && after.substr( 0, 1 ).search( /\S/ ) >= 0 | after.length > 0 && after.substr( 0, 1 ).search( /\S/ ) >= 0 ) { | ||
before += ' '; | before += ' '; | ||
} | } | ||
cat_point = before.length; | cat_point = before.length; | ||
if ( cat_point === 0 && after.length && after.substr( 0, 1 ) === '\n' ) after = after.substr( 1 ); | if ( cat_point === 0 && after.length > 0 && after.substr( 0, 1 ) === '\n' ) { | ||
after = after.substr( 1 ); | |||
} | |||
wikitext = before + after; | wikitext = before + after; | ||
if ( !keyChange ) { | if ( !keyChange ) { | ||
if ( | if ( HotCat.template_categories[ 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 ) { | |||
if ( toAdd && toAdd.length ) { | |||
matches = find_category( wikitext, toAdd ); | matches = find_category( wikitext, toAdd ); | ||
if ( matches && matches.length ) { | if ( matches && matches.length > 0 ) { | ||
return { text: wikitext, summary: summary, error: HotCat.messages.cat_exists.replace( /\$1/g, toAdd ) }; | |||
return { | } else { | ||
} else { | |||
var onCat = false; | var onCat = false; | ||
if ( cat_point < 0 ) { | if ( cat_point < 0 ) { | ||
Line 623: | Line 687: | ||
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 && suffix.substr( 0, 1 ) !== '\n' ) wikitext += '\n' + suffix; else wikitext += suffix; | if ( suffix.length > 0 && suffix.substr( 0, 1 ) !== '\n' ) { | ||
wikitext += '\n' + suffix; | |||
} else { | |||
wikitext += suffix; | |||
} | |||
} else { | } else { | ||
if ( wikitext.length && wikitext.substr( wikitext.length - 1, 1 ) !== '\n' ) wikitext += '\n'; | if ( wikitext.length > 0 && wikitext.substr( wikitext.length - 1, 1 ) !== '\n' ) { | ||
wikitext += '\n'; | |||
wikitext += ( wikitext.length ? '\n' : '' ) + newcatstring; | } | ||
wikitext += ( wikitext.length > 0 ? '\n' : '' ) + newcatstring; | |||
} | } | ||
if ( keyChange ) { | if ( keyChange ) { | ||
var k = key || ''; | var k = key || ''; | ||
if ( k.length ) k = k.substr( 1 ); | if ( k.length > 0 ) { k = k.substr( 1 ); } | ||
summary.push( substitute( HotCat.messages.cat_keychange, [ null, toAdd, k ] ) ); | |||
summary.push( substitute( | |||
} else { | } else { | ||
summary.push( | summary.push( HotCat.messages.cat_added.replace( /\$1/g, toAdd ) ); | ||
} | } | ||
if ( | if ( HotCat.uncat_regexp && !is_hidden ) { | ||
var txt = wikitext.replace( | var txt = wikitext.replace( HotCat.uncat_regexp, '' ); // Remove "uncat" templates | ||
if ( txt.length !== wikitext.length ) { | if ( txt.length !== wikitext.length ) { | ||
wikitext = txt; | wikitext = txt; | ||
summary.push( | summary.push( HotCat.messages.uncat_removed ); | ||
} | } | ||
} | } | ||
} | } | ||
} | } | ||
return { | return { text: wikitext, summary: summary, error: null }; | ||
} | } | ||
Line 657: | Line 721: | ||
function evtKeys( e ) { | function evtKeys( e ) { | ||
/* eslint-disable no-bitwise */ | /* eslint-disable no-bitwise */ | ||
e = e || window.event || window.Event; // W3C, IE, Netscape | |||
var code = 0; | var code = 0; | ||
if ( e.ctrlKey ) { // All modern browsers | if ( typeof e.ctrlKey !== 'undefined' ) { // All modern browsers | ||
// Ctrl-click seems to be overloaded in FF/Mac (it opens a pop-up menu), so treat cmd-click | |||
// as a ctrl-click, too. | |||
if ( e.ctrlKey || e.metaKey ) code |= 1; | if ( e.ctrlKey || e.metaKey ) { code |= 1; } | ||
if ( e.shiftKey ) { code |= 2; } | |||
if ( e. | } else if ( typeof e.modifiers !== 'undefined' ) { // Netscape... | ||
if ( e.modifiers & ( Event.CONTROL_MASK | Event.META_MASK ) ) { code |= 1; } | |||
if ( e.modifiers & Event.SHIFT_MASK ) { code |= 2; } | |||
} | } | ||
/* eslint-enable no-bitwise */ | |||
return code; | return code; | ||
} | } | ||
function evtKill( e ) { | function evtKill( e ) { | ||
if ( e.preventDefault ) { | e = e || window.event || window.Event; // W3C, IE, Netscape | ||
if ( typeof e.preventDefault !== 'undefined' ) { | |||
e.preventDefault(); | e.preventDefault(); | ||
e.stopPropagation(); | e.stopPropagation(); | ||
Line 677: | Line 746: | ||
} | } | ||
var catLine = null | var catLine = null; | ||
var onUpload = false; | |||
var editors = []; | |||
var commitButton = null; | |||
var commitForm = null; | |||
var multiSpan = null; | |||
var pageText = null; | |||
var pageTime = null; | |||
var pageWatched = false; | |||
var watchCreate = false; | |||
var watchEdit = false; | |||
var minorEdits = false; | |||
var editToken = null; | |||
var is_rtl = false; | |||
var serverTime = null; | |||
var lastRevId = null; | |||
var pageTextRevId = null; | |||
var conflictingUser = null; | |||
var newDOM = false; // true if MediaWiki serves the new UL-LI DOM for categories | |||
function setMultiInput() { | |||
if ( commitButton || onUpload ) { return; } | |||
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 | function checkMultiInput() { | ||
if ( !commitButton ) { return; } | |||
var has_changes = false; | |||
for ( var i = 0; i < editors.length; i++ ) { | |||
if ( editors[ i ].state !== CategoryEditor.UNCHANGED ) { | |||
has_changes = true; | |||
break; | |||
} | |||
} | |||
commitButton.disabled = !has_changes; | |||
} | } | ||
function | function currentTimestamp() { | ||
var | var now = new Date(); | ||
var ts = String( 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; | ||
Line 763: | Line 820: | ||
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. | ||
conf.wgServer + conf.wgScriptPath + '/api.php | getJSON( { | ||
'format=json&action=query&rawcontinue=&titles=' + encodeURIComponent( conf.wgPageName ) + | 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' + | |||
function ( json ) { | '&rvlimit=2&rvdir=newer&rvstartid=' + conf.wgCurRevisionId + | ||
'&meta=siteinfo%7Cuserinfo&uiprop=options', | |||
success: function ( json ) { | |||
setPage( json ); | setPage( json ); | ||
doEdit( fail ); | doEdit( fail ); | ||
}, | |||
error: function ( req ) { | |||
fail( req.status + ' ' + req.statusText ); | |||
} | } | ||
} ); | } ); | ||
} | } | ||
function multiChangeMsg( count ) { | function multiChangeMsg( count ) { | ||
var msg = | var msg = HotCat.messages.multi_change; | ||
if ( typeof msg !== 'string' && msg.length ) | if ( typeof msg !== 'string' && msg.length ) { | ||
if ( mw.language && mw.language.convertPlural ) { msg = mw.language.convertPlural( count, msg ); } else { msg = msg[ msg.length - 1 ]; } | if ( mw.language && mw.language.convertPlural ) { | ||
msg = mw.language.convertPlural( count, msg ); | |||
} else { | |||
msg = msg[ msg.length - 1 ]; | |||
} | |||
} | |||
return substitute( msg, [ null, String( count ) ] ); | return substitute( msg, [ null, String( count ) ] ); | ||
} | } | ||
function performChanges( failure, singleEditor ) { | function performChanges( failure, singleEditor ) { | ||
if ( pageText === null ) { | if ( pageText === null ) { | ||
failure( | failure( HotCat.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 ( | if ( HotCat.messages.cat_keychange.indexOf( '$2' ) < 0 ) { HotCat.messages.cat_keychange += '"$2"'; } | ||
// More backwards-compatibility with earlier HotCat versions: | // More backwards-compatibility with earlier HotCat versions: | ||
if ( ! | if ( !HotCat.messages.short_catchange ) { HotCat.messages.short_catchange = '[[' + HotCat.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 829: | Line 876: | ||
// 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 | var editingOldVersion = lastRevId !== null && lastRevId !== conf.wgCurRevisionId || pageTextRevId !== null && pageTextRevId !== conf.wgCurRevisionId; | ||
var selfEditConflict = editingOldVersion && conflictingUser && conflictingUser === conf.wgUserName; | |||
if ( singleEditor && !singleEditor.noCommit && ! | if ( singleEditor && !singleEditor.noCommit && !HotCat.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 = { | var result = { text: pageText }; | ||
var changed = [], added = [], deleted = [], changes = 0; | |||
var toEdit = singleEditor ? [ singleEditor ] : editors; | |||
var error = null; | |||
var i; | |||
for ( i = 0; i < toEdit.length; i++ ) { | for ( i = 0; i < toEdit.length; i++ ) { | ||
if ( toEdit[ i ].state === CategoryEditor.CHANGED ) { | |||
result = change_category( | result = change_category( | ||
result.text, | result.text, | ||
toEdit[ i ].originalCategory, | |||
toEdit[ i ].currentCategory, | |||
toEdit[ i ].currentKey, | |||
toEdit[ i ].currentHidden | |||
); | |||
if ( !result.error ) { | if ( !result.error ) { | ||
changes++; | changes++; | ||
if ( ! | if ( !toEdit[ i ].originalCategory || toEdit[ i ].originalCategory.length === 0 ) { | ||
added.push( | added.push( toEdit[ i ].currentCategory ); | ||
} else { | } else { | ||
changed.push( { | changed.push( { from: toEdit[ i ].originalCategory, to: toEdit[ i ].currentCategory } ); | ||
} | } | ||
} else if ( error === null ) { | } else if ( error === null ) { | ||
Line 875: | Line 913: | ||
} | } | ||
} else if ( | } else if ( | ||
toEdit[ i ].state === CategoryEditor.DELETED && | |||
result = change_category( | toEdit[ i ].originalCategory && | ||
toEdit[ i ].originalCategory.length > 0 | |||
) { | |||
result = change_category( result.text, toEdit[ i ].originalCategory, null, null, false ); | |||
if ( !result.error ) { | if ( !result.error ) { | ||
changes++; | changes++; | ||
deleted.push( | deleted.push( toEdit[ i ].originalCategory ); | ||
} else if ( error === null ) { | } else if ( error === null ) { | ||
error = result.error; | error = result.error; | ||
Line 890: | Line 928: | ||
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 = | commitForm.wpWatchthis.checked = conf.wgArticleId === 0 && watchCreate || watchEdit || pageWatched; | ||
if ( conf.wgArticleId || !!singleEditor ) { | if ( conf.wgArticleId > 0 || !!singleEditor ) { | ||
if ( changes === 1 ) { | |||
if ( | if ( result.summary && result.summary.length > 0 ) { | ||
if ( | commitForm.wpSummary.value = HotCat.messages.prefix + result.summary.join( HotCat.messages.separator ) + HotCat.messages.using; | ||
commitForm. | |||
} | } | ||
commitForm.wpMinoredit.checked = HotCat.single_minor || minorEdits; | |||
} else if ( changes > 1 ) { | |||
commitForm.wpMinoredit.checked = | |||
} else if ( changes ) { | |||
var summary = []; | var summary = []; | ||
var shortSummary = []; | var shortSummary = []; | ||
// Deleted | // Deleted | ||
for ( i = 0; i < deleted.length; i++ ) summary.push( '-' + substitute( | for ( i = 0; i < deleted.length; i++ ) { | ||
summary.push( '-' + substitute( HotCat.messages.short_catchange, [ null, deleted[ i ] ] ) ); | |||
if ( deleted.length === 1 ) shortSummary.push( '-' + substitute( | } | ||
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++ ) summary.push( '+' + substitute( | for ( i = 0; i < added.length; i++ ) { | ||
summary.push( '+' + substitute( HotCat.messages.short_catchange, [ null, added[ i ] ] ) ); | |||
if ( added.length === 1 ) shortSummary.push( '+' + substitute( | } | ||
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( | summary.push( '±' + substitute( HotCat.messages.short_catchange, [ null, changed[ i ].from ] ) + arrow + | ||
substitute( HotCat.messages.short_catchange, [ null, changed[ i ].to ] ) ); | |||
} else { | } else { | ||
summary.push( '±' + substitute( | summary.push( '±' + substitute( HotCat.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( | shortSummary.push( '±' + substitute( HotCat.messages.short_catchange, [ null, changed[ 0 ].from ] ) + arrow + | ||
substitute( HotCat.messages.short_catchange, [ null, changed[ 0 ].to ] ) ); | |||
} else { | } else { | ||
shortSummary.push( '±' + substitute( | shortSummary.push( '±' + substitute( HotCat.messages.short_catchange, [ null, changed[ 0 ].from ] ) ); | ||
} | } | ||
} else if ( changed.length ) { | } else if ( changed.length > 1 ) { | ||
shortSummary.push( '± ' + multiChangeMsg( changed.length ) ); | shortSummary.push( '± ' + multiChangeMsg( changed.length ) ); | ||
} | } | ||
if ( summary.length ) { | if ( summary.length > 0 ) { | ||
summary = summary.join( | summary = summary.join( HotCat.messages.separator ); | ||
if ( summary.length > 200 - | if ( summary.length > 200 - HotCat.messages.prefix.length - HotCat.messages.using.length ) { | ||
summary = shortSummary.join( HotCat.messages.separator ); | |||
commitForm.wpSummary.value = | } | ||
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 = String( 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 ) { | |||
var i; | |||
for ( i = 0; i < toResolve.length; i++ ) { | |||
toResolve[ i ].dab = null; | |||
toResolve[ i ].dabInput = toResolve[ i ].lastInput; | |||
} | |||
if ( noSuggestions ) { | |||
callback( toResolve ); | |||
return; | |||
} | |||
// 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 ) { | function resolveOne( page, toResolve ) { | ||
var cats = page.categories | 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++ ) { | for ( i = 0; i < toResolve.length; i++ ) { | ||
if ( | 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 = ( is_missing ? | toResolve[ i ].icon.src = armorUri( is_missing ? HotCat.existsNo : HotCat.existsYes ); | ||
} | } | ||
if ( is_missing ) return; | if ( is_missing ) { return; } | ||
if ( !is_redir && cats && ( | if ( !is_redir && cats && ( HotCat.disambig_category || HotCat.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; | ||
Line 987: | Line 1,052: | ||
if ( cat ) { | if ( cat ) { | ||
cat = cat.substring( cat.indexOf( ':' ) + 1 ).replace( /_/g, ' ' ); | cat = cat.substring( cat.indexOf( ':' ) + 1 ).replace( /_/g, ' ' ); | ||
if ( cat === | if ( cat === HotCat.disambig_category ) { | ||
is_dab = true; | is_dab = true; break; | ||
} else if ( cat === HotCat.redir_category ) { | |||
} else if ( cat === | is_redir = true; break; | ||
is_redir = true; | |||
} | } | ||
} | } | ||
} | } | ||
} | } | ||
if ( !is_redir && !is_dab ) return; | if ( !is_redir && !is_dab ) { return; } | ||
if ( !lks || | if ( !lks || lks.length === 0 ) { return; } | ||
var titles = []; | var titles = []; | ||
for ( i = 0; i < lks.length; i++ ) { | for ( i = 0; i < lks.length; i++ ) { | ||
Line 1,005: | Line 1,068: | ||
lks[ i ].ns === 14 && | lks[ i ].ns === 14 && | ||
// Name not empty | // Name not empty | ||
lks[ i ].title && lks[ i ].title.length | lks[ i ].title && lks[ i ].title.length > 0 | ||
) { | ) { | ||
// 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. | ||
Line 1,011: | Line 1,074: | ||
match = match.substring( match.indexOf( ':' ) + 1 ); | match = match.substring( match.indexOf( ':' ) + 1 ); | ||
// Exclude blacklisted categories. | // Exclude blacklisted categories. | ||
if ( ! | if ( !HotCat.blacklist || !HotCat.blacklist.test( match ) ) { | ||
titles.push( match ); | |||
} | |||
} | } | ||
} | } | ||
if ( | if ( titles.length === 0 ) { | ||
return; | |||
} | |||
for ( i = 0; i < toResolve.length; i++ ) { | for ( i = 0; i < toResolve.length; i++ ) { | ||
if ( | if ( toResolve.length > 1 && 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 ].inputExists = true; // Might actually be wrong if it's a redirect pointing to a non-existing category | ||
toResolve[ i ].icon.src = | toResolve[ i ].icon.src = armorUri( HotCat.existsYes ); | ||
if ( titles.length > 1 ) { | if ( titles.length > 1 ) { | ||
toResolve[ i ].dab = titles; | toResolve[ i ].dab = titles; | ||
Line 1,029: | Line 1,096: | ||
function resolveRedirects( toResolve, params ) { | function resolveRedirects( toResolve, params ) { | ||
if ( !params || !params.query || !params.query.pages ) return; | if ( !params || !params.query || !params.query.pages ) { return; } | ||
for ( var p in params.query.pages ) resolveOne( params.query.pages[ p ], toResolve ); | for ( var p in params.query.pages ) { resolveOne( params.query.pages[ p ], toResolve ); } | ||
} | } | ||
function | function multiSubmit() { | ||
var | var toResolve = []; | ||
for ( i = 0; i < | for ( var i = 0; i < editors.length; i++ ) { | ||
if ( editors[ i ].state === CategoryEditor.CHANGE_PENDING || editors[ i ].state === CategoryEditor.OPEN ) { | |||
toResolve.push( editors[ i ] ); | |||
} | |||
} | } | ||
if ( | if ( toResolve.length === 0 ) { | ||
initiateEdit( function ( failure ) { performChanges( failure ); }, function ( msg ) { alert( msg ); } ); | |||
return; | return; | ||
} | } | ||
resolveMulti( toResolve, function ( resolved ) { | |||
var firstDab = null; | |||
var dontChange = false; | |||
for ( var i = 0; i < resolved.length; i++ ) { | |||
if ( resolved[ i ].lastInput !== resolved[ i ].dabInput ) { | |||
// We didn't disable all the open editors, but we did asynchronous calls. It is | |||
// theoretically possible that the user changed something... | |||
var | dontChange = true; | ||
} else { | |||
if ( resolved[ i ].dab ) { | |||
if ( !firstDab ) { firstDab = resolved[ i ]; } | |||
} else { | |||
if ( resolved[ i ].acceptCheck( true ) ) { resolved[ i ].commit(); } | |||
} | |||
} | |||
} | } | ||
} | } | ||
if ( | if ( firstDab ) { | ||
showDab( firstDab ); | |||
} else if ( !dontChange ) { | |||
initiateEdit( function ( failure ) { performChanges( failure ); }, function ( msg ) { alert( msg ); } ); | |||
} | |||
} ); | |||
} | |||
var cat_prefix = null; | |||
if ( | var noSuggestions = false; | ||
var suggestionEngines = { | |||
opensearch: { | |||
uri: '/api.php?format=json&action=opensearch&namespace=14&limit=30&search=Category:$1', // $1 = search term | |||
// Function to convert result of uri into an array of category names | |||
handler: function ( queryResult, queryKey ) { | |||
}, | if ( queryResult && queryResult.length >= 2 ) { | ||
var key = queryResult[ 0 ].substring( queryResult[ 0 ].indexOf( ':' ) + 1 ); | |||
var titles = queryResult[ 1 ]; | |||
var exists = false; | |||
if ( !cat_prefix ) { cat_prefix = new RegExp( '^(' + HotCat.category_regexp + ':)' ); } | |||
for ( var i = 0; i < titles.length; i++ ) { | |||
cat_prefix.lastIndex = 0; | |||
var m = cat_prefix.exec( titles[ i ] ); | |||
if ( m && m.length > 1 ) { | |||
titles[ i ] = titles[ i ].substring( titles[ i ].indexOf( ':' ) + 1 ); // rm namespace | |||
if ( key === titles[ i ] ) { exists = true; } | |||
} else { | |||
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; | |||
} | } | ||
} | }, | ||
internalsearch: { | |||
uri: '/api.php?format=json&action=query&list=allpages&apnamespace=14&aplimit=30&apfrom=$1&apprefix=$1', | |||
handler: function ( queryResult ) { | |||
if ( queryResult && queryResult.query && queryResult.query.allpages ) { | |||
var titles = queryResult.query.allpages; | |||
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; | |||
} | } | ||
}, | |||
exists: { | |||
uri: '/api.php?format=json&action=query&prop=info&titles=Category:$1', | |||
handler: function ( queryResult, queryKey ) { | |||
if ( queryResult && queryResult.query && queryResult.query.pages && !queryResult.query.pages[ -1 ] ) { | |||
// Should have exactly 1 | |||
for ( var p in queryResult.query.pages ) { | |||
} | var title = queryResult.query.pages[ p ].title; | ||
title = title.substring( title.indexOf( ':' ) + 1 ); | |||
var titles = [ title ]; | |||
titles.exists = true; | |||
if ( queryKey !== title ) { titles.normalized = title; } // NFC | |||
return titles; | |||
} | |||
} | |||
return null; | |||
} | } | ||
} | }, | ||
subcategories: { | |||
uri: '/api.php?format=json&action=query&list=categorymembers&cmtype=subcat&cmlimit=max&cmtitle=Category:$1', | |||
handler: function ( queryResult ) { | |||
if ( queryResult && queryResult.query && queryResult.query.categorymembers ) { | |||
var titles = queryResult.query.categorymembers; | |||
uri: '/api.php?format=json&action= | |||
handler: function ( queryResult | |||
if ( queryResult && queryResult. | |||
var titles = queryResult | |||
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 | |||
} | } | ||
return titles; | return titles; | ||
} | } | ||
Line 1,213: | Line 1,208: | ||
} | } | ||
}, | }, | ||
parentcategories: { | |||
uri: '/api.php?format=json&action=query& | uri: '/api.php?format=json&action=query&prop=categories&titles=Category:$1&cllimit=max', | ||
handler: function ( queryResult ) { | handler: function ( queryResult ) { | ||
if ( queryResult && queryResult.query && | if ( queryResult && queryResult.query && queryResult.query.pages ) { | ||
for ( var p in queryResult.query.pages ) { | for ( var p in queryResult.query.pages ) { | ||
var | if ( queryResult.query.pages[ p ].categories ) { | ||
var titles = queryResult.query.pages[ p ].categories; | |||
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; | ||
} | } | ||
} | |||
}; | |||
var suggestionConfigs = { | |||
searchindex: { name: 'Search index', engines: [ 'opensearch' ], cache: {}, 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 } | |||
} | |||
}; | |||
var suggestionConfigs = { | |||
searchindex: { | |||
pagelist: { | |||
combined: { | |||
subcat: { | |||
parentcat: { | |||
}; | }; | ||
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 | ||
Line 1,322: | Line 1,242: | ||
CategoryEditor.DELETED = 4; | CategoryEditor.DELETED = 4; | ||
// 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. | ||
Line 1,328: | Line 1,247: | ||
function forceRedraw() { | function forceRedraw() { | ||
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, | var BS = 8, TAB = 9, RET = 13, ESC = 27, SPACE = 32, PGUP = 33, PGDOWN = 34, UP = 38, DOWN = 40, DEL = 46, IME = 229; | ||
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 ) { | |||
showDab( which ); | |||
} else { | |||
// 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 ); | |||
} | |||
} | |||
} | |||
} | |||
initialize: function ( line, span, after, key, is_hidden ) { | 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 = { | |||
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 | ||
Line 1,374: | Line 1,333: | ||
this.linkSpan = make( 'span' ); | this.linkSpan = make( 'span' ); | ||
this.linkSpan.className = 'noprint nopopups hotcatlink'; | this.linkSpan.className = 'noprint nopopups hotcatlink'; | ||
var lk = make( 'a' ); | var lk = make( 'a' ); lk.href = '#catlinks'; lk.onclick = this.open.bind( this ); | ||
lk.appendChild( make( HotCat.links.add, true ) ); lk.title = HotCat.tooltips.add; | |||
lk.appendChild( make( | |||
this.linkSpan.appendChild( lk ); | this.linkSpan.appendChild( lk ); | ||
span = make( newDOM ? 'li' : 'span' ); | 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 ); | span.appendChild( this.linkSpan ); | ||
if ( after ) after.parentNode.insertBefore( span, after.nextSibling ); else line.appendChild( span ); | if ( after ) { | ||
after.parentNode.insertBefore( span, after.nextSibling ); | |||
} else { | |||
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; | ||
Line 1,400: | Line 1,357: | ||
// 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 = | this.engine = HotCat.suggestions; | ||
this.span = span; | this.span = span; | ||
this.currentCategory = this.originalCategory; | this.currentCategory = this.originalCategory; | ||
Line 1,418: | Line 1,374: | ||
this.lastSavedExists = this.originalExists; | this.lastSavedExists = this.originalExists; | ||
this.lastSavedHidden = this.originalHidden; | this.lastSavedHidden = this.originalHidden; | ||
if ( this.catLink && this.currentKey ) this.catLink.title = this.currentKey; | if ( this.catLink && this.currentKey ) { | ||
this.catLink.title = this.currentKey; | |||
} | |||
editors[ editors.length ] = this; | editors[ editors.length ] = this; | ||
}, | }, | ||
Line 1,426: | Line 1,383: | ||
this.normalLinks = make( 'span' ); | this.normalLinks = make( 'span' ); | ||
var lk = null; | var lk = null; | ||
if ( this.originalCategory && this.originalCategory.length ) { | if ( this.originalCategory && this.originalCategory.length > 0 ) { | ||
lk = make( 'a' ); | lk = make( 'a' ); lk.href = '#catlinks'; lk.onclick = this.remove.bind( this ); | ||
lk.appendChild( make( HotCat.links.remove, true ) ); lk.title = HotCat.tooltips.remove; | |||
lk.appendChild( make( | |||
this.normalLinks.appendChild( make( ' ', true ) ); | this.normalLinks.appendChild( make( ' ', true ) ); | ||
this.normalLinks.appendChild( lk ); | this.normalLinks.appendChild( lk ); | ||
} | } | ||
if ( ! | if ( !HotCat.template_categories[ this.originalCategory ] ) { | ||
lk = make( 'a' ); | lk = make( 'a' ); lk.href = '#catlinks'; lk.onclick = this.open.bind( this ); | ||
lk.appendChild( make( HotCat.links.change, true ) ); lk.title = HotCat.tooltips.change; | |||
lk.appendChild( make( | |||
this.normalLinks.appendChild( make( ' ', true ) ); | this.normalLinks.appendChild( make( ' ', true ) ); | ||
this.normalLinks.appendChild( lk ); | this.normalLinks.appendChild( lk ); | ||
if ( !noSuggestions && | if ( !noSuggestions && HotCat.use_up_down ) { | ||
this.upDownLinks = make( 'span' ); | this.upDownLinks = make( 'span' ); | ||
lk = make( 'a' ); | lk = make( 'a' ); lk.href = '#catlinks'; lk.onclick = this.down.bind( this ); | ||
lk.appendChild( make( HotCat.links.down, true ) ); lk.title = HotCat.tooltips.down; | |||
lk.appendChild( make( | |||
this.upDownLinks.appendChild( make( ' ', true ) ); | this.upDownLinks.appendChild( make( ' ', true ) ); | ||
this.upDownLinks.appendChild( lk ); | this.upDownLinks.appendChild( lk ); | ||
lk = make( 'a' ); | lk = make( 'a' ); lk.href = '#catlinks'; lk.onclick = this.up.bind( this ); | ||
lk.appendChild( make( HotCat.links.up, true ) ); lk.title = HotCat.tooltips.up; | |||
lk.appendChild( make( | |||
this.upDownLinks.appendChild( make( ' ', true ) ); | this.upDownLinks.appendChild( make( ' ', true ) ); | ||
this.upDownLinks.appendChild( lk ); | this.upDownLinks.appendChild( lk ); | ||
Line 1,468: | Line 1,413: | ||
this.undelLink.className = 'nopopups hotcatlink'; | this.undelLink.className = 'nopopups hotcatlink'; | ||
this.undelLink.style.display = 'none'; | this.undelLink.style.display = 'none'; | ||
lk = make( 'a' ); | lk = make( 'a' ); lk.href = '#catlinks'; lk.onclick = this.restore.bind( this ); | ||
lk.appendChild( make( HotCat.links.restore, true ) ); lk.title = HotCat.tooltips.restore; | |||
lk.appendChild( make( | |||
this.undelLink.appendChild( make( ' ', true ) ); | this.undelLink.appendChild( make( ' ', true ) ); | ||
this.undelLink.appendChild( lk ); | this.undelLink.appendChild( lk ); | ||
Line 1,479: | Line 1,421: | ||
invokeSuggestions: function ( dont_autocomplete ) { | invokeSuggestions: function ( dont_autocomplete ) { | ||
if ( this.engine && suggestionConfigs[ this.engine ] && suggestionConfigs[ this.engine ].temp && !dont_autocomplete ) this.engine = | if ( this.engine && suggestionConfigs[ this.engine ] && suggestionConfigs[ this.engine ].temp && !dont_autocomplete ) { | ||
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 () { | window.setTimeout( function () { self.textchange( dont_autocomplete ); }, HotCat.suggest_delay ); | ||
}, | }, | ||
makeForm: function () { | makeForm: function () { | ||
var form = make( 'form' ); | var form = make( 'form' ); | ||
form.method = 'POST'; | 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' ); | var text = make( 'input' ); text.type = 'text'; text.size = HotCat.editbox_width; | ||
if ( !noSuggestions ) { | 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: | ||
Line 1,507: | Line 1,445: | ||
// - 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. | // detected by a keyDown IME with a keyUp of space, tab, escape, or return. (Example: IE8) | ||
text.onkeyup = function ( evt ) { | text.onkeyup = | ||
function ( evt ) { | |||
evt = evt || window.event || window.Event; // W3C, IE, Netscape | |||
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 ( key === UP || key === DOWN || key === PGUP || key === PGDOWN ) { | |||
// In case a browser doesn't generate keypress events for arrow keys... | |||
if ( self.keyCount === 0 ) { return self.processKey( evt ); } | |||
} else { | |||
if ( key === ESC && self.lastKey !== IME ) { | |||
if ( !self.resetKeySelection() ) { | |||
// No undo of key selection: treat ESC as "cancel". | |||
self.cancel(); | |||
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; | |||
}; | |||
text.onkeydown = | |||
function ( evt ) { | |||
evt = evt || window.event || window.Event; // W3C, IE, Netscape | |||
var key = evt.keyCode || 0; | |||
self.lastKey = key; | |||
self.keyCount = 0; | |||
// DOM Level < 3 IME input | |||
if ( !self.ime && key === IME && !self.usesComposition ) { | |||
// self.usesComposition catches browsers that may emit spurious keydown IME after a composition has ended | |||
self.ime = true; | |||
} else if ( self.ime && key !== IME && !( key >= 16 && key <= 20 || key >= 91 && key <= 93 || key === 144 ) ) { | |||
// Ignore control keys: ctrl, shift, alt, alt gr, caps lock, windows/apple cmd keys, num lock. Only the windows keys | |||
// 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). | |||
self.ime = false; | |||
} | } | ||
if ( self.ime ) { return 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 ) { | text.onkeypress = function ( evt ) { self.keyCount++; return self.processKey( evt ); }; | ||
$( text ).on( 'focus', function () { makeActive( self ); } ); | |||
$( text ).on( 'focus', function () { | |||
// 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; | // Don't test for text.selectionStart being defined; FF3.6.4 raises an exception when trying to access that | ||
// property while the element is not being displayed. | |||
$( text ).on( | $( text ).on( | ||
( text.onbeforedeactivate !== undefined && text.createTextRange ) ? 'beforedeactivate' : 'blur' | ( typeof text.onbeforedeactivate !== 'undefined' && text.createTextRange ) ? 'beforedeactivate' : 'blur' | ||
this.saveView.bind( this ) ); | , this.saveView.bind( 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. | ||
$( text ).on( 'compositionstart', function () { | $( text ).on( 'compositionstart', function () { self.lastKey = IME; self.usesComposition = true; 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 ); } ); | |||
$( text ).on( 'compositionend', function () { | |||
$( text ).on( 'textInput', function () { | |||
} catch ( any ) { | } 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 | ||
} | } | ||
$( text ).on( 'blur', function () { | $( text ).on( 'blur', function () { self.usesComposition = false; self.ime = false; } ); | ||
} | } | ||
this.text = text; | this.text = text; | ||
Line 1,601: | Line 1,522: | ||
if ( !noSuggestions ) { | if ( !noSuggestions ) { | ||
list = make( 'select' ); | list = make( 'select' ); | ||
list.onclick = function () { | list.onclick = function () { if ( self.highlightSuggestion( 0 ) ) { self.textchange( false, true ); } }; | ||
list.ondblclick = function ( e ) { if ( self.highlightSuggestion( 0 ) ) { self.accept( e ); } }; | |||
list.onchange = function () { self.highlightSuggestion( 0 ); self.text.focus(); }; | |||
list.ondblclick = function ( e ) { | |||
list.onchange = function () { | |||
list.onkeyup = function ( evt ) { | list.onkeyup = function ( evt ) { | ||
evt = evt || window.event || window.Event; // W3C, IE, Netscape | |||
if ( evt.keyCode === ESC ) { | if ( evt.keyCode === ESC ) { | ||
self.resetKeySelection(); | self.resetKeySelection(); | ||
self.text.focus(); | self.text.focus(); | ||
window.setTimeout( function () { | window.setTimeout( function () { self.textchange( true ); }, HotCat.suggest_delay ); | ||
} else if ( evt.keyCode === RET ) { | } else if ( evt.keyCode === RET ) { | ||
self.accept( evt ); | self.accept( evt ); | ||
} | } | ||
}; | }; | ||
if ( ! | if ( !HotCat.fixed_search ) { | ||
var engineSelector = make( 'select' ); | var engineSelector = make( 'select' ); | ||
for ( var key in suggestionConfigs ) { | for ( var key in suggestionConfigs ) { | ||
Line 1,628: | Line 1,541: | ||
var opt = make( 'option' ); | 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 ) ); | opt.appendChild( make( suggestionConfigs[ key ].name, true ) ); | ||
engineSelector.appendChild( opt ); | engineSelector.appendChild( opt ); | ||
Line 1,648: | Line 1,560: | ||
if ( | if ( | ||
onUpload && | onUpload && | ||
typeof UFUI !== 'undefined' && | |||
typeof UIElements !== 'undefined' && | |||
UFUI.getLabel | typeof UFUI.getLabel === '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' ); | var OK = make( 'input' ); OK.type = 'button'; | ||
OK.value = button_label( 'wpOkUploadLbl', HotCat.messages.ok ); | |||
OK.value = button_label( 'wpOkUploadLbl', | |||
OK.onclick = this.accept.bind( this ); | OK.onclick = this.accept.bind( this ); | ||
this.ok = OK; | this.ok = OK; | ||
var cancel = make( 'input' ); | var cancel = make( 'input' ); cancel.type = 'button'; | ||
cancel.value = button_label( 'wpCancelUploadLbl', HotCat.messages.cancel ); | |||
cancel.value = button_label( 'wpCancelUploadLbl', | |||
cancel.onclick = this.cancel.bind( this ); | cancel.onclick = this.cancel.bind( this ); | ||
this.cancelButton = cancel; | this.cancelButton = cancel; | ||
Line 1,681: | Line 1,590: | ||
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 | |||
// 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 ); | span.appendChild( text ); | ||
// | // IE8/IE9: 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 | |||
// 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 ( this.engineSelector ) span.appendChild( this.engineSelector ); | if ( !noSuggestions ) { span.appendChild( this.icon ); } | ||
if ( !noSuggestions ) span.appendChild( this.icon ); | |||
span.appendChild( OK ); | span.appendChild( OK ); | ||
span.appendChild( cancel ); | span.appendChild( cancel ); | ||
Line 1,716: | Line 1,624: | ||
} | } | ||
} | } | ||
if ( !this.form ) this.makeForm(); | if ( !this.form ) { | ||
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 = ( this.currentExists ? | this.icon.src = armorUri( this.currentExists ? HotCat.existsYes : HotCat.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; | ||
Line 1,732: | Line 1,639: | ||
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 = { | 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'; | ||
Line 1,754: | Line 1,657: | ||
var result = this.display( evt ); | var result = this.display( evt ); | ||
var v = this.lastSavedCategory; | var v = this.lastSavedCategory; | ||
if ( | if ( v.length === 0 ) { return result; } | ||
this.text.readOnly = !!readOnly; | this.text.readOnly = !!readOnly; | ||
this.engine = engine; | this.engine = engine; | ||
Line 1,764: | Line 1,666: | ||
open: function ( evt ) { | open: function ( evt ) { | ||
return this.show( evt, ( this.engine && suggestionConfigs[ this.engine ].temp ) ? | return this.show( evt, ( this.engine && suggestionConfigs[ this.engine ].temp ) ? HotCat.suggestions : this.engine ); | ||
}, | }, | ||
Line 1,783: | Line 1,685: | ||
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; | ||
Line 1,791: | Line 1,692: | ||
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 ) { this.catLink.title = this.currentKey; } else { this.catLink.title = ''; } | if ( this.currentKey && this.currentKey.length > 0 ) { | ||
this.catLink.title = this.currentKey; | |||
} else { | |||
this.catLink.title = ''; | |||
} | |||
} | |||
if ( this.state === CategoryEditor.UNCHANGED ) { | if ( this.state === CategoryEditor.UNCHANGED ) { | ||
if ( this.catLink ) this.catLink.style.backgroundColor = 'transparent'; | if ( this.catLink ) { this.catLink.style.backgroundColor = 'transparent'; } | ||
} else { | } else { | ||
if ( !onUpload ) { | if ( !onUpload ) { | ||
try { | try { | ||
this.catLink.style.backgroundColor = | this.catLink.style.backgroundColor = HotCat.bg_changed; | ||
} catch ( ex ) {} | } catch ( ex ) {} | ||
} | } | ||
Line 1,810: | Line 1,715: | ||
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 ); | ||
Line 1,820: | Line 1,725: | ||
} | } | ||
checkMultiInput(); | checkMultiInput(); | ||
var self = this; | |||
// eslint-disable-next-line no-delete-var | |||
window.setTimeout( function () { delete self; }, 10 ); | |||
}, | }, | ||
Line 1,834: | Line 1,742: | ||
this.lastSavedHidden = this.originalHidden; | this.lastSavedHidden = this.originalHidden; | ||
this.state = CategoryEditor.UNCHANGED; | this.state = CategoryEditor.UNCHANGED; | ||
if ( !this.currentCategory || | if ( !this.currentCategory || this.currentCategory.length === 0 ) { | ||
// It was a newly added category. Remove the whole editor. | // It was a newly added category. Remove the whole editor. | ||
this.removeEditor(); | this.removeEditor(); | ||
Line 1,841: | Line 1,749: | ||
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( | this.catLink.href = wikiPagePath( HotCat.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(); | ||
} | } | ||
Line 1,853: | Line 1,760: | ||
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; | ||
}, | }, | ||
Line 1,864: | Line 1,769: | ||
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, '' ); | var v = value[ 0 ].replace( /_/g, ' ' ).replace( /^\s+|\s+$/g, '' ); | ||
if ( | if ( HotCat.capitalizePageNames ) { v = capitalize( v ); } | ||
this.lastInput = v; | this.lastInput = v; | ||
v = replaceShortcuts( v, | v = replaceShortcuts( v, HotCat.shortcuts ); | ||
if ( | if ( v.length === 0 ) { | ||
this.cancel(); | this.cancel(); | ||
return false; | return false; | ||
} | } | ||
if ( !dontCheck && ( | if ( | ||
!dontCheck && ( | |||
conf.wgNamespaceNumber === 14 && v === conf.wgTitle || | |||
HotCat.blacklist && HotCat.blacklist.test( v ) | |||
) | |||
) { | |||
this.cancel(); | this.cancel(); | ||
return false; | return false; | ||
Line 1,900: | Line 1,807: | ||
resolved[ 0 ].commit( | resolved[ 0 ].commit( | ||
( resolved[ 0 ].currentCategory !== original ) ? | ( resolved[ 0 ].currentCategory !== original ) ? | ||
HotCat.messages.cat_resolved.replace( /\$1/g, original ) : | |||
null ); | null | ||
); | |||
} | } | ||
} | } | ||
Line 1,919: | Line 1,827: | ||
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( | this.catLink.href = wikiPagePath( HotCat.category_canonical + ':' + this.currentCategory ); | ||
this.catLink.className = this.currentExists ? '' : 'new'; | this.catLink.className = this.currentExists ? '' : 'new'; | ||
this.lastSavedCategory = this.currentCategory; | this.lastSavedCategory = this.currentCategory; | ||
Line 1,943: | Line 1,851: | ||
// Append an undo link. | // Append an undo link. | ||
var span = make( 'span' ); | var span = make( 'span' ); | ||
var lk = make( 'a' ); | var lk = make( 'a' ); lk.href = '#catlinks'; lk.onclick = this.rollback.bind( this ); | ||
lk.appendChild( make( HotCat.links.undo, true ) ); lk.title = HotCat.tooltips.undo; | |||
lk.appendChild( make( | |||
span.appendChild( make( ' ', true ) ); | span.appendChild( make( ' ', true ) ); | ||
span.appendChild( lk ); | span.appendChild( lk ); | ||
Line 1,954: | Line 1,859: | ||
if ( !onUpload ) { | if ( !onUpload ) { | ||
try { | try { | ||
this.catLink.style.backgroundColor = | this.catLink.style.backgroundColor = HotCat.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; | ||
Line 1,973: | Line 1,877: | ||
( | ( | ||
this.currentKey === this.originalKey || | this.currentKey === this.originalKey || | ||
this.currentKey === null && | this.currentKey === null && this.originalKey.length === 0 | ||
) | ) | ||
) || | ) || | ||
conf.wgNamespaceNumber === 14 && this.currentCategory === conf.wgTitle || | conf.wgNamespaceNumber === 14 && this.currentCategory === conf.wgTitle || | ||
HotCat.blacklist && HotCat.blacklist.test( this.currentCategory ) | |||
) { | ) { | ||
this.cancel(); | this.cancel(); | ||
Line 1,987: | Line 1,891: | ||
this.close(); | this.close(); | ||
var self = this; | var self = this; | ||
initiateEdit( function ( failure ) { | initiateEdit( function ( failure ) { performChanges( failure, self ); }, function ( msg ) { alert( msg ); } ); | ||
} | } | ||
}, | }, | ||
Line 2,018: | Line 1,918: | ||
this.catLink.style.cssText += '; text-decoration : line-through !important;'; | this.catLink.style.cssText += '; text-decoration : line-through !important;'; | ||
try { | try { | ||
this.catLink.style.backgroundColor = | this.catLink.style.backgroundColor = HotCat.bg_changed; | ||
} catch ( ex ) {} | } catch ( ex ) {} | ||
this.originalState = this.state; | this.originalState = this.state; | ||
Line 2,032: | Line 1,932: | ||
this.originalState = this.state; | this.originalState = this.state; | ||
this.state = CategoryEditor.DELETED; | this.state = CategoryEditor.DELETED; | ||
this.noCommit = noCommit || | this.noCommit = noCommit || HotCat.del_needs_diff; | ||
var self = this; | var self = this; | ||
initiateEdit( | initiateEdit( | ||
function ( failure ) { | function ( failure ) { performChanges( failure, self ); }, | ||
function ( msg ) { self.state = self.originalState; alert( msg ); } | |||
); | |||
function ( msg ) { | |||
} | } | ||
} | } | ||
Line 2,055: | Line 1,951: | ||
} else { | } else { | ||
try { | try { | ||
this.catLink.style.backgroundColor = | this.catLink.style.backgroundColor = HotCat.bg_changed; | ||
} catch ( ex ) {} | } catch ( ex ) {} | ||
} | } | ||
Line 2,067: | Line 1,963: | ||
selectEngine: function ( engineName ) { | selectEngine: function ( engineName ) { | ||
if ( !this.engineSelector ) return; | if ( !this.engineSelector ) { return; } | ||
for ( var i = 0; i < this.engineSelector.options.length; i++ ) this.engineSelector.options[ i ].selected = this.engineSelector.options[ i ].value === engineName; | for ( var i = 0; i < this.engineSelector.options.length; i++ ) { | ||
this.engineSelector.options[ i ].selected = this.engineSelector.options[ i ].value === engineName; | |||
} | |||
}, | }, | ||
Line 2,074: | Line 1,972: | ||
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( '^(' + | var re = new RegExp( '^(' + HotCat.category_regexp + '):' ); | ||
if ( re.test( v ) ) v = v.substring( v.indexOf( ':' ) + 1 ).replace( /^(\s|_)+/, '' ); | if ( re.test( v ) ) { | ||
v = v.substring( v.indexOf( ':' ) + 1 ).replace( /^(\s|_)+/, '' ); | |||
if ( | } | ||
if ( HotCat.capitalizePageNames ) { v = capitalize( v ); } | |||
// Only update the input field if there is a difference. | // Only update the input field if there is a difference. IE8 appears to reset the selection | ||
// reset | // and place the cursor at the front upon reset, which makes our autocompletetion become a | ||
if ( this.text.value !== null && this.text.value !== v ) this.text.value = v; | // nuisance. FF and IE6 don't seem to have this problem. | ||
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; | |||
var v = queryKey; | |||
var z = cleanKey; | |||
var 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.normalized ) cb.allTitles.normalized = cb.normalized; | if ( !cb.dontCache && !suggestionConfigs[ cb.engineName ].cache[ z ] ) { | ||
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; } | |||
if ( cb === thisObj.callbackObj ) thisObj.callbackObj = null; | // eslint-disable-next-line no-delete-var | ||
delete cb; | |||
cb | |||
} | } | ||
} | } | ||
getJSON( { | |||
uri: url, | |||
success: function ( json ) { | |||
var titles = e.handler( json, z ); | |||
if ( titles && titles.length > 0 ) { | |||
if ( cb.allTitles === null ) { | |||
cb.allTitles = titles; | |||
} else { | |||
cb.allTitles = cb.allTitles.concat( titles ); | |||
} | |||
if ( titles.exists ) { cb.exists = true; } | |||
if ( titles.normalized ) { cb.normalized = titles.normalized; } | |||
} | |||
done(); | |||
}, | |||
error: function ( req ) { | |||
if ( !req ) { | |||
noSuggestions = true; | |||
} | |||
cb.dontCache = true; | |||
done(); | |||
} | } | ||
} ); | } ); | ||
}, | }, | ||
Line 2,140: | Line 2,047: | ||
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 && | this.ok.disabled = v.length > 0 && HotCat.blacklist && HotCat.blacklist.test( v ); | ||
if ( noSuggestions ) { | if ( noSuggestions ) { | ||
// No Ajax: just make sure the list is hidden | |||
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'; } | ||
if ( this.icon ) this.icon.style.display = 'none'; | if ( this.icon ) { this.icon.style.display = 'none'; } | ||
return; | return; | ||
} | } | ||
if ( | if ( v.length === 0 ) { this.showSuggestions( [] ); return; } | ||
var cleanKey = v.replace( /[\u200E\u200F\u202A-\u202E]/g, '' ).replace( wikiTextBlankRE, ' ' ); | var cleanKey = v.replace( /[\u200E\u200F\u202A-\u202E]/g, '' ).replace( wikiTextBlankRE, ' ' ); | ||
cleanKey = replaceShortcuts( cleanKey, | cleanKey = replaceShortcuts( cleanKey, HotCat.shortcuts ); | ||
cleanKey = cleanKey.replace( /^\s+|\s+$/g, '' ); | cleanKey = cleanKey.replace( /^\s+|\s+$/g, '' ); | ||
if ( | 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'; | ||
Line 2,180: | Line 2,079: | ||
var engines = suggestionConfigs[ engineName ].engines; | var engines = suggestionConfigs[ engineName ].engines; | ||
this.callbackObj = | this.callbackObj = | ||
allTitles: null, | { allTitles: null, callsMade: 0, nofCalls: engines.length, noCompletion: dont_autocomplete, engineName: engineName }; | ||
this.makeCalls( engines, this.callbackObj, v, cleanKey ); | this.makeCalls( engines, this.callbackObj, v, cleanKey ); | ||
}, | }, | ||
Line 2,202: | Line 2,096: | ||
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.engineSelector ) this.engineSelector.style.display = 'none'; | if ( this.icon ) { this.icon.style.display = 'none'; } | ||
if ( this.icon ) this.icon.style.display = 'none'; | |||
this.inputExists = true; // Default... | this.inputExists = true; // Default... | ||
return; | return; | ||
Line 2,215: | Line 2,106: | ||
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 ) ) return; | if ( this.lastInput.indexOf( queryKey ) !== 0 ) { return; } | ||
if ( this.lastQuery && this.lastInput.indexOf( this.lastQuery ) === 0 && this.lastQuery.length > queryKey.length ) return; | if ( this.lastQuery && this.lastInput.indexOf( this.lastQuery ) === 0 && this.lastQuery.length > queryKey.length ) { return; } | ||
} | } | ||
this.lastQuery = queryKey; | this.lastQuery = queryKey; | ||
Line 2,228: | Line 2,119: | ||
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 = ( | v = ( HotCat.capitalizePageNames ? capitalize( v[ 0 ] ) : v[ 0 ] ); | ||
var vNormalized = v; | var vNormalized = v; | ||
var knownToExist = titles && titles.exists; | var knownToExist = titles && titles.exists; | ||
Line 2,234: | Line 2,125: | ||
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 | |||
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 ( | if ( HotCat.blacklist ) { | ||
for ( i = 0; i < titles.length; i++ ) { | for ( i = 0; i < titles.length; i++ ) { | ||
if ( | if ( HotCat.blacklist.test( titles[ i ] ) ) { | ||
titles.splice( i, 1 ); | titles.splice( i, 1 ); | ||
i--; | i--; | ||
Line 2,249: | Line 2,140: | ||
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 ( a.indexOf( b ) === 0 ) return 1; | if ( b.indexOf( a ) === 0 ) { return -1; } // b begins with a: a < b | ||
if ( b.indexOf( a ) === 0 ) return -1; | |||
// 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(), | var aLow = a.toLowerCase(), bLow = b.toLowerCase(); | ||
prefixMatchA = ( aLow.indexOf( vLow ) === 0 ? 1 : 0 ); | prefixMatchA = ( aLow.indexOf( vLow ) === 0 ? 1 : 0 ); | ||
prefixMatchB = ( bLow.indexOf( vLow ) === 0 ? 1 : 0 ); | prefixMatchB = ( bLow.indexOf( vLow ) === 0 ? 1 : 0 ); | ||
if ( prefixMatchA !== prefixMatchB ) return prefixMatchB - prefixMatchA; | if ( prefixMatchA !== prefixMatchB ) { return prefixMatchB - prefixMatchA; } | ||
if ( a < b ) { return -1; } | |||
if ( a < b ) return -1; | if ( b < a ) { return 1; } | ||
return 0; | |||
if ( b < a ) return 1; | } | ||
); | |||
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 ( | if ( i + 1 < titles.length && titles[ i ] === titles[ i + 1 ] || | ||
conf.wgNamespaceNumber === 14 && titles[ i ] === conf.wgTitle | conf.wgNamespaceNumber === 14 && titles[ i ] === conf.wgTitle | ||
) { | ) { | ||
Line 2,284: | Line 2,167: | ||
} | } | ||
} | } | ||
if ( !titles || | if ( !titles || titles.length === 0 ) { | ||
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'; | |||
if ( engineName && suggestionConfigs[ engineName ] && !suggestionConfigs[ engineName ].temp ) { | if ( engineName && suggestionConfigs[ engineName ] && !suggestionConfigs[ engineName ].temp ) { | ||
if ( this.icon ) this.icon.src = | if ( this.icon ) { this.icon.src = armorUri( HotCat.existsNo ); } | ||
this.inputExists = false; | this.inputExists = false; | ||
} | } | ||
Line 2,299: | Line 2,179: | ||
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, | var existing = completed || knownToExist || firstTitle === replaceShortcuts( v, HotCat.shortcuts ); | ||
if ( engineName && suggestionConfigs[ engineName ] && !suggestionConfigs[ engineName ].temp ) { | if ( engineName && suggestionConfigs[ engineName ] && !suggestionConfigs[ engineName ].temp ) { | ||
this.icon.src = ( existing ? | this.icon.src = armorUri( existing ? HotCat.existsYes : HotCat.existsNo ); | ||
this.inputExists = existing; | this.inputExists = existing; | ||
} | } | ||
Line 2,308: | Line 2,188: | ||
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++ ) { | for ( i = 0; i < titles.length; i++ ) { | ||
var opt = make( 'option' ); | var opt = make( 'option' ); | ||
Line 2,329: | Line 2,207: | ||
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 > | var nofItems = ( this.list.options.length > HotCat.list_size ? HotCat.list_size : 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'; | ||
Line 2,355: | Line 2,231: | ||
// Approximate calculation of maximum list size | // Approximate calculation of maximum list size | ||
var maxListHeight = listh; | var maxListHeight = listh; | ||
if ( nofItems < | if ( nofItems < HotCat.list_size ) { maxListHeight = ( listh / nofItems ) * HotCat.list_size; } | ||
function viewport( what ) { | function viewport( what ) { | ||
if ( is_webkit && !document.evaluate ) { | if ( is_webkit && !document.evaluate ) { | ||
// Safari < 3.0 | |||
return window[ 'inner' + what ]; | 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; | ||
} | } | ||
Line 2,375: | Line 2,252: | ||
// 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 ) { | |||
if ( !is_webkit ) result = scroll_offset( 'Width' ) - viewport( 'Width' ) - result; | 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. | ||
} | } | ||
Line 2,393: | Line 2,271: | ||
}; | }; | ||
} | } | ||
var t = 0, | var t = 0, l = 0; | ||
do { | do { | ||
t + | t = t + ( node.offsetTop || 0 ); | ||
l + | l = l + ( node.offsetLeft || 0 ); | ||
node = node.offsetParent; | node = node.offsetParent; | ||
} while ( node ); | } while ( node ); | ||
Line 2,406: | Line 2,283: | ||
} | } | ||
var textPos = position( this.text ) | var textPos = position( this.text ); | ||
var nl = 0; | |||
var nt = 0; | |||
var offset = 0; | |||
// Opera 9.5 somehow has offsetWidth = 0 here?? Use the next best value... | |||
var textBoxWidth = this.text.offsetWidth || this.text.clientWidth; | |||
if ( this.engineName ) { | if ( this.engineName ) { | ||
this.engineSelector.style.zIndex = 5; | this.engineSelector.style.zIndex = 5; | ||
Line 2,419: | Line 2,296: | ||
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 = ' | this.engineSelector.style.top = '0px'; | ||
this.engineSelector.style.display = ''; | this.engineSelector.style.display = ''; | ||
offset = this.engineSelector.offsetHeight; | offset = this.engineSelector.offsetHeight; | ||
Line 2,429: | Line 2,306: | ||
} | } | ||
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 | |||
// 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'; | ||
Line 2,461: | Line 2,338: | ||
w = view_w; | w = view_w; | ||
this.list.style.width = w + 'px'; | this.list.style.width = w + 'px'; | ||
if ( is_rtl ) left = right - w; else right = left + w; | if ( is_rtl ) { | ||
left = right - w; | |||
} else { | |||
right = left + w; | |||
} | |||
} | } | ||
var relative_offset = 0; | var relative_offset = 0; | ||
if ( left < scroll ) relative_offset = scroll - left; else if ( right > scroll + view_w ) relative_offset = -( right - scroll - view_w ); | if ( left < scroll ) { | ||
relative_offset = scroll - left; | |||
if ( is_rtl ) relative_offset = -relative_offset; | } else if ( right > scroll + view_w ) { | ||
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 ) ) { | if ( newVal.indexOf( actVal ) !== 0 ) { | ||
// 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,498: | Line 2,380: | ||
canSelect: function () { | canSelect: function () { | ||
return this.text.setSelectionRange || | return this.text.setSelectionRange || | ||
this.text.createTextRange || | |||
typeof this.text.selectionStart !== 'undefined' && | |||
typeof 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 ( this.text.selectionStart !== undefined ) { | } else if ( typeof this.text.selectionStart !== 'undefined' ) { | ||
if ( from > this.text.selectionStart ) { | if ( from > this.text.selectionStart ) { | ||
this.text.selectionEnd = to; | this.text.selectionEnd = to; | ||
Line 2,525: | Line 2,407: | ||
getSelection: function () { | getSelection: function () { | ||
var from = 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 ( this.text.selectionStart !== undefined ) { | } else if ( typeof this.text.selectionStart !== 'undefined' ) { | ||
from = this.text.selectionStart; | from = this.text.selectionStart; | ||
to = this.text.selectionEnd; | to = this.text.selectionEnd; | ||
Line 2,546: | Line 2,427: | ||
from = textRng.text.length; | from = textRng.text.length; | ||
} catch ( notFocused ) { | } catch ( notFocused ) { | ||
from = this.text.value.length; | from = this.text.value.length; to = from; // At end of text | ||
} | } | ||
} | } | ||
} | } | ||
return { | return { start: from, end: to }; | ||
}, | }, | ||
Line 2,571: | Line 2,448: | ||
break; | break; | ||
case PGUP: | case PGUP: | ||
dir = - | dir = -HotCat.list_size; | ||
break; | break; | ||
case PGDOWN: | case PGDOWN: | ||
dir = | dir = HotCat.list_size; | ||
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) | ||
Line 2,581: | Line 2,458: | ||
if ( dir ) { | if ( dir ) { | ||
if ( this.list.style.display !== 'none' ) { | if ( this.list.style.display !== 'none' ) { | ||
// List is visible, so there are suggestions | |||
this.highlightSuggestion( dir ); | this.highlightSuggestion( dir ); | ||
// Kill the event, otherwise some browsers (e.g., Firefox) may additionally treat an up-arrow | // Kill the event, otherwise some browsers (e.g., Firefox) may additionally treat an up-arrow | ||
Line 2,588: | Line 2,465: | ||
} else if ( | } else if ( | ||
this.keyCount <= 1 && | this.keyCount <= 1 && | ||
( !this.callbackObj || this.callbackObj.callsMade === this.callbackObj.nofCalls ) | ( !this.callbackObj || this.callbackObj.callsMade === this.callbackObj.nofCalls ) ) { | ||
// If no suggestions displayed, get them, unless we're already getting them. | // If no suggestions displayed, get them, unless we're already getting them. | ||
this.textchange(); | this.textchange(); | ||
Line 2,598: | Line 2,474: | ||
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 | ||
Line 2,621: | Line 2,496: | ||
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 = | if ( this.icon ) { this.icon.src = armorUri( HotCat.existsYes ); } | ||
this.state = CategoryEditor.CHANGE_PENDING; | this.state = CategoryEditor.CHANGE_PENDING; | ||
} | } | ||
Line 2,633: | Line 2,509: | ||
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 ) { | ||
Line 2,653: | Line 2,528: | ||
return false; | return false; | ||
} | } | ||
}; // end CategoryEditor.prototype | }; // end CategoryEditor.prototype | ||
Line 2,659: | Line 2,535: | ||
// 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 = ( | var config = ( typeof JSconfig !== 'undefined' && JSconfig.keys ) ? JSconfig.keys : {}; | ||
HotCat.dont_add_to_watchlist = | |||
( typeof window.hotcat_dont_add_to_watchlist !== 'undefined' ? | |||
!!window.hotcat_dont_add_to_watchlist : | |||
( typeof config.HotCatDontAddToWatchlist !== 'undefined' ? | |||
config.HotCatDontAddToWatchlist : | |||
HotCat.dont_add_to_watchlist | |||
) | |||
); | |||
HotCat.no_autocommit = | |||
( typeof window.hotcat_no_autocommit !== 'undefined' ? | |||
!!window.hotcat_no_autocommit : | |||
( typeof config.HotCatNoAutoCommit !== 'undefined' ? | |||
config.HotCatNoAutoCommit : | |||
HotCat.no_autocommit | |||
) | |||
); | |||
if ( typeof | HotCat.del_needs_diff = | ||
( typeof window.hotcat_del_needs_diff !== 'undefined' ? | |||
!!window.hotcat_del_needs_diff : | |||
( typeof config.HotCatDelNeedsDiff !== 'undefined' ? | |||
config.HotCatDelNeedsDiff : | |||
HotCat.del_needs_diff | |||
) | |||
); | |||
HotCat.suggest_delay = window.hotcat_suggestion_delay || | |||
config.HotCatSuggestionDelay || | |||
HotCat.suggest_delay; | |||
HotCat.editbox_width = window.hotcat_editbox_width || | |||
config.HotCatEditBoxWidth || | |||
HotCat.editbox_width; | |||
HotCat.suggestions = window.hotcat_suggestions || | |||
config.HotCatSuggestions || | |||
HotCat.suggestions; | |||
if ( typeof HotCat.suggestions !== 'string' || !suggestionConfigs[ HotCat.suggestions ] ) { | |||
HotCat.suggestions = 'combined'; | |||
} | } | ||
HotCat.fixed_search = | |||
( typeof window.hotcat_suggestions_fixed !== 'undefined' ? | |||
!!window.hotcat_suggestions_fixed : | |||
( typeof config.HotCatFixedSuggestions !== 'undefined' ? | |||
config.HotCatFixedSuggestions : | |||
HotCat.fixed_search | |||
) | |||
); | |||
HotCat.single_minor = | |||
( typeof window.hotcat_single_changes_are_minor !== 'undefined' ? | |||
!!window.hotcat_single_changes_are_minor : | |||
( typeof config.HotCatMinorSingleChanges !== 'undefined' ? | |||
config.HotCatMinorSingleChanges : | |||
HotCat.single_minor | |||
) | |||
); | |||
HotCat.bg_changed = window.hotcat_changed_background || | |||
config.HotCatChangedBackground || | |||
HotCat.bg_changed; | |||
HotCat.use_up_down = | |||
( typeof window.hotcat_use_category_links !== 'undefined' ? | |||
!!window.hotcat_use_category_links : | |||
( typeof config.HotCatUseCategoryLinks !== 'undefined' ? | |||
config.HotCatUseCategoryLinks : | |||
HotCat.use_up_down | |||
) | |||
); | |||
HotCat.list_size = window.hotcat_list_size || | |||
config.HotCatListSize || | |||
HotCat.list_size; | |||
// 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 ); | |||
if ( isNaN( | if ( isNaN( HotCat.list_size ) || HotCat.list_size < 5 ) { HotCat.list_size = 5; } | ||
if ( HotCat.list_size > 15 ) { HotCat.list_size = 15; } | |||
// Localize search engine names | // Localize search engine names | ||
if ( | if ( HotCat.engine_names ) { | ||
for ( var key in | for ( var key in HotCat.engine_names ) { | ||
if ( suggestionConfigs[ key ] && | if ( suggestionConfigs[ key ] && HotCat.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]] | ||
Line 2,798: | Line 2,633: | ||
case 'cologneblue': | case 'cologneblue': | ||
container = document.getElementById( 'quickbar' ); | container = document.getElementById( 'quickbar' ); | ||
// 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 ( | if ( param( 'title', lks[ i ].href ) === conf.wgPageName && | ||
param( 'action', lks[ i ].href ) === 'edit' ) { return true; } | |||
param( 'action', lks[ i ].href ) === 'edit' | } | ||
} | |||
return false; | return false; | ||
default: | default: | ||
// all modern skins: | // all modern skins: | ||
return document.getElementById( 'ca-edit' ) !== null; | return document.getElementById( 'ca-edit' ) !== null; | ||
} | } | ||
} | } | ||
Line 2,849: | Line 2,655: | ||
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 ( | if ( ( reupload && !!reupload.value ) || | ||
( destFile && ( destFile.disabled || destFile.readOnly ) ) ) { return; } // re-upload form... | |||
( destFile && ( destFile.disabled || destFile.readOnly ) ) | |||
// 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' ); | ||
Line 2,874: | Line 2,676: | ||
// Create the label | // Create the label | ||
var label = null; | var label = null; | ||
if ( | if ( typeof UFUI !== 'undefined' && | ||
typeof UIElements !== 'undefined' && | |||
typeof UFUI.getLabel === 'function' | |||
) { | |||
try { | try { | ||
label = UFUI.getLabel( 'wpCategoriesUploadLbl' ); | label = UFUI.getLabel( 'wpCategoriesUploadLbl' ); | ||
Line 2,883: | Line 2,688: | ||
if ( !label ) { | if ( !label ) { | ||
labelCell.id = 'hotcatLabel'; | labelCell.id = 'hotcatLabel'; | ||
labelCell.appendChild( make( | labelCell.appendChild( make( HotCat.categories, true ) ); | ||
} else { | } else { | ||
labelCell.id = 'hotcatLabelTranslated'; | labelCell.id = 'hotcatLabelTranslated'; | ||
Line 2,902: | Line 2,707: | ||
if ( oldSubmit ) { | if ( oldSubmit ) { | ||
if ( typeof oldSubmit === 'string' ) { | if ( typeof oldSubmit === 'string' ) { | ||
// eslint-disable-next-line no-eval | |||
do_submit = eval( oldSubmit ); | do_submit = eval( oldSubmit ); | ||
} else if ( oldSubmit | } else if ( typeof oldSubmit === 'function' ) { | ||
do_submit = oldSubmit.apply( form, arguments ); | 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' ) || document.getElementById( 'wpDesc' ); | var eb = document.getElementById( 'wpUploadDescription' ) || | ||
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 = '[[' + | var new_cat = '[[' + HotCat.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 | ||
Line 2,928: | Line 2,736: | ||
} | } | ||
if ( addedOne ) { | if ( addedOne ) { | ||
// 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, '' ); | ||
} | } | ||
Line 2,940: | Line 2,748: | ||
function isOnPage( span ) { | function isOnPage( span ) { | ||
if ( span.firstChild.nodeType !== Node.ELEMENT_NODE ) return null; | if ( span.firstChild.nodeType !== Node.ELEMENT_NODE ) { return null; } | ||
var catTitle = title( span.firstChild.getAttribute( 'href', 2 ) ); | |||
var catTitle = title( span.firstChild.getAttribute( 'href' ) ); | if ( !catTitle ) { return null; } | ||
if ( !catTitle ) return null; | |||
catTitle = catTitle.substr( catTitle.indexOf( ':' ) + 1 ).replace( /_/g, ' ' ); | catTitle = catTitle.substr( catTitle.indexOf( ':' ) + 1 ).replace( /_/g, ' ' ); | ||
if ( | if ( HotCat.blacklist && HotCat.blacklist.test( catTitle ) ) { return null; } | ||
var result = { title: catTitle, match: [ '', '', '' ] }; | |||
var result = { | if ( pageText === null ) { return result; } | ||
if ( pageText === null ) return result; | |||
if ( cleanedText === null ) { | if ( cleanedText === null ) { | ||
cleanedText = pageText | cleanedText = pageText | ||
Line 2,967: | Line 2,768: | ||
function findByClass( scope, tag, className ) { | function findByClass( scope, tag, className ) { | ||
var result = | var result = window.jQuery( scope ).find( tag + '.' + className ); | ||
return ( result && result.length ) ? result[ 0 ] : null; | 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 ) { | ||
Line 2,989: | Line 2,790: | ||
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' ); | ||
Line 2,997: | Line 2,798: | ||
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 = | label.title = HotCat.categories; | ||
label.appendChild( make( | label.appendChild( make( HotCat.categories, true ) ); | ||
catLine.appendChild( label ); | catLine.appendChild( label ); | ||
catLine.appendChild( make( ':', true ) ); | catLine.appendChild( make( ':', true ) ); | ||
Line 3,010: | Line 2,811: | ||
container.className = 'catlinks noprint'; | container.className = 'catlinks noprint'; | ||
container.style.display = ''; | container.style.display = ''; | ||
if ( !hiddenCats ) container.appendChild( catLine ); else container.insertBefore( catLine, hiddenCats ); | if ( !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 | ||
Line 3,019: | Line 2,824: | ||
var i; | var i; | ||
var cats = line.getElementsByTagName( 'li' ); | var cats = line.getElementsByTagName( 'li' ); | ||
if ( cats.length ) { | if ( cats.length > 0 ) { | ||
newDOM = true; | newDOM = true; line = cats[ 0 ].parentNode; | ||
} else { | } else { | ||
cats = line.getElementsByTagName( 'span' ); | cats = line.getElementsByTagName( 'span' ); | ||
Line 3,027: | Line 2,831: | ||
// 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 ]; } | ||
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 ) { | if ( test !== null && test.match !== null ) { | ||
// eslint-disable-next-line no-new | |||
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 ? copyCats[ copyCats.length - 1 ] : null; | return copyCats.length > 0 ? copyCats[ copyCats.length - 1 ] : null; | ||
} | } | ||
Line 3,044: | Line 2,848: | ||
if ( !onUpload ) { | if ( !onUpload ) { | ||
if ( pageText !== null && hiddenCats ) { | if ( pageText !== null && hiddenCats ) { | ||
if ( is_rtl ) hiddenCats.dir = 'rtl'; | if ( is_rtl ) { hiddenCats.dir = 'rtl'; } | ||
createEditors( hiddenCats, true ); | createEditors( hiddenCats, true ); | ||
} | } | ||
Line 3,050: | Line 2,854: | ||
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>' + | multiSpan.innerHTML = '(<a>' + HotCat.addmulti + '</a>)'; | ||
var lk = multiSpan.getElementsByTagName( 'a' )[ 0 ]; | var lk = multiSpan.getElementsByTagName( 'a' )[ 0 ]; | ||
lk.onclick = function ( evt ) { | lk.onclick = function ( evt ) { setMultiInput(); checkMultiInput(); return evtKill( evt ); }; | ||
lk.title = HotCat.multi_tooltip; | |||
lk.title = | |||
lk.style.cursor = 'pointer'; | lk.style.cursor = 'pointer'; | ||
} | } | ||
cleanedText = null; | cleanedText = null; | ||
if ( additionalWork | if ( typeof additionalWork === 'function' ) { additionalWork(); } | ||
setupCompleted.loaded(); // Trigger signal; execute registered functions | |||
$( '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' : String( 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 ) { | |||
// ResourceLoader's JSParser doesn't like .case, so override eslint. | |||
// eslint-disable-next-line dot-notation | |||
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'; | ||
Line 3,078: | Line 2,929: | ||
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 ) + '&action=submit">' + | conf.wgScript + '?title=' + encodeURIComponent( conf.wgPageName ) + | ||
'<input type="hidden" name="wpTextbox1">' + | '&action=submit">' + | ||
'<input type="hidden" name="model" value="wikitext">' + | '<input type="hidden" name="wpTextbox1" />' + | ||
'<input type="hidden" name="format" value="text/x-wiki">' + | '<input type="hidden" name="model" value="wikitext" />' + | ||
'<input type="hidden" name="wpSummary" value="">' + | '<input type="hidden" name="format" value="text/x-wiki" />' + | ||
'<input type="checkbox" name="wpMinoredit" value="1">' + | '<input type="hidden" name="wpSummary" value="" />' + | ||
'<input type="checkbox" name="wpWatchthis" value="1">' + | '<input type="checkbox" name="wpMinoredit" value="1" />' + | ||
'<input type="hidden" name="wpAutoSummary" value=" | '<input type="checkbox" name="wpWatchthis" value="1" />' + | ||
'<input type="hidden" name="wpEdittime">' + | '<input type="hidden" name="wpAutoSummary" value="" />' + | ||
'<input type="hidden" name="wpStarttime">' + | '<input type="hidden" name="wpEdittime" />' + | ||
'<input type="hidden" name="wpDiff" value="wpDiff">' + | '<input type="hidden" name="wpStarttime" />' + | ||
'<input type="hidden" name="oldid" value="0">' + | '<input type="hidden" name="wpDiff" value="wpDiff" />' + | ||
'<input type="submit" name="hcCommit" value="hcCommit">' + | '<input type="hidden" name="oldid" value="0" />' + | ||
'<input type="hidden" name="wpEditToken">' + | '<input type="submit" name="hcCommit" value="hcCommit" />' + | ||
'<input type="hidden" name="wpUltimateParam" value="1" | '<input type="hidden" name="wpEditToken" />' + | ||
'<input type="hidden" name="wpUltimateParam" value="1" />' + | |||
'<input type="hidden" value="ℳ𝒲♥𝓊𝓃𝒾𝒸ℴ𝒹ℯ" name="wpUnicodeCheck">' + | '<input type="hidden" value="ℳ𝒲♥𝓊𝓃𝒾𝒸ℴ𝒹ℯ" name="wpUnicodeCheck" />' + | ||
'</form>'; | '</form>'; | ||
commitForm = document.getElementById( 'hotcatCommitForm' ); | commitForm = document.getElementById( 'hotcatCommitForm' ); | ||
Line 3,101: | Line 2,952: | ||
function getPage() { | function getPage() { | ||
// We know we have an article here. | // We know we have an article here. | ||
if ( | if ( conf.wgArticleId === 0 ) { | ||
// Doesn't exist yet. Disable on non-existing User pages -- might be a global user page. | // Doesn't exist yet. | ||
if ( conf.wgNamespaceNumber === 2 ) { | |||
// Disable on non-existing User pages -- might be a global user page. | |||
return; | |||
} | |||
pageText = ''; | pageText = ''; | ||
pageTime = null; | pageTime = null; | ||
Line 3,109: | Line 2,963: | ||
} 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 ) + | |||
'&prop=info%7Crevisions&rvprop=content%7Ctimestamp%7Cids&meta=siteinfo&rvlimit=1&rvstartid=' + | |||
conf.wgCurRevisionId; | |||
var s = make( 'script' ); | var s = make( 'script' ); | ||
s.src = url; | s.src = armorUri( url ); | ||
s.type = 'text/javascript'; | |||
HotCat.start = function ( json ) { setPage( json ); setup( createCommitForm ); }; | |||
document.getElementsByTagName( 'head' )[ 0 ].appendChild( s ); | document.getElementsByTagName( 'head' )[ 0 ].appendChild( s ); | ||
setupTimeout = window.setTimeout( function () { | setupTimeout = window.setTimeout( function () { setup( createCommitForm ); }, 4000 ); // 4 sec, just in case getting the wikitext takes longer. | ||
} | } | ||
} | } | ||
function | function run() { | ||
if ( HotCat.started ) { return; } | |||
HotCat.started = true; | |||
loadTrigger.register( really_run ); | |||
} | |||
function really_run() { | |||
initialize(); | |||
if ( !HotCat.upload_disabled && conf.wgNamespaceNumber === -1 && conf.wgCanonicalSpecialPageName === 'Upload' && conf.wgUserName ) { | |||
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 ); | ||
} | |||
} ); | |||
if ( | } else { | ||
if ( !conf.wgIsArticle || conf.wgAction !== 'view' || param( 'diff' ) !== null || param( 'oldid' ) !== null || !can_edit() || HotCat.disable() ) { return; } | |||
getPage(); | |||
} | |||
} | |||
// Legacy stuff | |||
function closeForm() { | |||
// | // Close all open editors without redirect resolution and other asynchronous stuff. | ||
for ( var i = 0; i < editors.length; i++ ) { | |||
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(); | |||
} | |||
} | } | ||
} | } | ||
} | } | ||
Line 3,172: | Line 3,030: | ||
var text = editors[ i ].currentCategory; | var text = editors[ i ].currentCategory; | ||
var key = editors[ i ].currentKey; | var key = editors[ i ].currentKey; | ||
if ( text && text.length ) { | if ( text && text.length > 0 ) { | ||
if ( key !== null ) text += '|' + key; | if ( key !== null ) { text += '|' + key; } | ||
if ( result === null ) result = text; else result + | if ( result === null ) { | ||
result = text; | |||
} else { | |||
result = result + '\n' + text; | |||
} | |||
} | } | ||
} | } | ||
Line 3,180: | Line 3,042: | ||
} | } | ||
function | function setState( state ) { | ||
var cats = state.split( '\n' ); | |||
if ( cats.length === 0 ) { return null; } | |||
if ( | if ( initialized && editors.length === 1 && editors[ 0 ].isAddCategory ) { | ||
// Insert new spans and create new editors for them. | |||
var newSpans = []; | |||
var before = editors.length === 1 ? editors[ 0 ].span : null; | |||
if ( | var i; | ||
for ( i = 0; i < cats.length; i++ ) { | |||
if ( cats[ i ].length === 0 ) { continue; } | |||
if ( | var cat = cats[ i ].split( '|' ); | ||
var key = cat.length > 1 ? cat[ 1 ] : null; | |||
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 ); | |||
} | |||
for ( i = 0; i < newSpans.length; i++ ) { | |||
// eslint-disable-next-line no-new | |||
new CategoryEditor( catLine, newSpans[ i ].element, newSpans[ i ].title, newSpans[ i ].key ); | |||
} | |||
} | } | ||
return null; | |||
} | } | ||
// Export legacy functions | // Export legacy functions | ||
window.hotcat_get_state = function () { | window.hotcat_get_state = function () { return getState(); }; | ||
window.hotcat_set_state = function ( state ) { return setState( state ); }; | |||
window.hotcat_close_form = function () { closeForm(); }; | |||
window.hotcat_set_state = function ( state ) { | |||
window.hotcat_close_form = function () { | |||
// Make sure we don't get conflicts with AjaxCategories (core development that should one day | // Make sure we don't get conflicts with AjaxCategories (core development that should one day | ||
Line 3,230: | Line 3,096: | ||
editors = []; | editors = []; | ||
initialized = false; | initialized = false; | ||
HotCat.started = false; | |||
run(); | run(); | ||
} ); | } ); | ||
} | } | ||
// We can safely trigger just after user configuration is loaded. | var startHotCat = function () { | ||
$( 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 ); | |||
}( jQuery, mediaWiki ) ); | }( jQuery, mediaWiki ) ); | ||
// </nowiki> | // </nowiki> |