MediaWiki:Gadget-HotCat.js: Difference between revisions
Content added Content deleted
(Upgraded to V2.36 to fix the "It appears your browser does not support Unicode" false-positive error.) |
(updated from https://commons.wikimedia.org/w/index.php?title=MediaWiki:Gadget-HotCat.js&action=raw&ctype=text/javascript) |
||
Line 1: | Line 1: | ||
/** |
|||
// <nowiki> |
|||
HotCat V2.43 |
|||
/* |
|||
HotCat V2.36 |
|||
Ajax-based simple Category manager. Allows adding/removing/changing categories on a page view. |
|||
Supports multiple category changes, as well as redirect and disambiguation resolution. Also |
|||
plugs into the upload form. Search engines to use for the suggestion list are configurable, and |
|||
can be selected interactively. |
|||
Documentation: https://commons.wikimedia.org/wiki/Help:Gadget-HotCat |
|||
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) |
|||
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: |
|||
<=1.26: https://commons.wikimedia.org/w/index.php?title=MediaWiki:Gadget-HotCat.js&oldid=211134664 |
|||
*/ |
*/ |
||
// <nowiki> |
|||
/* eslint-disable vars-on-top, one-var, camelcase, no-use-before-define, no-alert */ |
|||
/* eslint-disable vars-on-top, one-var, camelcase, no-alert, curly */ |
|||
/* global HotCat, mediaWiki, UFUI, JSconfig, UploadForm */ |
|||
/* global jQuery, mediaWiki, UFUI, JSconfig, UploadForm */ |
|||
/* jslint strict:false, nonew:false, bitwise:true */ |
|||
( 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 |
||
// account for values changing, e.g. wgCurRevisionId after a VE edit |
// account for values changing, e.g. wgCurRevisionId after a VE edit |
||
var |
|||
var conf = mw.config.values; |
|||
conf = $.extend( {}, mw.config.values, { |
|||
// when running on mobile domain - do not use wgServer. |
|||
wgServer: window.location.host.indexOf('.m.') > -1 ? |
|||
'//' + window.location.host : mw.config.get( 'wgServer' ) |
|||
} ); |
|||
// Guard against double inclusions (in old IE/Opera element ids become window properties) |
|||
if ( |
|||
if ( ( window.HotCat && !window.HotCat.nodeName ) || |
|||
// Guard against double inclusions (in old IE/Opera element ids become window properties) |
|||
conf.wgAction === 'edit' ) // Not on edit mode |
|||
( window.HotCat && !window.HotCat.nodeName ) || |
|||
// Not on edit pages |
|||
conf.wgAction === 'edit' |
|||
) { |
|||
return; |
return; |
||
} |
|||
// Configuration stuff. |
// Configuration stuff. |
||
window.HotCat = { |
var HC = window.HotCat = { |
||
// Localize these messages to the main language of your wiki. |
|||
messages: { |
messages: { |
||
cat_removed: 'removed [[Category:$1]]', |
cat_removed: 'removed [[Category:$1]]', |
||
Line 75: | Line 74: | ||
// 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 94: | Line 93: | ||
// 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)' }, |
|||
change: '(±)', |
|||
remove: '(\u2212)', |
|||
add: '(+)', |
|||
restore: '(×)', |
|||
undo: '(×)', |
|||
down: '(\u2193)', |
|||
up: '(\u2191)' |
|||
}, |
|||
changeTag: conf.wgUserName ? 'HotCat' : '', // if tag is missing, edit is rejected |
|||
// The tooltips for the above links |
// The tooltips for the above links |
||
tooltips: { |
tooltips: { |
||
Line 115: | Line 123: | ||
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 || // 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* |
uncat_regexp: /\{\{\s*[Uu]ncategorized\s*[^}]*\}\}\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 146: | Line 152: | ||
parentcat: 'Parent categories' |
parentcat: 'Parent categories' |
||
}, |
}, |
||
// Set to false if your wiki has case-sensitive page names. MediaWiki has two modes: either the first letter |
|||
// Override the decision of whether HotCat should help users by automatically |
|||
// of a page is automatically capitalized ("first-letter"; Category:aa === Category:Aa), or it isn't |
|||
// capitalising the title in the user input text if the wiki has case-sensitive page names. |
|||
// ("case-sensitive"; Category:aa !== Category:Aa). It doesn't currently have a fully case-insensitive mode |
|||
// Basically, this will make an API query to check the MediaWiki configuration and HotCat then sets |
|||
// (which would mean Category:aa === Category:Aa === Category:AA === Category:aA) |
|||
// this to true for most wikis, and to false on Wiktionary. |
|||
// HotCat tries to set this correctly automatically using an API query. It's still a good idea to manually |
|||
// |
|||
// configure it correctly; either directly here if you copied HotCat, or in the local configuration file |
|||
// You can set this directly if there is a problem with it. For example, Georgian Wikipedia (kawiki), |
|||
// MediaWiki:Gadget-HotCat.js/local_defaults if you hotlink to the Commons-version, to ensure it is set even |
|||
// is known to have different capitalisation logic between MediaWiki PHP and JavaScript. As such, automatic |
|||
// if that API query should fail for some strange reason. |
|||
// case changes in JavaScript by HotCat would be wrong. |
|||
capitalizePageNames: true, |
|||
capitalizePageNames: null, |
|||
// If upload_disabled is true, HotCat will not be used on the Upload form. |
// If upload_disabled is true, HotCat will not be used on the Upload form. |
||
upload_disabled: false, |
upload_disabled: false, |
||
Line 165: | Line 172: | ||
// 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: '#FCA', |
||
// 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 185: | Line 192: | ||
use_up_down: true, |
use_up_down: true, |
||
// Default list size |
// Default list size |
||
listSize: 10, |
|||
// 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 194: | Line 201: | ||
shortcuts: null, |
shortcuts: null, |
||
addShortcuts: function ( map ) { |
addShortcuts: function ( map ) { |
||
if ( !map ) |
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' ) |
if ( !map.hasOwnProperty( k ) || typeof k !== 'string' ) continue; |
||
var v = map[ k ]; |
var v = map[ k ]; |
||
if ( typeof v !== 'string' ) |
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 ( k.length |
if ( !k.length || !v.length ) continue; |
||
window.HotCat.shortcuts[ k ] = v; |
window.HotCat.shortcuts[ k ] = v; |
||
} |
} |
||
Line 212: | Line 222: | ||
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 cat_prefix = null; |
|||
// And even more compatbility. HotCat was developed without jQuery, and anyway current jQuery |
|||
var noSuggestions = false; |
|||
// (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; |
|||
} |
|||
function LoadTrigger( needed ) { |
|||
// Define methods in a closure so that self reference is available, |
|||
var req = getRequest(); |
|||
// also allows method calls to be detached. |
|||
if ( !req && settings && settings.error ) { settings.error( req ); } |
|||
var self = this; |
|||
if ( !req || !settings || !settings.uri ) { return req; } |
|||
self.queue = []; |
|||
// eslint-disable-next-line no-use-before-define |
|||
self.needed = needed; |
|||
var uri = armorUri( settings.uri ); |
|||
self.register = function ( callback ) { |
|||
var args = settings.data || null; |
|||
if ( self.needed <= 0 ) callback(); // Execute directly |
|||
var method; |
|||
else self.queue.push( callback ); |
|||
if ( args && uri.length + args.length + 1 > 2000 ) { |
|||
}; |
|||
// We lose caching, but at least we can make the request |
|||
self.loaded = function () { |
|||
method = 'POST'; |
|||
self.needed--; |
|||
req.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded' ); |
|||
if ( self.needed === 0 ) { |
|||
// Run queued callbacks once |
|||
method = 'GET'; |
|||
for ( var i = 0; i < self.queue.length; i++ ) self.queue[ i ](); |
|||
self.queue = []; |
|||
} |
} |
||
req.open( method, uri, true ); |
|||
req.onreadystatechange = function () { |
|||
if ( req.readyState !== 4 ) { return; } |
|||
if ( req.status !== 200 || !req.responseText || !( /^\s*[{[]/.test( req.responseText ) ) ) { |
|||
if ( settings.error ) { settings.error( req ); } |
|||
} else { |
|||
// 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 ); |
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, callback ) { |
||
var head = document.getElementsByTagName( 'head' )[ 0 ]; |
|||
var s = document.createElement( 'script' ); |
var s = document.createElement( 'script' ); |
||
s. |
s.src = uri; |
||
var called = false; |
|||
s.setAttribute( 'type', 'text/javascript' ); |
|||
var done = false; |
|||
function |
s.onload = s.onerror = function () { |
||
if ( |
if ( !called && callback ) { |
||
called = true; |
|||
callback(); |
|||
s.onload = s.onreadystatechange = s.onerror = null; // Properly clean up to avoid memory leaks in IE |
|||
} |
|||
if ( head && s.parentNode ) { head.removeChild( s ); } |
|||
if ( s.parentNode ) { |
|||
loadTrigger.loaded(); |
|||
s.parentNode.removeChild( s ); |
|||
} |
|||
s.onload = s.onreadystatechange = function () { // onreadystatechange for IE, onload for all others |
|||
if ( done ) { return; } |
|||
if ( !this.readyState || this.readyState === 'loaded' || this.readyState === 'complete' ) { |
|||
afterLoad(); |
|||
} |
} |
||
}; |
}; |
||
document.head.appendChild( s ); |
|||
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, callback ) { |
||
load( conf.wgServer + conf.wgScript + '?title=' + encodeURIComponent( page ) + '&action=raw&ctype=text/javascript' ); |
load( conf.wgServer + conf.wgScript + '?title=' + encodeURIComponent( page ) + '&action=raw&ctype=text/javascript', callback ); |
||
} |
} |
||
function loadURI( href ) { |
function loadURI( href, callback ) { |
||
var url = href; |
var url = href; |
||
if ( url.substring( 0, 2 ) === '//' ) |
if ( url.substring( 0, 2 ) === '//' ) url = window.location.protocol + url; else if ( url.substring( 0, 1 ) === '/' ) url = conf.wgServer + url; |
||
url = window.location.protocol + url; |
|||
load( url, callback ); |
|||
url = conf.wgServer + url; |
|||
} |
|||
load( url ); |
|||
} |
} |
||
// Load local configurations, overriding the pre-set default values in the HotCat object above. This is always loaded |
// Load local configurations, overriding the pre-set default values in the HotCat object above. This is always loaded |
||
// from the wiki where this script is executing, even if this script itself is hotlinked from |
// from the wiki where this script is executing, even if this script itself is hotlinked from Commons. This can |
||
// be used to change the default settings, or to provide localized interface texts for edit summaries and so on. |
// be used to change the default settings, or to provide localized interface texts for edit summaries and so on. |
||
loadJS( 'MediaWiki:Gadget-HotCat.js/local_defaults' ); |
loadJS( 'MediaWiki:Gadget-HotCat.js/local_defaults', loadTrigger.loaded ); |
||
// Load localized UI texts. These are the texts that HotCat displays on the page itself. Texts shown in edit summaries |
// Load localized UI texts. These are the texts that HotCat displays on the page itself. Texts shown in edit summaries |
||
Line 356: | Line 285: | ||
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 |
// explicitly if you're not on Commons and don't want that. |
||
if ( |
if ( window.hotcat_translations_from_commons === undefined ) window.hotcat_translations_from_commons = true; |
||
window.hotcat_translations_from_commons = true; |
|||
} |
|||
// Localization hook to localize HotCat messages, tooltips, and engine names for wgUserLanguage. |
// Localization hook to localize HotCat messages, tooltips, and engine names for wgUserLanguage. |
||
if ( window.hotcat_translations_from_commons && conf.wgServer.indexOf( '//commons' ) < 0 ) { |
if ( window.hotcat_translations_from_commons && conf.wgServer.indexOf( '//commons' ) < 0 ) { |
||
loadURI( '//commons.wikimedia.org/w/index.php?title=' + |
loadURI( '//commons.wikimedia.org/w/index.php?title=' + |
||
'MediaWiki:Gadget-HotCat.js/' + conf.wgUserLanguage + |
|||
'&action=raw&ctype=text/javascript', loadTrigger.loaded ); |
|||
); |
|||
} else { |
} else { |
||
// Load translations locally |
// Load translations locally |
||
loadJS( 'MediaWiki:Gadget-HotCat.js/' + conf.wgUserLanguage ); |
loadJS( 'MediaWiki:Gadget-HotCat.js/' + conf.wgUserLanguage, loadTrigger.loaded ); |
||
} |
} |
||
} else { |
} else { |
||
Line 400: | Line 327: | ||
var namespaceIds = conf.wgNamespaceIds; |
var namespaceIds = conf.wgNamespaceIds; |
||
function autoLocalize( namespaceNumber, fallback ) { |
function autoLocalize( namespaceNumber, fallback ) { |
||
function |
function createRegexpStr( name ) { |
||
if ( !name || name.length |
if ( !name || !name.length ) 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.charAt( i ), |
||
ll = initial.toLowerCase(), |
|||
ul = initial.toUpperCase(); |
|||
if ( ll === ul ) |
if ( ll === ul ) regex_name += initial; else regex_name += '[' + ll + ul + ']'; |
||
regex_name += initial; |
|||
} else { |
|||
regex_name += '[' + ll + ul + ']'; |
|||
} |
|||
} |
} |
||
return regex_name |
return regex_name |
||
Line 420: | Line 344: | ||
fallback = fallback.toLowerCase(); |
fallback = fallback.toLowerCase(); |
||
var canonical = formattedNamespaces[ String( namespaceNumber ) ].toLowerCase(); |
var canonical = formattedNamespaces[ String( namespaceNumber ) ].toLowerCase(); |
||
var regexp = |
var regexp = createRegexpStr( canonical ); |
||
if ( fallback && canonical !== fallback ) |
if ( fallback && canonical !== fallback ) regexp += '|' + createRegexpStr( fallback ); |
||
if ( namespaceIds ) { |
if ( namespaceIds ) { |
||
for ( var cat_name in namespaceIds ) { |
for ( var cat_name in namespaceIds ) { |
||
Line 430: | Line 355: | ||
namespaceIds[ cat_name ] === namespaceNumber |
namespaceIds[ cat_name ] === namespaceNumber |
||
) { |
) { |
||
regexp += '|' + |
regexp += '|' + createRegexpStr( cat_name ); |
||
} |
} |
||
} |
} |
||
Line 437: | Line 362: | ||
} |
} |
||
HC.category_canonical = formattedNamespaces[ '14' ]; |
|||
HC.category_regexp = autoLocalize( 14, 'category' ); |
|||
if ( formattedNamespaces[ '10' ] ) |
if ( formattedNamespaces[ '10' ] ) HC.template_regexp = autoLocalize( 10, 'template' ); |
||
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 ) |
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 = 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 ) |
if ( m && m.length > 1 ) return decodeURIComponent( m[ 1 ] ); |
||
return null; |
return null; |
||
} |
} |
||
function title( href ) { |
function title( href ) { |
||
if ( !href ) |
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 466: | Line 391: | ||
// href="/wiki/..." |
// href="/wiki/..." |
||
var prefix = conf.wgArticlePath.replace( '$1', '' ); |
var prefix = conf.wgArticlePath.replace( '$1', '' ); |
||
if ( href.indexOf( prefix ) |
if ( href.indexOf( prefix ) ) prefix = conf.wgServer + prefix; // Fully expanded URL? |
||
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 482: | Line 403: | ||
} |
} |
||
function capitalize( str ) { |
function capitalize( str ) { |
||
if ( !str || str.length |
if ( !str || !str.length ) return str; |
||
return str.substr( 0, 1 ).toUpperCase() + str.substr( 1 ); |
return str.substr( 0, 1 ).toUpperCase() + str.substr( 1 ); |
||
} |
} |
||
Line 515: | Line 437: | ||
// 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 ) |
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 ) |
if ( prefix === lead ) return 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 529: | Line 451: | ||
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 HC.capitalizePageNames ? capitalize( s ) : s; |
||
}; |
}; |
||
}() ); |
}() ); |
||
Line 539: | Line 465: | ||
var findCatsRE = |
var findCatsRE = |
||
new RegExp( '\\[\\[' + wikiTextBlankOrBidi + '(?:' + HC.category_regexp + ')' + wikiTextBlankOrBidi + ':[^\\]]+\\]\\]', 'g' ); |
|||
function replaceByBlanks( match ) { |
function replaceByBlanks( match ) { |
||
Line 547: | Line 473: | ||
function find_category( wikitext, category, once ) { |
function find_category( wikitext, category, once ) { |
||
var cat_regex = null; |
var cat_regex = null; |
||
if ( |
if ( HC.template_categories[ category ] ) { |
||
cat_regex = new RegExp( |
cat_regex = new RegExp( |
||
'\\{\\{' + wikiTextBlankOrBidi + '(' + |
'\\{\\{' + wikiTextBlankOrBidi + '(' + HC.template_regexp + '(?=' + wikiTextBlankOrBidi + ':))?' + wikiTextBlankOrBidi + |
||
'(?:' + |
'(?:' + HC.template_categories[ category ] + ')' + |
||
wikiTextBlankOrBidi + '(\\|.*?)?\\}\\}', |
wikiTextBlankOrBidi + '(\\|.*?)?\\}\\}', |
||
'g' |
'g' |
||
Line 558: | Line 484: | ||
var initial = cat_name.substr( 0, 1 ); |
var initial = cat_name.substr( 0, 1 ); |
||
cat_regex = new RegExp( |
cat_regex = new RegExp( |
||
'\\[\\[' + wikiTextBlankOrBidi + '(' + |
'\\[\\[' + wikiTextBlankOrBidi + '(' + HC.category_regexp + ')' + wikiTextBlankOrBidi + ':' + wikiTextBlankOrBidi + |
||
( initial === '\\' || ! |
( initial === '\\' || !HC.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 568: | Line 493: | ||
); |
); |
||
} |
} |
||
if ( once ) |
if ( once ) return cat_regex.exec( wikitext ); |
||
var copiedtext = wikitext |
var copiedtext = wikitext |
||
.replace( /<!--(\s|\S)*?-->/g, replaceByBlanks ) |
.replace( /<!--(\s|\S)*?-->/g, replaceByBlanks ) |
||
Line 575: | Line 501: | ||
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 592: | Line 520: | ||
var index = -1; |
var index = -1; |
||
findCatsRE.lastIndex = 0; |
findCatsRE.lastIndex = 0; |
||
while ( findCatsRE.exec( copiedtext ) !== null ) |
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 ) |
if ( match ) index = match.index; |
||
return { idx: index, onCat: false }; |
|||
return { |
|||
idx: index, |
|||
onCat: false |
|||
}; |
|||
} |
} |
||
return { |
return { |
||
idx: index, |
|||
onCat: index >= 0 |
|||
}; |
|||
} |
} |
||
var summary = [] |
var summary = [], |
||
nameSpace = HC.category_canonical, |
|||
cat_point = -1, // Position of removed category; |
|||
keyChange = ( toRemove && toAdd && toRemove === toAdd && toAdd.length ), |
|||
matches; |
|||
if ( key ) { key = '|' + key; } |
|||
if ( key ) key = '|' + key; |
|||
var keyChange = ( toRemove && toAdd && toRemove === toAdd && toAdd.length > 0 ); |
|||
// Remove |
|||
if ( toRemove && toRemove.length |
if ( toRemove && toRemove.length ) { |
||
matches = find_category( wikitext, toRemove ); |
matches = find_category( wikitext, toRemove ); |
||
if ( !matches || matches.length |
if ( !matches || !matches.length ) { |
||
return { |
|||
return { text: wikitext, summary: summary, error: HotCat.messages.cat_notFound.replace( /\$1/g, toRemove ) }; |
|||
text: wikitext, |
|||
summary: summary, |
|||
error: HC.messages.cat_notFound.replace( /\$1/g, toRemove ) |
|||
}; |
|||
} else { |
} else { |
||
var before = wikitext.substring( 0, matches[ 0 ].match.index ) |
var before = wikitext.substring( 0, matches[ 0 ].match.index ), |
||
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; |
// nameSpace = matches[ 0 ].match[ 1 ] || nameSpace; Canonical namespace should be always preferred |
||
if ( key === null ) |
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 637: | Line 578: | ||
// 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 ) |
while ( i >= 0 && before.charAt( i ) !== '\n' && before.substr( i, 1 ).search( /\s/ ) >= 0 ) i--; |
||
var j = 0; |
var j = 0; |
||
while ( j < after.length && after.charAt( j ) !== '\n' && after.substr( j, 1 ).search( /\s/ ) >= 0 ) |
while ( j < after.length && after.charAt( j ) !== '\n' && after.substr( j, 1 ).search( /\s/ ) >= 0 ) j++; |
||
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' && ( !after.length || j < after.length && after.charAt( j ) === '\n' ) ) i--; |
|||
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.length && before.substring( before.length - 1 ).search( /\S/ ) >= 0 && |
||
after.length |
after.length && after.substr( 0, 1 ).search( /\S/ ) >= 0 |
||
) { |
|||
before += ' '; |
before += ' '; |
||
} |
} |
||
cat_point = before.length; |
cat_point = before.length; |
||
if ( cat_point === 0 && after.length |
if ( cat_point === 0 && after.length && after.substr( 0, 1 ) === '\n' ) after = after.substr( 1 ); |
||
after = after.substr( 1 ); |
|||
} |
|||
wikitext = before + after; |
wikitext = before + after; |
||
if ( !keyChange ) { |
if ( !keyChange ) { |
||
if ( |
if ( HC.template_categories[ toRemove ] ) { summary.push( HC.messages.template_removed.replace( /\$1/g, toRemove ) ); } else { summary.push( HC.messages.cat_removed.replace( /\$1/g, toRemove ) ); } |
||
summary.push( HotCat.messages.template_removed.replace( /\$1/g, toRemove ) ); |
|||
} else { |
|||
summary.push( HotCat.messages.cat_removed.replace( /\$1/g, toRemove ) ); |
|||
} |
|||
} |
} |
||
} |
} |
||
} |
} |
||
// Add |
|||
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 ) { |
||
// Already exists |
|||
return { text: wikitext, summary: summary, error: HotCat.messages.cat_exists.replace( /\$1/g, toAdd ) }; |
|||
return { |
|||
text: wikitext, |
|||
summary: summary, |
|||
error: HC.messages.cat_exists.replace( /\$1/g, toAdd ) |
|||
}; |
|||
} else { |
} else { |
||
var onCat = false; |
var onCat = false; |
||
Line 687: | Line 629: | ||
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 |
if ( suffix.length && suffix.substr( 0, 1 ) !== '\n' ) wikitext += '\n' + suffix; else wikitext += suffix; |
||
wikitext += '\n' + suffix; |
|||
} else { |
|||
wikitext += suffix; |
|||
} |
|||
} else { |
} else { |
||
if ( wikitext.length |
if ( wikitext.length && wikitext.substr( wikitext.length - 1, 1 ) !== '\n' ) wikitext += '\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 |
if ( k.length ) k = k.substr( 1 ); |
||
summary.push( substitute( HotCat.messages.cat_keychange, [ null, toAdd, k ] ) ); |
|||
summary.push( substitute( HC.messages.cat_keychange, [ null, toAdd, k ] ) ); |
|||
} else { |
} else { |
||
summary.push( |
summary.push( HC.messages.cat_added.replace( /\$1/g, toAdd ) ); |
||
} |
} |
||
if ( |
if ( HC.uncat_regexp && !is_hidden ) { |
||
var txt = wikitext.replace( |
var txt = wikitext.replace( HC.uncat_regexp, '' ); // Remove "uncat" templates |
||
if ( txt.length !== wikitext.length ) { |
if ( txt.length !== wikitext.length ) { |
||
wikitext = txt; |
wikitext = txt; |
||
summary.push( |
summary.push( HC.messages.uncat_removed ); |
||
} |
} |
||
} |
} |
||
} |
} |
||
} |
} |
||
return { |
|||
return { text: wikitext, summary: summary, error: null }; |
|||
text: wikitext, |
|||
summary: summary, |
|||
error: null |
|||
}; |
|||
} |
} |
||
Line 721: | Line 663: | ||
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 ( |
if ( e.ctrlKey ) { // All modern browsers |
||
// Ctrl-click seems to be overloaded in FF/Mac (it opens a pop-up menu), so treat cmd-click |
|||
// as a ctrl-click, too. |
|||
if ( e.ctrlKey || e.metaKey ) |
if ( e.ctrlKey || e.metaKey ) code |= 1; |
||
if ( e.shiftKey ) { code |= 2; } |
|||
if ( e.shiftKey ) code |= 2; |
|||
} 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 746: | Line 683: | ||
} |
} |
||
var catLine = null |
var catLine = null, |
||
onUpload = false, |
|||
editors = [], |
|||
commitButton = null, |
|||
commitForm = null, |
|||
multiSpan = null, |
|||
pageText = null, |
|||
pageTime = null, |
|||
pageWatched = false, |
|||
watchCreate = false, |
|||
watchEdit = false, |
|||
minorEdits = false, |
|||
editToken = null, |
|||
is_rtl = false, |
|||
serverTime = null, |
|||
lastRevId = null, |
|||
pageTextRevId = null, |
|||
conflictingUser = null, |
|||
newDOM = false; // true if MediaWiki serves the new UL-LI DOM for categories |
|||
function |
function CategoryEditor() { |
||
this.initialize.apply( this, arguments ); |
|||
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 setPage( json ) { |
||
var startTime = null; |
|||
if ( !commitButton ) { return; } |
|||
if ( json && json.query ) { |
|||
var has_changes = false; |
|||
if ( json.query.pages ) { |
|||
var page = json.query.pages[ !conf.wgArticleId ? '-1' : String( conf.wgArticleId ) ]; |
|||
if ( editors[ i ].state !== CategoryEditor.UNCHANGED ) { |
|||
if ( page ) { |
|||
if ( page.revisions && page.revisions.length ) { |
|||
break; |
|||
// Revisions are sorted by revision ID, hence [ 0 ] is the one we asked for, and possibly there's a [ 1 ] if we're |
|||
// not on the latest revision (edit conflicts and such). |
|||
pageText = page.revisions[ 0 ][ '*' ]; |
|||
if ( page.revisions[ 0 ].timestamp ) pageTime = page.revisions[ 0 ].timestamp.replace( /\D/g, '' ); |
|||
if ( page.revisions[ 0 ].revid ) pageTextRevId = page.revisions[ 0 ].revid; |
|||
if ( page.revisions.length > 1 ) conflictingUser = page.revisions[ 1 ].user; |
|||
} |
|||
if ( page.lastrevid ) lastRevId = page.lastrevid; |
|||
if ( page.starttimestamp ) startTime = page.starttimestamp.replace( /\D/g, '' ); |
|||
pageWatched = typeof page.watched === 'string'; |
|||
editToken = page.edittoken; |
|||
if ( page.langlinks && ( !json[ 'query-continue' ] || !json[ 'query-continue' ].langlinks ) ) { |
|||
// We have interlanguage links, and we got them all. |
|||
var re = ''; |
|||
for ( var i = 0; i < page.langlinks.length; i++ ) re += ( i > 0 ? '|' : '' ) + page.langlinks[ i ].lang.replace( /([\\^$.?*+()])/g, '\\$1' ); |
|||
if ( re.length ) interlanguageRE = new RegExp( '((^|\\n\\r?)(\\[\\[\\s*(' + re + ')\\s*:[^\\]]+\\]\\]\\s*))+$' ); |
|||
} |
|||
} |
|||
} |
|||
// Siteinfo |
|||
if ( json.query.general ) { |
|||
if ( json.query.general.time && !startTime ) startTime = json.query.general.time.replace( /\D/g, '' ); |
|||
if ( HC.capitalizePageNames === null ) { |
|||
// ResourceLoader's JSParser doesn't like .case, so override eslint. |
|||
// eslint-disable-next-line dot-notation |
|||
HC.capitalizePageNames = ( json.query.general[ 'case' ] === 'first-letter' ); |
|||
} |
|||
} |
|||
serverTime = startTime; |
|||
// Userinfo |
|||
if ( json.query.userinfo && json.query.userinfo.options ) { |
|||
watchCreate = !HC.dont_add_to_watchlist && json.query.userinfo.options.watchcreations === '1'; |
|||
watchEdit = !HC.dont_add_to_watchlist && json.query.userinfo.options.watchdefault === '1'; |
|||
minorEdits = json.query.userinfo.options.minordefault === 1; |
|||
// If the user has the "All edits are minor" preference enabled, we should honor that |
|||
// for single category changes, no matter what the site configuration is. |
|||
if ( minorEdits ) HC.single_minor = true; |
|||
} |
} |
||
} |
} |
||
commitButton.disabled = !has_changes; |
|||
} |
|||
function currentTimestamp() { |
|||
var now = new Date(); |
|||
var ts = 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 ) |
if ( saveInProgress ) return; |
||
saveInProgress = true; |
saveInProgress = true; |
||
var oldButtonState; |
var oldButtonState; |
||
Line 820: | Line 772: | ||
function fail() { |
function fail() { |
||
saveInProgress = false; |
saveInProgress = false; |
||
if ( commitButton ) |
if ( commitButton ) commitButton.disabled = oldButtonState; |
||
failure.apply( this, arguments ); |
failure.apply( this, arguments ); |
||
} |
} |
||
// Must use Ajax here to get the user options and the edit token. |
// Must use Ajax here to get the user options and the edit token. |
||
$.getJSON( |
|||
conf.wgServer + conf.wgScriptPath + '/api.php?' + |
|||
getJSON( { |
|||
'format=json&action=query&rawcontinue=&titles=' + encodeURIComponent( conf.wgPageName ) + |
|||
uri: conf.wgServer + conf.wgScriptPath + '/api.php', |
|||
'&prop=info%7Crevisions%7Clanglinks&inprop=watched&intoken=edit&rvprop=content%7Ctimestamp%7Cids%7Cuser&lllimit=500' + |
|||
data: 'format=json&action=query&rawcontinue=&titles=' + encodeURIComponent( conf.wgPageName ) + |
|||
'&rvlimit=2&rvdir=newer&rvstartid=' + conf.wgCurRevisionId + '&meta=siteinfo%7Cuserinfo&uiprop=options', |
|||
'&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 ); |
|||
} |
} |
||
).fail( function ( req ) { |
|||
fail( req.status + ' ' + req.statusText ); |
|||
} ); |
} ); |
||
} |
} |
||
function multiChangeMsg( count ) { |
function multiChangeMsg( count ) { |
||
var msg = |
var msg = HC.messages.multi_change; |
||
if ( typeof msg !== 'string' && msg.length ) |
if ( typeof msg !== 'string' && msg.length ) |
||
if ( mw.language && mw.language.convertPlural ) { |
if ( mw.language && mw.language.convertPlural ) { msg = mw.language.convertPlural( count, msg ); } else { msg = msg[ msg.length - 1 ]; } |
||
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 currentTimestamp() { |
|||
var now = new Date(); |
|||
var ts = String( now.getUTCFullYear() ); |
|||
function two( s ) { |
|||
return s.substr( s.length - 2 ); |
|||
} |
|||
ts += |
|||
two( '0' + ( now.getUTCMonth() + 1 ) ) + |
|||
two( '0' + now.getUTCDate() ) + |
|||
two( '00' + now.getUTCHours() ) + |
|||
two( '00' + now.getUTCMinutes() ) + |
|||
two( '00' + now.getUTCSeconds() ); |
|||
return ts; |
|||
} |
} |
||
function performChanges( failure, singleEditor ) { |
function performChanges( failure, singleEditor ) { |
||
if ( pageText === null ) { |
if ( pageText === null ) { |
||
failure( |
failure( HC.messages.multi_error ); |
||
return; |
return; |
||
} |
} |
||
// Backwards compatibility after message change (added $2 to cat_keychange) |
// Backwards compatibility after message change (added $2 to cat_keychange) |
||
if ( |
if ( HC.messages.cat_keychange.indexOf( '$2' ) < 0 ) HC.messages.cat_keychange += '"$2"'; |
||
// More backwards-compatibility with earlier HotCat versions: |
// More backwards-compatibility with earlier HotCat versions: |
||
if ( ! |
if ( !HC.messages.short_catchange ) HC.messages.short_catchange = '[[' + HC.category_canonical + ':$1]]'; |
||
// Create a form and submit it. We don't use the edit API (api.php?action=edit) because |
// Create a form and submit it. We don't use the edit API (api.php?action=edit) because |
||
// (a) sensibly reporting back errors like edit conflicts is always a hassle, and |
// (a) sensibly reporting back errors like edit conflicts is always a hassle, and |
||
Line 876: | Line 838: | ||
// 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 selfEditConflict = ( lastRevId !== null && lastRevId !== conf.wgCurRevisionId || pageTextRevId !== null && |
||
pageTextRevId !== conf.wgCurRevisionId ) && conflictingUser && conflictingUser === conf.wgUserName; |
|||
if ( singleEditor && !singleEditor.noCommit && ! |
if ( singleEditor && !singleEditor.noCommit && !HC.no_autocommit && editToken && !selfEditConflict ) { |
||
// If we do have an edit conflict, but not with ourself, that's no reason not to attempt to save: the server side may actually be able to |
// If we do have an edit conflict, but not with ourself, that's no reason not to attempt to save: the server side may actually be able to |
||
// merge the changes. We just need to make sure that we do present a diff view if it's a self edit conflict. |
// merge the changes. We just need to make sure that we do present a diff view if it's a self edit conflict. |
||
commitForm.wpEditToken.value = editToken; |
commitForm.wpEditToken.value = editToken; |
||
action = commitForm.wpDiff; |
action = commitForm.wpDiff; |
||
if ( action ) |
if ( action ) action.name = action.value = 'wpSave'; |
||
} else { |
} else { |
||
action = commitForm.wpSave; |
action = commitForm.wpSave; |
||
if ( action ) |
if ( action ) action.name = action.value = 'wpDiff'; |
||
} |
} |
||
var result = { |
var result = { |
||
text: pageText |
|||
var changed = [], added = [], deleted = [], changes = 0; |
|||
}, |
|||
var toEdit = singleEditor ? [ singleEditor ] : editors; |
|||
changed = [], |
|||
added = [], |
|||
deleted = [], |
|||
changes = 0, |
|||
toEdit = singleEditor ? [ singleEditor ] : editors, |
|||
error = null, |
|||
edit, |
|||
i; |
|||
for ( i = 0; i < toEdit.length; i++ ) { |
for ( i = 0; i < toEdit.length; i++ ) { |
||
edit = toEdit[ i ]; |
|||
if ( edit.state === CategoryEditor.CHANGED ) { |
|||
result = change_category( |
result = change_category( |
||
result.text, |
result.text, |
||
edit.originalCategory, |
|||
edit.currentCategory, |
|||
edit.currentKey, |
|||
edit.currentHidden ); |
|||
); |
|||
if ( !result.error ) { |
if ( !result.error ) { |
||
changes++; |
changes++; |
||
if ( ! |
if ( !edit.originalCategory || !edit.originalCategory.length ) { |
||
added.push( |
added.push( edit.currentCategory ); |
||
} else { |
} else { |
||
changed.push( { |
changed.push( { |
||
from: edit.originalCategory, |
|||
to: edit.currentCategory |
|||
} ); |
|||
} |
} |
||
} else if ( error === null ) { |
} else if ( error === null ) { |
||
Line 913: | Line 884: | ||
} |
} |
||
} else if ( |
} else if ( |
||
edit.state === CategoryEditor.DELETED && edit.originalCategory && edit.originalCategory.length ) { |
|||
result = change_category( |
|||
toEdit[ i ].originalCategory && |
|||
result.text, |
|||
toEdit[ i ].originalCategory.length > 0 |
|||
edit.originalCategory, |
|||
) { |
|||
null, null, false ); |
|||
if ( !result.error ) { |
if ( !result.error ) { |
||
changes++; |
changes++; |
||
deleted.push( |
deleted.push( edit.originalCategory ); |
||
} else if ( error === null ) { |
} else if ( error === null ) { |
||
error = result.error; |
error = result.error; |
||
Line 928: | Line 899: | ||
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 ) |
if ( action ) action.name = action.value = 'wpDiff'; |
||
} |
} |
||
// Fill in the form and submit it |
// Fill in the form and submit it |
||
commitForm.wpAutoSummary.value = 'd41d8cd98f00b204e9800998ecf8427e'; // MD5 hash of the empty string |
|||
commitForm.wpMinoredit.checked = minorEdits; |
commitForm.wpMinoredit.checked = minorEdits; |
||
commitForm.wpWatchthis.checked = conf.wgArticleId |
commitForm.wpWatchthis.checked = !conf.wgArticleId && watchCreate || watchEdit || pageWatched; |
||
if ( conf.wgArticleId |
if ( conf.wgArticleId || !!singleEditor ) { |
||
// Prepare change-tag save |
|||
if ( changes === 1 ) { |
|||
if ( action && action.value === 'wpSave' ) { |
|||
if ( HC.changeTag ) { |
|||
commitForm.wpSummary.value = HotCat.messages.prefix + result.summary.join( HotCat.messages.separator ) + HotCat.messages.using; |
|||
commitForm.wpChangeTags.value = HC.changeTag; |
|||
HC.messages.using = ''; |
|||
HC.messages.prefix = ''; |
|||
} |
} |
||
} else { |
|||
commitForm.wpMinoredit.checked = HotCat.single_minor || minorEdits; |
|||
commitForm.wpAutoSummary.value = HC.changeTag; |
|||
} else if ( changes > 1 ) { |
|||
} |
|||
if ( changes === 1 ) { |
|||
if ( result.summary && result.summary.length ) commitForm.wpSummary.value = HC.messages.prefix + result.summary.join( HC.messages.separator ) + HC.messages.using; |
|||
commitForm.wpMinoredit.checked = HC.single_minor || minorEdits; |
|||
} else if ( changes ) { |
|||
var summary = []; |
var summary = []; |
||
var shortSummary = []; |
var shortSummary = []; |
||
// Deleted |
// Deleted |
||
for ( i = 0; i < deleted.length; i++ ) |
for ( i = 0; i < deleted.length; i++ ) summary.push( '−' + substitute( HC.messages.short_catchange, [ null, deleted[ i ] ] ) ); |
||
summary.push( '-' + substitute( HotCat.messages.short_catchange, [ null, deleted[ i ] ] ) ); |
|||
if ( deleted.length === 1 ) shortSummary.push( '−' + substitute( HC.messages.short_catchange, [ null, deleted[ 0 ] ] ) ); else if ( deleted.length ) shortSummary.push( '− ' + multiChangeMsg( deleted.length ) ); |
|||
} |
|||
if ( deleted.length === 1 ) { |
|||
shortSummary.push( '-' + substitute( HotCat.messages.short_catchange, [ null, deleted[ 0 ] ] ) ); |
|||
} else if ( deleted.length > 1 ) { |
|||
shortSummary.push( '- ' + multiChangeMsg( deleted.length ) ); |
|||
} |
|||
// Added |
// Added |
||
for ( i = 0; i < added.length; i++ ) |
for ( i = 0; i < added.length; i++ ) summary.push( '+' + substitute( HC.messages.short_catchange, [ null, added[ i ] ] ) ); |
||
summary.push( '+' + substitute( HotCat.messages.short_catchange, [ null, added[ i ] ] ) ); |
|||
if ( added.length === 1 ) shortSummary.push( '+' + substitute( HC.messages.short_catchange, [ null, added[ 0 ] ] ) ); else if ( added.length ) shortSummary.push( '+ ' + multiChangeMsg( added.length ) ); |
|||
} |
|||
if ( added.length === 1 ) { |
|||
shortSummary.push( '+' + substitute( HotCat.messages.short_catchange, [ null, added[ 0 ] ] ) ); |
|||
} else if ( added.length > 1 ) { |
|||
shortSummary.push( '+ ' + multiChangeMsg( added.length ) ); |
|||
} |
|||
// Changed |
// Changed |
||
var arrow = is_rtl ? '\u2190' : '\u2192'; // left and right arrows. Don't use ← and → in the code. |
var arrow = is_rtl ? '\u2190' : '\u2192'; // left and right arrows. Don't use ← and → in the code. |
||
for ( i = 0; i < changed.length; i++ ) { |
for ( i = 0; i < changed.length; i++ ) { |
||
if ( changed[ i ].from !== changed[ i ].to ) { |
if ( changed[ i ].from !== changed[ i ].to ) { |
||
summary.push( |
|||
summary.push( '±' + substitute( HotCat.messages.short_catchange, [ null, changed[ i ].from ] ) + arrow + |
|||
'±' + substitute( HC.messages.short_catchange, [ null, changed[ i ].from ] ) + arrow + |
|||
substitute( HC.messages.short_catchange, [ null, changed[ i ].to ] ) |
|||
); |
|||
} else { |
} else { |
||
summary.push( '±' + substitute( |
summary.push( '±' + substitute( HC.messages.short_catchange, [ null, changed[ i ].from ] ) ); |
||
} |
} |
||
} |
} |
||
if ( changed.length === 1 ) { |
if ( changed.length === 1 ) { |
||
if ( changed[ 0 ].from !== changed[ 0 ].to ) { |
if ( changed[ 0 ].from !== changed[ 0 ].to ) { |
||
shortSummary.push( |
shortSummary.push( |
||
'±' + substitute( HC.messages.short_catchange, [ null, changed[ 0 ].from ] ) + arrow + |
|||
substitute( HC.messages.short_catchange, [ null, changed[ 0 ].to ] ) |
|||
); |
|||
} else { |
} else { |
||
shortSummary.push( '±' + substitute( |
shortSummary.push( '±' + substitute( HC.messages.short_catchange, [ null, changed[ 0 ].from ] ) ); |
||
} |
} |
||
} else if ( changed.length |
} else if ( changed.length ) { |
||
shortSummary.push( '± ' + multiChangeMsg( changed.length ) ); |
shortSummary.push( '± ' + multiChangeMsg( changed.length ) ); |
||
} |
} |
||
if ( summary.length |
if ( summary.length ) { |
||
summary = summary.join( |
summary = summary.join( HC.messages.separator ); |
||
if ( summary.length > 200 - |
if ( summary.length > 200 - HC.messages.prefix.length - HC.messages.using.length ) summary = shortSummary.join( HC.messages.separator ); |
||
summary = shortSummary.join( HotCat.messages.separator ); |
|||
commitForm.wpSummary.value = HC.messages.prefix + summary + HC.messages.using; |
|||
} |
|||
commitForm.wpSummary.value = HotCat.messages.prefix + summary + HotCat.messages.using; |
|||
} |
} |
||
} |
} |
||
} |
} |
||
commitForm.wpTextbox1.value = result.text; |
commitForm.wpTextbox1.value = result.text; |
||
commitForm.wpStarttime.value = serverTime || currentTimestamp(); |
commitForm.wpStarttime.value = serverTime || currentTimestamp(); |
||
commitForm.wpEdittime.value = pageTime || commitForm.wpStarttime.value; |
commitForm.wpEdittime.value = pageTime || commitForm.wpStarttime.value; |
||
if ( selfEditConflict ) |
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, |
||
lks = page.links, |
|||
is_dab = false, |
|||
is_redir = typeof page.redirect === 'string', // Hard redirect? |
|||
is_hidden = page.categoryinfo && typeof page.categoryinfo.hidden === 'string', |
|||
is_missing = typeof page.missing === 'string', |
|||
i; |
|||
for ( i = 0; i < toResolve.length; i++ ) { |
for ( i = 0; i < toResolve.length; i++ ) { |
||
if ( |
if ( i && 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 = |
toResolve[ i ].icon.src = ( is_missing ? HC.existsNo : HC.existsYes ); |
||
} |
} |
||
if ( is_missing ) |
if ( is_missing ) return; |
||
if ( !is_redir && cats && ( |
if ( !is_redir && cats && ( HC.disambig_category || HC.redir_category ) ) { |
||
for ( var c = 0; c < cats.length; c++ ) { |
for ( var c = 0; c < cats.length; c++ ) { |
||
var cat = cats[ c ].title; |
var cat = cats[ c ].title; |
||
Line 1,052: | Line 996: | ||
if ( cat ) { |
if ( cat ) { |
||
cat = cat.substring( cat.indexOf( ':' ) + 1 ).replace( /_/g, ' ' ); |
cat = cat.substring( cat.indexOf( ':' ) + 1 ).replace( /_/g, ' ' ); |
||
if ( cat === |
if ( cat === HC.disambig_category ) { |
||
is_dab = true |
is_dab = true; |
||
break; |
|||
} else if ( cat === HotCat.redir_category ) { |
|||
} else if ( cat === HC.redir_category ) { |
|||
is_redir = true; break; |
|||
is_redir = true; |
|||
break; |
|||
} |
} |
||
} |
} |
||
} |
} |
||
} |
} |
||
if ( !is_redir && !is_dab ) |
if ( !is_redir && !is_dab ) return; |
||
if ( !lks || lks.length |
if ( !lks || !lks.length ) return; |
||
var titles = []; |
var titles = []; |
||
for ( i = 0; i < lks.length; i++ ) { |
for ( i = 0; i < lks.length; i++ ) { |
||
Line 1,068: | Line 1,014: | ||
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 |
||
) { |
) { |
||
// 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,074: | Line 1,020: | ||
match = match.substring( match.indexOf( ':' ) + 1 ); |
match = match.substring( match.indexOf( ':' ) + 1 ); |
||
// Exclude blacklisted categories. |
// Exclude blacklisted categories. |
||
if ( ! |
if ( !HC.blacklist || !HC.blacklist.test( match ) ) titles.push( match ); |
||
titles.push( match ); |
|||
} |
|||
} |
} |
||
} |
} |
||
if ( titles.length |
if ( !titles.length ) return; |
||
return; |
|||
} |
|||
for ( i = 0; i < toResolve.length; i++ ) { |
for ( i = 0; i < toResolve.length; i++ ) { |
||
if ( |
if ( i && toResolve[ i ].dabInputCleaned !== page.title.substring( page.title.indexOf( ':' ) + 1 ) ) continue; |
||
toResolve[ i ].inputExists = true; // Might actually be wrong if it's a redirect pointing to a non-existing category |
toResolve[ i ].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 = HC.existsYes; |
||
if ( titles.length > 1 ) { |
if ( titles.length > 1 ) { |
||
toResolve[ i ].dab = titles; |
toResolve[ i ].dab = titles; |
||
Line 1,096: | Line 1,038: | ||
function resolveRedirects( toResolve, params ) { |
function resolveRedirects( toResolve, params ) { |
||
if ( !params || !params.query || !params.query.pages ) |
if ( !params || !params.query || !params.query.pages ) return; |
||
for ( var p in params.query.pages ) |
for ( var p in params.query.pages ) resolveOne( params.query.pages[ p ], toResolve ); |
||
} |
} |
||
function |
function resolveMulti( toResolve, callback ) { |
||
var |
var i; |
||
for ( |
for ( i = 0; i < toResolve.length; i++ ) { |
||
toResolve[ i ].dab = null; |
|||
if ( editors[ i ].state === CategoryEditor.CHANGE_PENDING || editors[ i ].state === CategoryEditor.OPEN ) { |
|||
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, HC.shortcuts ); |
|||
toResolve[ i ].dabInputCleaned = v; |
|||
args += encodeURIComponent( 'Category:' + v ); |
|||
if ( i + 1 < toResolve.length ) args += '%7C'; |
|||
} |
|||
$.getJSON( conf.wgServer + conf.wgScriptPath + '/api.php?' + args, |
|||
function ( json ) { |
|||
resolveRedirects( toResolve, json ); |
|||
callback( toResolve ); |
|||
} ).fail( function ( req ) { |
|||
if ( !req ) noSuggestions = true; |
|||
callback( toResolve ); |
|||
} ); |
|||
} |
|||
function makeActive( which ) { |
|||
if ( which.is_active ) return; |
|||
for ( var i = 0; i < editors.length; i++ ) |
|||
if ( editors[ i ] !== which ) editors[ i ].inactivate(); |
|||
which.is_active = true; |
|||
if ( which.dab ) { |
|||
// eslint-disable-next-line no-use-before-define |
|||
showDab( which ); |
|||
} else { |
|||
// Check for programmatic value changes. |
|||
var expectedInput = which.lastRealInput || which.lastInput || ''; |
|||
var actualValue = which.text.value || ''; |
|||
if ( !expectedInput.length && actualValue.length || expectedInput.length && actualValue.indexOf( expectedInput ) ) { |
|||
// Somehow the field's value appears to have changed, and which.lastSelection therefore is no longer valid. Try to set the |
|||
// cursor at the end of the category, and do not display the old suggestion list. |
|||
which.showsList = false; |
|||
var v = actualValue.split( '|' ); |
|||
which.lastRealInput = which.lastInput = v[ 0 ]; |
|||
if ( v.length > 1 ) which.currentKey = v[ 1 ]; |
|||
if ( which.lastSelection ) { |
|||
which.lastSelection = { |
|||
start: v[ 0 ].length, |
|||
end: v[ 0 ].length |
|||
}; |
|||
} |
|||
} |
|||
if ( which.showsList ) which.displayList(); |
|||
if ( which.lastSelection ) { |
|||
if ( is_webkit ) { |
|||
// WebKit (Safari, Chrome) has problems selecting inside focus() |
|||
// See http://code.google.com/p/chromium/issues/detail?id=32865#c6 |
|||
window.setTimeout( |
|||
function () { |
|||
which.setSelection( which.lastSelection.start, which.lastSelection.end ); |
|||
}, |
|||
1 ); |
|||
} else { |
|||
which.setSelection( which.lastSelection.start, which.lastSelection.end ); |
|||
} |
|||
} |
} |
||
} |
} |
||
} |
|||
if ( toResolve.length === 0 ) { |
|||
initiateEdit( function ( failure ) { performChanges( failure ); }, function ( msg ) { alert( msg ); } ); |
|||
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; |
|||
} |
|||
} |
|||
function multiSubmit() { |
|||
var toResolve = []; |
|||
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 ( !toResolve.length ) { |
|||
initiateEdit( function ( failure ) { |
|||
performChanges( failure ); |
|||
}, function ( msg ) { |
|||
alert( msg ); |
|||
} ); |
|||
return; |
return; |
||
} |
} |
||
Line 1,121: | Line 1,153: | ||
} else { |
} else { |
||
if ( resolved[ i ].dab ) { |
if ( resolved[ i ].dab ) { |
||
if ( !firstDab ) |
if ( !firstDab ) firstDab = resolved[ i ]; |
||
} else { |
} else { |
||
if ( resolved[ i ].acceptCheck( true ) ) |
if ( resolved[ i ].acceptCheck( true ) ) resolved[ i ].commit(); |
||
} |
} |
||
} |
} |
||
Line 1,130: | Line 1,162: | ||
showDab( firstDab ); |
showDab( firstDab ); |
||
} else if ( !dontChange ) { |
} else if ( !dontChange ) { |
||
initiateEdit( function ( failure ) { |
initiateEdit( function ( failure ) { |
||
performChanges( failure ); |
|||
}, function ( msg ) { |
|||
alert( msg ); |
|||
} ); |
|||
} |
} |
||
} ); |
} ); |
||
} |
} |
||
function setMultiInput() { |
|||
var cat_prefix = null; |
|||
if ( commitButton || onUpload ) return; |
|||
var noSuggestions = false; |
|||
commitButton = make( 'input' ); |
|||
commitButton.type = 'button'; |
|||
commitButton.value = HC.messages.commit; |
|||
commitButton.onclick = multiSubmit; |
|||
if ( multiSpan ) multiSpan.parentNode.replaceChild( commitButton, multiSpan ); else catLine.appendChild( commitButton ); |
|||
} |
|||
function checkMultiInput() { |
|||
if ( !commitButton ) return; |
|||
var hasChanges = false; |
|||
for ( var i = 0; i < editors.length; i++ ) { |
|||
if ( editors[ i ].state !== CategoryEditor.UNCHANGED ) { |
|||
hasChanges = true; |
|||
break; |
|||
} |
|||
} |
|||
commitButton.disabled = !hasChanges; |
|||
} |
|||
var suggestionEngines = { |
var suggestionEngines = { |
||
opensearch: { |
opensearch: { |
||
Line 1,146: | Line 1,201: | ||
var titles = queryResult[ 1 ]; |
var titles = queryResult[ 1 ]; |
||
var exists = false; |
var exists = false; |
||
if ( !cat_prefix ) |
if ( !cat_prefix ) cat_prefix = new RegExp( '^(' + HC.category_regexp + '):' ); |
||
for ( var i = 0; i < titles.length; i++ ) { |
for ( var i = 0; i < titles.length; i++ ) { |
||
cat_prefix.lastIndex = 0; |
cat_prefix.lastIndex = 0; |
||
Line 1,152: | Line 1,208: | ||
if ( m && m.length > 1 ) { |
if ( m && m.length > 1 ) { |
||
titles[ i ] = titles[ i ].substring( titles[ i ].indexOf( ':' ) + 1 ); // rm namespace |
titles[ i ] = titles[ i ].substring( titles[ i ].indexOf( ':' ) + 1 ); // rm namespace |
||
if ( key === titles[ i ] ) |
if ( key === titles[ i ] ) exists = true; |
||
} else { |
} else { |
||
titles.splice( i, 1 ); // Nope, it's not a category after all. |
titles.splice( i, 1 ); // Nope, it's not a category after all. |
||
Line 1,159: | Line 1,215: | ||
} |
} |
||
titles.exists = exists; |
titles.exists = exists; |
||
if ( queryKey !== key ) |
if ( queryKey !== key ) titles.normalized = key; |
||
// Remember the NFC normalized key we got back from the server |
|||
return titles; |
return titles; |
||
} |
} |
||
Line 1,170: | Line 1,227: | ||
if ( queryResult && queryResult.query && queryResult.query.allpages ) { |
if ( queryResult && queryResult.query && queryResult.query.allpages ) { |
||
var titles = queryResult.query.allpages; |
var titles = queryResult.query.allpages; |
||
for ( var i = 0; i < titles.length; i++ ) |
for ( var i = 0; i < titles.length; i++ ) titles[ i ] = titles[ i ].title.substring( titles[ i ].title.indexOf( ':' ) + 1 ); // rm namespace |
||
titles[ i ] = titles[ i ].title.substring( titles[ i ].title.indexOf( ':' ) + 1 ); // rm namespace |
|||
} |
|||
return titles; |
return titles; |
||
} |
} |
||
Line 1,188: | Line 1,244: | ||
var titles = [ title ]; |
var titles = [ title ]; |
||
titles.exists = true; |
titles.exists = true; |
||
if ( queryKey !== title ) |
if ( queryKey !== title ) titles.normalized = title; |
||
// NFC |
|||
return titles; |
return titles; |
||
} |
} |
||
Line 1,200: | Line 1,257: | ||
if ( queryResult && queryResult.query && queryResult.query.categorymembers ) { |
if ( queryResult && queryResult.query && queryResult.query.categorymembers ) { |
||
var titles = queryResult.query.categorymembers; |
var titles = queryResult.query.categorymembers; |
||
for ( var i = 0; i < titles.length; i++ ) |
for ( var i = 0; i < titles.length; i++ ) titles[ i ] = titles[ i ].title.substring( titles[ i ].title.indexOf( ':' ) + 1 ); // rm namespace |
||
titles[ i ] = titles[ i ].title.substring( titles[ i ].title.indexOf( ':' ) + 1 ); // rm namespace |
|||
} |
|||
return titles; |
return titles; |
||
} |
} |
||
Line 1,215: | Line 1,271: | ||
if ( queryResult.query.pages[ p ].categories ) { |
if ( queryResult.query.pages[ p ].categories ) { |
||
var titles = queryResult.query.pages[ p ].categories; |
var titles = queryResult.query.pages[ p ].categories; |
||
for ( var i = 0; i < titles.length; i++ ) |
for ( var i = 0; i < titles.length; i++ ) titles[ i ] = titles[ i ].title.substring( titles[ i ].title.indexOf( ':' ) + 1 ); // rm namespace |
||
titles[ i ] = titles[ i ].title.substring( titles[ i ].title.indexOf( ':' ) + 1 ); // rm namespace |
|||
} |
|||
return titles; |
return titles; |
||
} |
} |
||
Line 1,228: | Line 1,283: | ||
var suggestionConfigs = { |
var suggestionConfigs = { |
||
searchindex: { |
|||
searchindex: { name: 'Search index', engines: [ 'opensearch' ], cache: {}, show: true, temp: false, noCompletion: false }, |
|||
name: 'Search index', |
|||
pagelist: { name: 'Page list', engines: [ 'internalsearch', 'exists' ], cache: {}, show: true, temp: false, noCompletion: false }, |
|||
engines: [ 'opensearch' ], |
|||
combined: { name: 'Combined search', engines: [ 'opensearch', 'internalsearch' ], cache: {}, show: true, temp: false, noCompletion: false }, |
|||
cache: {}, |
|||
subcat: { name: 'Subcategories', engines: [ 'subcategories' ], cache: {}, show: true, temp: true, noCompletion: true }, |
|||
show: true, |
|||
parentcat: { name: 'Parent categories', engines: [ 'parentcategories' ], cache: {}, show: true, temp: true, noCompletion: true } |
|||
temp: false, |
|||
noCompletion: false |
|||
}, |
|||
pagelist: { |
|||
name: 'Page list', |
|||
engines: [ 'internalsearch', 'exists' ], |
|||
cache: {}, |
|||
show: true, |
|||
temp: false, |
|||
noCompletion: false |
|||
}, |
|||
combined: { |
|||
name: 'Combined search', |
|||
engines: [ 'opensearch', 'internalsearch' ], |
|||
cache: {}, |
|||
show: true, |
|||
temp: false, |
|||
noCompletion: false |
|||
}, |
|||
subcat: { |
|||
name: 'Subcategories', |
|||
engines: [ 'subcategories' ], |
|||
cache: {}, |
|||
show: true, |
|||
temp: true, |
|||
noCompletion: true |
|||
}, |
|||
parentcat: { |
|||
name: 'Parent categories', |
|||
engines: [ 'parentcategories' ], |
|||
cache: {}, |
|||
show: true, |
|||
temp: true, |
|||
noCompletion: true |
|||
} |
|||
}; |
}; |
||
function CategoryEditor() { this.initialize.apply( this, arguments ); } |
|||
CategoryEditor.UNCHANGED = 0; |
CategoryEditor.UNCHANGED = 0; |
||
CategoryEditor.OPEN = 1; // Open, but no input yet |
CategoryEditor.OPEN = 1; // Open, but no input yet |
||
Line 1,242: | Line 1,331: | ||
CategoryEditor.DELETED = 4; |
CategoryEditor.DELETED = 4; |
||
// Support: IE6 |
|||
// IE6 sometimes forgets to redraw the list when editors are opened or closed. |
// IE6 sometimes forgets to redraw the list when editors are opened or closed. |
||
// Adding/removing a dummy element helps, at least when opening editors. |
// Adding/removing a dummy element helps, at least when opening editors. |
||
Line 1,247: | Line 1,337: | ||
function forceRedraw() { |
function forceRedraw() { |
||
if ( dummyElement.parentNode ) |
if ( dummyElement.parentNode ) document.body.removeChild( dummyElement ); else document.body.appendChild( dummyElement ); |
||
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; |
|||
TAB = 9, |
|||
RET = 13, |
|||
function makeActive( which ) { |
|||
ESC = 27, |
|||
if ( which.is_active ) { return; } |
|||
SPACE = 32, |
|||
for ( var i = 0; i < editors.length; i++ ) { |
|||
PGUP = 33, |
|||
if ( editors[ i ] !== which ) { editors[ i ].inactivate(); } |
|||
PGDOWN = 34, |
|||
} |
|||
UP = 38, |
|||
which.is_active = true; |
|||
DOWN = 40, |
|||
if ( which.dab ) { |
|||
DEL = 46, |
|||
showDab( which ); |
|||
IME = 229; |
|||
// Check for programmatic value changes. |
|||
var expectedInput = which.lastRealInput || which.lastInput || ''; |
|||
var actualValue = which.text.value || ''; |
|||
if ( expectedInput.length === 0 && actualValue.length > 0 || expectedInput.length > 0 && actualValue.indexOf( expectedInput ) !== 0 ) { |
|||
// Somehow the field's value appears to have changed, and which.lastSelection therefore is no longer valid. Try to set the |
|||
// cursor at the end of the category, and do not display the old suggestion list. |
|||
which.showsList = false; |
|||
var v = actualValue.split( '|' ); |
|||
which.lastRealInput = which.lastInput = v[ 0 ]; |
|||
if ( v.length > 1 ) { which.currentKey = v[ 1 ]; } |
|||
if ( which.lastSelection ) { which.lastSelection = { start: v[ 0 ].length, end: v[ 0 ].length }; } |
|||
} |
|||
if ( which.showsList ) { which.displayList(); } |
|||
if ( which.lastSelection ) { |
|||
if ( is_webkit ) { |
|||
// WebKit (Safari, Chrome) has problems selecting inside focus() |
|||
// See http://code.google.com/p/chromium/issues/detail?id=32865#c6 |
|||
window.setTimeout( |
|||
function () { which.setSelection( which.lastSelection.start, which.lastSelection.end ); }, |
|||
1 |
|||
); |
|||
} else { |
|||
which.setSelection( which.lastSelection.start, which.lastSelection.end ); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
function showDab( which ) { |
|||
if ( !which.is_active ) { |
|||
makeActive( which ); |
|||
} else { |
|||
which.showSuggestions( which.dab, false, null, null ); // do autocompletion, no key, no engine selector |
|||
which.dab = null; |
|||
} |
|||
} |
|||
CategoryEditor.prototype = { |
CategoryEditor.prototype = { |
||
Line 1,323: | Line 1,373: | ||
after.parentNode.insertBefore( span, after.nextSibling ); |
after.parentNode.insertBefore( span, after.nextSibling ); |
||
after = after.nextSibling; |
after = after.nextSibling; |
||
} else { |
} else if (line) { |
||
line.appendChild( span ); |
line.appendChild( span ); |
||
} |
} |
||
} else if ( line.firstChild ) { |
} else if ( line && line.firstChild ) { |
||
span.appendChild( make( ' ', true ) ); |
span.appendChild( make( ' ', true ) ); |
||
line.appendChild( span ); |
line.appendChild( span ); |
||
Line 1,333: | Line 1,383: | ||
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.appendChild( make( HotCat.links.add, true ) ); lk.title = HotCat.tooltips.add; |
|||
lk.onclick = this.open.bind( this ); |
|||
lk.appendChild( make( HC.links.add, true ) ); |
|||
lk.title = HC.tooltips.add; |
|||
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 ) |
if ( is_rtl ) span.dir = 'rtl'; |
||
span.appendChild( this.linkSpan ); |
span.appendChild( this.linkSpan ); |
||
if ( after ) { |
if ( after ) { |
||
after.parentNode.insertBefore( span, after.nextSibling ); |
after.parentNode.insertBefore( span, after.nextSibling ); |
||
} else { |
} else if ( line ) { |
||
line.appendChild( span ); |
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 ) |
if ( is_rtl ) span.dir = 'rtl'; |
||
this.isAddCategory = false; |
this.isAddCategory = false; |
||
this.catLink = span.firstChild; |
this.catLink = span.firstChild; |
||
Line 1,357: | Line 1,413: | ||
// Create change and del links |
// Create change and del links |
||
this.makeLinkSpan(); |
this.makeLinkSpan(); |
||
if ( !this.originalExists && this.upDownLinks ) |
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 = HC.suggestions; |
||
this.span = span; |
this.span = span; |
||
this.currentCategory = this.originalCategory; |
this.currentCategory = this.originalCategory; |
||
Line 1,374: | Line 1,431: | ||
this.lastSavedExists = this.originalExists; |
this.lastSavedExists = this.originalExists; |
||
this.lastSavedHidden = this.originalHidden; |
this.lastSavedHidden = this.originalHidden; |
||
if ( this.catLink && this.currentKey ) |
if ( this.catLink && this.currentKey ) this.catLink.title = this.currentKey; |
||
this.catLink.title = this.currentKey; |
|||
} |
|||
editors[ editors.length ] = this; |
editors[ editors.length ] = this; |
||
}, |
}, |
||
Line 1,383: | Line 1,439: | ||
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 ) { |
||
lk = make( 'a' |
lk = make( 'a' ); |
||
lk.href = '#catlinks'; |
|||
lk.appendChild( make( HotCat.links.remove, true ) ); lk.title = HotCat.tooltips.remove; |
|||
lk.onclick = this.remove.bind( this ); |
|||
lk.appendChild( make( HC.links.remove, true ) ); |
|||
lk.title = HC.tooltips.remove; |
|||
this.normalLinks.appendChild( make( ' ', true ) ); |
this.normalLinks.appendChild( make( ' ', true ) ); |
||
this.normalLinks.appendChild( lk ); |
this.normalLinks.appendChild( lk ); |
||
} |
} |
||
if ( ! |
if ( !HC.template_categories[ this.originalCategory ] ) { |
||
lk = make( 'a' |
lk = make( 'a' ); |
||
lk.href = '#catlinks'; |
|||
lk.appendChild( make( HotCat.links.change, true ) ); lk.title = HotCat.tooltips.change; |
|||
lk.onclick = this.open.bind( this ); |
|||
lk.appendChild( make( HC.links.change, true ) ); |
|||
lk.title = HC.tooltips.change; |
|||
this.normalLinks.appendChild( make( ' ', true ) ); |
this.normalLinks.appendChild( make( ' ', true ) ); |
||
this.normalLinks.appendChild( lk ); |
this.normalLinks.appendChild( lk ); |
||
if ( !noSuggestions && |
if ( !noSuggestions && HC.use_up_down ) { |
||
this.upDownLinks = make( 'span' ); |
this.upDownLinks = make( 'span' ); |
||
lk = make( 'a' |
lk = make( 'a' ); |
||
lk.href = '#catlinks'; |
|||
lk.appendChild( make( HotCat.links.down, true ) ); lk.title = HotCat.tooltips.down; |
|||
lk.onclick = this.down.bind( this ); |
|||
lk.appendChild( make( HC.links.down, true ) ); |
|||
lk.title = HC.tooltips.down; |
|||
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.appendChild( make( HotCat.links.up, true ) ); lk.title = HotCat.tooltips.up; |
|||
lk.onclick = this.up.bind( this ); |
|||
lk.appendChild( make( HC.links.up, true ) ); |
|||
lk.title = HC.tooltips.up; |
|||
this.upDownLinks.appendChild( make( ' ', true ) ); |
this.upDownLinks.appendChild( make( ' ', true ) ); |
||
this.upDownLinks.appendChild( lk ); |
this.upDownLinks.appendChild( lk ); |
||
Line 1,413: | Line 1,481: | ||
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.appendChild( make( HotCat.links.restore, true ) ); lk.title = HotCat.tooltips.restore; |
|||
lk.onclick = this.restore.bind( this ); |
|||
lk.appendChild( make( HC.links.restore, true ) ); |
|||
lk.title = HC.tooltips.restore; |
|||
this.undelLink.appendChild( make( ' ', true ) ); |
this.undelLink.appendChild( make( ' ', true ) ); |
||
this.undelLink.appendChild( lk ); |
this.undelLink.appendChild( lk ); |
||
Line 1,421: | Line 1,492: | ||
invokeSuggestions: function ( dont_autocomplete ) { |
invokeSuggestions: function ( dont_autocomplete ) { |
||
if ( this.engine && suggestionConfigs[ this.engine ] && suggestionConfigs[ this.engine ].temp && !dont_autocomplete ) |
if ( this.engine && suggestionConfigs[ this.engine ] && suggestionConfigs[ this.engine ].temp && !dont_autocomplete ) this.engine = HC.suggestions; // Reset to a search upon input |
||
this.engine = HotCat.suggestions; // Reset to a search upon input |
|||
} |
|||
this.state = CategoryEditor.CHANGE_PENDING; |
this.state = CategoryEditor.CHANGE_PENDING; |
||
var self = this; |
var self = this; |
||
window.setTimeout( function () { |
window.setTimeout( function () { |
||
self.textchange( dont_autocomplete ); |
|||
}, HC.suggest_delay ); |
|||
}, |
}, |
||
makeForm: function () { |
makeForm: function () { |
||
var form = make( 'form' ); |
var form = make( 'form' ); |
||
form.method = 'POST' |
form.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 = HC.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,445: | Line 1,520: | ||
// - 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. |
||
text.onkeyup = |
text.onkeyup = function ( evt ) { |
||
var key = evt.keyCode || 0; |
|||
if ( self.ime && self.lastKey === IME && !self.usesComposition && ( key === TAB || key === RET || key === ESC || key === SPACE ) ) self.ime = false; |
|||
evt = evt || window.event || window.Event; // W3C, IE, Netscape |
|||
var key = evt.keyCode || 0; |
|||
if ( self.ime ) return true; |
|||
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 ); |
|||
} |
} |
||
// Also do this for ESC as a workaround for Firefox bug 524360 |
|||
return true; |
|||
// https://bugzilla.mozilla.org/show_bug.cgi?id=524360 |
|||
}; |
|||
self.invokeSuggestions( key === BS || key === DEL || key === ESC ); |
|||
text.onkeydown = |
|||
} |
|||
return true; |
|||
evt = evt || window.event || window.Event; // W3C, IE, Netscape |
|||
}; |
|||
var key = evt.keyCode || 0; |
|||
text.onkeydown = function ( evt ) { |
|||
self.lastKey = key; |
|||
var key = evt.keyCode || 0; |
|||
self.lastKey = key; |
|||
// DOM Level < 3 IME input |
|||
self.keyCount = 0; |
|||
if ( !self.ime && key === IME && !self.usesComposition ) { |
|||
// DOM Level < 3 IME input |
|||
// self.usesComposition catches browsers that may emit spurious keydown IME after a composition has ended |
|||
if ( !self.ime && key === IME && !self.usesComposition ) { |
|||
// self.usesComposition catches browsers that may emit spurious keydown IME after a composition has ended |
|||
} else if ( self.ime && key !== IME && !( key >= 16 && key <= 20 || key >= 91 && key <= 93 || key === 144 ) ) { |
|||
self.ime = true; |
|||
// Ignore control keys: ctrl, shift, alt, alt gr, caps lock, windows/apple cmd keys, num lock. Only the windows keys |
|||
} else if ( self.ime && key !== IME && !( key >= 16 && key <= 20 || key >= 91 && key <= 93 || key === 144 ) ) { |
|||
// terminate IME (apple cmd doesn't), but they also cause a blur, so it's OK to ignore them here. |
|||
// Ignore control keys: ctrl, shift, alt, alt gr, caps lock, windows/apple cmd keys, num lock. Only the windows keys |
|||
// Note: Safari 4 (530.17) propagates ESC out of an IME composition (observed at least on Win XP). |
|||
// terminate IME (apple cmd doesn't), but they also cause a blur, so it's OK to ignore them here. |
|||
self.ime = false; |
|||
// Note: Safari 4 (530.17) propagates ESC out of an IME composition (observed at least on Win XP). |
|||
} |
|||
self.ime = false; |
|||
} |
|||
// Handle return explicitly, to override the default form submission to be able to check for ctrl |
|||
if ( self.ime ) return true; |
|||
// Inhibit default behavior of ESC (revert to last real input in FF: we do that ourselves) |
|||
// 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++; |
|||
$( text ).on( 'focus', function () { makeActive( self ); } ); |
|||
return self.processKey( evt ); |
|||
}; |
|||
$( text ).on( 'focus', function () { |
|||
makeActive( self ); |
|||
} ); |
|||
// On IE, blur events are asynchronous, and may thus arrive after the element has lost the focus. Since IE |
// On IE, blur events are asynchronous, and may thus arrive after the element has lost the focus. Since IE |
||
// can get the selection only while the element is active (has the focus), we may not always get the selection. |
// can get the selection only while the element is active (has the focus), we may not always get the selection. |
||
// Therefore, use an IE-specific synchronous event on IE... |
// Therefore, use an IE-specific synchronous event on IE... |
||
// Don't test for text.selectionStart being defined; |
// Don't test for text.selectionStart being defined; |
||
// property while the element is not being displayed. |
|||
$( text ).on( |
$( text ).on( |
||
( |
( text.onbeforedeactivate !== undefined && text.createTextRange ) ? 'beforedeactivate' : 'blur', |
||
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; |
|||
$( text ).on( 'compositionend', function () { self.lastKey = IME; self.usesComposition = true; self.ime = false; } ); |
|||
self.usesComposition = true; |
|||
$( text ).on( 'textInput', function () { self.ime = false; self.invokeSuggestions( false ); } ); |
|||
self.ime = true; |
|||
} ); |
|||
$( text ).on( 'compositionend', function () { |
|||
self.lastKey = IME; |
|||
self.usesComposition = true; |
|||
self.ime = false; |
|||
} ); |
|||
$( text ).on( 'textInput', function () { |
|||
self.ime = false; |
|||
self.invokeSuggestions( false ); |
|||
} ); |
|||
} catch ( any ) { |
} 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,522: | Line 1,614: | ||
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.onchange = function () { self.highlightSuggestion( 0 ); self.text.focus(); }; |
|||
list.ondblclick = function ( e ) { |
|||
if ( self.highlightSuggestion( 0 ) ) self.accept( e ); |
|||
}; |
|||
list.onchange = function () { |
|||
self.highlightSuggestion( 0 ); |
|||
self.text.focus(); |
|||
}; |
|||
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 ); |
|||
}, HC.suggest_delay ); |
|||
} else if ( evt.keyCode === RET ) { |
} else if ( evt.keyCode === RET ) { |
||
self.accept( evt ); |
self.accept( evt ); |
||
} |
} |
||
}; |
}; |
||
if ( ! |
if ( !HC.fixed_search ) { |
||
var engineSelector = make( 'select' ); |
var engineSelector = make( 'select' ); |
||
for ( var key in suggestionConfigs ) { |
for ( var key in suggestionConfigs ) { |
||
Line 1,541: | Line 1,641: | ||
var opt = make( 'option' ); |
var opt = make( 'option' ); |
||
opt.value = key; |
opt.value = key; |
||
if ( key === this.engine ) |
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,560: | Line 1,661: | ||
if ( |
if ( |
||
onUpload && |
onUpload && |
||
window.UFUI !== undefined && |
|||
window.UIElements !== undefined && |
|||
UFUI.getLabel instanceof Function |
|||
) { |
) { |
||
try { |
try { |
||
label = UFUI.getLabel( id, true ); |
label = UFUI.getLabel( id, true ); |
||
// Extract the plain text. IE doesn't know that Node.TEXT_NODE === 3 |
// Extract the plain text. IE doesn't know that Node.TEXT_NODE === 3 |
||
while ( label && label.nodeType !== 3 ) |
while ( label && label.nodeType !== 3 ) label = label.firstChild; |
||
} catch ( ex ) { |
} catch ( ex ) { |
||
label = null; |
label = null; |
||
} |
} |
||
} |
} |
||
if ( !label || !label.data ) |
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', HC.messages.ok ); |
|||
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. |
cancel.type = 'button'; |
||
cancel.value = button_label( 'wpCancelUploadLbl', HC.messages.cancel ); |
|||
cancel.onclick = this.cancel.bind( this ); |
cancel.onclick = this.cancel.bind( this ); |
||
this.cancelButton = cancel; |
this.cancelButton = cancel; |
||
Line 1,590: | Line 1,694: | ||
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 ); |
||
// Support: IE8, IE9 |
|||
// IE8/IE9: put some text into this span (a0 is nbsp) and make sure it always stays on the |
|||
// Put some text into this span (a0 is nbsp) and make sure it always stays on the same |
|||
// 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 ) |
if ( list ) span.appendChild( list ); |
||
if ( this.engineSelector ) { span.appendChild( this.engineSelector ); } |
|||
if ( |
if ( this.engineSelector ) span.appendChild( this.engineSelector ); |
||
if ( !noSuggestions ) span.appendChild( this.icon ); |
|||
span.appendChild( OK ); |
span.appendChild( OK ); |
||
span.appendChild( cancel ); |
span.appendChild( cancel ); |
||
Line 1,612: | Line 1,717: | ||
display: function ( evt ) { |
display: function ( evt ) { |
||
if ( this.isAddCategory && !onUpload ) { |
if ( this.isAddCategory && !onUpload && this.line ) { |
||
// eslint-disable-next-line no-new |
// eslint-disable-next-line no-new |
||
new CategoryEditor( this.line, null, this.span, true ); // Create a new one |
new CategoryEditor( this.line, null, this.span, true ); // Create a new one |
||
Line 1,624: | Line 1,729: | ||
} |
} |
||
} |
} |
||
if ( !this.form ) |
if ( !this.form ) this.makeForm(); |
||
this.makeForm(); |
|||
if ( this.list ) this.list.style.display = 'none'; |
|||
} |
|||
if ( this.list ) { this.list.style.display = 'none'; } |
|||
if ( this.engineSelector ) |
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.icon.src = ( this.currentExists ? HC.existsYes : HC.existsNo ); |
||
this.text.value = this.currentCategory + ( this.currentKey !== null ? '|' + this.currentKey : '' ); |
this.text.value = this.currentCategory + ( this.currentKey !== null ? '|' + this.currentKey : '' ); |
||
this.originalState = this.state; |
this.originalState = this.state; |
||
Line 1,639: | Line 1,745: | ||
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 ) |
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,657: | Line 1,767: | ||
var result = this.display( evt ); |
var result = this.display( evt ); |
||
var v = this.lastSavedCategory; |
var v = this.lastSavedCategory; |
||
if ( v.length |
if ( !v.length ) return result; |
||
this.text.readOnly = !!readOnly; |
this.text.readOnly = !!readOnly; |
||
this.engine = engine; |
this.engine = engine; |
||
Line 1,666: | Line 1,777: | ||
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 ) ? HC.suggestions : this.engine ); |
||
}, |
}, |
||
Line 1,685: | Line 1,796: | ||
this.inactivate(); |
this.inactivate(); |
||
this.form.style.display = 'none'; |
this.form.style.display = 'none'; |
||
if ( this.catLink ) |
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,692: | Line 1,804: | ||
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 |
if ( this.currentKey && this.currentKey.length ) { this.catLink.title = this.currentKey; } else { this.catLink.title = ''; } |
||
this.catLink.title = this.currentKey; |
|||
} else { |
|||
this.catLink.title = ''; |
|||
} |
|||
} |
|||
if ( this.state === CategoryEditor.UNCHANGED ) { |
if ( this.state === CategoryEditor.UNCHANGED ) { |
||
if ( this.catLink ) |
if ( this.catLink ) this.catLink.style.backgroundColor = 'transparent'; |
||
} else { |
} else { |
||
if ( !onUpload ) { |
if ( !onUpload ) { |
||
try { |
try { |
||
this.catLink.style.backgroundColor = |
this.catLink.style.backgroundColor = HC.bg_changed; |
||
} catch ( ex ) {} |
} catch ( ex ) {} |
||
} |
} |
||
Line 1,715: | Line 1,823: | ||
if ( !newDOM ) { |
if ( !newDOM ) { |
||
var next = this.span.nextSibling; |
var next = this.span.nextSibling; |
||
if ( next ) |
if ( next ) next.parentNode.removeChild( next ); |
||
} |
|||
if (this.span && this.span.parentNode) { |
|||
this.span.parentNode.removeChild( this.span ); |
|||
} |
} |
||
this.span.parentNode.removeChild( this.span ); |
|||
for ( var i = 0; i < editors.length; i++ ) { |
for ( var i = 0; i < editors.length; i++ ) { |
||
if ( editors[ i ] === this ) { |
if ( editors[ i ] === this ) { |
||
Line 1,725: | Line 1,835: | ||
} |
} |
||
checkMultiInput(); |
checkMultiInput(); |
||
var self = this; |
|||
// eslint-disable-next-line no-delete-var |
|||
window.setTimeout( function () { delete self; }, 10 ); |
|||
}, |
}, |
||
Line 1,742: | Line 1,849: | ||
this.lastSavedHidden = this.originalHidden; |
this.lastSavedHidden = this.originalHidden; |
||
this.state = CategoryEditor.UNCHANGED; |
this.state = CategoryEditor.UNCHANGED; |
||
if ( !this.currentCategory || this.currentCategory.length |
if ( !this.currentCategory || !this.currentCategory.length ) { |
||
// It was a newly added category. Remove the whole editor. |
// It was a newly added category. Remove the whole editor. |
||
this.removeEditor(); |
this.removeEditor(); |
||
Line 1,749: | Line 1,856: | ||
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( HC.category_canonical + ':' + this.currentCategory ); |
||
this.catLink.title = this.currentKey || ''; |
this.catLink.title = this.currentKey || ''; |
||
this.catLink.className = this.currentExists ? '' : 'new'; |
this.catLink.className = this.currentExists ? '' : 'new'; |
||
this.catLink.style.backgroundColor = 'transparent'; |
this.catLink.style.backgroundColor = 'transparent'; |
||
if ( this.upDownLinks ) |
if ( this.upDownLinks ) this.upDownLinks.style.display = this.currentExists ? '' : 'none'; |
||
checkMultiInput(); |
checkMultiInput(); |
||
} |
} |
||
Line 1,760: | Line 1,868: | ||
inactivate: function () { |
inactivate: function () { |
||
if ( this.list ) |
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,769: | Line 1,879: | ||
var value = this.text.value.split( '|' ); |
var value = this.text.value.split( '|' ); |
||
var key = null; |
var key = null; |
||
if ( value.length > 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 ( HC.capitalizePageNames ) v = capitalize( v ); |
||
this.lastInput = v; |
this.lastInput = v; |
||
v = replaceShortcuts( v, |
v = replaceShortcuts( v, HC.shortcuts ); |
||
if ( v.length |
if ( !v.length ) { |
||
this.cancel(); |
this.cancel(); |
||
return false; |
return false; |
||
} |
} |
||
if ( |
if ( !dontCheck && ( |
||
conf.wgNamespaceNumber === 14 && v === conf.wgTitle || HC.blacklist && HC.blacklist.test( v ) ) ) { |
|||
!dontCheck && ( |
|||
conf.wgNamespaceNumber === 14 && v === conf.wgTitle || |
|||
HotCat.blacklist && HotCat.blacklist.test( v ) |
|||
) |
|||
) { |
|||
this.cancel(); |
this.cancel(); |
||
return false; |
return false; |
||
Line 1,807: | Line 1,915: | ||
resolved[ 0 ].commit( |
resolved[ 0 ].commit( |
||
( resolved[ 0 ].currentCategory !== original ) ? |
( resolved[ 0 ].currentCategory !== original ) ? |
||
HC.messages.cat_resolved.replace( /\$1/g, original ) : |
|||
null |
null ); |
||
); |
|||
} |
} |
||
} |
} |
||
Line 1,827: | Line 1,934: | ||
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( HC.category_canonical + ':' + this.currentCategory ); |
||
this.catLink.className = this.currentExists ? '' : 'new'; |
this.catLink.className = this.currentExists ? '' : 'new'; |
||
this.lastSavedCategory = this.currentCategory; |
this.lastSavedCategory = this.currentCategory; |
||
Line 1,839: | Line 1,946: | ||
this.catLink.style.display = ''; |
this.catLink.style.display = ''; |
||
if ( this.isAddCategory ) { |
if ( this.isAddCategory ) { |
||
if ( onUpload ) { |
if ( onUpload && this.line ) { |
||
// eslint-disable-next-line no-new |
// eslint-disable-next-line no-new |
||
new CategoryEditor( this.line, null, this.span, true ); // Create a new one |
new CategoryEditor( this.line, null, this.span, true ); // Create a new one |
||
Line 1,851: | Line 1,958: | ||
// 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.appendChild( make( HotCat.links.undo, true ) ); lk.title = HotCat.tooltips.undo; |
|||
lk.onclick = this.rollback.bind( this ); |
|||
lk.appendChild( make( HC.links.undo, true ) ); |
|||
lk.title = HC.tooltips.undo; |
|||
span.appendChild( make( ' ', true ) ); |
span.appendChild( make( ' ', true ) ); |
||
span.appendChild( lk ); |
span.appendChild( lk ); |
||
Line 1,859: | Line 1,969: | ||
if ( !onUpload ) { |
if ( !onUpload ) { |
||
try { |
try { |
||
this.catLink.style.backgroundColor = |
this.catLink.style.backgroundColor = HC.bg_changed; |
||
} catch ( ex ) {} |
} catch ( ex ) {} |
||
} |
} |
||
} |
} |
||
if ( this.upDownLinks ) |
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,877: | Line 1,988: | ||
( |
( |
||
this.currentKey === this.originalKey || |
this.currentKey === this.originalKey || |
||
this.currentKey === null && this.originalKey.length |
this.currentKey === null && !this.originalKey.length |
||
) |
) |
||
) || |
) || |
||
conf.wgNamespaceNumber === 14 && this.currentCategory === conf.wgTitle || |
conf.wgNamespaceNumber === 14 && this.currentCategory === conf.wgTitle || |
||
HC.blacklist && HC.blacklist.test( this.currentCategory ) |
|||
) { |
) { |
||
this.cancel(); |
this.cancel(); |
||
return; |
return; |
||
} |
} |
||
this.close(); |
|||
if ( commitButton || onUpload ) { |
|||
if ( !commitButton && !onUpload ) { |
|||
this.close(); |
|||
} else { |
|||
this.close(); |
|||
var self = this; |
var self = this; |
||
initiateEdit( function ( failure ) { |
initiateEdit( function ( failure ) { |
||
performChanges( failure, self ); |
|||
}, function ( msg ) { |
|||
alert( msg ); |
|||
} ); |
|||
} |
} |
||
}, |
}, |
||
Line 1,918: | Line 2,031: | ||
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 = HC.bg_changed; |
||
} catch ( ex ) {} |
} catch ( ex ) {} |
||
this.originalState = this.state; |
this.originalState = this.state; |
||
Line 1,932: | Line 2,045: | ||
this.originalState = this.state; |
this.originalState = this.state; |
||
this.state = CategoryEditor.DELETED; |
this.state = CategoryEditor.DELETED; |
||
this.noCommit = noCommit || |
this.noCommit = noCommit || HC.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 ) { |
|||
self.state = self.originalState; |
|||
alert( msg ); |
|||
} ); |
|||
} |
} |
||
} |
} |
||
Line 1,951: | Line 2,068: | ||
} else { |
} else { |
||
try { |
try { |
||
this.catLink.style.backgroundColor = |
this.catLink.style.backgroundColor = HC.bg_changed; |
||
} catch ( ex ) {} |
} catch ( ex ) {} |
||
} |
} |
||
Line 1,963: | Line 2,080: | ||
selectEngine: function ( engineName ) { |
selectEngine: function ( engineName ) { |
||
if ( !this.engineSelector ) |
if ( !this.engineSelector ) return; |
||
for ( var i = 0; i < this.engineSelector.options.length; i++ ) |
for ( var i = 0; i < this.engineSelector.options.length; i++ ) this.engineSelector.options[ i ].selected = this.engineSelector.options[ i ].value === engineName; |
||
this.engineSelector.options[ i ].selected = this.engineSelector.options[ i ].value === engineName; |
|||
} |
|||
}, |
}, |
||
Line 1,972: | Line 2,087: | ||
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( '^(' + HC.category_regexp + '):' ); |
||
if ( re.test( v ) ) |
if ( re.test( v ) ) v = v.substring( v.indexOf( ':' ) + 1 ).replace( /^(\s|_)+/, '' ); |
||
v = v.replace(/\u200E$/, ''); // Trim ending left-to-right mark |
|||
if ( HC.capitalizePageNames ) v = capitalize( v ); |
|||
} |
|||
if ( HotCat.capitalizePageNames ) { v = capitalize( v ); } |
|||
// Only update the input field if there is a difference. |
// Only update the input field if there is a difference. Various browsers otherwise |
||
// reset the selection and cursor position after each value re-assignment. |
|||
// 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, |
||
e = engine, |
|||
v = queryKey, |
|||
z = cleanKey, |
|||
thisObj = this; |
|||
function done() { |
function done() { |
||
cb.callsMade++; |
cb.callsMade++; |
||
if ( cb.callsMade === cb.nofCalls ) { |
if ( cb.callsMade === cb.nofCalls ) { |
||
if ( cb.exists ) |
if ( cb.exists ) cb.allTitles.exists = true; |
||
if ( cb.normalized ) { cb.allTitles.normalized = cb.normalized; } |
|||
if ( |
if ( cb.normalized ) cb.allTitles.normalized = cb.normalized; |
||
suggestionConfigs[ cb.engineName ].cache[ z ] = cb.allTitles; |
|||
if ( !cb.dontCache && !suggestionConfigs[ cb.engineName ].cache[ z ] ) suggestionConfigs[ cb.engineName ].cache[ z ] = cb.allTitles; |
|||
} |
|||
thisObj.text.readOnly = false; |
thisObj.text.readOnly = false; |
||
if ( !cb.cancelled ) |
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 = undefined; |
|||
} |
} |
||
} |
} |
||
getJSON( { |
$.getJSON( url, function ( json ) { |
||
var titles = e.handler( json, z ); |
|||
uri: url, |
|||
if ( titles && titles.length ) { |
|||
if ( cb.allTitles === null ) cb.allTitles = titles; else cb.allTitles = cb.allTitles.concat( titles ); |
|||
var titles = e.handler( json, z ); |
|||
if ( titles |
if ( titles.exists ) cb.exists = true; |
||
if ( titles.normalized ) cb.normalized = titles.normalized; |
|||
cb.allTitles = titles; |
|||
} else { |
|||
cb.allTitles = cb.allTitles.concat( titles ); |
|||
} |
|||
if ( titles.exists ) { cb.exists = true; } |
|||
if ( titles.normalized ) { cb.normalized = titles.normalized; } |
|||
} |
|||
done(); |
|||
}, |
|||
error: function ( req ) { |
|||
if ( !req ) { |
|||
noSuggestions = true; |
|||
} |
|||
cb.dontCache = true; |
|||
done(); |
|||
} |
} |
||
done(); |
|||
} ).fail( function ( req ) { |
|||
if ( !req ) noSuggestions = true; |
|||
cb.dontCache = true; |
|||
done(); |
|||
} ); |
} ); |
||
}, |
}, |
||
Line 2,047: | Line 2,153: | ||
this.currentKey = null; |
this.currentKey = null; |
||
} |
} |
||
if ( this.lastInput === v && !force ) |
if ( this.lastInput === v && !force ) return; // No change |
||
if ( this.lastInput !== v ) |
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 && HC.blacklist && HC.blacklist.test( v ); |
||
if ( noSuggestions ) { |
if ( noSuggestions ) { |
||
// No Ajax: just make sure the list is hidden |
|||
if ( this.list ) |
if ( this.list ) this.list.style.display = 'none'; |
||
if ( this.engineSelector ) |
if ( this.engineSelector ) this.engineSelector.style.display = 'none'; |
||
if ( this.icon ) |
if ( this.icon ) this.icon.style.display = 'none'; |
||
return; |
return; |
||
} |
} |
||
if ( v.length |
if ( !v.length ) { |
||
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, HC.shortcuts ); |
||
cleanKey = cleanKey.replace( /^\s+|\s+$/g, '' ); |
cleanKey = cleanKey.replace( /^\s+|\s+$/g, '' ); |
||
if ( cleanKey.length |
if ( !cleanKey.length ) { |
||
this.showSuggestions( [] ); |
|||
return; |
|||
} |
|||
if ( this.callbackObj ) this.callbackObj.cancelled = true; |
|||
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,079: | Line 2,193: | ||
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 }; |
|||
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,096: | Line 2,215: | ||
this.dab = null; |
this.dab = null; |
||
this.showsList = false; |
this.showsList = false; |
||
if ( !this.list ) |
if ( !this.list ) return; |
||
if ( noSuggestions ) { |
if ( noSuggestions ) { |
||
if ( this.list ) |
if ( this.list ) this.list.style.display = 'none'; |
||
if ( this.engineSelector ) { this.engineSelector.style.display = 'none'; } |
|||
if ( this. |
if ( this.engineSelector ) this.engineSelector.style.display = 'none'; |
||
if ( this.icon ) this.icon.style.display = 'none'; |
|||
this.inputExists = true; // Default... |
this.inputExists = true; // Default... |
||
return; |
return; |
||
Line 2,106: | Line 2,228: | ||
this.engineName = engineName; |
this.engineName = engineName; |
||
if ( engineName ) { |
if ( engineName ) { |
||
if ( !this.engineSelector ) |
if ( !this.engineSelector ) this.engineName = null; |
||
} else { |
} else { |
||
if ( this.engineSelector ) |
if ( this.engineSelector ) this.engineSelector.style.display = 'none'; |
||
} |
} |
||
if ( queryKey ) { |
if ( queryKey ) { |
||
if ( this.lastInput.indexOf( queryKey ) |
if ( this.lastInput.indexOf( queryKey ) ) return; |
||
if ( this.lastQuery && this.lastInput.indexOf( this.lastQuery ) === 0 && this.lastQuery.length > queryKey.length ) |
if ( this.lastQuery && this.lastInput.indexOf( this.lastQuery ) === 0 && this.lastQuery.length > queryKey.length ) return; |
||
} |
} |
||
this.lastQuery = queryKey; |
this.lastQuery = queryKey; |
||
Line 2,119: | Line 2,241: | ||
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 = ( HC.capitalizePageNames ? capitalize( v[ 0 ] ) : v[ 0 ] ); |
||
var vNormalized = v; |
var vNormalized = v; |
||
var knownToExist = titles && titles.exists; |
var knownToExist = titles && titles.exists; |
||
Line 2,125: | Line 2,247: | ||
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 ( HC.blacklist ) { |
||
for ( i = 0; i < titles.length; i++ ) { |
for ( i = 0; i < titles.length; i++ ) { |
||
if ( |
if ( HC.blacklist.test( titles[ i ] ) ) { |
||
titles.splice( i, 1 ); |
titles.splice( i, 1 ); |
||
i--; |
i--; |
||
Line 2,140: | Line 2,262: | ||
titles.sort( |
titles.sort( |
||
function ( a, b ) { |
function ( a, b ) { |
||
if ( a === b ) |
if ( a === b ) return 0; |
||
if ( a.indexOf( b ) === 0 ) { return 1; } // a begins with b: a > b |
|||
if ( |
if ( a.indexOf( b ) === 0 ) return 1; |
||
// a begins with b: a > b |
|||
if ( b.indexOf( a ) === 0 ) return -1; |
|||
// b begins with a: a < b |
|||
// Opensearch may return stuff not beginning with the search prefix! |
// Opensearch may return stuff not beginning with the search prefix! |
||
var prefixMatchA = ( a.indexOf( vNormalized ) === 0 ? 1 : 0 ); |
var prefixMatchA = ( a.indexOf( vNormalized ) === 0 ? 1 : 0 ); |
||
var prefixMatchB = ( b.indexOf( vNormalized ) === 0 ? 1 : 0 ); |
var prefixMatchB = ( b.indexOf( vNormalized ) === 0 ? 1 : 0 ); |
||
if ( prefixMatchA !== prefixMatchB ) |
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 ) |
if ( prefixMatchA !== prefixMatchB ) return prefixMatchB - prefixMatchA; |
||
if ( a < b ) { return -1; } |
|||
if ( |
if ( a < b ) return -1; |
||
if ( b < a ) return 1; |
|||
return 0; |
return 0; |
||
} |
} ); |
||
); |
|||
// Remove duplicates and self-references |
// Remove duplicates and self-references |
||
for ( i = 0; i < titles.length; i++ ) { |
for ( i = 0; i < titles.length; i++ ) { |
||
if ( |
|||
if ( i + 1 < titles.length && titles[ i ] === titles[ i + 1 ] || |
|||
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,167: | Line 2,297: | ||
} |
} |
||
} |
} |
||
if ( !titles || titles.length |
if ( !titles || !titles.length ) { |
||
if ( this.list ) |
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 ) |
if ( this.icon ) this.icon.src = HC.existsNo; |
||
this.inputExists = false; |
this.inputExists = false; |
||
} |
} |
||
Line 2,179: | Line 2,312: | ||
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, HC.shortcuts ); |
||
if ( engineName && suggestionConfigs[ engineName ] && !suggestionConfigs[ engineName ].temp ) { |
if ( engineName && suggestionConfigs[ engineName ] && !suggestionConfigs[ engineName ].temp ) { |
||
this.icon.src = |
this.icon.src = ( existing ? HC.existsYes : HC.existsNo ); |
||
this.inputExists = existing; |
this.inputExists = existing; |
||
} |
} |
||
Line 2,188: | Line 2,321: | ||
if ( titles.length === 1 ) { |
if ( titles.length === 1 ) { |
||
this.list.style.display = 'none'; |
this.list.style.display = 'none'; |
||
if ( this.engineSelector ) |
if ( this.engineSelector ) this.engineSelector.style.display = 'none'; |
||
return; |
return; |
||
} |
} |
||
} |
} |
||
// (Re-)fill the list |
// (Re-)fill the list |
||
while ( 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,207: | Line 2,342: | ||
if ( !this.is_active ) { |
if ( !this.is_active ) { |
||
this.list.style.display = 'none'; |
this.list.style.display = 'none'; |
||
if ( this.engineSelector ) |
if ( this.engineSelector ) this.engineSelector.style.display = 'none'; |
||
return; |
return; |
||
} |
} |
||
var nofItems = ( this.list.options.length > |
var nofItems = ( this.list.options.length > HC.listSize ? HC.listSize : this.list.options.length ); |
||
if ( nofItems <= 1 ) |
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,231: | Line 2,368: | ||
// Approximate calculation of maximum list size |
// Approximate calculation of maximum list size |
||
var maxListHeight = listh; |
var maxListHeight = listh; |
||
if ( nofItems < |
if ( nofItems < HC.listSize ) maxListHeight = ( listh / nofItems ) * HC.listSize; |
||
function viewport( what ) { |
function viewport( what ) { |
||
if ( is_webkit && !document.evaluate ) { |
if ( is_webkit && !document.evaluate ) { |
||
// Safari < 3.0 |
|||
return window[ 'inner' + what ]; |
return window[ 'inner' + what ]; |
||
} |
} |
||
var s = 'client' + what; |
var s = 'client' + what; |
||
if ( window.opera ) |
if ( window.opera ) return document.body[ s ]; |
||
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,252: | Line 2,388: | ||
// 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 ) |
if ( result < 0 ) result = -result; |
||
if ( !is_webkit ) { |
|||
if ( !is_webkit ) result = scroll_offset( 'Width' ) - viewport( 'Width' ) - result; |
|||
} |
|||
// Now all have webkit behavior, i.e. zero if at the leftmost edge. |
// Now all have webkit behavior, i.e. zero if at the leftmost edge. |
||
} |
} |
||
Line 2,271: | Line 2,406: | ||
}; |
}; |
||
} |
} |
||
var t = 0, |
var t = 0, |
||
l = 0; |
|||
do { |
do { |
||
t += ( node.offsetTop || 0 ); |
|||
l += ( node.offsetLeft || 0 ); |
|||
node = node.offsetParent; |
node = node.offsetParent; |
||
} while ( node ); |
} while ( node ); |
||
Line 2,283: | Line 2,419: | ||
} |
} |
||
var textPos = position( this.text ) |
var textPos = position( this.text ), |
||
nl = 0, |
|||
nt = 0, |
|||
offset = 0, |
|||
// Opera 9.5 somehow has offsetWidth = 0 here?? Use the next best value... |
// Opera 9.5 somehow has offsetWidth = 0 here?? Use the next best value... |
||
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,296: | Line 2,432: | ||
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 = '0'; |
||
this.engineSelector.style.display = ''; |
this.engineSelector.style.display = ''; |
||
offset = this.engineSelector.offsetHeight; |
offset = this.engineSelector.offsetHeight; |
||
Line 2,306: | Line 2,442: | ||
} |
} |
||
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 ) |
if ( this.engineName ) this.engineSelector.style.top = this.text.offsetHeight + 'px'; |
||
} else { |
} else { |
||
nt = -listh - offset - 1; |
nt = -listh - offset - 1; |
||
if ( this.engineName ) |
if ( this.engineName ) this.engineSelector.style.top = -( offset + 1 ) + 'px'; |
||
} |
} |
||
this.list.style.top = nt + 'px'; |
this.list.style.top = nt + 'px'; |
||
Line 2,338: | Line 2,474: | ||
w = view_w; |
w = view_w; |
||
this.list.style.width = w + 'px'; |
this.list.style.width = w + 'px'; |
||
if ( is_rtl ) |
if ( is_rtl ) left = right - w; else right = left + w; |
||
left = right - w; |
|||
} else { |
|||
right = left + w; |
|||
} |
|||
} |
} |
||
var relative_offset = 0; |
var relative_offset = 0; |
||
if ( left < scroll ) 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 ) |
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 ) ) { |
||
// 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 ) |
if ( this.lastRealInput === actVal ) this.lastRealInput = normalizedActVal; |
||
actVal = normalizedActVal; |
actVal = normalizedActVal; |
||
} else { |
} else { |
||
Line 2,380: | Line 2,511: | ||
canSelect: function () { |
canSelect: function () { |
||
return this.text.setSelectionRange || |
return this.text.setSelectionRange || |
||
this.text.createTextRange || |
|||
this.text.selectionStart !== undefined && |
|||
this.text.selectionEnd !== undefined; |
|||
}, |
}, |
||
setSelection: function ( from, to ) { |
setSelection: function ( from, to ) { |
||
// this.text must be focused (at least on IE) |
// this.text must be focused (at least on IE) |
||
if ( !this.text.value ) |
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 ( |
} else if ( this.text.selectionStart !== undefined ) { |
||
if ( from > this.text.selectionStart ) { |
if ( from > this.text.selectionStart ) { |
||
this.text.selectionEnd = to; |
this.text.selectionEnd = to; |
||
Line 2,407: | Line 2,538: | ||
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 ( |
} else if ( this.text.selectionStart !== undefined ) { |
||
from = this.text.selectionStart; |
from = this.text.selectionStart; |
||
to = this.text.selectionEnd; |
to = this.text.selectionEnd; |
||
Line 2,427: | Line 2,559: | ||
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,448: | Line 2,584: | ||
break; |
break; |
||
case PGUP: |
case PGUP: |
||
dir = - |
dir = -HC.listSize; |
||
break; |
break; |
||
case PGDOWN: |
case PGDOWN: |
||
dir = |
dir = HC.listSize; |
||
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,458: | Line 2,594: | ||
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,465: | Line 2,601: | ||
} 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,474: | Line 2,611: | ||
highlightSuggestion: function ( dir ) { |
highlightSuggestion: function ( dir ) { |
||
if ( noSuggestions || !this.list || this.list.style.display === 'none' ) |
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 ) |
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 ) |
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 ) |
if ( curr >= 0 && curr < this.list.options.length && dir !== 0 ) this.list.options[ curr ].selected = false; |
||
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,496: | Line 2,634: | ||
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() ) |
if ( this.canSelect() ) this.setSelection( this.list.options[ tgt ].text.length, this.list.options[ tgt ].text.length ); |
||
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 ) |
if ( this.icon ) this.icon.src = HC.existsYes; |
||
this.state = CategoryEditor.CHANGE_PENDING; |
this.state = CategoryEditor.CHANGE_PENDING; |
||
} |
} |
||
Line 2,509: | Line 2,646: | ||
resetKeySelection: function () { |
resetKeySelection: function () { |
||
if ( noSuggestions || !this.list || this.list.style.display === 'none' ) |
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,528: | Line 2,666: | ||
return false; |
return false; |
||
} |
} |
||
}; // end CategoryEditor.prototype |
}; // end CategoryEditor.prototype |
||
function initialize() { |
function initialize() { |
||
// User configurations |
// User configurations: Do this here, called from the onload handler, so that users can |
||
// override it easily in their own user script files by just declaring variables. JSconfig |
// override it easily in their own user script files by just declaring variables. JSconfig |
||
// is some feature used at Wikimedia Commons. |
// is some feature used at Wikimedia Commons. |
||
var config = ( |
var config = ( window.JSconfig !== undefined && JSconfig.keys ) ? JSconfig.keys : {}; |
||
HC.dont_add_to_watchlist = ( window.hotcat_dont_add_to_watchlist !== undefined ? |
|||
!!window.hotcat_dont_add_to_watchlist : |
|||
( config.HotCatDontAddToWatchlist !== undefined ? config.HotCatDontAddToWatchlist : |
|||
!!window.hotcat_dont_add_to_watchlist : |
|||
HC.dont_add_to_watchlist ) ); |
|||
( typeof config.HotCatDontAddToWatchlist !== 'undefined' ? |
|||
HC.no_autocommit = ( window.hotcat_no_autocommit !== undefined ? |
|||
config.HotCatDontAddToWatchlist : |
|||
!!window.hotcat_no_autocommit : ( config.HotCatNoAutoCommit !== undefined ? |
|||
HotCat.dont_add_to_watchlist |
|||
config.HotCatNoAutoCommit : |
|||
) |
|||
// On talk namespace default autocommit off |
|||
); |
|||
( conf.wgNamespaceNumber % 2 ? |
|||
HotCat.no_autocommit = |
|||
true : HC.no_autocommit ) ) ); |
|||
( typeof window.hotcat_no_autocommit !== 'undefined' ? |
|||
HC.del_needs_diff = ( window.hotcat_del_needs_diff !== undefined ? |
|||
!!window.hotcat_no_autocommit : |
|||
!!window.hotcat_del_needs_diff : |
|||
( typeof config.HotCatNoAutoCommit !== 'undefined' ? |
|||
( config.HotCatDelNeedsDiff !== undefined ? |
|||
config.HotCatDelNeedsDiff : |
|||
HotCat.no_autocommit |
|||
HC.del_needs_diff ) ); |
|||
) |
|||
HC.suggest_delay = window.hotcat_suggestion_delay || config.HotCatSuggestionDelay || HC.suggest_delay; |
|||
); |
|||
HC.editbox_width = window.hotcat_editbox_width || config.HotCatEditBoxWidth || HC.editbox_width; |
|||
HotCat.del_needs_diff = |
|||
HC.suggestions = window.hotcat_suggestions || config.HotCatSuggestions || HC.suggestions; |
|||
( typeof window.hotcat_del_needs_diff !== 'undefined' ? |
|||
if ( typeof HC.suggestions !== 'string' || !suggestionConfigs[ HC.suggestions ] ) HC.suggestions = 'combined'; |
|||
!!window.hotcat_del_needs_diff : |
|||
( typeof config.HotCatDelNeedsDiff !== 'undefined' ? |
|||
HC.fixed_search = ( window.hotcat_suggestions_fixed !== undefined ? |
|||
config.HotCatDelNeedsDiff : |
|||
!!window.hotcat_suggestions_fixed : ( config.HotCatFixedSuggestions !== undefined ? |
|||
HotCat.del_needs_diff |
|||
config.HotCatFixedSuggestions : HC.fixed_search ) ); |
|||
) |
|||
HC.single_minor = ( window.hotcat_single_changes_are_minor !== undefined ? |
|||
); |
|||
!!window.hotcat_single_changes_are_minor : |
|||
HotCat.suggest_delay = window.hotcat_suggestion_delay || |
|||
( config.HotCatMinorSingleChanges !== undefined ? |
|||
config.HotCatSuggestionDelay || |
|||
config.HotCatMinorSingleChanges : |
|||
HotCat.suggest_delay; |
|||
HC.single_minor ) ); |
|||
HotCat.editbox_width = window.hotcat_editbox_width || |
|||
HC.bg_changed = window.hotcat_changed_background || config.HotCatChangedBackground || HC.bg_changed; |
|||
config.HotCatEditBoxWidth || |
|||
HC.use_up_down = ( window.hotcat_use_category_links !== undefined ? |
|||
HotCat.editbox_width; |
|||
!!window.hotcat_use_category_links : |
|||
HotCat.suggestions = window.hotcat_suggestions || |
|||
( config.HotCatUseCategoryLinks !== undefined ? |
|||
config.HotCatUseCategoryLinks : |
|||
HotCat.suggestions; |
|||
HC.use_up_down ) ); |
|||
if ( typeof HotCat.suggestions !== 'string' || !suggestionConfigs[ HotCat.suggestions ] ) { |
|||
HC.listSize = window.hotcat_list_size || config.HotCatListSize || HC.listSize; |
|||
HotCat.suggestions = 'combined'; |
|||
if ( conf.wgDBname !== 'commonswiki' ) HC.changeTag = config.HotCatChangeTag || ''; |
|||
// The next whole shebang is needed, because manual tags get not submitted except of save |
|||
if ( HC.changeTag ) { |
|||
var eForm = document.editform, |
|||
catRegExp = new RegExp( '^\\[\\[(' + HC.category_regexp + '):' ), |
|||
oldTxt; |
|||
// Returns true if minor change |
|||
var isMinorChange = function () { |
|||
var newTxt = eForm.wpTextbox1; |
|||
if ( !newTxt ) return; |
|||
newTxt = newTxt.value; |
|||
var oldLines = oldTxt.match( /^.*$/gm ), |
|||
newLines = newTxt.match( /^.*$/gm ), |
|||
cArr; // changes |
|||
var except = function ( aArr, bArr ) { |
|||
var result = [], |
|||
lArr, // larger |
|||
sArr; // smaller |
|||
if ( aArr.length < bArr.length ) { |
|||
lArr = bArr; |
|||
sArr = aArr; |
|||
} else { |
|||
lArr = aArr; |
|||
sArr = bArr; |
|||
} |
|||
for ( var i = 0; i < lArr.length; i++ ) { |
|||
var item = lArr[ i ]; |
|||
var ind = $.inArray( item, sArr ); |
|||
if ( ind === -1 ) result.push( item ); |
|||
else sArr.splice( ind, 1 ); // don't check this item again |
|||
} |
|||
return result.concat( sArr ); |
|||
}; |
|||
cArr = except( oldLines, newLines ); |
|||
if ( cArr.length ) { |
|||
cArr = $.grep( cArr, function ( c ) { |
|||
c = $.trim( c ); |
|||
return ( c && !catRegExp.test( c ) ); |
|||
} ); |
|||
} |
|||
if ( !cArr.length ) { |
|||
oldTxt = newTxt; |
|||
return true; |
|||
} |
|||
}; |
|||
if ( conf.wgAction === 'submit' && conf.wgArticleId && eForm && eForm.wpSummary && document.getElementById( 'wikiDiff' ) ) { |
|||
var sum = eForm.wpSummary, |
|||
sumA = eForm.wpAutoSummary; |
|||
if ( sum.value && sumA.value === HC.changeTag ) { // HotCat diff |
|||
// MD5 hash of the empty string, as HotCat edit is based on empty sum |
|||
sumA.value = sumA.value.replace( HC.changeTag, 'd41d8cd98f00b204e9800998ecf8427e' ); |
|||
// Attr creation and event handling is not same in all (old) browsers so use $ |
|||
var $ct = $( '<input type="hidden" name="wpChangeTags">' ).val( HC.changeTag ); |
|||
$( eForm ).append( $ct ); |
|||
oldTxt = eForm.wpTextbox1.value; |
|||
$( '#wpSave' ).one( 'click', function () { |
|||
if ( $ct.val() ) |
|||
sum.value = sum.value.replace( ( HC.messages.using || HC.messages.prefix ), '' ); |
|||
} ); |
|||
var removeChangeTag = function () { |
|||
$( eForm.wpTextbox1 ).add( sum ).one( 'input', function () { |
|||
window.setTimeout( function () { |
|||
if ( !isMinorChange() ) $ct.val( '' ); |
|||
else removeChangeTag(); |
|||
}, 500 ); |
|||
} ); |
|||
}; |
|||
removeChangeTag(); |
|||
} |
|||
} |
|||
} |
} |
||
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 |
||
HC.listSize = parseInt( HC.listSize, 10 ); |
|||
if ( isNaN( |
if ( isNaN( HC.listSize ) || HC.listSize < 5 ) HC.listSize = 5; |
||
if ( HotCat.list_size > 15 ) { HotCat.list_size = 15; } |
|||
HC.listSize = Math.min( HC.listSize, 30 ); // Max size |
|||
// Localize search engine names |
// Localize search engine names |
||
if ( |
if ( HC.engine_names ) { |
||
for ( var key in |
for ( var key in HC.engine_names ) |
||
if ( suggestionConfigs[ key ] && |
if ( suggestionConfigs[ key ] && HC.engine_names[ key ] ) suggestionConfigs[ key ].name = HC.engine_names[ key ]; |
||
suggestionConfigs[ key ].name = HotCat.engine_names[ key ]; |
|||
} |
|||
} |
|||
} |
} |
||
// Catch both native RTL and "faked" RTL through [[MediaWiki:Rtl.js]] |
// Catch both native RTL and "faked" RTL through [[MediaWiki:Rtl.js]] |
||
Line 2,633: | Line 2,813: | ||
case 'cologneblue': |
case 'cologneblue': |
||
container = document.getElementById( 'quickbar' ); |
container = document.getElementById( 'quickbar' ); |
||
/* fall through */ |
|||
case 'standard': |
case 'standard': |
||
case 'nostalgia': |
case 'nostalgia': |
||
if ( !container ) |
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( ' |
param( 'title', lks[ i ].href ) === conf.wgPageName && |
||
param( 'action', lks[ i ].href ) === 'edit' |
|||
) { |
|||
return true; |
|||
} |
|||
} |
} |
||
return false; |
return false; |
||
Line 2,646: | Line 2,830: | ||
// all modern skins: |
// all modern skins: |
||
return document.getElementById( 'ca-edit' ) !== null; |
return document.getElementById( 'ca-edit' ) !== null; |
||
} |
|||
} |
|||
// Legacy stuff |
|||
function closeForm() { |
|||
// Close all open editors without redirect resolution and other asynchronous stuff. |
|||
for ( var i = 0; i < editors.length; i++ ) { |
|||
var edit = editors[ i ]; |
|||
if ( edit.state === CategoryEditor.OPEN ) { |
|||
edit.cancel(); |
|||
} else if ( edit.state === CategoryEditor.CHANGE_PENDING ) { |
|||
edit.sanitizeInput(); |
|||
var value = edit.text.value.split( '|' ); |
|||
var key = null; |
|||
if ( value.length > 1 ) key = value[ 1 ]; |
|||
var v = value[ 0 ].replace( /_/g, ' ' ).replace( /^\s+|\s+$/g, '' ); |
|||
if ( !v.length ) { |
|||
edit.cancel(); |
|||
} else { |
|||
edit.currentCategory = v; |
|||
edit.currentKey = key; |
|||
edit.currentExists = this.inputExists; |
|||
edit.close(); |
|||
} |
|||
} |
|||
} |
} |
||
} |
} |
||
Line 2,655: | Line 2,864: | ||
if ( !ip ) { |
if ( !ip ) { |
||
ip = document.getElementById( 'wpDestFile' ); |
ip = document.getElementById( 'wpDestFile' ); |
||
while ( ip && ip.nodeName.toLowerCase() !== 'table' ) |
while ( ip && ip.nodeName.toLowerCase() !== 'table' ) ip = ip.parentNode; |
||
} |
} |
||
if ( !ip ) |
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 ) || |
|||
( reupload && !!reupload.value ) || |
|||
( destFile && ( destFile.disabled || destFile.readOnly ) ) ) { return; } // re-upload form... |
|||
( destFile && ( destFile.disabled || destFile.readOnly ) ) |
|||
) { |
|||
return; // re-upload form... |
|||
} |
|||
// Insert a table row with two fields (label and empty category bar) |
// Insert a table row with two fields (label and empty category bar) |
||
var labelCell = make( 'td' ); |
var labelCell = make( 'td' ); |
||
Line 2,676: | Line 2,889: | ||
// Create the label |
// Create the label |
||
var label = null; |
var label = null; |
||
if ( window.UFUI && window.UIElements && UFUI.getLabel instanceof Function ) { |
|||
if ( typeof UFUI !== 'undefined' && |
|||
typeof UIElements !== 'undefined' && |
|||
typeof UFUI.getLabel === 'function' |
|||
) { |
|||
try { |
try { |
||
label = UFUI.getLabel( 'wpCategoriesUploadLbl' ); |
label = UFUI.getLabel( 'wpCategoriesUploadLbl' ); |
||
Line 2,688: | Line 2,898: | ||
if ( !label ) { |
if ( !label ) { |
||
labelCell.id = 'hotcatLabel'; |
labelCell.id = 'hotcatLabel'; |
||
labelCell.appendChild( make( |
labelCell.appendChild( make( HC.categories, true ) ); |
||
} else { |
} else { |
||
labelCell.id = 'hotcatLabelTranslated'; |
labelCell.id = 'hotcatLabelTranslated'; |
||
Line 2,707: | Line 2,917: | ||
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 ( |
} else if ( oldSubmit instanceof Function ) { |
||
do_submit = oldSubmit.apply( form, arguments ); |
do_submit = oldSubmit.apply( form, arguments ); |
||
} |
} |
||
} |
} |
||
if ( !do_submit ) |
if ( !do_submit ) return false; |
||
return false; |
|||
} |
|||
closeForm(); |
closeForm(); |
||
// Copy the categories |
// Copy the categories |
||
var eb = document.getElementById( 'wpUploadDescription' ) || |
var eb = document.getElementById( 'wpUploadDescription' ) || document.getElementById( 'wpDesc' ); |
||
document.getElementById( 'wpDesc' ); |
|||
var addedOne = false; |
var addedOne = false; |
||
for ( var i = 0; i < editors.length; i++ ) { |
for ( var i = 0; i < editors.length; i++ ) { |
||
var t = editors[ i ].currentCategory; |
var t = editors[ i ].currentCategory; |
||
if ( !t ) |
if ( !t ) continue; |
||
var key = editors[ i ].currentKey; |
var key = editors[ i ].currentKey; |
||
var new_cat = '[[' + |
var new_cat = '[[' + HC.category_canonical + ':' + t + ( key ? '|' + key : '' ) + ']]'; |
||
// Only add if not already present |
// Only add if not already present |
||
var cleanedText = eb.value |
var cleanedText = eb.value |
||
Line 2,736: | Line 2,943: | ||
} |
} |
||
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,748: | Line 2,955: | ||
function isOnPage( span ) { |
function isOnPage( span ) { |
||
if ( span.firstChild.nodeType !== Node.ELEMENT_NODE ) |
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; |
|||
catTitle = catTitle.substr( catTitle.indexOf( ':' ) + 1 ).replace( /_/g, ' ' ); |
catTitle = catTitle.substr( catTitle.indexOf( ':' ) + 1 ).replace( /_/g, ' ' ); |
||
if ( |
if ( HC.blacklist && HC.blacklist.test( catTitle ) ) return null; |
||
var result = { title: catTitle, match: [ '', '', '' ] }; |
|||
var result = { |
|||
if ( pageText === null ) { return result; } |
|||
title: catTitle, |
|||
match: [ '', '', '' ] |
|||
}; |
|||
if ( pageText === null ) return result; |
|||
if ( cleanedText === null ) { |
if ( cleanedText === null ) { |
||
cleanedText = pageText |
cleanedText = pageText |
||
Line 2,768: | Line 2,982: | ||
function findByClass( scope, tag, className ) { |
function findByClass( scope, tag, className ) { |
||
var result = |
var result = $( 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 ) |
if ( initialized ) return; |
||
initialized = true; |
initialized = true; |
||
if ( setupTimeout ) { |
if ( setupTimeout ) { |
||
Line 2,790: | Line 3,004: | ||
if ( !hiddenCats ) { |
if ( !hiddenCats ) { |
||
footer = findByClass( document, 'div', 'printfooter' ); |
footer = findByClass( document, 'div', 'printfooter' ); |
||
if ( !footer ) |
if ( !footer ) return; // Don't know where to insert the category line |
||
} |
} |
||
catLine = make( 'div' ); |
catLine = make( 'div' ); |
||
Line 2,798: | Line 3,012: | ||
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 = HC.categories; |
||
label.appendChild( make( |
label.appendChild( make( HC.categories, true ) ); |
||
catLine.appendChild( label ); |
catLine.appendChild( label ); |
||
catLine.appendChild( make( ':', true ) ); |
catLine.appendChild( make( ':', true ) ); |
||
Line 2,811: | Line 3,025: | ||
container.className = 'catlinks noprint'; |
container.className = 'catlinks noprint'; |
||
container.style.display = ''; |
container.style.display = ''; |
||
if ( !hiddenCats ) |
if ( !hiddenCats ) container.appendChild( catLine ); else container.insertBefore( catLine, hiddenCats ); |
||
container.appendChild( catLine ); |
|||
} else { |
|||
container.insertBefore( catLine, hiddenCats ); |
|||
} |
|||
} // end if catLine exists |
} // end if catLine exists |
||
if ( is_rtl ) |
if ( is_rtl ) catLine.dir = 'rtl'; |
||
// Create editors for all existing categories |
// Create editors for all existing categories |
||
Line 2,824: | Line 3,034: | ||
var i; |
var i; |
||
var cats = line.getElementsByTagName( 'li' ); |
var cats = line.getElementsByTagName( 'li' ); |
||
if ( cats.length |
if ( cats.length ) { |
||
newDOM = true |
newDOM = true; |
||
line = cats[ 0 ].parentNode; |
|||
} else { |
} else { |
||
cats = line.getElementsByTagName( 'span' ); |
cats = line.getElementsByTagName( 'span' ); |
||
Line 2,831: | Line 3,042: | ||
// 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++ ) |
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 && line ) { |
||
// 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 |
return copyCats.length ? copyCats[ copyCats.length - 1 ] : null; |
||
} |
} |
||
Line 2,848: | Line 3,059: | ||
if ( !onUpload ) { |
if ( !onUpload ) { |
||
if ( pageText !== null && hiddenCats ) { |
if ( pageText !== null && hiddenCats ) { |
||
if ( is_rtl ) |
if ( is_rtl ) hiddenCats.dir = 'rtl'; |
||
createEditors( hiddenCats, true ); |
createEditors( hiddenCats, true ); |
||
} |
} |
||
Line 2,854: | Line 3,065: | ||
var enableMulti = make( 'span' ); |
var enableMulti = make( 'span' ); |
||
enableMulti.className = 'noprint'; |
enableMulti.className = 'noprint'; |
||
if ( is_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>' + HC.addmulti + '</a>)'; |
||
var lk = multiSpan.getElementsByTagName( 'a' )[ 0 ]; |
var lk = multiSpan.getElementsByTagName( 'a' )[ 0 ]; |
||
lk.onclick = function ( evt ) { |
lk.onclick = function ( evt ) { |
||
setMultiInput(); |
|||
lk.title = HotCat.multi_tooltip; |
|||
checkMultiInput(); |
|||
return evtKill( evt ); |
|||
}; |
|||
lk.title = HC.multi_tooltip; |
|||
lk.style.cursor = 'pointer'; |
lk.style.cursor = 'pointer'; |
||
} |
} |
||
cleanedText = null; |
cleanedText = null; |
||
if ( |
if ( additionalWork instanceof Function ) additionalWork(); |
||
mw.hook( 'hotcat.ready' ).fire(); // Execute registered callback 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 ) |
if ( commitForm ) return; |
||
var formContainer = make( 'div' ); |
var formContainer = make( 'div' ); |
||
formContainer.style.display = 'none'; |
formContainer.style.display = 'none'; |
||
Line 2,929: | Line 3,093: | ||
formContainer.innerHTML = |
formContainer.innerHTML = |
||
'<form id="hotcatCommitForm" method="post" enctype="multipart/form-data" action="' + |
'<form id="hotcatCommitForm" method="post" enctype="multipart/form-data" action="' + |
||
conf.wgScript + '?title=' + encodeURIComponent( conf.wgPageName ) + |
conf.wgScript + '?title=' + encodeURIComponent( conf.wgPageName ) + '&action=submit">' + |
||
' |
'<input type="hidden" name="wpTextbox1">' + |
||
'<input type="hidden" name=" |
'<input type="hidden" name="model" value="' + conf.wgPageContentModel + '">' + |
||
'<input type="hidden" name=" |
'<input type="hidden" name="format" value="text/x-wiki">' + |
||
'<input type="hidden" name=" |
'<input type="hidden" name="wpSummary" value="">' + |
||
'<input type=" |
'<input type="checkbox" name="wpMinoredit" value="1">' + |
||
'<input type="checkbox" name=" |
'<input type="checkbox" name="wpWatchthis" value="1">' + |
||
'<input type=" |
'<input type="hidden" name="wpAutoSummary" value="d41d8cd98f00b204e9800998ecf8427e">' + |
||
'<input type="hidden" name=" |
'<input type="hidden" name="wpEdittime">' + |
||
'<input type="hidden" name=" |
'<input type="hidden" name="wpStarttime">' + |
||
'<input type="hidden" name=" |
'<input type="hidden" name="wpDiff" value="wpDiff">' + |
||
'<input type="hidden" name=" |
'<input type="hidden" name="oldid" value="0">' + |
||
'<input type=" |
'<input type="submit" name="hcCommit" value="hcCommit">' + |
||
'<input type=" |
'<input type="hidden" name="wpEditToken">' + |
||
'<input type="hidden" name=" |
'<input type="hidden" name="wpUltimateParam" value="1">' + |
||
'<input type="hidden" name=" |
'<input type="hidden" name="wpChangeTags">' + |
||
'<input type="hidden" value="ℳ𝒲♥𝓊𝓃𝒾𝒸ℴ𝒹ℯ" name="wpUnicodeCheck" |
'<input type="hidden" value="ℳ𝒲♥𝓊𝓃𝒾𝒸ℴ𝒹ℯ" name="wpUnicodeCheck">' + |
||
'</form>'; |
'</form>'; |
||
commitForm = document.getElementById( 'hotcatCommitForm' ); |
commitForm = document.getElementById( 'hotcatCommitForm' ); |
||
Line 2,952: | Line 3,116: | ||
function getPage() { |
function getPage() { |
||
// We know we have an article here. |
// We know we have an article here. |
||
if ( conf.wgArticleId |
if ( !conf.wgArticleId ) { |
||
// Doesn't exist yet. |
// Doesn't exist yet. Disable on non-existing User pages -- might be a global user page. |
||
if ( conf.wgNamespaceNumber === 2 ) |
if ( conf.wgNamespaceNumber === 2 ) return; |
||
// Disable on non-existing User pages -- might be a global user page. |
|||
return; |
|||
} |
|||
pageText = ''; |
pageText = ''; |
||
pageTime = null; |
pageTime = null; |
||
Line 2,963: | Line 3,124: | ||
} 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 = |
s.src = url; |
||
HC.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 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 ); |
|||
} |
|||
} ); |
|||
} 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(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
function getState() { |
|||
var result = null; |
|||
for ( var i = 0; i < editors.length; i++ ) { |
|||
var text = editors[ i ].currentCategory; |
|||
var key = editors[ i ].currentKey; |
|||
if ( text && text.length > 0 ) { |
|||
if ( key !== null ) { text += '|' + key; } |
|||
if ( result === null ) { |
|||
result = text; |
|||
} else { |
|||
result = result + '\n' + text; |
|||
} |
|||
} |
|||
} |
|||
return result; |
|||
} |
} |
||
function setState( state ) { |
function setState( state ) { |
||
var cats = state.split( '\n' ); |
var cats = state.split( '\n' ); |
||
if ( cats.length |
if ( !cats.length ) return null; |
||
if ( initialized && editors.length === 1 && editors[ 0 ].isAddCategory ) { |
if ( initialized && editors.length === 1 && editors[ 0 ].isAddCategory ) { |
||
// Insert new spans and create new editors for them. |
// Insert new spans and create new editors for them. |
||
Line 3,051: | Line 3,150: | ||
var i; |
var i; |
||
for ( i = 0; i < cats.length; i++ ) { |
for ( i = 0; i < cats.length; i++ ) { |
||
if ( cats[ i ].length |
if ( !cats[ i ].length ) continue; |
||
var cat = cats[ i ].split( '|' ); |
var cat = cats[ i ].split( '|' ); |
||
var key = cat.length > 1 ? cat[ 1 ] : null; |
var key = cat.length > 1 ? cat[ 1 ] : null; |
||
cat = cat[ 0 ]; |
cat = cat[ 0 ]; |
||
var lk = make( 'a' |
var lk = make( 'a' ); |
||
lk.href = wikiPagePath( HC.category_canonical + ':' + cat ); |
|||
lk.appendChild( make( cat, true ) ); |
lk.appendChild( make( cat, true ) ); |
||
lk.title = cat; |
lk.title = cat; |
||
var span = make( 'span' ); |
var span = make( 'span' ); |
||
span.appendChild( lk ); |
span.appendChild( lk ); |
||
if ( i |
if ( !i ) catLine.insertBefore( make( ' ', true ), before ); |
||
catLine.insertBefore( span, before ); |
catLine.insertBefore( span, before ); |
||
if ( before && i + 1 < cats.length ) |
if ( before && i + 1 < cats.length ) parent.insertBefore( make( ' | ', true ), before ); |
||
newSpans.push( { element: span, title: cat, key: key } ); |
|||
newSpans.push( { |
|||
element: span, |
|||
title: cat, |
|||
key: key |
|||
} ); |
|||
} |
} |
||
// And change the last one... |
// And change the last one... |
||
if ( before ) |
if ( before ) before.parentNode.insertBefore( make( ' | ', true ), before ); |
||
before.parentNode.insertBefore( make( ' | ', true ), before ); |
|||
} |
|||
for ( i = 0; i < newSpans.length; i++ ) { |
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 ); |
new CategoryEditor( catLine, newSpans[ i ].element, newSpans[ i ].title, newSpans[ i ].key ); |
||
} |
} |
||
} |
} |
||
return null; |
return null; |
||
} |
|||
function getState() { |
|||
var result = null; |
|||
for ( var i = 0; i < editors.length; i++ ) { |
|||
var text = editors[ i ].currentCategory; |
|||
var key = editors[ i ].currentKey; |
|||
if ( text && text.length ) { |
|||
if ( key !== null ) text += '|' + key; |
|||
if ( result === null ) result = text; else result += '\n' + text; |
|||
} |
|||
} |
|||
return result; |
|||
} |
|||
function really_run() { |
|||
initialize(); |
|||
if ( !HC.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 ( window.UploadForm && UploadForm.previous_hotcat_state ) UploadForm.previous_hotcat_state = setState( UploadForm.previous_hotcat_state ); |
|||
} ); |
|||
} else { |
|||
if ( !conf.wgIsArticle || conf.wgAction !== 'view' || param( 'diff' ) !== null || param( 'oldid' ) !== null || !can_edit() || HC.disable() ) return; |
|||
getPage(); |
|||
} |
|||
} |
|||
function run() { |
|||
if ( HC.started ) return; |
|||
HC.started = true; |
|||
loadTrigger.register( really_run ); |
|||
} |
} |
||
// 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 ) { |
|||
return setState( state ); |
|||
}; |
|||
window.hotcat_close_form = function () { |
|||
closeForm(); |
|||
}; |
|||
HC.runWhenReady = function ( callback ) { |
|||
// run user-registered code once HotCat is fully set up and ready. |
|||
mw.hook( 'hotcat.ready' ).add( callback ); |
|||
}; |
|||
// Make sure we don't get conflicts with AjaxCategories (core development that should one day |
// Make sure we don't get conflicts with AjaxCategories (core development that should one day |
||
Line 3,092: | Line 3,241: | ||
// Reload HotCat after (VE) edits (bug T103285) |
// Reload HotCat after (VE) edits (bug T103285) |
||
mw.hook( 'postEdit' ).add( function () { |
mw.hook( 'postEdit' ).add( function () { |
||
// Reset HotCat in case this is a soft reload ( |
// Reset HotCat in case this is a soft reload (e.g. VisualEditor edit), unless the categories |
||
// were not re-rendered and our interface is still there (e.g. DiscussionTools edit) |
|||
if ( document.querySelector( '#catlinks .hotcatlink' ) ) { |
|||
return; |
|||
} |
|||
catLine = null; |
catLine = null; |
||
editors = []; |
editors = []; |
||
initialized = false; |
initialized = false; |
||
HC.started = false; |
|||
run(); |
run(); |
||
} ); |
} ); |
||
} |
} |
||
// We can safely trigger just after user configuration is loaded. |
|||
var startHotCat = function () { |
|||
// Use always() instead of then() to also start HotCat if the user module has problems. |
|||
$( run ); |
|||
$.when( mw.loader.using( 'user' ), $.ready ).always( 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> |