|
|
| Zeile 1: |
Zeile 1: |
| //<source lang="javascript"> | | //<source lang="javascript"> |
| | | |
| /* | | /* |
| HotCat V2.17
| | HotCat V2.30 |
| | | |
| Ajax-based simple Category manager. Allows adding/removing/changing categories on a page view.
| | Ajax-based simple Category manager. Allows adding/removing/changing categories on a page view. |
| Supports multiple category changes, as well as redirect and disambiguation resolution. Also
| | Supports multiple category changes, as well as redirect and disambiguation resolution. Also |
| plugs into the upload form. Search engines to use for the suggestion list are configurable, and
| | plugs into the upload form. Search engines to use for the suggestion list are configurable, and |
| can be selected interactively.
| | can be selected interactively. |
| | | |
| Documentation: https://commons.wikimedia.org/wiki/Help:Gadget-HotCat
| | Documentation: https://commons.wikimedia.org/wiki/Help:Gadget-HotCat |
| List of main authors: https://commons.wikimedia.org/wiki/Help:Gadget-HotCat/Version_history
| | List of main authors: https://commons.wikimedia.org/wiki/Help:Gadget-HotCat/Version_history |
| | | |
| License: Quadruple licensed GFDL, GPL, LGPL and Creative Commons Attribution 3.0 (CC-BY-3.0)
| | License: Quadruple licensed GFDL, GPL, LGPL and Creative Commons Attribution 3.0 (CC-BY-3.0) |
| | | |
| Choose whichever license of these you like best :-)
| | Choose whichever license of these you like best :-) |
| */ | | */ |
| | | |
| /* | | /* |
| This code is MW version safe. It should run on any MediaWiki installation >= MW 1.15. Note: if
| | This code is MW version safe. It should run on any MediaWiki installation >= MW 1.15. Note: if |
| running on MW >= 1.17 configured with $wgLegacyJavaScriptGlobals != true, it will still force
| | running on MW >= 1.17 configured with $wgLegacyJavaScriptGlobals != true, it will still force |
| publishing the wg* globals in the window object. Note that HotCat is supposed to run with or
| | publishing the wg* globals in the window object. Note that HotCat is supposed to run with or |
| without jQuery, and also on older installations that do not yet have window.mediaWiki. If you
| | without jQuery, and also on older installations that do not yet have window.mediaWiki. If you |
| use any of these newer features, make sure you qualify them by checking whether they exist at
| | use any of these newer features, make sure you qualify them by checking whether they exist at |
| all, and by providing some meaningful fallback implementation if not. To start itself, HotCat
| | all, and by providing some meaningful fallback implementation if not. To start itself, HotCat |
| uses either jQuery(document).ready(), if available (preferred), or the old addOnloadHook().
| | uses jQuery(document).ready(). If it doesn't exist, HotCat won't start. |
| If neither exists, HotCat won't start.
| |
| */ | | */ |
| if ((typeof wgAction == 'undefined') && window.mediaWiki && window.mediaWiki.config) { // Compatibility hack | | /* jshint ignore:start */ // This old code uses too many coding conventions incompatible with jshint. |
| window.wgAction = window.mediaWiki.config.get('wgAction');
| | |
| | if (typeof wgAction == 'undefined' && window.mediaWiki && window.mediaWiki.config) { // Compatibility hack |
| | window.wgAction = window.mediaWiki.config.get('wgAction'); |
| } | | } |
| if (typeof (window.HotCat) == 'undefined' && wgAction != 'edit') { // Guard against double inclusions, and inactivate on edit pages | | if ((typeof window.HotCat == 'undefined' || window.HotCat.nodeName) && wgAction != 'edit') { // Guard against double inclusions, and inactivate on edit pages |
| | | |
| // Configuration stuff. | | // Configuration stuff. |
| var HotCat = {
| | window.HotCat = { |
| isCommonsVersion : false
| |
| // If you copy HotCat to your wiki, you should set this to false!
| |
| | |
| // Localize these messages to the main language of your wiki. | | // Localize these messages to the main language of your wiki. |
| ,messages : | | messages : |
| { cat_removed : 'Entferne [[Kategorie:$1]]' | | {cat_removed : 'removed [[Category:$1]]' |
| ,template_removed : 'Entferne {{[[Kategorie:$1|$1]]}}'
| | ,template_removed : 'removed {{[[Category:$1]]}}' |
| ,cat_added : 'Ergänze [[Kategorie:$1]]'
| | ,cat_added : 'added [[Category:$1]]' |
| ,cat_keychange: 'neuer Sortierschlüssel für [[Kategorie:$1]]: "$2"' // $2 is the new key
| | ,cat_keychange: 'new key for [[Category:$1]]: "$2"' // $2 is the new key |
| ,cat_notFound : 'Kategorie "$1" konnte nicht gefunden werden'
| | ,cat_notFound : 'Category "$1" not found' |
| ,cat_exists : 'Kategorie "$1" bereits enthalten; nicht ergänzt'
| | ,cat_exists : 'Category "$1" already exists; not added.' |
| ,cat_resolved : ' (Weiterleitung [[Kategorie:$1]] aufgelöst)'
| | ,cat_resolved : ' (redirect [[Category:$1]] resolved)' |
| ,uncat_removed: 'entferne {{uncategorized}}'
| | ,uncat_removed: 'removed {{uncategorized}}' |
| ,separator : '; '
| | ,separator : '; ' |
| ,prefix : '[[Hilfe:HotCat|HC]]: '
| | ,prefix : "" |
| // Some text to prefix to the edit summary.
| | // Some text to prefix to the edit summary. |
| ,using : ""
| | ,using : ' using [[Help:Gadget-HotCat|HotCat]]' |
| // Some text to append to the edit summary. Named 'using' for historical reasons. If you prefer
| | // Some text to append to the edit summary. Named 'using' for historical reasons. If you prefer |
| // to have a marker at the front, use prefix and set this to the empty string.
| | // to have a marker at the front, use prefix and set this to the empty string. |
| ,multi_change : '$1 Kategorien'
| | ,multi_change : '$1 categories' |
| // $1 is replaced by a number. If your language has several plural forms (c.f. [[:en:Dual (grammatical form)]]),
| | // $1 is replaced by a number. If your language has several plural forms (c.f. [[:en:Dual (grammatical form)]]), |
| // you can set this to an array of strings suitable for passing to mw.language.configPlural().
| | // you can set this to an array of strings suitable for passing to mw.language.configPlural(). |
| // If that function doesn't exist, HotCat will simply fall back to using the last
| | // If that function doesn't exist, HotCat will simply fall back to using the last |
| // entry in the array.
| | // entry in the array. |
| ,commit : 'Speichern'
| | ,commit : 'Save' |
| // Button text. Localize to wgContentLanguage here; localize to wgUserLanguage in a subpage,
| | // Button text. Localize to wgContentLanguage here; localize to wgUserLanguage in a subpage, |
| // see localization hook below.
| | // see localization hook below. |
| ,ok : 'OK'
| | ,ok : 'OK' |
| // Button text. Localize to wgContentLanguage here; localize to wgUserLanguage in a subpage,
| | // Button text. Localize to wgContentLanguage here; localize to wgUserLanguage in a subpage, |
| // see localization hook below.
| | // see localization hook below. |
| ,cancel : 'Abbrechen'
| | ,cancel : 'Cancel' |
| // Button text. Localize to wgContentLanguage here; localize to wgUserLanguage in a subpage,
| | // Button text. Localize to wgContentLanguage here; localize to wgUserLanguage in a subpage, |
| // see localization hook below.
| | // see localization hook below. |
| ,multi_error : 'Quelltext konnte nicht abrufen werden. Deine Änderungen wurden deshalb nicht gespeichert.'
| | ,multi_error : 'Could not retrieve the page text from the server. Therefore, your category changes ' |
| // Localize to wgContentLanguage here; localize to wgUserLanguage in a subpage,
| | +'cannot be saved. We apologize for the inconvenience.' |
| // see localization hook below.
| | // Localize to wgContentLanguage here; localize to wgUserLanguage in a subpage, |
| }
| | // see localization hook below. |
| | ,short_catchange : null |
| | // 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 |
| | // by a category name. |
| | } |
| ,category_regexp : '[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]' | | ,category_regexp : '[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]' |
| // Regular sub-expression matching all possible names for the category namespace. Is automatically localized
| | // Regular sub-expression matching all possible names for the category namespace. Is automatically localized |
| // correctly if you're running MediaWiki 1.16 or later. Otherwise, set it appropriately, e.g. at the German
| | // correctly if you're running MediaWiki 1.16 or later. Otherwise, set it appropriately, e.g. at the German |
| // Wikipedia, use '[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]|[Kk][Aa][Tt][Ee][Gg][Oo][Rr][Ii][Ee]', or at the
| | // Wikipedia, use '[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]|[Kk][Aa][Tt][Ee][Gg][Oo][Rr][Ii][Ee]', or at the |
| // Chinese Wikipedia, use '[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]|分类|分類'. Note that namespaces are case-
| | // Chinese Wikipedia, use '[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]|分类|分類'. Note that namespaces are case- |
| // insensitive!
| | // insensitive! |
| ,category_canonical : 'Kategorie' | | ,category_canonical : 'Category' |
| // The standard category name on your wiki. Is automatically localized correctly if you're running
| | // The standard category name on your wiki. Is automatically localized correctly if you're running |
| // MediaWiki 1.16 or later; otherwise, set it to the preferred category name (e.g., "Kategorie").
| | // MediaWiki 1.16 or later; otherwise, set it to the preferred category name (e.g., "Kategorie"). |
| ,categories : 'Kategorien' | | ,categories : 'Categories' |
| // Plural of category_canonical.
| | // Plural of category_canonical. |
| ,disambig_category : null | | ,disambig_category : 'Disambiguation' |
| // Any category in this category is deemed a disambiguation category; i.e., a category that should not contain
| | // Any category in this category is deemed a disambiguation category; i.e., a category that should not contain |
| // any items, but that contains links to other categories where stuff should be categorized. If you don't have
| | // any items, but that contains links to other categories where stuff should be categorized. If you don't have |
| // that concept on your wiki, set it to null.
| | // that concept on your wiki, set it to null. Use blanks, not underscores. |
| ,redir_category : null | | ,redir_category : 'Category redirects' |
| // Any category in this category is deemed a (soft) redirect to some other category defined by the first link
| | // Any category in this category is deemed a (soft) redirect to some other category defined by a link |
| // to another category. If your wiki doesn't have soft category redirects, set this to null.
| | // to another non-blacklisted category. If your wiki doesn't have soft category redirects, set this to null. |
| ,links : {change: '(±)', remove: '(−)', add: '(+)', restore: '(×)', undo: '(×)', down: '(↓)', up: '(↑)'} | | // If a soft-redirected category contains more than one link to another non-blacklisted category, it's considered |
| // The little modification links displayed after category names.
| | // a disambiguation category instead. |
| | ,links : {change: '(±)', remove: '(\u2212)', add: '(+)', restore: '(×)', undo: '(×)', down: '(\u2193)', up: '(\u2191)'} |
| | // The little modification links displayed after category names. U+2212 is a minus sign; U+2193 and U+2191 are |
| | // downward and upward pointing arrows. Do not use ↓ and ↑ in the code! |
| ,tooltips : { | | ,tooltips : { |
| change: 'Ändern'
| | change: 'Modify' |
| ,remove: 'Entfernen'
| | ,remove: 'Remove' |
| ,add: 'Neue Kategorie hinzufügen'
| | ,add: 'Add a new category' |
| ,restore: 'Wiederherstellen'
| | ,restore: 'Undo changes' |
| ,undo: 'Zurücksetzen'
| | ,undo: 'Undo changes' |
| ,down: 'durch Unterkategorie ersetzen'
| | ,down: 'Open for modifying and display subcategories' |
| ,up: 'durch Überkategorie ersetzen'
| | ,up: 'Open for modifying and display parent categories' |
| } | | } |
| // The tooltips for the above links
| | // The tooltips for the above links |
| ,addmulti : '<span>+<sup>+</sup></span>' | | ,addmulti : '<span>+<sup>+</sup></span>' |
| // The HTML content of the "enter multi-mode" link at the front.
| | // The HTML content of the "enter multi-mode" link at the front. |
| ,multi_tooltip : 'Mehrere Kategorien ändern' | | ,multi_tooltip : 'Modify several categories' |
| // Tooltip for the "enter multi-mode" link
| | // Tooltip for the "enter multi-mode" link |
| ,disable : | | ,disable : |
| function () { // Return true to disable HotCat. HotCat guarantees that the wg* globals exist here.
| | function () { // Return true to disable HotCat. HotCat guarantees that the wg* globals exist here. |
| var ns = wgNamespaceNumber;
| | var ns = wgNamespaceNumber; |
| return ( ns < 0 // Special pages; Special:Upload is handled differently
| | return ( ns < 0 // Special pages; Special:Upload is handled differently |
| || ns === 10 // Templates
| | || ns === 10 // Templates |
| || ns === 8 // MediaWiki
| | || ns === 828 // Module (Lua) |
| || ns === 6 && wgArticleId === 0 // Non-existing file pages
| | || ns === 8 // MediaWiki |
| || ns === 2 && /\.(js|css)$/.test(wgTitle) // User scripts
| | || ns === 6 && wgArticleId === 0 // Non-existing file pages |
| || typeof (wgNamespaceIds) != 'undefined'
| | || ns === 2 && /\.(js|css)$/.test(wgTitle) // User scripts |
| && ( ns === wgNamespaceIds['creator']
| | || typeof (wgNamespaceIds) != 'undefined' |
| || ns === wgNamespaceIds['timedtext']
| | && ( ns === wgNamespaceIds['creator'] |
| || ns === wgNamespaceIds['institution']
| | || ns === wgNamespaceIds['timedtext'] |
| )
| | || ns === wgNamespaceIds['institution'] |
| );
| | ) |
| }
| | ); |
| ,uncat_regexp : null | | } |
| // A regexp matching a templates used to mark uncategorized pages, if your wiki does have that.
| | ,uncat_regexp : /\{\{\s*([Uu]ncat(egori[sz]ed( image)?)?|[Nn]ocat|[Nn]eedscategory)[^}]*\}\}\s*(<\!--.*?--\>)?/g |
| // If not, set it to null.
| | // A regexp matching a templates used to mark uncategorized pages, if your wiki does have that. |
| | // If not, set it to null. |
| ,existsYes : '//upload.wikimedia.org/wikipedia/commons/thumb/b/be/P_yes.svg/20px-P_yes.svg.png' | | ,existsYes : '//upload.wikimedia.org/wikipedia/commons/thumb/b/be/P_yes.svg/20px-P_yes.svg.png' |
| ,existsNo : '//upload.wikimedia.org/wikipedia/commons/thumb/4/42/P_no.svg/20px-P_no.svg.png' | | ,existsNo : '//upload.wikimedia.org/wikipedia/commons/thumb/4/42/P_no.svg/20px-P_no.svg.png' |
| // The images used for the little indication icon. Should not need changing.
| | // The images used for the little indication icon. Should not need changing. |
| ,template_regexp : '[Tt][Ee][Mm][Pp][Ll][Aa][Tt][Ee]' | | ,template_regexp : '[Tt][Ee][Mm][Pp][Ll][Aa][Tt][Ee]' |
| // Regexp to recognize templates. Like "category" above; autolocalized for MW 1.16+, otherwise set manually here.
| | // Regexp to recognize templates. Like "category" above; autolocalized for MW 1.16+, otherwise set manually here. |
| // On the German Wikipedia, you might use '[Tt][Ee][Mm][Pp][Ll][Aa][Tt][Ee]|[Vv][Oo][Rr][Ll][Aa][Gg][Ee]'.
| | // On the German Wikipedia, you might use '[Tt][Ee][Mm][Pp][Ll][Aa][Tt][Ee]|[Vv][Oo][Rr][Ll][Aa][Gg][Ee]'. |
| ,template_categories : {} | | ,template_categories : {} |
| // a list of categories which can be removed by removing a template
| | // a list of categories which can be removed by removing a template |
| // key: the category without namespace
| | // key: the category without namespace |
| // value: A regexp matching the template name, again without namespace
| | // value: A regexp matching the template name, again without namespace |
| // If you don't have this at your wiki, or don't want this, set it to an empty object {}.
| | // If you don't have this at your wiki, or don't want this, set it to an empty object {}. |
| ,engine_names : { | | ,engine_names : { |
| searchindex : 'Indexsuche'
| | searchindex : 'Search index' |
| ,pagelist : 'Seitenliste'
| | ,pagelist : 'Page list' |
| ,combined : 'Kombinierte Suche'
| | ,combined : 'Combined search' |
| ,subcat : 'Unterkategorien'
| | ,subcat : 'Subcategories' |
| ,parentcat : 'Überkategorien'
| | ,parentcat : 'Parent categories' |
| } | | } |
| // Names for the search engines
| | // Names for the search engines |
| ,capitalizePageNames : true | | ,capitalizePageNames : true |
| // Set to false if your wiki has case-sensitive page names. MediaWiki has two modes: either the first letter
| | // Set to false if your wiki has case-sensitive page names. MediaWiki has two modes: either the first letter |
| // of a page is automatically capitalized ("first-letter"; Category:aa == Category:Aa), or it isn't
| | // of a page is automatically capitalized ("first-letter"; Category:aa == Category:Aa), or it isn't |
| // ("case-sensitive"; Category:aa != Category:Aa). It doesn't currently have a fully case-insensitive mode
| | // ("case-sensitive"; Category:aa != Category:Aa). It doesn't currently have a fully case-insensitive mode |
| // (which would mean Category:aa == Category:Aa == Category:AA == Category:aA)
| | // (which would mean Category:aa == Category:Aa == Category:AA == Category:aA) |
| // HotCat tries to set this correctly automatically using an API query. It's still a good idea to manually
| | // 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
| | // configure it correctly; either directly here if you copied HotCat, or in the local configuration file |
| // MediaWiki:Gadget-HotCat.js/local_defaults if you hotlink to the Commons-version, to ensure it is set even
| | // MediaWiki:Gadget-HotCat.js/local_defaults if you hotlink to the Commons-version, to ensure it is set even |
| // if that API query should fail for some strange reason.
| | // if that API query should fail for some strange reason. |
| ,upload_disabled : false | | ,upload_disabled : false |
| // 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. |
| ,blacklist : null | | ,blacklist : null |
| // Single regular expression matching blacklisted categories that cannot be changed or
| | // Single regular expression matching blacklisted categories that cannot be changed or |
| // added using HotCat. For instance /\bstubs?$/ (any category ending with the word "stub"
| | // added using HotCat. For instance /\bstubs?$/ (any category ending with the word "stub" |
| // or "stubs"), or /(\bstubs?$)|\bmaintenance\b/ (stub categories and any category with the
| | // or "stubs"), or /(\bstubs?$)|\bmaintenance\b/ (stub categories and any category with the |
| // word "maintenance" in its title.
| | // word "maintenance" in its title. |
| | | |
| // Stuff changeable by users:
| | // Stuff changeable by users: |
| ,bg_changed : '#F8CCB0' | | ,bg_changed : '#F8CCB0' |
| // Background for changed categories in multi-edit mode. Default is a very light salmon pink.
| | // Background for changed categories in multi-edit mode. Default is a very light salmon pink. |
| ,no_autocommit : false | | ,no_autocommit : false |
| // If true, HotCat will never automatically submit changes. HotCat will only open an edit page with
| | // If true, HotCat will never automatically submit changes. HotCat will only open an edit page with |
| // the changes; users must always save explicitly.
| | // the changes; users must always save explicitly. |
| | ,del_needs_diff : false |
| | // If true, the "category deletion" link "(-)" will never save automatically but always show an |
| | // edit page where the user has to save the edit manually. Is false by default because that's the |
| | // traditional behavior. This setting overrides no_autocommit for "(-)" links. |
| ,suggest_delay : 100 | | ,suggest_delay : 100 |
| // Time, in milliseconds, that HotCat waits after a keystroke before making a request to the
| | // Time, in milliseconds, that HotCat waits after a keystroke before making a request to the |
| // server to get suggestions.
| | // server to get suggestions. |
| ,editbox_width : 40 | | ,editbox_width : 40 |
| // Default width, in characters, of the text input field.
| | // Default width, in characters, of the text input field. |
| ,suggestions : 'combined' | | ,suggestions : 'combined' |
| // One of the engine_names above, to be used as the default suggestion engine.
| | // One of the engine_names above, to be used as the default suggestion engine. |
| ,fixed_search : false | | ,fixed_search : false |
| // If true, always use the default engine, and never display a selector.
| | // If true, always use the default engine, and never display a selector. |
| ,use_up_down : true | | ,use_up_down : true |
| // If false, do not display the "up" and "down" links
| | // If false, do not display the "up" and "down" links |
| ,list_size : 5 | | ,list_size : 5 |
| // Default list size
| | // Default list size |
| ,single_minor : true | | ,single_minor : true |
| // If true, single category changes are marked as minor edits. If false, they're not.
| | // If true, single category changes are marked as minor edits. If false, they're not. |
| ,dont_add_to_watchlist : false | | ,dont_add_to_watchlist : false |
| // If true, never add a page to the user's watchlist. If false, pages get added to the watchlist if
| | // If true, never add a page to the user's watchlist. If false, pages get added to the watchlist if |
| // the user has the "Add pages I edit to my watchlist" or the "Add pages I create to my watchlist"
| | // the user has the "Add pages I edit to my watchlist" or the "Add pages I create to my watchlist" |
| // options in his or her preferences set.
| | // options in his or her preferences set. |
| | ,shortcuts : null |
| | ,addShortcuts : |
| | function (map) { |
| | if (!map) return; |
| | window.HotCat.shortcuts = window.HotCat.shortcuts || {}; |
| | for (var k in map) { |
| | if (!map.hasOwnProperty (k) || typeof k != 'string') continue; |
| | var v = map[k]; |
| | if (typeof v != 'string') continue; |
| | k = k.replace (/^\s+|\s+$/g, ""); |
| | v = v.replace (/^\s+|\s+$/g, ""); |
| | if (k.length === 0 || v.length === 0) continue; |
| | window.HotCat.shortcuts[k] = v; |
| | } |
| | } |
| }; | | }; |
| | | |
| // Make sure this is exported, so that localizations *can* actually modify parts of it, and the
| |
| // guard at the top actually works. (If we're loaded as an extension module through the resource
| |
| // loader, this outer scope may actually be a closure, not the global "window" scope.)
| |
| if (typeof (window.HotCat) == 'undefined') window.HotCat = HotCat;
| |
| | |
| (function () { // Local scope to avoid polluting the global namespace with declarations | | (function () { // Local scope to avoid polluting the global namespace with declarations |
|
| |
| // Backwards compatibility stuff. We want HotCat to work with either wg* globals, or with mw.config.get().
| |
| // Our "solution" is to publish the wg* globals if they're not already published.
| |
| if (window.mediaWiki && window.mediaWiki.config) {
| |
| var globals = window.mediaWiki.config.get();
| |
| if (globals && globals !== window) {
| |
| for (var k in globals) window[k] = globals[k];
| |
| window.mediWiki.config = new window.mediaWiki.Map(true); // Make config point to window again.
| |
| }
| |
| globals = null;
| |
| }
| |
| // More backwards compatibility. We have four places where we test for the browser: once for
| |
| // Safari < 3.0, once for WebKit (Chrome or Safari, any versions), and twice for IE <= 6.
| |
| var ua = navigator.userAgent.toLowerCase();
| |
| var is_ie6 = /msie ([0-9]{1,}[\.0-9]{0,})/.exec(ua) != null && parseFloat(RegExp.$1) <= 6.0;
| |
| var is_webkit = /applewebkit\/\d+/.test(ua) && ua.indexOf ('spoofer') < 0;
| |
| // And even more compatbility. HotCat was developed without jQuery, and anyway current jQuery
| |
| // (1.7.1) doesn't seem to support in jquery.getJSON() or jQuery.ajax() the automatic
| |
| // switching from GET to POST requests if the query arguments would make the uri too long.
| |
| // (IE has a hard limit of 2083 bytes, and the servers may have limits around 4 or 8kB.)
| |
| // Anyway, HotCat is supposed to run on wikis without jQuery, so we'd have to supply some
| |
| // ajax routines ourselves in any case. We can't rely on the old sajax_init_object(), newer
| |
| // MW versions (>= 1.19) might not have it.
| |
| var getJSON = (function () {
| |
| function getRequest () {
| |
| var request = null;
| |
| try {
| |
| request = new window.XMLHttpRequest();
| |
| } catch (anything) {
| |
| if (window.ActiveXObject) {
| |
| try {
| |
| request = new window.ActiveXObject('Microsoft.XMLHTTP');
| |
| } catch (any) {
| |
| }
| |
| } // end if IE
| |
| } // end try-catch
| |
| return request;
| |
| }
| |
|
| |
| function makeRequest (settings) {
| |
| var req = getRequest();
| |
| if (!req && settings && settings.error) settings.error (req);
| |
| if (!req || !settings || !settings.uri) return req;
| |
| var uri = armorUri (settings.uri);
| |
| var args = settings.data || null;
| |
| var method;
| |
| if (args && uri.length + args.length + 1 > 2000) {
| |
| // We lose caching, but at least we can make the request
| |
| method = 'POST';
| |
| req.setRequestHeader ('Content-Type', 'application/x-www-form-urlencoded');
| |
| } else {
| |
| method = 'GET';
| |
| if (args) uri += '?' + args;
| |
| args = null;
| |
| }
| |
| req.open (method, uri, true);
| |
| req.onreadystatechange = function () {
| |
| if (req.readyState != 4) return;
| |
| if (req.status != 200 || !req.responseText || !(/^\s*[\{\[]/.test(req.responseText))) {
| |
| if (settings.error) settings.error (req);
| |
| } else {
| |
| if (settings.success) settings.success (eval ('(' + req.responseText + ')'));
| |
| }
| |
| };
| |
| req.setRequestHeader ('Pragma', 'cache=yes');
| |
| req.setRequestHeader ('Cache-Control', 'no-transform');
| |
| req.send (args);
| |
| return req;
| |
| }
| |
|
| |
| return makeRequest;
| |
| })();
| |
|
| |
| function armorUri (uri) {
| |
| // Avoid protocol-relative URIs, IE7 has a bug with them in Ajax calls
| |
| if (uri.length >= 2 && uri.substring(0, 2) == '//') return document.location.protocol + uri;
| |
| return uri;
| |
| }
| |
|
| |
| function LoadTrigger () { this.initialize.apply (this, arguments); };
| |
| LoadTrigger.prototype = {
| |
| initialize : function (needed) {
| |
| this.queue = [];
| |
| this.toLoad = needed;
| |
| },
| |
|
| |
| register : function (callback) {
| |
| if (this.toLoad <= 0) {
| |
| callback (); // Execute directly
| |
| } else {
| |
| this.queue[this.queue.length] = callback;
| |
| }
| |
| },
| |
|
| |
| loaded : function () {
| |
| if (this.toLoad > 0) {
| |
| this.toLoad--;
| |
| if (this.toLoad === 0) {
| |
| // Run queued callbacks once
| |
| for (var i = 0; i < this.queue.length; i++) this.queue[i]();
| |
| this.queue = [];
| |
| }
| |
| }
| |
| }
| |
|
| |
| };
| |
|
| |
| var setupCompleted = new LoadTrigger(1);
| |
| // Used to run user-registered code once HotCat is fully set up and ready.
| |
| HotCat.runWhenReady = function (callback) {setupCompleted.register(callback);};
| |
|
| |
| var loadTrigger = new LoadTrigger(2);
| |
| // Used to delay running the HotCat setup until /local_defaults and localizations have been loaded.
| |
|
| |
| function load (uri) {
| |
| var head = document.getElementsByTagName ('head')[0];
| |
| var s = document.createElement ('script');
| |
| s.setAttribute ('src', armorUri(uri));
| |
| s.setAttribute ('type', 'text/javascript');
| |
| var done = false;
| |
|
| |
| function afterLoad () {
| |
| if (done) return;
| |
| done = true;
| |
| s.onload = s.onreadystatechange = s.onerror = null; // Properly clean up to avoid memory leaks in IE
| |
| if (head && s.parentNode) head.removeChild (s);
| |
| loadTrigger.loaded();
| |
| }
| |
|
| |
| s.onload = s.onreadystatechange = function () { // onreadystatechange for IE, onload for all others
| |
| if (done) return;
| |
| if (!this.readyState || this.readyState === 'loaded' || this.readyState === 'complete') {
| |
| afterLoad ();
| |
| }
| |
| };
| |
| s.onerror = afterLoad; // Clean up, but otherwise ignore errors
| |
| head.insertBefore (s, head.firstChild); // appendChild may trigger bugs in IE6 here
| |
| }
| |
|
| |
| function loadJS (page) {
| |
| load (wgServer + wgScript + '?title=' + encodeURIComponent (page) + '&action=raw&ctype=text/javascript');
| |
| }
| |
|
| |
| function loadURI (href) {
| |
| var url = href;
| |
| if (url.substring (0, 2) == '//') {
| |
| url = window.location.protocol + url;
| |
| } else if (url.substring (0, 1) == '/') {
| |
| url = wgServer + url;
| |
| }
| |
| load (url);
| |
| }
| |
|
| |
| if (HotCat.isCommonsVersion && wgServer.indexOf ('/commons') < 0) {
| |
| // We're running in some other wiki, which hotlinks to the Commons version. The other wiki can put local settings
| |
| // in this file to override the Commons settings for all user languages. For instance, if on your wiki people do
| |
| // not like automatic saving, you'd add in that file the line HotCat.no_autocommit = true; If you hotlink, you
| |
| // *must* adapt HotCat.categories in this file to the local translation in wgContentLanguage of your wiki of the
| |
| // English plural "Categories", and you should provide translations in wgContentLanguage of your wiki of all messages,
| |
| // tooltips, and of the engine names.
| |
| loadJS ('MediaWiki:Gadget-HotCat.js/local_defaults');
| |
| } else {
| |
| loadTrigger.loaded();
| |
| }
| |
|
| |
| if (wgUserLanguage != 'en') {
| |
| // Localization hook to localize HotCat messages, tooltips, and engine names for wgUserLanguage.
| |
| if (window.hotcat_translations_from_commons && wgServer.indexOf ('/commons') < 0) {
| |
| loadURI (
| |
| ((wgServer.indexOf( "https://secure.wikimedia.org") === 0)
| |
| ? '/wikipedia/commons/w/index.php?title='
| |
| : '//commons.wikimedia.org/w/index.php?title='
| |
| )
| |
| + 'MediaWiki:Gadget-HotCat.js/' + wgUserLanguage
| |
| + '&action=raw&ctype=text/javascript&smaxage=21600&maxage=86400'
| |
| );
| |
| } else {
| |
| // Load translations locally
| |
| loadJS ('MediaWiki:Gadget-HotCat.js/' + wgUserLanguage);
| |
| }
| |
| } else {
| |
| loadTrigger.loaded();
| |
| }
| |
|
| |
| // No further changes should be necessary here.
| |
|
| |
| // The following regular expression strings are used when searching for categories in wikitext.
| |
| var wikiTextBlank = '[\\t _\\xA0\\u1680\\u180E\\u2000-\\u200A\\u2028\\u2029\\u202F\\u205F\\u3000]+';
| |
| var wikiTextBlankRE = new RegExp (wikiTextBlank, 'g');
| |
| // Regexp for handling blanks inside a category title or namespace name.
| |
| // See http://svn.wikimedia.org/viewvc/mediawiki/trunk/phase3/includes/Title.php?revision=104051&view=markup#l2722
| |
| // See also http://www.fileformat.info/info/unicode/category/Zs/list.htm
| |
| // MediaWiki collapses several contiguous blanks inside a page title to one single blank. It also replace a
| |
| // number of special whitespace characters by simple blanks. And finally, blanks are treated as underscores.
| |
| // Therefore, when looking for page titles in wikitext, we must handle all these cases.
| |
| // Note: we _do_ include the horizontal tab in the above list, even though the MediaWiki software for some reason
| |
| // appears to not handle it. The zero-width space \u200B is _not_ handled as a space inside titles by MW.
| |
| var wikiTextBlankOrBidi = '[\\t _\\xA0\\u1680\\u180E\\u2000-\\u200B\\u200E\\u200F\\u2028-\\u202F\\u205F\\u3000]*';
| |
| // Whitespace regexp for handling whitespace between link components. Including the horizontal tab, but not \n\r\f\v:
| |
| // a link must be on one single line.
| |
| // MediaWiki also removes Unicode bidi override characters in page titles (and namespace names) completely.
| |
| // This is *not* handled, as it would require us to allow any of [\u200E\u200F\u202A-\u202E] between any two
| |
| // characters inside a category link. It _could_ be done though... We _do_ handle strange spaces, including the
| |
| // zero-width space \u200B, and bidi overrides between the components of a category link (adjacent to the colon,
| |
| // or adjacent to and inside of "[[" and "]]").
| |
|
| |
| // First auto-localize the regexps for the category and the template namespaces.
| |
| if (typeof (wgFormattedNamespaces) != 'undefined') {
| |
| function autoLocalize (namespaceNumber, fallback) {
| |
| function create_regexp_str (name)
| |
| {
| |
| if (!name || name.length === 0) return "";
| |
| var regex_name = "";
| |
| for (var i = 0; i < name.length; i++){
| |
| var initial = name.substr (i, 1);
| |
| var ll = initial.toLowerCase ();
| |
| var ul = initial.toUpperCase ();
| |
| if (ll == ul){
| |
| regex_name += initial;
| |
| } else {
| |
| regex_name += '[' + ll + ul + ']';
| |
| }
| |
| }
| |
| return regex_name.replace(/([\\\^\$\.\?\*\+\(\)])/g, '\\$1')
| |
| .replace (wikiTextBlankRE, wikiTextBlank);
| |
| }
| |
|
| |
| fallback = fallback.toLowerCase();
| |
| var canonical = wgFormattedNamespaces["" + namespaceNumber].toLowerCase();
| |
| var regexp = create_regexp_str (canonical);
| |
| if (fallback && canonical != fallback) regexp += '|' + create_regexp_str (fallback);
| |
| for (var cat_name in wgNamespaceIds) {
| |
| if ( typeof (cat_name) == 'string'
| |
| && cat_name.toLowerCase () != canonical
| |
| && cat_name.toLowerCase () != fallback
| |
| && wgNamespaceIds[cat_name] == namespaceNumber)
| |
| {
| |
| regexp += '|' + create_regexp_str (cat_name);
| |
| }
| |
| }
| |
| return regexp;
| |
| }
| |
|
| |
| if (wgFormattedNamespaces['14']) {
| |
| HotCat.category_canonical = wgFormattedNamespaces['14'];
| |
| HotCat.category_regexp = autoLocalize (14, 'category');
| |
| }
| |
| if (wgFormattedNamespaces['10']) {
| |
| HotCat.template_regexp = autoLocalize (10, 'template');
| |
| }
| |
| }
| |
|
| |
| // Utility functions. Yes, this duplicates some functionality that also exists in other places, but
| |
| // to keep this whole stuff in a single file not depending on any other on-wiki Javascripts, we re-do
| |
| // these few operations here.
| |
| function bind (func, target) {
| |
| var f = func, tgt = target;
| |
| return function () { return f.apply (tgt, arguments); };
| |
| }
| |
| function make (arg, literal) {
| |
| if (!arg) return null;
| |
| return literal ? document.createTextNode (arg) : document.createElement (arg);
| |
| }
| |
| function param (name, uri) {
| |
| if (typeof (uri) == 'undefined' || uri === null) uri = document.location.href;
| |
| var re = new RegExp ('[&?]' + name + '=([^&#]*)');
| |
| var m = re.exec (uri);
| |
| if (m && m.length > 1) return decodeURIComponent(m[1]);
| |
| return null;
| |
| }
| |
| function title (href) {
| |
| if (!href) return null;
| |
| var script = wgScript + '?';
| |
| if (href.indexOf (script) === 0 || href.indexOf (wgServer + script) === 0 || wgServer.substring(0, 2) == '//' && href.indexOf (document.location.protocol + wgServer + script) === 0) {
| |
| // href="/w/index.php?title=..."
| |
| return param ('title', href);
| |
| } else {
| |
| // href="/wiki/..."
| |
| var prefix = wgArticlePath.replace ('$1', "");
| |
| if (href.indexOf (prefix) != 0) prefix = wgServer + prefix; // Fully expanded URL?
| |
| if (href.indexOf (prefix) != 0 && prefix.substring(0, 2) == '//') prefix = document.location.protocol + prefix; // Protocol-relative wgServer?
| |
| if (href.indexOf (prefix) === 0)
| |
| return decodeURIComponent (href.substring (prefix.length));
| |
| }
| |
| return null;
| |
| }
| |
| function hasClass (elem, name) {
| |
| return (' ' + elem.className + ' ').indexOf (' ' + name + ' ') >= 0;
| |
| }
| |
| function capitalize (str) {
| |
| if (!str || str.length === 0) return str;
| |
| return str.substr(0, 1).toUpperCase() + str.substr (1);
| |
| }
| |
| function wikiPagePath (pageName) {
| |
| // Note: do not simply use encodeURI, it doesn't encode '&', which might break if wgArticlePath actually has the $1 in
| |
| // a query parameter.
| |
| return wgArticlePath.replace('$1', encodeURIComponent (pageName).replace(/%3A/g, ':').replace(/%2F/g, '/'));
| |
| }
| |
| function substitute (str, map) {
| |
| // Replace $1, $2, or ${key1}, ${key2} by values from map. $$ is replaced by a single $.
| |
| return str.replace(
| |
| /\$(\$|(\d+)|\{([^{}]+)\})/g
| |
| ,function (match, dollar, idx, key) {
| |
| if (dollar == '$') return '$';
| |
| var k = key || idx;
| |
| var replacement = typeof (map[k]) === 'function' ? map[k](match, k) : map[k];
| |
| return typeof (replacement) === 'string' ? replacement : (replacement || match);
| |
| }
| |
| );
| |
| }
| |
|
| |
| // Text modification
| |
|
| |
| var findCatsRE =
| |
| new RegExp ('\\[\\[' + wikiTextBlankOrBidi + '(?:' + HotCat.category_regexp + ')' + wikiTextBlankOrBidi + ':[^\\]]+\\]\\]', 'g');
| |
|
| |
| function replaceByBlanks (match) {
| |
| return match.replace(/(\s|\S)/g, ' '); // /./ doesn't match linebreaks. /(\s|\S)/ does.
| |
| }
| |
|
| |
| function find_category (wikitext, category, once) {
| |
| var cat_regex = null;
| |
| if(HotCat.template_categories[category]){
| |
| cat_regex = new RegExp ('\\{\\{' + wikiTextBlankOrBidi + '(' + HotCat.template_regexp + '(?=' + wikiTextBlankOrBidi + ':))?' + wikiTextBlankOrBidi
| |
| + '(?:' + HotCat.template_categories[category] + ')'
| |
| + wikiTextBlankOrBidi + '(\\|.*?)?\\}\\}', 'g'
| |
| );
| |
| } else {
| |
| var cat_name = category.replace(/([\\\^\$\.\?\*\+\(\)])/g, '\\$1');
| |
| var initial = cat_name.substr (0, 1);
| |
| cat_regex = new RegExp ('\\[\\[' + wikiTextBlankOrBidi + '(' + HotCat.category_regexp + ')' + wikiTextBlankOrBidi + ':' + wikiTextBlankOrBidi
| |
| + (initial == '\\' || !HotCat.capitalizePageNames
| |
| ? initial
| |
| : '[' + initial.toUpperCase() + initial.toLowerCase() + ']')
| |
| + cat_name.substring (1).replace (wikiTextBlankRE, wikiTextBlank)
| |
| + wikiTextBlankOrBidi + '(\\|.*?)?\\]\\]', 'g'
| |
| );
| |
| }
| |
| if (once) return cat_regex.exec (wikitext);
| |
| var copiedtext = wikitext.replace(/<\!--(\s|\S)*?--\>/g, replaceByBlanks)
| |
| .replace(/<nowiki\>(\s|\S)*?<\/nowiki>/g, replaceByBlanks);
| |
| var result = [];
| |
| var curr_match = null;
| |
| while ((curr_match = cat_regex.exec (copiedtext)) != null) {
| |
| result.push ({match : curr_match});
| |
| }
| |
| result.re = cat_regex;
| |
| return result; // An array containing all matches, with positions, in result[i].match
| |
| }
| |
|
| |
| var interlanguageRE = null;
| |
|
| |
| function change_category (wikitext, toRemove, toAdd, key, is_hidden) {
| |
|
| |
| function find_insertionpoint (wikitext) {
| |
| var copiedtext = wikitext.replace(/<\!--(\s|\S)*?--\>/g, replaceByBlanks)
| |
| .replace(/<nowiki\>(\s|\S)*?<\/nowiki>/g, replaceByBlanks);
| |
| // Search in copiedtext to avoid that we insert inside an HTML comment or a nowiki "element".
| |
| var index = -1;
| |
| findCatsRE.lastIndex = 0;
| |
| while (findCatsRE.exec(copiedtext) != null) index = findCatsRE.lastIndex;
| |
| if (index < 0) {
| |
| // Find the index of the first interlanguage link...
| |
| var match = null;
| |
| 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);
| |
| } else {
| |
| match = interlanguageRE.exec(copiedtext);
| |
| }
| |
| if (match) index = match.index;
| |
| return {idx : index, onCat : false};
| |
| }
| |
| return {idx : index, onCat : index >= 0};
| |
| }
| |
|
| |
| var summary = [];
| |
| var nameSpace = HotCat.category_canonical;
| |
| var cat_point = -1; // Position of removed category;
| |
|
| |
| if (key) key = '|' + key;
| |
| var keyChange = (toRemove && toAdd && toRemove == toAdd && toAdd.length > 0);
| |
| if (toRemove && toRemove.length > 0) {
| |
| var matches = find_category (wikitext, toRemove);
| |
| if (!matches || matches.length === 0) {
| |
| return {text: wikitext, 'summary': summary, error: HotCat.messages.cat_notFound.replace (/\$1/g, toRemove)};
| |
| } else {
| |
| var before = wikitext.substring (0, matches[0].match.index);
| |
| var after = wikitext.substring (matches[0].match.index + matches[0].match[0].length);
| |
| if (matches.length > 1) {
| |
| // Remove all occurrences in after
| |
| matches.re.lastIndex = 0;
| |
| after = after.replace (matches.re, "");
| |
| }
| |
| if (toAdd) {
| |
| nameSpace = matches[0].match[1] || nameSpace;
| |
| 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.
| |
| // If we then have two linefeeds in a row, remove one. Otherwise, if we have two non-
| |
| // whitespace characters, insert a blank.
| |
| var i = before.length - 1;
| |
| while (i >= 0 && before.charAt (i) != '\n' && before.substr (i, 1).search (/\s/) >= 0) i--;
| |
| var j = 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 = before.substring (0, i+1); else before = "";
| |
| if (j < after.length) after = after.substring (j); else after = "";
| |
| if (before.length > 0 && before.substring (before.length - 1).search (/\S/) >= 0
| |
| && after.length > 0 && after.substr (0, 1).search (/\S/) >= 0)
| |
| before += ' ';
| |
| cat_point = before.length;
| |
| wikitext = before + after;
| |
| if (!keyChange) {
| |
| if(HotCat.template_categories[toRemove]) {
| |
| summary.push (HotCat.messages.template_removed.replace (/\$1/g, toRemove));
| |
| } else {
| |
| summary.push (HotCat.messages.cat_removed.replace (/\$1/g, toRemove));
| |
| }
| |
| }
| |
| }
| |
| }
| |
| if (toAdd && toAdd.length > 0) {
| |
| var matches = find_category (wikitext, toAdd);
| |
| if (matches && matches.length > 0) {
| |
| return {text: wikitext, 'summary': summary, error : HotCat.messages.cat_exists.replace (/\$1/g, toAdd)};
| |
| } else {
| |
| var onCat = false;
| |
| if (cat_point < 0) {
| |
| var point = find_insertionpoint (wikitext);
| |
| cat_point = point.idx;
| |
| onCat = point.onCat;
| |
| } else {
| |
| onCat = true;
| |
| }
| |
| var newcatstring = '[[' + nameSpace + ':' + toAdd + (key || "") + ']]';
| |
| if (cat_point >= 0) {
| |
| var suffix = wikitext.substring (cat_point);
| |
| wikitext = wikitext.substring (0, cat_point) + (cat_point > 0 ? '\n' : "") + newcatstring + (!onCat ? '\n' : "");
| |
| if (suffix.length > 0 && suffix.substr(0, 1) != '\n') {
| |
| wikitext += '\n' + suffix;
| |
| } else {
| |
| wikitext += suffix;
| |
| }
| |
| } else {
| |
| if (wikitext.length > 0 && wikitext.substr (wikitext.length - 1, 1) != '\n')
| |
| wikitext += '\n';
| |
| wikitext += '\n' + newcatstring;
| |
| }
| |
| if (keyChange) {
| |
| var k = key || "";
| |
| if (k.length > 0) k = k.substr (1);
| |
| summary.push (substitute (HotCat.messages.cat_keychange, [null, toAdd, k]));
| |
| } else {
| |
| summary.push (HotCat.messages.cat_added.replace (/\$1/g, toAdd));
| |
| }
| |
| if (HotCat.uncat_regexp && !is_hidden) {
| |
| var txt = wikitext.replace (HotCat.uncat_regexp, ""); // Remove "uncat" templates
| |
| if (txt.length != wikitext.length) {
| |
| wikitext = txt;
| |
| summary.push (HotCat.messages.uncat_removed);
| |
| }
| |
| }
| |
| }
| |
| }
| |
| return {text: wikitext, 'summary': summary, error: null};
| |
| }
| |
|
| |
| // The real HotCat UI
| |
|
| |
| function evtKeys (e) {
| |
| e = e || window.event || window.Event; // W3C, IE, Netscape
| |
| var code = 0;
| |
| if (typeof (e.ctrlKey) != 'undefined') { // All modern browsers
| |
| // Ctrl-click seems to be overloaded in FF/Mac (it opens a pop-up menu), so treat cmd-click
| |
| // as a ctrl-click, too.
| |
| if (e.ctrlKey || e.metaKey) code |= 1;
| |
| if (e.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;
| |
| }
| |
| return code;
| |
| }
| |
| function evtKill (e) {
| |
| e = e || window.event || window.Event; // W3C, IE, Netscape
| |
| if (typeof (e.preventDefault) != 'undefined') {
| |
| e.preventDefault ();
| |
| e.stopPropagation ();
| |
| } else
| |
| e.cancelBubble = true;
| |
| return false;
| |
| }
| |
| function addEvent (node, evt, f, capture)
| |
| {
| |
| if (window.jQuery && (!capture || !node.addEventListener)) window.jQuery (node).bind (evt, f);
| |
| else if (node.attachEvent) node.attachEvent ('on' + evt, f);
| |
| else if (node.addEventListener) node.addEventListener (evt, f, capture);
| |
| else node['on' + evt] = f;
| |
| }
| |
|
| |
| var catLine = null;
| |
| var onUpload = false;
| |
| var editors = [];
| |
|
| |
| var commitButton = null;
| |
| var commitForm = null;
| |
| var multiSpan = null;
| |
|
| |
| var pageText = null;
| |
| var pageTime = null;
| |
| var pageWatched = false;
| |
| var watchCreate = false;
| |
| var watchEdit = false;
| |
| var minorEdits = false;
| |
| var editToken = null;
| |
|
| |
| var is_rtl = false;
| |
| var serverTime = null;
| |
|
| |
| var newDOM = false; // true if MediaWiki serves the new UL-LI DOM for categories
| |
|
| |
| function setMultiInput () {
| |
| if (commitButton || onUpload) return;
| |
| commitButton = make ('input');
| |
| commitButton.type = 'button';
| |
| commitButton.value = HotCat.messages.commit;
| |
| commitButton.onclick = multiSubmit;
| |
| if (multiSpan) {
| |
| multiSpan.parentNode.replaceChild (commitButton, multiSpan);
| |
| } else {
| |
| catLine.appendChild (commitButton);
| |
| }
| |
| }
| |
|
| |
| function checkMultiInput () {
| |
| if (!commitButton) return;
| |
| var has_changes = false;
| |
| for (var i = 0; i < editors.length; i++) {
| |
| if (editors[i].state != CategoryEditor.UNCHANGED) {
| |
| has_changes = true;
| |
| break;
| |
| }
| |
| }
| |
| commitButton.disabled = !has_changes;
| |
| }
| |
|
| |
| function currentTimestamp () {
| |
| var now = new Date();
| |
| var ts = "" + now.getUTCFullYear();
| |
| function two (s) { return s.substr (s.length - 2); }
| |
| ts = ts
| |
| + two ('0' + (now.getUTCMonth() + 1))
| |
| + two ('0' + now.getUTCDate())
| |
| + two ('00' + now.getUTCHours())
| |
| + two ('00' + now.getUTCMinutes())
| |
| + two ('00' + now.getUTCSeconds());
| |
| return ts;
| |
| }
| |
|
| |
| function initiateEdit (doEdit, failure) {
| |
| // Must use Ajax here to get the user options and the edit token.
| |
| getJSON ({
| |
| uri : wgServer + wgScriptPath + '/api.php'
| |
| ,data : 'format=json&action=query&titles=' + encodeURIComponent (wgPageName)
| |
| + '&prop=info%7Crevisions%7Clanglinks&inprop=watched&intoken=edit&rvprop=content%7Ctimestamp&lllimit=500'
| |
| + '&rvlimit=1&rvstartid=' + wgCurRevisionId
| |
| + '&meta=siteinfo%7Cuserinfo&uiprop=options'
| |
| ,success : function (json) { setPage(json); doEdit(failure); }
| |
| ,error : function (req) { failure(req.status + ' ' + req.statusText); }
| |
| });
| |
| }
| |
|
| |
| function multiChangeMsg (count) {
| |
| var msg = HotCat.messages.multi_change;
| |
| if (typeof (msg) != 'string' && msg.length) {
| |
| if (window.mediaWiki && window.mediaWiki.language && window.mediaWiki.language.convertPlural) {
| |
| msg = window.mediaWiki.language.convertPlural (count, msg);
| |
| } else {
| |
| msg = msg[msg.length-1];
| |
| }
| |
| }
| |
| return substitute (msg, [null, "" + count]);
| |
| }
| |
|
| |
| function performChanges (failure, singleEditor) {
| |
| if (pageText === null) {
| |
| failure (HotCat.messages.multi_error);
| |
| return;
| |
| }
| |
| // Backwards compatibility after message change (added $2 to cat_keychange)
| |
| if (HotCat.messages.cat_keychange.indexOf ('$2') < 0) {
| |
| HotCat.messages.cat_keychange += '"$2"';
| |
| }
| |
| // 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
| |
| // (b) we want to show a diff for multi-edits anyway.
| |
| // Using the form, we can do (b) and we get (a) for free. And, of course, using the form
| |
| // automatically reloads the page with the updated categories on a successful submit, which
| |
| // we would have to do explicitly if we used the edit API.
| |
| var action;
| |
| if (singleEditor && !singleEditor.noCommit && !HotCat.no_autocommit && editToken) {
| |
| commitForm.wpEditToken.value = editToken;
| |
| action = commitForm.wpDiff;
| |
| if (action) action.name = action.value = 'wpSave';
| |
| } else {
| |
| action = commitForm.wpSave;
| |
| if (action) action.name = action.value = 'wpDiff';
| |
| }
| |
| var result = { text : pageText };
| |
| var changed = [], added = [], deleted = [], changes = 0;
| |
| var toEdit = !!singleEditor ? [singleEditor] : editors;
| |
| var error = null;
| |
| for (var i=0; i < toEdit.length; i++) {
| |
| if (toEdit[i].state == CategoryEditor.CHANGED) {
| |
| result = change_category (
| |
| result.text
| |
| , toEdit[i].originalCategory
| |
| , toEdit[i].currentCategory
| |
| , toEdit[i].currentKey
| |
| , toEdit[i].currentHidden
| |
| );
| |
| if (!result.error) {
| |
| changes++;
| |
| if (!toEdit[i].originalCategory || toEdit[i].originalCategory.length === 0) {
| |
| added.push (toEdit[i].currentCategory);
| |
| } else {
| |
| changed.push ({from : toEdit[i].originalCategory, to : toEdit[i].currentCategory});
| |
| }
| |
| } else if (error === null) {
| |
| error = result.error;
| |
| }
| |
| } else if ( toEdit[i].state == CategoryEditor.DELETED
| |
| && toEdit[i].originalCategory
| |
| && toEdit[i].originalCategory.length > 0)
| |
| {
| |
| result = change_category (result.text, toEdit[i].originalCategory, null, null, false);
| |
| if (!result.error) {
| |
| changes++;
| |
| deleted.push (toEdit[i].originalCategory);
| |
| } else if (error === null) {
| |
| error = result.error;
| |
| }
| |
| }
| |
| }
| |
| if (error !== null) { // Do not commit if there were errors
| |
| action = commitForm.wpSave;
| |
| if (action) action.name = action.value = 'wpDiff';
| |
| }
| |
| if (changes === 0 && !singleEditor) return;
| |
| // Fill in the form and submit it
| |
| commitForm.wpAutoSummary.value = 'd41d8cd98f00b204e9800998ecf8427e'; // MD5 hash of the empty string
| |
| commitForm.wpMinoredit.checked = minorEdits;
| |
| commitForm.wpWatchthis.checked = wgArticleId == 0 && watchCreate || watchEdit || pageWatched;
| |
| if (wgArticleId > 0 || !!singleEditor) {
| |
| if (changes == 1) {
| |
| if (result.summary && result.summary.length > 0)
| |
| commitForm.wpSummary.value = HotCat.messages.prefix + result.summary.join (HotCat.messages.separator) + HotCat.messages.using;
| |
| commitForm.wpMinoredit.checked = HotCat.single_minor || minorEdits;
| |
| } else if (changes > 1) {
| |
| var summary = [];
| |
| var shortSummary = [];
| |
| // Deleted
| |
| for (var i=0; i < deleted.length; i++) {
| |
| summary.push ('−[[' + HotCat.category_canonical + ':' + deleted[i] + ']]');
| |
| }
| |
| if (deleted.length == 1)
| |
| shortSummary.push ('−[[' + HotCat.category_canonical + ':' + deleted[0] + ']]');
| |
| else if (deleted.length > 1)
| |
| shortSummary.push ('− ' + multiChangeMsg (deleted.length));
| |
| // Added
| |
| for (var i=0; i < added.length; i++) {
| |
| summary.push ('+[[' + HotCat.category_canonical + ':' + added[i] + ']]');
| |
| }
| |
| if (added.length == 1)
| |
| shortSummary.push ('+[[' + HotCat.category_canonical + ':' + added[0] + ']]');
| |
| else if (added.length > 1)
| |
| shortSummary.push ('+ ' + multiChangeMsg (added.length));
| |
| // Changed
| |
| var arrow = "]]→[[";
| |
| if (is_rtl) arrow = "]]←[[";
| |
| for (var i=0; i < changed.length; i++) {
| |
| if (changed[i].from != changed[i].to) {
| |
| summary.push ('±[[' + HotCat.category_canonical + ':' + changed[i].from + arrow
| |
| + HotCat.category_canonical + ':' + changed[i].to + ']]');
| |
| } else {
| |
| summary.push ('±[[' + HotCat.category_canonical + ':' + changed[i].from + ']]');
| |
| }
| |
| }
| |
| if (changed.length == 1) {
| |
| if (changed[0].from != changed[0].to) {
| |
| shortSummary.push ('±[[' + HotCat.category_canonical + ':' + changed[0].from + arrow
| |
| + HotCat.category_canonical + ':' + changed[0].to + ']]');
| |
| } else {
| |
| shortSummary.push ('±[[' + HotCat.category_canonical + ':' + changed[0].from + ']]');
| |
| }
| |
| } else if (changed.length > 1) {
| |
| shortSummary.push ('± ' + multiChangeMsg (changed.length));
| |
| }
| |
| if (summary.length > 0) {
| |
| summary = summary.join (HotCat.messages.separator);
| |
| if (summary.length > 200 - HotCat.messages.prefix.length - HotCat.messages.using.length) {
| |
| summary = shortSummary.join (HotCat.messages.separator);
| |
| }
| |
| commitForm.wpSummary.value = HotCat.messages.prefix + summary + HotCat.messages.using;
| |
| }
| |
| }
| |
| }
| |
| commitForm.wpTextbox1.value = result.text;
| |
| commitForm.wpStarttime.value = serverTime || currentTimestamp ();
| |
| commitForm.wpEdittime.value = pageTime || commitForm.wpStarttime.value;
| |
| // Submit the form in a way that triggers onsubmit events: commitForm.submit() doesn't.
| |
| commitForm.hcCommit.click();
| |
| }
| |
|
| |
| function resolveMulti (toResolve, callback) {
| |
| for (var 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 (var i = 0; i < toResolve.length; i++) {
| |
| args += encodeURIComponent ('Category:' + toResolve[i].dabInput);
| |
| if (i+1 < toResolve.length) args += '%7C';
| |
| }
| |
| getJSON({
| |
| uri : wgServer + wgScriptPath + '/api.php'
| |
| ,data : args
| |
| ,success: function (json) { resolveRedirects (toResolve, json); callback (toResolve); }
| |
| ,error: function (req) { if (!req) noSuggestions = true; callback (toResolve); }
| |
| });
| |
| }
| |
|
| |
| function resolveOne (page, toResolve) {
| |
| var cats = page.categories;
| |
| var lks = page.links;
| |
| var is_dab = false;
| |
| var is_redir = typeof (page.redirect) == 'string'; // Hard redirect?
| |
| var is_hidden = page.categoryinfo && typeof (page.categoryinfo.hidden) == 'string';
| |
| for (var j = 0; j < toResolve.length; j++) {
| |
| if (toResolve[j].dabInput != page.title.substring (page.title.indexOf (':') + 1)) continue;
| |
| toResolve[j].currentHidden = is_hidden;
| |
| }
| |
| if (!is_redir && cats && (HotCat.disambig_category || HotCat.redir_category)) {
| |
| for (var c = 0; c < cats.length; c++) {
| |
| var cat = cats[c]['title'];
| |
| // Strip namespace prefix
| |
| if (cat) {
| |
| cat = cat.substring (cat.indexOf (':') + 1).replace(/_/g, ' ');
| |
| if (cat == HotCat.disambig_category) {
| |
| is_dab = true; break;
| |
| } else if (cat == HotCat.redir_category) {
| |
| is_redir = true; break;
| |
| }
| |
| }
| |
| }
| |
| }
| |
| if (!is_redir && !is_dab) return;
| |
| if (!lks || lks.length === 0) return;
| |
| var titles = [];
| |
| for (var i = 0; i < lks.length; i++) {
| |
| if ( lks[i]['ns'] == 14 // Category namespace
| |
| && lks[i]['title'] && lks[i]['title'].length > 0) // Name not empty
| |
| {
| |
| // Internal link to existing thingy. Extract the page name and remove the namespace.
| |
| var match = lks[i]['title'];
| |
| titles.push (match.substring (match.indexOf (':') + 1));
| |
| if (is_redir) break;
| |
| }
| |
| }
| |
| for (var j = 0; j < toResolve.length; j++) {
| |
| if (toResolve[j].dabInput != page.title.substring (page.title.indexOf (':') + 1)) continue;
| |
| if (titles.length > 1) {
| |
| toResolve[j].dab = titles;
| |
| } else {
| |
| toResolve[j].inputExists = true; // Might actually be wrong...
| |
| toResolve[j].icon.src = armorUri(HotCat.existsYes);
| |
| toResolve[j].text.value =
| |
| titles[0] + (toResolve[j].currentKey != null ? '|' + toResolve[j].currentKey : "");
| |
| }
| |
| }
| |
| }
| |
|
| |
| function resolveRedirects (toResolve, params) {
| |
| if (!params || !params.query || !params.query.pages) return;
| |
| for (var p in params.query.pages) resolveOne (params.query.pages[p], toResolve);
| |
| }
| |
|
| |
| function 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 === 0) {
| |
| initiateEdit (function (failure) {performChanges (failure);}, function (msg) {alert (msg);});
| |
| return;
| |
| }
| |
| resolveMulti (
| |
| toResolve
| |
| , function (resolved) {
| |
| var firstDab = null;
| |
| var dontChange = false;
| |
| for (var i = 0; i < resolved.length; i++) {
| |
| if (resolved[i].lastInput != resolved[i].dabInput) {
| |
| // We didn't disable all the open editors, but we did asynchronous calls. It is
| |
| // theoretically possible that the user changed something...
| |
| dontChange = true;
| |
| } else {
| |
| if (resolved[i].dab) {
| |
| if (!firstDab) firstDab = resolved[i];
| |
| } else {
| |
| if (resolved[i].acceptCheck(true)) resolved[i].commit();
| |
| }
| |
| }
| |
| }
| |
| if (firstDab) {
| |
| CategoryEditor.makeActive (firstDab);
| |
| } else if (!dontChange) {
| |
| initiateEdit (function (failure) {performChanges (failure);}, function (msg) {alert (msg);});
| |
| }
| |
| }
| |
| );
| |
| }
| |
|
| |
| var cat_prefix = null;
| |
| var noSuggestions = false;
| |
| var suggestionEngines = {
| |
| opensearch :
| |
| { uri : '/api.php?format=json&action=opensearch&namespace=14&limit=30&search=Category:$1' // $1 = search term
| |
| ,handler : // Function to convert result of uri into an array of category names
| |
| function (queryResult, queryKey) {
| |
| if ( queryResult != null && queryResult.length == 2
| |
| && queryResult[0].toLowerCase() == 'category:' + queryKey.toLowerCase()
| |
| )
| |
| {
| |
| var titles = queryResult[1];
| |
| if (!cat_prefix) cat_prefix = new RegExp ('^(' + HotCat.category_regexp + ':)');
| |
| for (var i = 0; i < titles.length; i++) {
| |
| cat_prefix.lastIndex = 0;
| |
| var m = cat_prefix.exec (titles[i]);
| |
| if (m && m.length > 1) {
| |
| titles[i] = titles[i].substring (titles[i].indexOf (':') + 1); // rm namespace
| |
| } else {
| |
| titles.splice (i, 1); // Nope, it's not a category after all.
| |
| i--;
| |
| }
| |
| }
| |
| return titles;
| |
| }
| |
| return null;
| |
| }
| |
| }
| |
| ,internalsearch :
| |
| { uri : '/api.php?format=json&action=query&list=allpages&apnamespace=14&aplimit=30&apfrom=$1'
| |
| ,handler :
| |
| function (queryResult, queryKey) {
| |
| if (queryResult && queryResult.query && queryResult.query.allpages) {
| |
| var titles = queryResult.query.allpages;
| |
| var key = queryKey.toLowerCase();
| |
| for (var i = 0; i < titles.length; i++) {
| |
| titles[i] = titles[i].title.substring (titles[i].title.indexOf (':') + 1); // rm namespace
| |
| if (titles[i].toLowerCase().indexOf (key) != 0) {
| |
| titles.splice (i, 1); // Doesn't start with the query key
| |
| i--;
| |
| }
| |
| }
| |
| return titles;
| |
| }
| |
| return null;
| |
| }
| |
| }
| |
| ,subcategories :
| |
| // I don't understand why they didn't map cmnamespace=14 automatically to cmtype=subcat,
| |
| // which gives better results and is faster.
| |
| { uri : '/api.php?format=json&action=query&list=categorymembers'
| |
| +(function (version) {
| |
| var m = version.match(/^(\d+)\.(\d+)/);
| |
| var major = 0, minor = 0;
| |
| if (m && m.length > 1) {
| |
| major = parseInt (m[1], 10);
| |
| minor = (m.length > 2 ? parseInt (m[2], 10) : 0);
| |
| }
| |
| if (major > 1 || major === 1 && minor > 17) return '&cmtype=subcat'; // Since MW1.18
| |
| return '&cmnamespace=14';
| |
| }
| |
| )(wgVersion)
| |
| +'&cmlimit=max&cmtitle=Category:$1'
| |
| ,handler :
| |
| function (queryResult, queryKey) {
| |
| if (queryResult && queryResult.query && queryResult.query.categorymembers) {
| |
| var titles = queryResult.query.categorymembers;
| |
| for (var i = 0; i < titles.length; i++) {
| |
| titles[i] = titles[i].title.substring (titles[i].title.indexOf (':') + 1); // rm namespace
| |
| }
| |
| return titles;
| |
| }
| |
| return null;
| |
| }
| |
| }
| |
| ,parentcategories :
| |
| { uri : '/api.php?format=json&action=query&prop=categories&titles=Category:$1&cllimit=max'
| |
| ,handler :
| |
| function (queryResult, queryKey) {
| |
| if (queryResult && queryResult.query && queryResult.query.pages) {
| |
| for (var p in queryResult.query.pages) {
| |
| if (queryResult.query.pages[p].categories) {
| |
| var titles = queryResult.query.pages[p].categories;
| |
| for (var i = 0; i < titles.length; i++) {
| |
| titles[i] = titles[i].title.substring (titles[i].title.indexOf (':') + 1); // rm namespace
| |
| }
| |
| return titles;
| |
| }
| |
| }
| |
| }
| |
| return null;
| |
| }
| |
| }
| |
| };
| |
|
| |
| var suggestionConfigs = {
| |
| searchindex : {name: 'Search index', engines: ['opensearch'], cache: {}, show: true, temp: false, noCompletion : false}
| |
| ,pagelist : {name: 'Page list', engines: ['internalsearch'], 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.OPEN = 1; // Open, but no input yet
| |
| CategoryEditor.CHANGE_PENDING = 2; // Open, some input made
| |
| CategoryEditor.CHANGED = 3;
| |
| CategoryEditor.DELETED = 4;
| |
|
| |
| // IE6 sometimes forgets to redraw the list when editors are opened or closed.
| |
| // Adding/removing a dummy element helps, at least when opening editors.
| |
| CategoryEditor.dummyElement = make ('\xa0', true);
| |
|
| |
| CategoryEditor.forceRedraw = function () {
| |
| if (!is_ie6) return;
| |
| if (CategoryEditor.dummyElement.parentNode) {
| |
| document.body.removeChild (CategoryEditor.dummyElement);
| |
| } else {
| |
| document.body.appendChild (CategoryEditor.dummyElement);
| |
| }
| |
| }
| |
|
| |
| CategoryEditor.makeActive = function (toActivate) {
| |
| if (toActivate.is_active) return;
| |
| for (var i = 0; i < editors.length; i++) {
| |
| if (editors[i] != toActivate) editors[i].inactivate ();
| |
| }
| |
| toActivate.is_active = true;
| |
| if (toActivate.dab) {
| |
| toActivate.showSuggestions (toActivate.dab, false, null, null); // do autocompletion, no key, no engine selector
| |
| toActivate.dab = null;
| |
| } else {
| |
| if (toActivate.showsList) toActivate.displayList();
| |
| if (toActivate.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 () { toActivate.setSelection (toActivate.lastSelection.start, toActivate.lastSelection.end); }
| |
| ,1
| |
| );
| |
| } else {
| |
| toActivate.setSelection (toActivate.lastSelection.start, toActivate.lastSelection.end);
| |
| }
| |
| }
| |
| }
| |
| };
| |
|
| |
| CategoryEditor.prototype = {
| |
|
| |
| initialize : function (line, span, after, key, is_hidden) {
| |
| // If a span is given, 'after' is the category title, otherwise it may be an element after which to
| |
| // insert the new span. 'key' is likewise overloaded; if a span is given, it is the category key (if
| |
| // known), otherwise it is a boolean indicating whether a bar shall be prepended.
| |
| if (!span) {
| |
| this.isAddCategory = true;
| |
| // Create add span and append to catLinks
| |
| this.originalCategory = "";
| |
| this.originalKey = null;
| |
| this.originalExists = false;
| |
| if (!newDOM) {
| |
| span = make ('span');
| |
| span.className = 'noprint';
| |
| if (key) {
| |
| span.appendChild (make (' | ', true));
| |
| if (after) {
| |
| after.parentNode.insertBefore (span, after.nextSibling);
| |
| after = after.nextSibling;
| |
| } else {
| |
| line.appendChild (span);
| |
| }
| |
| } else if (line.firstChild) {
| |
| span.appendChild (make (' ', true));
| |
| line.appendChild (span);
| |
| }
| |
| }
| |
| this.linkSpan = make ('span');
| |
| this.linkSpan.className = 'noprint nopopups hotcatlink';
| |
| var lk = make ('a'); lk.href = '#catlinks'; lk.onclick = bind (this.open, this);
| |
| lk.appendChild (make (HotCat.links.add, true)); lk.title = HotCat.tooltips.add;
| |
| this.linkSpan.appendChild (lk);
| |
| span = make (newDOM ? 'li' : 'span');
| |
| span.className = 'noprint';
| |
| if (is_rtl) span.dir = 'rtl';
| |
| span.appendChild (this.linkSpan);
| |
| if (after)
| |
| after.parentNode.insertBefore (span, after.nextSibling);
| |
| else
| |
| line.appendChild (span);
| |
| this.normalLinks = null;
| |
| this.undelLink = null;
| |
| this.catLink = null;
| |
| } else {
| |
| if (is_rtl) span.dir = 'rtl';
| |
| this.isAddCategory = false;
| |
| this.catLink = span.firstChild;
| |
| this.originalCategory = after;
| |
| this.originalKey = (key && key.length > 1) ? key.substr(1) : null; // > 1 because it includes the leading bar
| |
| this.originalExists = !hasClass (this.catLink, 'new');
| |
| // Create change and del links
| |
| this.makeLinkSpan ();
| |
| if (!this.originalExists && this.upDownLinks) this.upDownLinks.style.display = 'none';
| |
| span.appendChild (this.linkSpan);
| |
| }
| |
| this.originalHidden = is_hidden;
| |
| this.line = line;
| |
| this.engine = HotCat.suggestions;
| |
| this.span = span;
| |
| this.currentCategory = this.originalCategory;
| |
| this.currentExists = this.originalExists;
| |
| this.currentHidden = this.originalHidden;
| |
| this.currentKey = this.originalKey;
| |
| this.state = CategoryEditor.UNCHANGED;
| |
| this.lastSavedState = CategoryEditor.UNCHANGED;
| |
| this.lastSavedCategory = this.originalCategory;
| |
| this.lastSavedKey = this.originalKey;
| |
| this.lastSavedExists = this.originalExists;
| |
| this.lastSavedHidden = this.originalHidden;
| |
| if (this.catLink && this.currentKey) {
| |
| this.catLink.title = this.currentKey;
| |
| }
| |
| editors[editors.length] = this;
| |
| },
| |
|
| |
| makeLinkSpan : function () {
| |
| this.normalLinks = make ('span');
| |
| var lk = null;
| |
| if (this.originalCategory && this.originalCategory.length > 0) {
| |
| lk = make ('a'); lk.href = '#catlinks'; lk.onclick = bind (this.remove, this);
| |
| lk.appendChild (make (HotCat.links.remove, true)); lk.title = HotCat.tooltips.remove;
| |
| this.normalLinks.appendChild (make (' ', true));
| |
| this.normalLinks.appendChild (lk);
| |
| }
| |
| if (!HotCat.template_categories[this.originalCategory]) {
| |
| lk = make ('a'); lk.href = '#catlinks'; lk.onclick = bind (this.open, this);
| |
| lk.appendChild (make (HotCat.links.change, true)); lk.title = HotCat.tooltips.change;
| |
| this.normalLinks.appendChild (make (' ', true));
| |
| this.normalLinks.appendChild (lk);
| |
| if (!noSuggestions && HotCat.use_up_down) {
| |
| this.upDownLinks = make ('span');
| |
| lk = make ('a'); lk.href = '#catlinks'; lk.onclick = bind (this.down, this);
| |
| lk.appendChild (make (HotCat.links.down, true)); lk.title = HotCat.tooltips.down;
| |
| this.upDownLinks.appendChild (make (' ', true));
| |
| this.upDownLinks.appendChild (lk);
| |
| lk = make ('a'); lk.href = '#catlinks'; lk.onclick = bind (this.up, this);
| |
| lk.appendChild (make (HotCat.links.up, true)); lk.title = HotCat.tooltips.up;
| |
| this.upDownLinks.appendChild (make (' ', true));
| |
| this.upDownLinks.appendChild (lk);
| |
| this.normalLinks.appendChild (this.upDownLinks);
| |
| }
| |
| }
| |
| this.linkSpan = make ('span');
| |
| this.linkSpan.className = 'noprint nopopups hotcatlink';
| |
| this.linkSpan.appendChild (this.normalLinks);
| |
| this.undelLink = make ('span');
| |
| this.undelLink.className = 'nopopups hotcatlink';
| |
| this.undelLink.style.display = 'none';
| |
| lk = make ('a'); lk.href = '#catlinks'; lk.onclick = bind (this.restore, this);
| |
| lk.appendChild (make (HotCat.links.restore, true)); lk.title = HotCat.tooltips.restore;
| |
| this.undelLink.appendChild (make (' ', true));
| |
| this.undelLink.appendChild (lk);
| |
| this.linkSpan.appendChild (this.undelLink);
| |
| },
| |
|
| |
| makeForm : function () {
| |
| var form = make ('form');
| |
| form.method = 'POST'; form.onsubmit = bind (this.accept, this);
| |
| this.form = form;
| |
|
| |
| var text = make ('input'); text.type = 'text'; text.size = HotCat.editbox_width;
| |
| if (!noSuggestions) {
| |
| text.onkeyup =
| |
| bind (
| |
| function (evt) {
| |
| evt = evt || window.event || window.Event; // W3C, IE, Netscape
| |
| var key = evt.keyCode || 0;
| |
| if (key === 38 || key === 40 || key === 33 || key === 34) { // Up and down arrows, page up/down
| |
| // In case a browser doesn't generate keypress events for arrow keys...
| |
| if (this.keyCount === 0) return this.processKey (evt);
| |
| } else {
| |
| if (key == 27) { // ESC
| |
| if (!this.resetKeySelection ()) {
| |
| // No undo of key selection: treat ESC as "cancel".
| |
| this.cancel ();
| |
| return;
| |
| }
| |
| }
| |
| // Also do this for ESC as a workaround for Firefox bug 524360
| |
| // https://bugzilla.mozilla.org/show_bug.cgi?id=524360
| |
| var dont_autocomplete = (key == 8 || key == 46 || key == 27); // BS, DEL, ESC
| |
| if (this.engine && suggestionConfigs[this.engine] && suggestionConfigs[this.engine].temp && !dont_autocomplete) {
| |
| this.engine = HotCat.suggestions; // Reset to a search upon input
| |
| }
| |
| this.state = CategoryEditor.CHANGE_PENDING;
| |
| var self = this;
| |
| window.setTimeout (function () {self.textchange (dont_autocomplete);}, HotCat.suggest_delay);
| |
| }
| |
| return true;
| |
| }
| |
| ,this
| |
| );
| |
| text.onkeydown =
| |
| bind (
| |
| function (evt) {
| |
| evt = evt || window.event || window.Event; // W3C, IE, Netscape
| |
| this.lastKey = evt.keyCode || 0;
| |
| this.keyCount = 0;
| |
| // Handle return explicitly, to override the default form submission to be able to check for ctrl
| |
| if (this.lastKey == 13) return this.accept (evt);
| |
| // Inhibit default behavior of ESC (revert to last real input in FF: we do that ourselves)
| |
| if (this.lastKey == 27) return evtKill (evt);
| |
| return true;
| |
| }
| |
| ,this
| |
| );
| |
| // And handle continued pressing of arrow keys
| |
| text.onkeypress = bind (function (evt) {this.keyCount++; return this.processKey (evt);}, this);
| |
| }
| |
| this.text = text;
| |
|
| |
| this.icon = make ('img');
| |
|
| |
| var list = null;
| |
| if (!noSuggestions) {
| |
| list = make ('select');
| |
| list.onclick = bind ( function (e) { if (this.highlightSuggestion (0)) this.textchange (false, true); }, this);
| |
| list.ondblclick = bind (function (e) { if (this.highlightSuggestion (0)) this.accept (e); }, this);
| |
| list.onchange = bind (function (e) { this.highlightSuggestion (0); this.text.focus(); }, this);
| |
| list.onkeyup =
| |
| bind (
| |
| function (evt) {
| |
| evt = evt || window.event || window.Event; // W3C, IE, Netscape
| |
| if (evt.keyCode == 27) {
| |
| this.resetKeySelection ();
| |
| this.text.focus();
| |
| var self = this;
| |
| window.setTimeout (function () {self.textchange (true);}, HotCat.suggest_delay);
| |
| } else if (evt.keyCode == 13) {
| |
| this.accept (evt);
| |
| }
| |
| }
| |
| ,this
| |
| );
| |
| if (!HotCat.fixed_search) {
| |
| var engineSelector = make ('select');
| |
| for (var key in suggestionConfigs) {
| |
| if (suggestionConfigs[key].show) {
| |
| var opt = make ('option');
| |
| opt.value = key;
| |
| if (key == this.engine) opt.selected = true;
| |
| opt.appendChild (make (suggestionConfigs[key].name, true));
| |
| engineSelector.appendChild (opt);
| |
| }
| |
| }
| |
| engineSelector.onchange = bind (
| |
| function () {
| |
| this.engine = this.engineSelector.options[this.engineSelector.selectedIndex].value;
| |
| this.text.focus();
| |
| this.textchange (true, true); // Don't autocomplete, force re-display of list
| |
| }
| |
| ,this
| |
| );
| |
| this.engineSelector = engineSelector;
| |
| }
| |
| }
| |
| this.list = list;
| |
|
| |
| function button_label (id, defaultText) {
| |
| var label = null;
| |
| if ( onUpload
| |
| && typeof (UFUI) != 'undefined'
| |
| && typeof (UIElements) != 'undefined'
| |
| && typeof (UFUI.getLabel) == 'function') {
| |
| try {
| |
| label = UFUI.getLabel (id, true);
| |
| // Extract the plain text. IE doesn't know that Node.TEXT_NODE == 3
| |
| while (label && label.nodeType != 3) label = label.firstChild;
| |
| } catch (ex) {
| |
| label = null;
| |
| }
| |
| }
| |
| if (!label || !label.data) return defaultText;
| |
| return label.data;
| |
| }
| |
|
| |
| // Do not use type 'submit'; we cannot detect modifier keys if we do
| |
| var OK = make ('input'); OK.type = 'button';
| |
| OK.value = button_label ('wpOkUploadLbl', HotCat.messages.ok);
| |
| OK.onclick = bind (this.accept, this);
| |
| this.ok = OK;
| |
|
| |
| var cancel = make ('input'); cancel.type = 'button';
| |
| cancel.value = button_label ('wpCancelUploadLbl', HotCat.messages.cancel);
| |
| cancel.onclick = bind (this.cancel, this);
| |
| this.cancelButton = cancel;
| |
|
| |
| var span = make ('span');
| |
| span.className = 'hotcatinput';
| |
| 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);
| |
|
| |
| // IE8/IE9: put some text into this span (a0 is nbsp) and make sure it always stays on the
| |
| // same line as the input field, otherwise, IE8/9 miscalculates the height of the span and
| |
| // then the engine selector may overlap the input field.
| |
| span.appendChild (make ('\xa0', true));
| |
| span.style.whiteSpace = 'nowrap';
| |
|
| |
| if (list) span.appendChild (list);
| |
| if (this.engineSelector) span.appendChild (this.engineSelector);
| |
| if (!noSuggestions) span.appendChild (this.icon);
| |
| span.appendChild (OK);
| |
| span.appendChild (cancel);
| |
| form.appendChild(span);
| |
| form.style.display = 'none';
| |
| this.span.appendChild (form);
| |
| addEvent (text, 'focus', bind (function () { CategoryEditor.makeActive (this); }, this));
| |
| // 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.
| |
| // Therefore, use an IE-specific synchronous event on IE...
| |
| // Don't test for text.selectionStart being defined; FF3.6.4 raises an exception when trying to access that
| |
| // property while the element is not being displayed.
| |
| addEvent (text, (typeof text.onbeforedeactivate != 'undefined' && text.createTextRange) ? 'beforedeactivate' : 'blur', bind (this.saveView, this));
| |
| },
| |
|
| |
| display : function (evt) {
| |
| if (this.isAddCategory && !onUpload) {
| |
| var newAdder = new CategoryEditor (this.line, null, this.span, true); // Create a new one
| |
| }
| |
| if (!commitButton && !onUpload) {
| |
| for (var i = 0; i < editors.length; i++) {
| |
| if (editors[i].state != CategoryEditor.UNCHANGED) {
| |
| setMultiInput();
| |
| break;
| |
| }
| |
| }
| |
| }
| |
| if (!this.form) {
| |
| this.makeForm ();
| |
| }
| |
| if (this.list) this.list.style.display = 'none';
| |
| if (this.engineSelector) this.engineSelector.style.display = 'none';
| |
| this.currentCategory = this.lastSavedCategory;
| |
| this.currentExists = this.lastSavedExists;
| |
| this.currentHidden = this.lastSavedHidden;
| |
| this.currentKey = this.lastSavedKey;
| |
| this.icon.src = armorUri(this.currentExists ? HotCat.existsYes : HotCat.existsNo);
| |
| this.text.value = this.currentCategory + (this.currentKey != null ? '|' + this.currentKey : "");
| |
| this.originalState = this.state;
| |
| this.lastInput = this.currentCategory;
| |
| this.inputExists = this.currentExists;
| |
| this.state = this.state == CategoryEditor.UNCHANGED ? CategoryEditor.OPEN : CategoryEditor.CHANGE_PENDING;
| |
| this.lastSelection = {start: this.currentCategory.length, end: this.currentCategory.length};
| |
| this.showsList = false;
| |
| // Display the form
| |
| if (this.catLink) this.catLink.style.display = 'none';
| |
| this.linkSpan.style.display = 'none';
| |
| this.form.style.display = 'inline';
| |
| this.ok.disabled = false;
| |
| // Kill the event before focussing, otherwise IE will kill the onfocus event!
| |
| var result = evtKill (evt);
| |
| CategoryEditor.makeActive (this);
| |
| this.text.focus();
| |
| this.text.readOnly = false;
| |
| checkMultiInput ();
| |
| return result;
| |
| },
| |
|
| |
| show : function (evt, engine, readOnly) {
| |
| var result = this.display (evt);
| |
| var v = this.lastSavedCategory;
| |
| if (v.length === 0) return result;
| |
| this.text.readOnly = !!readOnly;
| |
| this.engine = engine;
| |
| this.textchange (false, true); // do autocompletion, force display of suggestions
| |
| CategoryEditor.forceRedraw ();
| |
| return result;
| |
| },
| |
| | | |
| open : function (evt) {
| | // Backwards compatibility stuff. We want HotCat to work with either wg* globals, or with mw.config.get(). |
| return this.show (evt, (this.engine && suggestionConfigs[this.engine].temp) ? HotCat.suggestions : this.engine);
| | // Our "solution" is to publish the wg* globals if they're not already published. |
| },
| | if (window.mediaWiki && window.mediaWiki.config) { |
| | | var globals = window.mediaWiki.config.get(); |
| down : function (evt) {
| | if (globals && globals !== window) { |
| return this.show (evt, 'subcat', true);
| | for (var k in globals) window[k] = globals[k]; |
| },
| | window.mediaWiki.config = new window.mediaWiki.Map(true); // Make config point to window again. |
| | | } |
| up : function (evt) {
| | globals = null; |
| return this.show (evt, 'parentcat');
| | } |
| },
| | // More backwards compatibility. We have a few places where we test for the browser: once for |
| | | // Safari < 3.0, twice for WebKit (Chrome or Safari, any versions), twice for IE <= 6, and |
| cancel : function () {
| | // once for IE < 8. |
| if (this.isAddCategory && !onUpload) {
| | var ua = navigator.userAgent.toLowerCase(); |
| this.removeEditor(); // We added a new adder when opening
| | var is_ie6 = /msie ([0-9]{1,}[\.0-9]{0,})/.exec(ua) !== null && parseFloat(RegExp.$1) <= 6.0; |
| return;
| | var is_ie_lt8 = /msie ([0-9]{1,}[\.0-9]{0,})/.exec(ua) !== null && parseFloat(RegExp.$1) < 8.0; |
| }
| | var is_webkit = /applewebkit\/\d+/.test(ua) && ua.indexOf ('spoofer') < 0; |
| // Close, re-display link
| | // And even more compatbility. HotCat was developed without jQuery, and anyway current jQuery |
| this.inactivate();
| | // (1.7.1) doesn't seem to support in jquery.getJSON() or jQuery.ajax() the automatic |
| this.form.style.display = 'none';
| | // switching from GET to POST requests if the query arguments would make the uri too long. |
| if (this.catLink) this.catLink.style.display = "";
| | // (IE has a hard limit of 2083 bytes, and the servers may have limits around 4 or 8kB.) |
| this.linkSpan.style.display = "";
| | // Anyway, HotCat is supposed to run on wikis without jQuery, so we'd have to supply some |
| this.state = this.originalState;
| | // ajax routines ourselves in any case. We can't rely on the old sajax_init_object(), newer |
| this.currentCategory = this.lastSavedCategory;
| | // MW versions (>= 1.19) might not have it. |
| this.currentKey = this.lastSavedKey;
| | var getJSON = (function () { |
| this.currentExists = this.lastSavedExists;
| | function getRequest () { |
| this.currentHidden = this.lastSavedHidden;
| | var request = null; |
| if (this.catLink) {
| | try { |
| if (this.currentkey && this.currentKey.length > 0) {
| | request = new window.XMLHttpRequest(); |
| this.catLink.title = this.currentKey;
| | } catch (anything) { |
| } else {
| | if (window.ActiveXObject) { |
| this.catLink.title = null;
| | try { |
| }
| | request = new window.ActiveXObject('Microsoft.XMLHTTP'); |
| }
| | } catch (any) { |
| if (this.state == CategoryEditor.UNCHANGED) {
| | } |
| if (this.catLink) this.catLink.style.backgroundColor = 'transparent';
| | } // end if IE |
| } else {
| | } // end try-catch |
| if (!onUpload) {
| | return request; |
| try {
| | } |
| this.catLink.style.backgroundColor = HotCat.bg_changed;
| | |
| } catch (ex) {}
| | return function (settings) { |
| }
| | var req = getRequest(); |
| }
| | if (!req && settings && settings.error) settings.error (req); |
| checkMultiInput ();
| | if (!req || !settings || !settings.uri) return req; |
| CategoryEditor.forceRedraw ();
| | var uri = armorUri (settings.uri); |
| },
| | var args = settings.data || null; |
| | | var method; |
| removeEditor : function () {
| | if (args && uri.length + args.length + 1 > 2000) { |
| if (!newDOM) {
| | // We lose caching, but at least we can make the request |
| var next = this.span.nextSibling;
| | method = 'POST'; |
| if (next) next.parentNode.removeChild (next);
| | req.setRequestHeader ('Content-Type', 'application/x-www-form-urlencoded'); |
| }
| | } else { |
| this.span.parentNode.removeChild (this.span);
| | method = 'GET'; |
| for (var i = 0; i < editors.length; i++) {
| | if (args) uri += '?' + args; |
| if (editors[i] == this) {
| | args = null; |
| editors.splice (i, 1);
| | } |
| break;
| | req.open (method, uri, true); |
| }
| | req.onreadystatechange = function () { |
| }
| | if (req.readyState != 4) return; |
| checkMultiInput ();
| | if (req.status != 200 || !req.responseText || !(/^\s*[\{\[]/.test(req.responseText))) { |
| var self = this;
| | if (settings.error) settings.error (req); |
| window.setTimeout (function () {delete self;}, 10);
| | } else { |
| },
| | if (settings.success) settings.success (eval ('(' + req.responseText + ')')); |
| | | } |
| rollback : function (evt) {
| | }; |
| this.undoLink.parentNode.removeChild (this.undoLink);
| | req.setRequestHeader ('Pragma', 'cache=yes'); |
| this.undoLink = null;
| | req.setRequestHeader ('Cache-Control', 'no-transform'); |
| this.currentCategory = this.originalCategory;
| | req.send (args); |
| this.currentKey = this.originalKey;
| | return req; |
| this.currentExists = this.originalExists;
| | }; |
| this.currentHidden = this.originalHidden;
| | })(); |
| this.lastSavedCategory = this.originalCategory;
| | |
| this.lastSavedKey = this.originalKey;
| | function armorUri (uri) { |
| this.lastSavedExists = this.originalExists;
| | // Avoid protocol-relative URIs, IE7 has a bug with them in Ajax calls |
| this.lastSavedHidden = this.originalHidden;
| | if (uri.length >= 2 && uri.substring(0, 2) == '//') return document.location.protocol + uri; |
| this.state = CategoryEditor.UNCHANGED;
| | return uri; |
| if (!this.currentCategory || this.currentCategory.length === 0) {
| | } |
| // It was a newly added category. Remove the whole editor.
| | |
| this.removeEditor();
| | function LoadTrigger (needed) { |
| } else {
| | this.queue = []; |
| // Redisplay the link...
| | this.toLoad = needed; |
| this.catLink.removeChild (this.catLink.firstChild);
| | } |
| this.catLink.appendChild (make (this.currentCategory, true));
| | LoadTrigger.prototype = { |
| this.catLink.href = wikiPagePath (HotCat.category_canonical + ':' + this.currentCategory);
| | register : function (callback) { |
| this.catLink.title = this.currentKey;
| | if (this.toLoad <= 0) { |
| this.catLink.className = this.currentExists ? "" : 'new';
| | callback (); // Execute directly |
| this.catLink.style.backgroundColor = 'transparent';
| | } else { |
| if (this.upDownLinks) this.upDownLinks.style.display = this.currentExists ? "" : 'none';
| | this.queue[this.queue.length] = callback; |
| checkMultiInput ();
| | } |
| }
| | }, |
| return evtKill (evt);
| | |
| },
| | loaded : function () { |
| | | if (this.toLoad > 0) { |
| inactivate : function () {
| | this.toLoad--; |
| if (this.list) this.list.style.display = 'none';
| | if (this.toLoad === 0) { |
| if (this.engineSelector) this.engineSelector.style.display = 'none';
| | // Run queued callbacks once |
| this.is_active = false;
| | for (var i = 0; i < this.queue.length; i++) this.queue[i](); |
| },
| | this.queue = []; |
| | | } |
| acceptCheck : function (dontCheck) {
| | } |
| this.sanitizeInput ();
| | } |
| var value = this.text.value.split('|');
| | |
| var key = null;
| | }; |
| if (value.length > 1) key = value[1];
| | |
| var v = value[0].replace(/_/g, ' ').replace(/^\s+|\s+$/g, "");
| | var setupCompleted = new LoadTrigger(1); |
| if (HotCat.capitalizePageNames) v = capitalize (v);
| | // Used to run user-registered code once HotCat is fully set up and ready. |
| this.lastInput = v;
| | HotCat.runWhenReady = function (callback) {setupCompleted.register(callback);}; |
| if (v.length === 0) {
| | |
| this.cancel ();
| | var loadTrigger = new LoadTrigger(2); |
| return false;
| | // Used to delay running the HotCat setup until /local_defaults and localizations have been loaded. |
| }
| | |
| if (!dontCheck
| | function load (uri) { |
| && ( wgNamespaceNumber == 14 && v == wgTitle
| | var head = document.getElementsByTagName ('head')[0]; |
| || HotCat.blacklist != null && HotCat.blacklist.test(v))
| | var s = document.createElement ('script'); |
| ) {
| | s.setAttribute ('src', armorUri(uri)); |
| this.cancel ();
| | s.setAttribute ('type', 'text/javascript'); |
| return false;
| | var done = false; |
| }
| | |
| this.currentCategory = v;
| | function afterLoad () { |
| this.currentKey = key;
| | if (done) return; |
| this.currentExists = this.inputExists;
| | done = true; |
| return true;
| | s.onload = s.onreadystatechange = s.onerror = null; // Properly clean up to avoid memory leaks in IE |
| },
| | if (head && s.parentNode) head.removeChild (s); |
| | | loadTrigger.loaded(); |
| accept : function (evt) {
| | } |
| this.noCommit = (evtKeys (evt) & 1) != 0;
| | |
| var result = evtKill (evt);
| | s.onload = s.onreadystatechange = function () { // onreadystatechange for IE, onload for all others |
| if (this.acceptCheck ()) {
| | if (done) return; |
| var toResolve = [this];
| | if (!this.readyState || this.readyState === 'loaded' || this.readyState === 'complete') { |
| var original = this.currentCategory;
| | afterLoad (); |
| resolveMulti (
| | } |
| toResolve
| | }; |
| ,
| | s.onerror = afterLoad; // Clean up, but otherwise ignore errors |
| | head.insertBefore (s, head.firstChild); // appendChild may trigger bugs in IE6 here |
| | } |
| | |
| | function loadJS (page) { |
| | load (wgServer + wgScript + '?title=' + encodeURIComponent (page) + '&action=raw&ctype=text/javascript'); |
| | } |
| | |
| | function loadURI (href) { |
| | var url = href; |
| | if (url.substring (0, 2) == '//') { |
| | url = window.location.protocol + url; |
| | } else if (url.substring (0, 1) == '/') { |
| | url = wgServer + url; |
| | } |
| | load (url); |
| | } |
| | |
| | // Load local configurations, overriding the pre-set default values in the HotCat object above. This is always loaded |
| | // from the wiki where this script is executing, even if this script itself is hotlinked from the Commons. This can |
| | // 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'); |
| | |
| | // Load localized UI texts. These are the texts that HotCat displays on the page itself. Texts shown in edit summaries |
| | // should be localized in /local_defaults above. |
| | if (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 |
| | // explicitly if you're not on the Commons and don't want that. |
| | if (typeof window.hotcat_translations_from_commons == 'undefined') { |
| | window.hotcat_translations_from_commons = wgServer.indexOf('//commons') < 0; |
| | } |
| | // Localization hook to localize HotCat messages, tooltips, and engine names for wgUserLanguage. |
| | if (window.hotcat_translations_from_commons && wgServer.indexOf('//commons') < 0) { |
| | loadURI ('//commons.wikimedia.org/w/index.php?title=' |
| | + 'MediaWiki:Gadget-HotCat.js/' + wgUserLanguage |
| | + '&action=raw&ctype=text/javascript&smaxage=21600&maxage=86400' |
| | ); |
| | } else { |
| | // Load translations locally |
| | loadJS ('MediaWiki:Gadget-HotCat.js/' + wgUserLanguage); |
| | } |
| | } else { |
| | loadTrigger.loaded(); |
| | } |
| | |
| | // No further changes should be necessary here. |
| | |
| | // The following regular expression strings are used when searching for categories in wikitext. |
| | var wikiTextBlank = '[\\t _\\xA0\\u1680\\u180E\\u2000-\\u200A\\u2028\\u2029\\u202F\\u205F\\u3000]+'; |
| | var wikiTextBlankRE = new RegExp (wikiTextBlank, 'g'); |
| | // Regexp for handling blanks inside a category title or namespace name. |
| | // See http://svn.wikimedia.org/viewvc/mediawiki/trunk/phase3/includes/Title.php?revision=104051&view=markup#l2722 |
| | // See also http://www.fileformat.info/info/unicode/category/Zs/list.htm |
| | // MediaWiki collapses several contiguous blanks inside a page title to one single blank. It also replace a |
| | // number of special whitespace characters by simple blanks. And finally, blanks are treated as underscores. |
| | // Therefore, when looking for page titles in wikitext, we must handle all these cases. |
| | // Note: we _do_ include the horizontal tab in the above list, even though the MediaWiki software for some reason |
| | // appears to not handle it. The zero-width space \u200B is _not_ handled as a space inside titles by MW. |
| | var wikiTextBlankOrBidi = '[\\t _\\xA0\\u1680\\u180E\\u2000-\\u200B\\u200E\\u200F\\u2028-\\u202F\\u205F\\u3000]*'; |
| | // Whitespace regexp for handling whitespace between link components. Including the horizontal tab, but not \n\r\f\v: |
| | // a link must be on one single line. |
| | // MediaWiki also removes Unicode bidi override characters in page titles (and namespace names) completely. |
| | // This is *not* handled, as it would require us to allow any of [\u200E\u200F\u202A-\u202E] between any two |
| | // characters inside a category link. It _could_ be done though... We _do_ handle strange spaces, including the |
| | // zero-width space \u200B, and bidi overrides between the components of a category link (adjacent to the colon, |
| | // or adjacent to and inside of "[[" and "]]"). |
| | |
| | // First auto-localize the regexps for the category and the template namespaces. |
| | if (typeof (wgFormattedNamespaces) != 'undefined') { |
| | function autoLocalize (namespaceNumber, fallback) { |
| | function create_regexp_str (name) |
| | { |
| | if (!name || name.length === 0) return ""; |
| | var regex_name = ""; |
| | for (var i = 0; i < name.length; i++){ |
| | var initial = name.substr (i, 1); |
| | var ll = initial.toLowerCase (); |
| | var ul = initial.toUpperCase (); |
| | if (ll == ul |
| })(); | | })(); |
| | | |
| } // end if (guard) | | } // end if (guard) |
| //</source> | | //</source> |