|
|
Rad 1: |
Rad 1: |
| /* JavaScript som skrivs här körs varje gång en användare laddar en sida. */ | | /* JavaScript som skrivs här körs varje gång en användare laddar en sida. */ |
|
| |
|
| //<source lang="javascript"> | | /*Hotcat*/ |
| | |
| | var hotcat_translations_from_commons = true; |
| | | |
| /*
| | if( wgServer.indexOf( 'https://secure.wikimedia.org' ) == 0 ) { |
| Ajax-based simple Category manager. Allows adding/removing/changing categories on a page view.
| | importScriptURI( '/wikipedia/commons/w/index.php?title=MediaWiki:Gadget-HotCat.js&action=raw&ctype=text/javascript' ); |
| Supports multiple category changes, as well as redirect and disambiguation resolution. Also
| | } else { |
| plugs into the upload form. Search engines to use for the suggestion list are configurable, and
| | importScriptURI( 'http://commons.wikimedia.org/w/index.php?title=MediaWiki:Gadget-HotCat.js&action=raw&ctype=text/javascript' ); |
| can be selected interactively.
| | } |
|
| |
| Author: [[User:Lupo]], April-May 2010
| |
| License: Quadruple licensed GFDL, GPL, LGPL and Creative Commons Attribution 3.0 (CC-BY-3.0)
| |
|
| |
| Choose whichever license of these you like best :-)
| |
| */
| |
|
| |
| // Globals:
| |
| // (inline script on the page):
| |
| // wgNamespaceNumber, wgCanonicalSpecialPageName, wgNamespaceIds (optional), wgFormattedNamespaces (optional)
| |
| // wgScript, wgServer, wgArticlePath, wgScriptPath, wgAction, wgPageName, wgTitle, wgUserName, wgIsArticle,
| |
| // wgArticleId
| |
| // ajax.js
| |
| // sajax_init_object
| |
| // wikibits.js
| |
| // addOnloadHook, window.ie6_bugs
| |
|
| |
| if (typeof (HotCat) == 'undefined') { // Guard against double inclusions
| |
|
| |
| // Configuration stuff.
| |
| var HotCat = {
| |
| // Localize these messages to the main language of your wiki.
| |
| messages :
| |
| { cat_removed : 'tog bort [[Kategori:$1]]'
| |
| ,cat_added : 'lade till [[Kategori:$1]]'
| |
| ,cat_keychange: 'ändrade sorteringen i [[Kategori:$1]]: '
| |
| ,cat_notFound : 'Kategorin "$1" hittades inte'
| |
| ,cat_exists : 'Kategorin "$1" finns redan, lades inte till.'
| |
| ,cat_resolved : ' (omdirigeringen [[Kategori:$1]] rättades)'
| |
| ,uncat_removed: 'tog bort {{uncategorized}}'
| |
| ,using : ' med [[Wikipedia:Hotcat|Hotcat]]'
| |
| ,multi_change : '$1 kategorier'
| |
| // $1 is replaced by a number
| |
| ,commit : 'Spara'
| |
| // Button text. Localize to wgContentLanguage here; localize to wgUserLanguage in a subpage,
| |
| // see localization hook below.
| |
| ,ok : 'OK'
| |
| // Button text. Localize to wgContentLanguage here; localize to wgUserLanguage in a subpage,
| |
| // see localization hook below.
| |
| ,cancel : 'Avbryt'
| |
| // Button text. Localize to wgContentLanguage here; localize to wgUserLanguage in a subpage,
| |
| // see localization hook below.
| |
| ,multi_error : 'Kunde inte hämta sidans text från servern. Dina ändringar sparades inte.'
| |
| +' Försök igen senare.'
| |
| // Localize to wgContentLanguage here; localize to wgUserLanguage in a subpage,
| |
| // see localization hook below.
| |
| }
| |
| ,category_regexp : '[Cc]ategory|[Kk]ategori'
| |
| // 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
| |
| // Wikipedia, use '[Cc]ategory|[Kk]ategorie', or at the Chinese Wikipedia, use '[Cc]ategory|分类|分類'.
| |
| ,category_canonical : 'Kategori'
| |
| // 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").
| |
| ,categories : 'Kategorier'
| |
| // Plural of category_canonical
| |
| ,disambig_category : null
| |
| // 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
| |
| // that concept on your wiki, set it to null.
| |
| ,redir_category : 'Kategoriomdirigering'
| |
| // Any category in this category is deemed a (soft) redirect to some other category defined by the first link
| |
| // to another category. If your wiki doesn't have soft category redirects, set this to null.
| |
| ,links : {change: '(±)', remove: '(-)', add: '(+)', restore: '(×)', undo: '(×)'}
| |
| // The little modification links displayed after category names.
| |
| ,addmulti : '<span>+<sup>+</sup></span>'
| |
| // The HTML content of the "enter multi-mode" link at the front.
| |
| ,disable :
| |
| function () { // Return true to disable HotCat
| |
| return ( wgNamespaceNumber < 0 // Special pages; Special:Upload is handled differently
| |
| || wgNamespaceNumber == 6 // Fil
| |
| || wgNamespaceNumber == 7 // Fildiskussion
| |
| || wgNamespaceNumber == 10 // Mall
| |
| || typeof (wgNamespaceIds) != 'unknown'
| |
| && ( wgNamespaceNumber == wgNamespaceIds['creator']
| |
| || wgNamespaceNumber == wgNamespaceIds['timedtext']
| |
| )
| |
| );
| |
| }
| |
| ,uncat_regexp : /\{\{\s*([Uu]ncat(egori[sz]ed( image)?)?|[Nn]ocat|[Nn]eedscategory)[^}]*\}\}/g
| |
| // A regexp matching a templates used to mark uncategorized pages, if your wiki does have that.
| |
| // If not, set it to null.
| |
| ,existsYes : 'http://upload.wikimedia.org/wikipedia/commons/thumb/b/be/P_yes.svg/20px-P_yes.svg.png'
| |
| ,existsNo : 'http://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.
| |
| };
| |
|
| |
| if (wgUserLanguage != wgContentLanguage) importScript ('MediaWiki:Gadget-HotCat.js/' + wgUserLanguage);
| |
| // Localization hook to localize HotCat.messages.commit and HotCat.messages.multi_error. For German, the
| |
| // file would be "MediaWiki:Gadget-HotCat.js/de", and its contents could be for instance
| |
| //
| |
| // HotCat.messages.commit = 'Speichern';
| |
| // HotCat.messages.ok = 'OK';
| |
| // HotCat.messages.cancel = 'Abbrechen';
| |
| // HotCat.messages.multi_error = 'Seitentext konnte nicht vom Server geladen werden. Die Änderungen können '
| |
| // +'leider nicht gespeichert werden.';
| |
|
| |
| // No further changes should be necessary here. However, you should also localize the search engine names.
| |
| // Search in this file for "Localize the names here", and localize the names there.
| |
|
| |
| (function () {
| |
|
| |
| // First auto-localize the category names
| |
| if (typeof (wgFormattedNamespaces) != 'undefined' && wgFormattedNamespaces['14']) {
| |
| function create_regexp_str (name)
| |
| {
| |
| if (!name || name.length == 0) return "";
| |
| var initial = name.substr (0, 1);
| |
| name = name.substring(1).replace (/[ _]/g, '[ _]').replace(/([\\\^\$\.\?\*\+\(\)])/g, '\\$1');
| |
| return '[' + initial.toLowerCase () + initial.toUpperCase () + ']' + name;
| |
| }
| |
|
| |
| HotCat.category_canonical = wgFormattedNamespaces['14'];
| |
| HotCat.category_regexp += '|' + create_regexp_str (HotCat.category_canonical);
| |
| for (var cat_name in wgNamespaceIds) {
| |
| if ( typeof (cat_name) == 'string'
| |
| && cat_name.toLowerCase () != HotCat.category_canonical.toLowerCase ()
| |
| && wgNamespaceIds[cat_name] == 14)
| |
| {
| |
| HotCat.category_regexp += '|' + create_regexp_str (cat_name);
| |
| }
| |
| }
| |
| }
| |
| | |
| // 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 = 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) {
| |
| // 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)
| |
| 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);
| |
| }
| |
|
| |
| // Text modification
| |
|
| |
| var findCatsRE =
| |
| new RegExp ('\\[\\[\\s*(?:' + HotCat.category_regexp + ')\\s*:\[^\\]\]+\\]\\]', '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_name = category.replace(/([\\\^\$\.\?\*\+\(\)])/g, '\\$1');
| |
| var initial = cat_name.substr (0, 1);
| |
| var cat_regex = new RegExp ('\\[\\[\\s*(' + HotCat.category_regexp + ')\\s*:\\s*'
| |
| + (initial == '\\'
| |
| ? initial
| |
| : '[' + initial.toUpperCase() + initial.toLowerCase() + ']')
| |
| + cat_name.substring (1).replace (/[ _]/g, '[ _]')
| |
| + '\\s*(\\|.*?)?\\]\\]', '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
| |
| }
| |
|
| |
| function change_category (wikitext, toRemove, toAdd, key) {
| |
|
| |
| 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;
| |
| // We should try to find interwiki links here, but that's for later.
| |
| return index;
| |
| }
| |
|
| |
| 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', 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) 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' && 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) summary.push (HotCat.messages.cat_removed.replace ('$1', 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', toAdd)};
| |
| } else {
| |
| if (cat_point < 0)
| |
| cat_point = find_insertionpoint (wikitext);
| |
| var newcatstring = '\n[[' + nameSpace + ':' + toAdd + (key || "") + ']]';
| |
| if (cat_point >= 0) {
| |
| wikitext = wikitext.substring (0, cat_point) + newcatstring + wikitext.substring (cat_point);
| |
| } else {
| |
| wikitext += newcatstring;
| |
| }
| |
| if (keyChange) {
| |
| summary.push (HotCat.messages.cat_keychange.replace ('$1', toAdd) + '"' + key.substring(1) + '"');
| |
| } else {
| |
| summary.push (HotCat.messages.cat_added.replace ('$1', toAdd));
| |
| }
| |
| if (HotCat.uncat_regexp) {
| |
| 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};
| |
| }
| |
|
| |
| if (wgAction == 'edit') {
| |
| // Legacy code based on URI parameters, can add/remove/change only one single category. Still
| |
| // used for single-category changes, and newly to supply a default edit summary for multi category
| |
| // changes.
| |
| var toRemove = param ('hotcat_removecat');
| |
| var toAdd = param ('hotcat_newcat');
| |
| if (toAdd) {
| |
| toAdd = capitalize (toAdd.replace (/_/g, ' ').replace (/^\s+|\s+$/g, ""));
| |
| if (toAdd.length == 0) toAdd = null;
| |
| }
| |
| if (toRemove) {
| |
| toRemove = capitalize (toRemove.replace (/_/g, ' ').replace (/^\s+|\s+$/g, ""));
| |
| if (toRemove.length == 0) toRemove = null;
| |
| }
| |
| if (toAdd || toRemove) {
| |
| addOnloadHook (function () {
| |
| if (!document.editform || !document.editform.wpTextbox1) return;
| |
| var comment = param ('hotcat_comment') || "";
| |
| var cat_key = param ('hotcat_sortkey');
| |
| var result = change_category (document.editform.wpTextbox1.value, toRemove, toAdd, cat_key);
| |
| var do_commit = !HotCat.noCommit && !result.error && param ('hotcat_nocommit') != '1';
| |
| document.editform.wpTextbox1.value = result.text;
| |
| if (result.summary && result.summary.length > 0)
| |
| document.editform.wpSummary.value = result.summary.join ('; ') + comment + HotCat.messages.using;
| |
| document.editform.wpMinoredit.checked = true ;
| |
| if (result.error) alert (result.error);
| |
| if (do_commit) {
| |
| // Hide the entire edit section so as not to tempt the user into editing...
| |
| var content = document.getElementById ('bodyContent') // monobook & vector skin
| |
| || document.getElementById ('mw_contentholder') // modern skin
| |
| || document.getElementById ('article'); // classic skins
| |
| if (content) content.style.display = 'none';
| |
| document.editform.submit();
| |
| }
| |
| });
| |
| }
| |
| return;
| |
| }
| |
|
| |
| // 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;
| |
| }
| |
|
| |
| var catLine = null;
| |
| var onUpload = false;
| |
| var editors = [];
| |
|
| |
| var commitButton = null;
| |
| var commitForm = null;
| |
| var multiSpan = null;
| |
|
| |
| var pageText = null;
| |
| var pageTime = null;
| |
| var watchCreate = false;
| |
| var watchEdit = false;
| |
| var minorEdits = false;
| |
|
| |
| var is_rtl = false;
| |
|
| |
| 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);
| |
| }
| |
| // Get the preferences, so that we can set wpWatchthis correctly later on. Must use Ajax here.
| |
| if (wgUserName) {
| |
| var request = sajax_init_object ();
| |
| request.open
| |
| ('GET', wgServer + wgScriptPath + '/api.php?format=json&action=query&meta=userinfo&uiprop=options', true);
| |
| request.onreadystatechange =
| |
| function () {
| |
| if (request.readyState != 4) return;
| |
| if (request.status == 200 && request.responseText && request.responseText.charAt(0) == '{') {
| |
| var json = eval ('(' + request.responseText + ')');
| |
| if (json && json.query && json.query.userinfo && json.query.userinfo.options) {
| |
| watchCreate = json.query.userinfo.options.watchcreations == '1';
| |
| watchEdit = json.query.userinfo.options.watchdefault == '1';
| |
| minorEdits = json.query.userinfo.options.minordefault == 1;
| |
| }
| |
| }
| |
| };
| |
| request.setRequestHeader ('Pragma', 'cache=yes');
| |
| request.setRequestHeader ('Cache-Control', 'no-transform');
| |
| request.send (null);
| |
| }
| |
| }
| |
|
| |
| 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 performChanges () {
| |
| // Don't use the edit API or LAPI, it's always bothersome to report back errors like edit
| |
| // conflicts. Instead, make one remote call (blocking, because we can't continue anyway if
| |
| // it doesn't succeed), getting the page text. Perform the changes on the text, then construct
| |
| // a form to submit all this as a diff.
| |
| if (pageText === null) {
| |
| var request = sajax_init_object ();
| |
| var uri = wgServer + wgScriptPath
| |
| + '/api.php?format=json&action=query&titles=' + encodeURIComponent (wgPageName)
| |
| + '&prop=info%7Crevisions&rvprop=content%7Ctimestamp';
| |
| request.open ('GET', uri, false); // Yes, synchronous
| |
| request.send (null);
| |
| if (request.status == 200 && request.responseText && request.responseText.charAt(0) == '{') {
| |
| setPage (eval ('(' + request.responseText + ')'));
| |
| }
| |
| }
| |
| if (pageText === null) {
| |
| alert (HotCat.messages.multi_error);
| |
| return;
| |
| }
| |
| // Create a form and submit it
| |
| if (!commitForm) {
| |
| var formContainer = make ('div');
| |
| formContainer.style.display = 'none';
| |
| document.body.appendChild (formContainer);
| |
| formContainer.innerHTML =
| |
| '<form method="post" enctype="multipart/form-data" action="'
| |
| + wgScript + '?title=' + encodeURIComponent (wgPageName)
| |
| + '&action=edit">'
| |
| + '<input type="hidden" name="wpTextbox1" />'
| |
| + '<input type="hidden" name="wpSummary" value="" />'
| |
| + '<input type="checkbox" name="wpMinoredit" value="1" />'
| |
| + '<input type="checkbox" name="wpWatchthis" value="1" />'
| |
| + '<input type="hidden" name="wpEdittime" />'
| |
| + '<input type="hidden" name="wpStarttime" />'
| |
| + '<input type="hidden" name="wpDiff" value="wpDiff" />'
| |
| + '</form>';
| |
| commitForm = formContainer.firstChild;
| |
| }
| |
| var result = { text : pageText };
| |
| var changed = [], added = [], deleted = [], changes = 0;
| |
| for (var i=0; i < editors.length; i++) {
| |
| if (editors[i].state == CategoryEditor.CHANGED) {
| |
| result = change_category (
| |
| result.text
| |
| , editors[i].originalCategory
| |
| , editors[i].currentCategory
| |
| , editors[i].currentKey
| |
| );
| |
| if (!result.error) {
| |
| changes++;
| |
| if (!editors[i].originalCategory || editors[i].originalCategory.length == 0) {
| |
| added.push (editors[i].currentCategory);
| |
| } else {
| |
| changed.push ({from : editors[i].originalCategory, to : editors[i].currentCategory});
| |
| }
| |
| }
| |
| } else if (editors[i].state == CategoryEditor.DELETED) {
| |
| result = change_category (result.text, editors[i].originalCategory, null, null);
| |
| if (!result.error) {
| |
| changes++;
| |
| deleted.push (editors[i].originalCategory);
| |
| }
| |
| }
| |
| }
| |
| // Fill in the form and submit it
| |
| commitForm.wpMinoredit.checked = minorEdits;
| |
| commitForm.wpWatchthis.checked = wgArticleId == 0 && watchCreate || watchEdit;
| |
| if (wgArticleId > 0) {
| |
| if (changes == 1) {
| |
| if (result.summary && result.summary.length > 0)
| |
| commitForm.wpSummary.value = result.summary.join ('; ') + HotCat.messages.using;
| |
| commitForm.wpMinoredit.checked = true;
| |
| } else if (changes > 1) {
| |
| var summary = [];
| |
| if (deleted.length == 1)
| |
| summary.push ('-[[' + HotCat.category_canonical + ':' + deleted[0] + ']]');
| |
| else if (deleted.length > 1)
| |
| summary.push ('- ' + HotCat.messages.multi_change.replace ('$1', "" + deleted.length));
| |
| if (added.length == 1)
| |
| summary.push ('+[[' + HotCat.category_canonical + ':' + added[0] + ']]');
| |
| else if (added.length > 1)
| |
| summary.push ('+ ' + HotCat.messages.multi_change.replace ('$1', "" + added.length));
| |
| if (changed.length == 1) {
| |
| if (changed[0].from != changed[0].to) {
| |
| summary.push ('±[[' + HotCat.category_canonical + ':' + changed[0].from + ']]→[['
| |
| + HotCat.category_canonical + ':' + changed[0].to + ']]');
| |
| } else {
| |
| summary.push ('±[[' + HotCat.category_canonical + ':' + changed[0].from + ']]');
| |
| }
| |
| } else if (changed.length > 1) {
| |
| summary.push ('± ' + HotCat.messages.multi_change.replace ('$1', "" + changed.length));
| |
| }
| |
| if (summary.length > 0)
| |
| commitForm.wpSummary.value = summary.join ('; ') + HotCat.messages.using;
| |
| }
| |
| }
| |
| commitForm.wpTextbox1.value = result.text;
| |
| commitForm.wpStarttime.value = currentTimestamp ();
| |
| commitForm.wpEdittime.value = pageTime || commitForm.wpStarttime.value;
| |
| commitForm.submit();
| |
| }
| |
| | |
| 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;
| |
| }
| |
| var request = sajax_init_object ();
| |
| if (!request) {
| |
| noSuggestions = true;
| |
| callback (toResolve);
| |
| return;
| |
| }
| |
| var url = wgServer + wgScriptPath + '/api.php';
| |
| // 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&plnamespace=14&pllimit=50'
| |
| + '&cllimit=' + (toResolve.length * 10) // Category limit is global, link limit is per page
| |
| + '&format=json&titles=';
| |
| for (var i = 0; i < toResolve.length; i++) {
| |
| args += encodeURIComponent ('Category:' + toResolve[i].dabInput);
| |
| if (i+1 < toResolve.length) args += '%7C';
| |
| }
| |
| if (url.length + args.length + 1 > 2000) { // Lowest common denominator: IE has a URI length limit of 2083
| |
| request.open ('POST', url, true);
| |
| request.setRequestHeader ('Content-Type', 'application/x-www-form-urlencoded');
| |
| } else {
| |
| url += '?' + args; args = null;
| |
| request.open ('GET', url, true);
| |
| }
| |
| request.onreadystatechange =
| |
| function () {
| |
| if (request.readyState != 4) return;
| |
| if (request.status != 200) {
| |
| callback (toResolve);
| |
| return;
| |
| }
| |
| resolveRedirects (toResolve, eval ('(' + request.responseText + ')'));
| |
| callback (toResolve);
| |
| };
| |
| request.setRequestHeader ('Pragma', 'cache=yes');
| |
| request.setRequestHeader ('Cache-Control', 'no-transform');
| |
| request.send (args);
| |
| }
| |
|
| |
| 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?
| |
| 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 = 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) {
| |
| performChanges ();
| |
| 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) {
| |
| performChanges ();
| |
| }
| |
| }
| |
| );
| |
| }
| |
|
| |
| var noSuggestions = false;
| |
| var suggestionEngines = {
| |
| opensearch :
| |
| { uri : '/api.php?format=json&action=opensearch&namespace=14&limit=30&search=$1' // $1 = search term
| |
| ,handler : // Function to convert result of uri into an array of category names
| |
| function (responseText, queryKey) {
| |
| if (responseText.charAt (0) != '[') return null;
| |
| var queryResult = eval ('(' + responseText + ')');
| |
| if (queryResult != null && queryResult.length == 2 && queryResult[0] == queryKey) {
| |
| var titles = queryResult[1];
| |
| for (var i = 0; i < titles.length; i++) {
| |
| titles[i] = titles[i].substring (titles[i].indexOf (':') + 1); // rm namespace
| |
| }
| |
| return titles;
| |
| }
| |
| return null;
| |
| }
| |
| }
| |
| ,internalsearch :
| |
| { uri : '/api.php?format=json&action=query&list=allpages&apnamespace=14&aplimit=30&apfrom=$1'
| |
| ,handler :
| |
| function (responseText, queryKey) {
| |
| if (responseText.charAt (0) != '{') return null;
| |
| var queryResult = eval ('(' + responseText + ')');
| |
| if (queryResult && queryResult.query && queryResult.query.allpages) {
| |
| var titles = queryResult.query.allpages;
| |
| for (var i = 0; i < titles.length; i++) {
| |
| titles[i] = titles[i].title.substring (titles[i].title.indexOf (':') + 1); // rm namespace
| |
| if (titles[i].indexOf (queryKey) != 0) {
| |
| titles.splice (i, 1); // Doesn't start with the query key
| |
| i--;
| |
| }
| |
| }
| |
| return titles;
| |
| }
| |
| return null;
| |
| }
| |
| }
| |
| };
| |
|
| |
| // Localize the names here.
| |
| var suggestionConfigs = {
| |
| searchindex : {name: 'Search index', engines: ['opensearch'], cache: {}}
| |
| ,pagelist : {name: 'Page list', engines: ['internalsearch'], cache: {}}
| |
| ,combined : {name: 'Combined search', engines: ['opensearch', 'internalsearch'], cache: {}}
| |
| };
| |
|
| |
| 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;
| |
|
| |
| CategoryEditor.makeActive = function (toActivate) {
| |
| 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, true); // do autocompletion, no key, no engine selector
| |
| toActivate.dab = null;
| |
| }
| |
| };
| |
|
| |
| CategoryEditor.prototype = {
| |
|
| |
| initialize : function (line, span, after, key) {
| |
| // 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;
| |
| 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';
| |
| var lk = make ('a'); lk.href = '#catlinks'; lk.onclick = bind (this.open, this);
| |
| lk.appendChild (make (HotCat.links.add, true));
| |
| this.linkSpan.appendChild (lk);
| |
| span = make ('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 ();
| |
| span.appendChild (this.linkSpan);
| |
| }
| |
| this.line = line;
| |
| this.engine = HotCat.suggestions;
| |
| this.span = span;
| |
| this.currentCategory = this.originalCategory;
| |
| this.currentExists = this.originalExists;
| |
| this.currentKey = this.originalKey;
| |
| this.state = CategoryEditor.UNCHANGED;
| |
| this.lastSavedState = CategoryEditor.UNCHANGED;
| |
| this.lastSavedCategory = this.originalCategory;
| |
| this.lastSavedKey = this.originalKey;
| |
| this.lastSavedExists = this.originalExists;
| |
| editors[editors.length] = this;
| |
| },
| |
|
| |
| makeLinkSpan : function () {
| |
| this.normalLinks = make ('span');
| |
| var lk = make ('a'); lk.href = '#catlinks'; lk.onclick = bind (this.remove, this);
| |
| lk.appendChild (make (HotCat.links.remove, true));
| |
| this.normalLinks.appendChild (make (' ', true));
| |
| this.normalLinks.appendChild (lk);
| |
| lk = make ('a'); lk.href = '#catlinks'; lk.onclick = bind (this.open, this);
| |
| lk.appendChild (make (HotCat.links.change, true));
| |
| this.normalLinks.appendChild (make (' ', true));
| |
| this.normalLinks.appendChild (lk);
| |
| this.linkSpan = make ('span');
| |
| this.linkSpan.className = 'noprint';
| |
| this.linkSpan.appendChild (this.normalLinks);
| |
| this.undelLink = make ('span');
| |
| this.undelLink.style.display = 'none';
| |
| lk = make ('a'); lk.href = '#catlinks'; lk.onclick = bind (this.restore, this);
| |
| lk.appendChild (make (HotCat.links.restore, true));
| |
| this.undelLink.appendChild (make (' ', true));
| |
| this.undelLink.appendChild (lk);
| |
| this.linkSpan.appendChild (this.undelLink);
| |
| },
| |
|
| |
| open : 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) {
| |
| 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) {
| |
| // In case a browser doesn't generate keypress events for arrow keys...
| |
| if (this.keyCount == 0) return this.processKey (evt);
| |
| } else {
| |
| if (key == 27) this.resetKeySelection ();
| |
| // 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
| |
| this.state = CategoryEditor.CHANGE_PENDING;
| |
| var self = this;
| |
| window.setTimeout (function () {self.textchange (dont_autocomplete);}, HotCat.suggest_delay);
| |
| }
| |
| }
| |
| ,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 (evt.keyCode == 13) this.accept (evt);
| |
| }
| |
| ,this
| |
| );
| |
| // And handle continued pressing of arrow keys
| |
| text.onkeypress = bind (function (evt) {this.keyCount++; return this.processKey (evt);}, this);
| |
| }
| |
| text.onfocus = bind (function () { CategoryEditor.makeActive (this); }, this);
| |
| this.text = text;
| |
|
| |
| this.icon = make ('img');
| |
|
| |
| var list = null;
| |
| if (!noSuggestions) {
| |
| list = make ('select');
| |
| list.onclick = bind (function () { if (this.setValueFromList ()) this.textchange (); }, this);
| |
| list.ondblclick = bind (function (e) { if (this.setValueFromList ()) this.accept (e); }, this);
| |
| list.onchange = bind (function (e) { this.setValueFromList (); 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) {
| |
| 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.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;
| |
| }
| |
|
| |
| var OK = make ('input'); OK.type = 'submit';
| |
| OK.value = button_label ('wpOkUploadLbl', HotCat.messages.ok);
| |
| 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;
| |
|
| |
| if (list) form.appendChild (list);
| |
| if (this.engineSelector) form.appendChild (this.engineSelector);
| |
| form.appendChild (text);
| |
| if (!noSuggestions) form.appendChild (this.icon);
| |
| form.appendChild (OK);
| |
| form.appendChild (cancel);
| |
| form.style.display = 'none';
| |
| this.span.appendChild (form);
| |
| }
| |
| if (this.list) this.list.style.display = 'none';
| |
| if (this.engineSelector) this.engineSelector.style.display = 'none';
| |
| this.currentCategroy = this.lastSavedCategory;
| |
| this.currentExits = this.lastSavedExists;
| |
| this.currentKey = this.lastSavedKey;
| |
| this.icon.src = this.currentExists ? HotCat.existsYes : HotCat.existsNo;
| |
| this.text.value = this.currentCategory + (this.currentKey ? '|' + 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;
| |
| // Display the form
| |
| if (this.catLink) this.catLink.style.display = 'none';
| |
| this.linkSpan.style.display = 'none';
| |
| this.form.style.display = 'inline';
| |
| CategoryEditor.makeActive (this);
| |
| // Kill the event before focussing, otherwise IE will kill the onfocus event!
| |
| var result = evtKill (evt);
| |
| this.text.focus();
| |
| return result;
| |
| },
| |
|
| |
| cancel : function () {
| |
| if (this.isAddCategory && !onUpload) {
| |
| this.removeEditor(); // We added a new adder when opening
| |
| return;
| |
| }
| |
| // Close, re-display link
| |
| if (this.list) this.list.style.display = 'none';
| |
| if (this.engineSelector) this.engineSelector.style.display = 'none';
| |
| this.form.style.display = 'none';
| |
| if (this.catLink) this.catLink.style.display = "";
| |
| this.linkSpan.style.display = "";
| |
| this.state = this.originalState;
| |
| this.currentCategory = this.lastSavedCategory;
| |
| this.currentKey = this.lastSavedKey;
| |
| this.currentExists = this.lastSavedExists;
| |
| },
| |
|
| |
| removeEditor : function () {
| |
| var next = this.span.nextSibling;
| |
| if (next) next.parentNode.removeChild (next);
| |
| this.span.parentNode.removeChild (this.span);
| |
| for (var i = 0; i < editors.length; i++) {
| |
| if (editors[i] == this) {
| |
| editors.splice (i, 1);
| |
| break;
| |
| }
| |
| }
| |
| var self = this;
| |
| window.setTimeout (function () {delete self;}, 10);
| |
| },
| |
|
| |
| rollback : function (evt) {
| |
| this.undoLink.parentNode.removeChild (this.undoLink);
| |
| this.undoLink = null;
| |
| this.currentCategory = this.originalCategory;
| |
| this.currentKey = this.originalKey;
| |
| this.currentExists = this.originalExists;
| |
| this.lastSavedCategory = this.originalCategory;
| |
| this.lastSavedKey = this.originalKey;
| |
| this.lastSavedExists = this.originalExists;
| |
| this.state = CategoryEditor.UNCHANGED;
| |
| if (!this.currentCategory || this.currentCategory.length == 0) {
| |
| // It was a newly added category. Remove the whole editor.
| |
| this.removeEditor();
| |
| } else {
| |
| // Redisplay the link...
| |
| this.catLink.removeChild (this.catLink.firstChild);
| |
| this.catLink.appendChild (make (this.currentCategory, true));
| |
| this.catLink.href = wgArticlePath.replace ('$1', 'Category:' + this.currentCategory);
| |
| this.catLink.title = "";
| |
| this.catLink.className = this.currentExists ? "" : 'new';
| |
| }
| |
| return evtKill (evt);
| |
| },
| |
|
| |
| inactivate : function () {
| |
| if (this.list) this.list.style.display = 'none';
| |
| if (this.engineSelector) this.engineSelector.style.display = 'none';
| |
| this.is_active = false;
| |
| },
| |
|
| |
| acceptCheck : function (dontCheck) {
| |
| this.sanitizeInput ();
| |
| var value = this.text.value.split('|');
| |
| var key = null;
| |
| if (value.length > 1) key = value[1];
| |
| var v = capitalize (value[0].replace(/_/g, ' ').replace(/^\s+|\s+$/g, ""));
| |
| this.lastInput = v;
| |
| if (v.length == 0) {
| |
| this.cancel ();
| |
| return false;
| |
| }
| |
| if ( !dontCheck
| |
| && ( ( v == this.lastSavedCategory
| |
| && ( key == this.lastSavedKey
| |
| || key === null && this.lastSavedKey.length == 0
| |
| || this.lastSavedKey === null && key.length == 0
| |
| )
| |
| )
| |
| || wgNamespaceNumber == 14 && v == wgTitle
| |
| )
| |
| )
| |
| {
| |
| this.cancel ();
| |
| return false;
| |
| }
| |
| this.currentCategory = v;
| |
| this.currentKey = key;
| |
| this.currentExists = this.inputExists;
| |
| return true;
| |
| },
| |
|
| |
| accept : function (evt) {
| |
| this.noCommit = (evtKeys (evt) & 1) != 0;
| |
| var result = evtKill (evt);
| |
| if (this.acceptCheck ()) {
| |
| var toResolve = [this];
| |
| var original = this.currentCategory;
| |
| resolveMulti (
| |
| toResolve
| |
| , function (resolved) {
| |
| if (resolved[0].dab) {
| |
| CategoryEditor.makeActive (resolved[0]);
| |
| } else {
| |
| if (resolved[0].acceptCheck(true)) {
| |
| resolved[0].commit (
| |
| (resolved[0].currentCategory != original)
| |
| ? HotCat.messages.cat_resolved.replace ('$1', original)
| |
| : null
| |
| );
| |
| }
| |
| }
| |
| }
| |
| );
| |
| }
| |
| return result;
| |
| },
| |
|
| |
| close : function () {
| |
| if (!this.catLink) {
| |
| // Create a catLink
| |
| this.catLink = make ('a');
| |
| this.catLink.appendChild (make ('foo', true));
| |
| this.catLink.style.display = 'none';
| |
| this.span.insertBefore (this.catLink, this.span.firstChild.nextSibling);
| |
| }
| |
| this.catLink.removeChild (this.catLink.firstChild);
| |
| this.catLink.appendChild (make (this.currentCategory, true));
| |
| this.catLink.href = wgArticlePath.replace ('$1', 'Category:' + this.currentCategory);
| |
| this.catLink.title = "";
| |
| this.catLink.className = this.currentExists ? "" : 'new';
| |
| this.lastSavedCategory = this.currentCategory;
| |
| this.lastSavedKey = this.currentKey;
| |
| this.lastSavedExists = this.currentExists;
| |
| // Close form and redisplay category
| |
| if (this.list) this.list.style.display = 'none';
| |
| if (this.engineSelector) this.engineSelector.style.display = 'none';
| |
| this.form.style.display = 'none';
| |
| this.catLink.style.display = "";
| |
| if (this.isAddCategory) {
| |
| if (onUpload) {
| |
| var newAdder = new CategoryEditor (this.line, null, this.span, true); // Create a new one
| |
| }
| |
| this.isAddCategory = false;
| |
| this.linkSpan.parentNode.removeChild (this.linkSpan);
| |
| this.makeLinkSpan ();
| |
| this.span.appendChild (this.linkSpan);
| |
| }
| |
| if (!this.undoLink && !onUpload) {
| |
| // Append an undo link.
| |
| var span = make ('span');
| |
| var lk = make ('a'); lk.href = '#catlinks'; lk.onclick = bind (this.rollback, this);
| |
| lk.appendChild (make (HotCat.links.undo, true));
| |
| span.appendChild (make (' ', true));
| |
| span.appendChild (lk);
| |
| this.normalLinks.appendChild (span);
| |
| this.undoLink = span;
| |
| }
| |
| this.linkSpan.style.display = "";
| |
| this.state = CategoryEditor.CHANGED;
| |
| },
| |
|
| |
| commit : function (comment) {
| |
| // Check again to catch problem cases after redirect resolution
| |
| if ( ( this.currentCategory == this.originalCategory
| |
| && (this.currentKey == this.originalKey
| |
| || this.currentKey === null && this.originalKey.length == 0
| |
| || this.originalKey === null && this.currentKey.length == 0
| |
| )
| |
| )
| |
| || wgNamespaceNumber == 14 && this.currentCategory == wgTitle
| |
| )
| |
| {
| |
| this.cancel ();
| |
| return;
| |
| }
| |
| if (commitButton || onUpload) {
| |
| this.close ();
| |
| } else {
| |
| if (this.list) this.list.style.display = 'none';
| |
| if (this.engineSelector) this.engineSelector.style.display = 'none';
| |
| // Execute change from this.originalCategory to this.currentCategory|this.currentKey,
| |
| var editlk = wgServer + wgScript + '?title=' + encodeURIComponent (wgPageName)
| |
| + '&action=edit';
| |
| var url = editlk + '&hotcat_newcat=' + encodeURIComponent (this.currentCategory);
| |
| if (this.currentKey) url += '&hotcat_sortkey=' + encodeURIComponent (this.currentKey);
| |
| if (this.originalCategory.length > 0)
| |
| url += '&hotcat_removecat=' + encodeURIComponent (this.originalCategory);
| |
| if (comment) url = url + '&hotcat_comment=' + encodeURIComponent (comment);
| |
| if (this.noCommit || HotCat.no_autocommit) url = url + '&hotcat_nocommit=1';
| |
| document.location = url;
| |
| }
| |
| },
| |
|
| |
| remove : function (evt) {
| |
| this.doRemove (evtKeys (evt) & 1);
| |
| return evtKill (evt);
| |
| },
| |
|
| |
| doRemove : function (noCommit) {
| |
| if (this.isAddCategory) { // Empty input on adding a new category
| |
| this.cancel ();
| |
| return;
| |
| }
| |
| if (!commitButton && !onUpload) {
| |
| for (var i = 0; i < editors.length; i++) {
| |
| if (editors[i].state != CategoryEditor.UNCHANGED) {
| |
| setMultiInput();
| |
| break;
| |
| }
| |
| }
| |
| }
| |
| if (commitButton) {
| |
| this.catLink.style.textDecoration = 'line-through';
| |
| this.originalState = this.state;
| |
| this.state = CategoryEditor.DELETED;
| |
| this.normalLinks.style.display = 'none';
| |
| this.undelLink.style.display = "";
| |
| } else {
| |
| if (onUpload) {
| |
| // Remove this editor completely
| |
| this.removeEditor ();
| |
| } else {
| |
| // Execute single category deletion.
| |
| var editlk = wgServer + wgScript + '?title=' + encodeURIComponent (wgPageName)
| |
| + '&action=edit';
| |
| if (noCommit || HotCat.no_autocommit) editlk += '&hotcat_nocommit=1';
| |
| document.location =
| |
| editlk + '&hotcat_removecat=' + encodeURIComponent (this.originalCategory);
| |
| }
| |
| }
| |
| },
| |
|
| |
| restore : function (evt) {
| |
| // Can occur only if we do have a commit button and are not on the upload form
| |
| this.catLink.style.textDecoration = "";
| |
| this.state = this.originalState;
| |
| this.normalLinks.style.display = "";
| |
| this.undelLink.style.display = 'none';
| |
| return evtKill (evt);
| |
| },
| |
|
| |
| // Internal operations
| |
|
| |
| setValueFromList : function (idx) {
| |
| if (typeof (idx) == 'undefined') idx = this.list.selectedIndex;
| |
| if (idx >= 0 && idx < this.list.options.length) {
| |
| var v = this.text.value.split ('|');
| |
| this.text.value = this.list.options[idx].text + (v.length > 1 ? '|' + v[1] : "");
| |
| this.inputExists = true; // Might be wrong if from a dab list...
| |
| if (this.icon) this.icon.src = HotCat.existsYes;
| |
| return true;
| |
| }
| |
| return false;
| |
| },
| |
|
| |
| sanitizeInput : function () {
| |
| var v = this.text.value || "";
| |
| v = v.replace(/^(\s|_)+/, ""); // Trim leading blanks and underscores
| |
| var re = new RegExp ('^(' + HotCat.category_regexp + '):');
| |
| if (re.test (v)) v = v.substring (v.indexOf (':') + 1);
| |
| v = capitalize (v);
| |
| // Only update the input field if there is a difference. IE8 appears to reset the selection
| |
| // and place the cursor at the front upon reset, which makes our autocompletetion become a
| |
| // nuisance. FF and IE6 don't seem to have this problem.
| |
| if (this.text.value != null && this.text.value != v)
| |
| this.text.value = v;
| |
| },
| |
|
| |
| makeCall : function (url, callbackObj, engine, queryKey) {
| |
| var cb = callbackObj;
| |
| var e = engine;
| |
| var v = queryKey;
| |
| var r = sajax_init_object ();
| |
| cb.requests.push (r);
| |
| r.open('GET', url, true);
| |
| r.onreadystatechange =
| |
| bind (
| |
| function () {
| |
| if (r.readyState == 4) {
| |
| if (r.status != 200) cb.dontCache = true;
| |
| if (r.status == 200 && r.responseText != null) {
| |
| var titles = e.handler (r.responseText, v);
| |
| if (titles && titles.length > 0) {
| |
| if (cb.allTitles == null) {
| |
| cb.allTitles = titles;
| |
| } else {
| |
| cb.allTitles = cb.allTitles.concat (titles);
| |
| }
| |
| }
| |
| }
| |
| cb.callsMade++;
| |
| }
| |
| if (cb.callsMade == cb.nofCalls) {
| |
| if (!cb.dontCache && !suggestionConfigs[cb.engineName].cache[v]) {
| |
| suggestionConfigs[cb.engineName].cache[v] = cb.allTitles;
| |
| }
| |
| if (!cb.cancelled) this.showSuggestions (cb.allTitles, cb.noCompletion, v);
| |
| if (cb === this.callbackObj) this.callbackObj = null;
| |
| delete cb;
| |
| }
| |
| }
| |
| ,this
| |
| );
| |
| r.setRequestHeader ('Pragma', 'cache=yes');
| |
| r.setRequestHeader ('Cache-Control', 'no-transform');
| |
| r.send (null);
| |
| },
| |
|
| |
| callbackObj : null,
| |
|
| |
| textchange : function (dont_autocomplete, force) {
| |
| // Hide all other lists
| |
| CategoryEditor.makeActive (this);
| |
| if (noSuggestions) {
| |
| // No Ajax: just make sure the list is hidden
| |
| if (this.list) this.list.style.display = 'none';
| |
| if (this.engineSelector) this.engineSelector.style.display = 'none';
| |
| if (this.icon) this.icon.style.display = 'none';
| |
| return;
| |
| }
| |
|
| |
| // Get input value, omit sort key, if any
| |
| this.sanitizeInput ();
| |
| var v = this.text.value;
| |
| // Disregard anything after a pipe.
| |
| var pipe = v.indexOf ('|');
| |
| if (pipe >= 0) v = v.substring (0, pipe);
| |
| if (this.lastInput == v && !force) return; // No change
| |
| this.lastInput = v;
| |
| this.lastRealInput = v;
| |
| if (v.length == 0) { this.showSuggestions([]); return; }
| |
| if (!sajax_init_object ()) {
| |
| noSuggestions = true;
| |
| if (this.list) this.list.style.display = 'none';
| |
| if (this.engineSelector) this.engineSelector.style.display = 'none';
| |
| if (this.icon) this.icon.style.display = 'none';
| |
| return;
| |
| }
| |
| if (this.callbackObj) this.callbackObj.cancelled = true;
| |
| var engine = suggestionConfigs[this.engine] ? this.engine : 'combined';
| |
|
| |
| if (suggestionConfigs[engine].cache[v]) {
| |
| this.showSuggestions (suggestionConfigs[engine].cache[v], dont_autocomplete, v);
| |
| return;
| |
| }
| |
|
| |
| var engines = suggestionConfigs[engine].engines;
| |
| this.callbackObj =
| |
| {allTitles: null, requests: [], callsMade: 0, nofCalls: engines.length, noCompletion: dont_autocomplete, engineName: engine};
| |
| for (var j = 0; j < engines.length; j++) {
| |
| engine = suggestionEngines[engines[j]];
| |
| var url = wgServer + wgScriptPath + engine.uri.replace (/\$1/g, encodeURIComponent (v));
| |
| this.makeCall (url, this.callbackObj, engine, v);
| |
| }
| |
| },
| |
|
| |
| showSuggestions : function (titles, dontAutocomplete, queryKey, noEngine) {
| |
| this.dab = null;
| |
| if (!this.list) return;
| |
| if (noSuggestions) {
| |
| if (this.list) this.list.style.display = 'none';
| |
| if (this.engineSelector) this.engineSelector.style.display = 'none';
| |
| if (this.icon) this.icon.style.display = 'none';
| |
| this.inputExists = true; // Default...
| |
| return;
| |
| }
| |
| if (!noEngine) {
| |
| noEngine = this.engineSelector == null;
| |
| } else {
| |
| if (this.engineSelector) this.engineSelector.style.display = 'none';
| |
| }
| |
| if (queryKey) {
| |
| if (this.lastInput.indexOf (queryKey) != 0) return;
| |
| if (this.lastQuery && this.lastInput.indexOf (this.lastQuery) == 0 && this.lastQuery.length > queryKey.length)
| |
| return;
| |
| }
| |
| this.lastQuery = queryKey;
| |
|
| |
| if (!titles || titles.length == 0) {
| |
| if (this.list) this.list.style.display = 'none';
| |
| if (this.engineSelector) this.engineSelector.style.display = 'none';
| |
| if (this.icon) this.icon.src = HotCat.existsNo;
| |
| this.inputExists = false;
| |
| return;
| |
| }
| |
|
| |
| // Get current input text
| |
| var v = this.text.value.split('|');
| |
| var key = v.length > 1 ? '|' + v[1] : "";
| |
| v = capitalize (v[0]);
| |
|
| |
| titles.sort (
| |
| function (a, b) {
| |
| if (a.indexOf (b) == 0) return 1; // a begins with b: a > b
| |
| if (b.indexOf (a) == 0) return -1; // b begins with a: a < b
| |
| // Opensearch may return stuff not beginning with the search prefix!
| |
| var prefixMatchA = (a.indexOf (v) == 0 ? 1 : 0);
| |
| var prefixMatchB = (b.indexOf (v) == 0 ? 1 : 0);
| |
| if (prefixMatchA != prefixMatchB) return prefixMatchB - prefixMatchA;
| |
| if (a < b) return -1;
| |
| if (b < a) return 1;
| |
| return 0;
| |
| }
| |
| );
| |
| // Remove duplicates
| |
| for (var i = 0; i+1 < titles.length; i++) {
| |
| if (titles[i] == titles[i+1]) {
| |
| titles.splice (i+1, 1);
| |
| i--;
| |
| }
| |
| }
| |
| var firstTitle = titles[0];
| |
| var completed = this.autoComplete (firstTitle, v, key, dontAutocomplete);
| |
| this.icon.src = completed ? HotCat.existsYes : HotCat.existsNo;
| |
| this.inputExists = completed;
| |
| if (completed) {
| |
| this.lastInput = firstTitle;
| |
| if (titles.length == 1) {
| |
| this.list.style.display = 'none';
| |
| if (this.engineSelector) this.engineSelector.style.display = 'none';
| |
| return;
| |
| }
| |
| }
| |
| if (!this.is_active) {
| |
| this.list.style.display = 'none';
| |
| if (this.engineSelector) this.engineSelector.style.display = 'none';
| |
| return;
| |
| }
| |
| var nofItems = (titles.length > 5 ? 5 : titles.length);
| |
| if (nofItems <= 1) nofItems = 2;
| |
| this.list.size = nofItems;
| |
| this.list.style.align = 'left';
| |
| this.list.style.zIndex = 5;
| |
| this.list.style.position = 'absolute';
| |
| // Compute initial list position. First the height.
| |
| var listh = 0;
| |
| if (this.list.style.display == 'none') {
| |
| // Off-screen display to get the height
| |
| this.list.style.top = this.text.offsetTop + 'px';
| |
| this.list.style.left = '-10000px';
| |
| this.list.style.display = "";
| |
| listh = this.list.offsetHeight;
| |
| this.list.style.display = 'none';
| |
| } else {
| |
| listh = this.list.offsetHeight;
| |
| }
| |
| // Approximate calculation of maximum list size
| |
| var maxListHeight = listh;
| |
| if (nofItems < 5) maxListHeight = (listh / nofItems) * 5;
| |
|
| |
| function scroll_offset (what) {
| |
| var s = 'scroll' + what;
| |
| return (document.documentElement ? document.documentElement[s] : 0)
| |
| || document.body[s] || 0;
| |
| }
| |
| function viewport (what) {
| |
| if (typeof (is_safari) != 'undefined' && is_safari && !document.evaluate)
| |
| return window['inner' + what];
| |
| var s = 'client' + what;
| |
| if (typeof (is_opera) != 'undefined' && is_opera) return document.body[s];
| |
| return (document.documentElement ? document.documentElement[s] : 0)
| |
| || document.body[s] || 0;
| |
| }
| |
| function position (node) {
| |
| // Stripped-down simplified position function. It's good enough for our purposes.
| |
| if (node.getBoundingClientRect) {
| |
| var box = node.getBoundingClientRect ();
| |
| return { x : Math.round (box.left + scroll_offset ('Left'))
| |
| ,y : Math.round (box.top + scroll_offset ('Top'))
| |
| };
| |
| }
| |
| var t = 0, l = 0;
| |
| do {
| |
| t = t + (node.offsetTop || 0);
| |
| l = l + (node.offsetLeft || 0);
| |
| node = node.offsetParent;
| |
| } while (node);
| |
| return {x : l, y : t};
| |
| }
| |
|
| |
| // IE6 seems to report in this.text.offsetTop and this.text.offsetLeft global offsets??
| |
| // Possibly this has something to do with the special status of input elements in IE as
| |
| // "windowed controls". Calculate the relative offsets manually.
| |
| var textPos = position (this.text);
| |
| var catLinePos = position (this.line);
| |
| var textTop = textPos.y - catLinePos.y;
| |
| var textLeft = textPos.x - catLinePos.x;
| |
| if (window.ie6_bugs) {
| |
| // IE6 somehow has a problem with inline-displayed forms (to which our list belongs), and will add the
| |
| // offset of the beginning of the text to the offsets we'd normally calculate, which in particular with
| |
| // right-aligned category lines as they occur in some older skins completely misplaces the lists, sometimes
| |
| // even off-screen. This appears to affect only the horizontal positioning of the list and of the
| |
| // engineSelector. Try to account for this bizarre behavior. Notes: dunno if that also occurs on IE7.
| |
| var textStartPos = position (this.line.firstChild);
| |
| textStartPos.x -= catLinePos.x;
| |
| textLeft -= textStartPos.x;
| |
| }
| |
| var nl = textLeft;
| |
| var nt = 0;
| |
| var offset = 0;
| |
| if (!noEngine) {
| |
| this.engineSelector.style.zIndex = 5;
| |
| this.engineSelector.style.position = 'absolute';
| |
| this.engineSelector.style.width = this.text.offsetWidth + 'px';
| |
| // Figure out the height of this selector: display it off-screen, then hide it again.
| |
| if (this.engineSelector.style.display == 'none') {
| |
| this.engineSelector.style.left = '-1000px';
| |
| this.engineSelector.style.top = textTop + 'px';
| |
| this.engineSelector.style.display = "";
| |
| offset = this.engineSelector.offsetHeight;
| |
| this.engineSelector.style.display = 'none';
| |
| } else {
| |
| offset = this.engineSelector.offsetHeight;
| |
| }
| |
| this.engineSelector.style.left = nl + 'px';
| |
| }
| |
| if (textPos.y < maxListHeight + offset) {
| |
| // The list might extend beyond the upper border of the page. Let's avoid that by placing it
| |
| // below the input text field.
| |
| nt = textTop + this.text.offsetHeight + offset + 1;
| |
| if (!noEngine) this.engineSelector.style.top = textTop + this.text.offsetHeight + 'px';
| |
| } else {
| |
| nt = textTop - listh - offset;
| |
| if (!noEngine) this.engineSelector.style.top = textTop - offset + 'px';
| |
| }
| |
| this.list.style.top = nt + 'px';
| |
| this.list.style.width = ""; // No fixed width (yet)
| |
| this.list.style.left = nl + 'px';
| |
| // (Re-)fill the list
| |
| while (this.list.firstChild) this.list.removeChild (this.list.firstChild);
| |
| for (var i = 0 ; i < titles.length ; i++) {
| |
| var opt = make ('option') ;
| |
| opt.appendChild (make (titles[i], true));
| |
| this.list.appendChild (opt);
| |
| }
| |
| if (!noEngine) this.engineSelector.style.display = "";
| |
| this.list.style.display = 'block';
| |
| // Set the width of the list
| |
| var scroll = scroll_offset ('Left');
| |
| var view_w = viewport ('Width');
| |
| var l_pos = position (this.list);
| |
| if (this.list.offsetWidth < this.text.offsetWidth) {
| |
| this.list.style.width = this.text.offsetWidth + 'px';
| |
| return;
| |
| }
| |
| // Make sure that the list fits horizontally into the browser window
| |
| var w = this.list.offsetWidth;
| |
| if (l_pos.x + w > scroll + view_w) {
| |
| if (w > view_w) w = view_w;
| |
| this.list.style.width = w + 'px';
| |
| this.list.style.left = nl - (l_pos.x + w - scroll - view_w) + 'px';
| |
| }
| |
| },
| |
|
| |
| autoComplete : function (newVal, actVal, key, dontModify) {
| |
| if (newVal == actVal) return true;
| |
| if (dontModify || newVal.indexOf (actVal) != 0) return false;
| |
| // Actual input is a prefix of the new text. Fill in new text, selecting the newly added suffix
| |
| // such that it can be easily removed by typing backspace if the suggestion is unwanted.
| |
| if (!( this.text.setSelectionRange
| |
| || this.text.createTextRange
| |
| || typeof (this.text.selectionStart) != 'undefined'
| |
| && typeof (this.text.selectionEnd) != 'undefined'
| |
| )
| |
| )
| |
| return false;
| |
| // Here we know that we can indeed select properly. If we can't doing this would be a major
| |
| // annoyance.
| |
| this.text.focus();
| |
| var start = actVal.length;
| |
| this.text.value = newVal + key;
| |
| if (this.text.setSelectionRange) // e.g. khtml
| |
| this.text.setSelectionRange (start, newVal.length);
| |
| else if (this.text.createTextRange) { // IE
| |
| var new_selection = this.text.createTextRange();
| |
| new_selection.move ('character', start);
| |
| new_selection.moveEnd ('character', newVal.length - start);
| |
| new_selection.select();
| |
| } else {
| |
| this.text.selectionStart = start;
| |
| this.text.selectionEnd = newVal.length;
| |
| }
| |
| return true;
| |
| },
| |
|
| |
| processKey : function (evt) {
| |
| if (this.lastKey == 38 || this.lastKey == 40) { // Up and down arrows
| |
| if (this.list.style.display != 'none') {
| |
| // List is visible, so there are suggestions
| |
| this.highlightSuggestion (this.lastKey == 38 ? -1 : 1);
| |
| // Kill the event, otherwise some browsers (e.g., Firefox) may additionally treat an up-arrow as
| |
| // "place the text cursor at the front", which we don't want here.
| |
| return evtKill (evt);
| |
| } else if ( this.keyCount <= 1
| |
| && (!this.callbackObj || this.callbackObj.callsMade == this.callbackObj.nofCalls)
| |
| )
| |
| {
| |
| // If no suggestions displayed, get them, unless we're already getting them.
| |
| this.textchange ();
| |
| }
| |
| }
| |
| return true;
| |
| },
| |
|
| |
| highlightSuggestion : function (dir) {
| |
| if (noSuggestions || !this.list || this.list.style.display == 'none') return;
| |
| var curr = this.list.selectedIndex;
| |
| var tgt = curr < 0 ? 0 : curr + dir;
| |
| tgt = tgt < 0 ? 0 : tgt;
| |
| if (tgt != curr && tgt < this.list.options.length) {
| |
| if (curr >= 0 && curr < this.list.options.length) this.list.options[curr].selected = false;
| |
| this.list.options[tgt].selected = true;
| |
| // Get current input text
| |
| var v = this.text.value.split('|');
| |
| var key = v.length > 1 ? '|' + v[1] : "";
| |
| var completed = this.autoComplete (this.list.options[tgt].text, this.lastInput, key, false);
| |
| if (!completed) {
| |
| this.text.value = this.list.options[tgt].text + key;
| |
| }
| |
| this.lastInput = this.list.options[tgt].text;
| |
| }
| |
| },
| |
|
| |
| resetKeySelection : function () {
| |
| if (noSuggestions || !this.list || this.list.style.display == 'none') return;
| |
| var curr = this.list.selectedIndex;
| |
| if (curr >= 0 && curr < this.list.options.length) {
| |
| this.list.options[curr].selected = false;
| |
| // Get current input text
| |
| var v = this.text.value.split('|');
| |
| var key = v.length > 1 ? '|' + v[1] : "";
| |
| this.text.value = this.lastRealInput + key;
| |
| this.lastInput = this.lastRealInput;
| |
| }
| |
| }
| |
|
| |
| }; // end CategoryEditor.prototype
| |
|
| |
| function initialize () {
| |
| // User configurations. Do this here, called from the onload handler, so that users can
| |
| // override it easily in their own user script files by just declaring variables. JSconfig
| |
| // is some feature used at Wikimedia Commons.
| |
| HotCat.no_autocommit = window.hotcat_no_autocommit
| |
| || typeof (JSconfig) != 'undefined' && JSconfig.keys['HotCatNoAutoCommit']
| |
| || 0;
| |
| HotCat.suggest_delay = window.hotcat_suggestion_delay
| |
| || typeof (JSconfig) != 'undefined' && JSconfig.keys['HotCatSuggestionDelay']
| |
| || 100;
| |
| HotCat.editbox_width = window.hotcat_editbox_width
| |
| || typeof (JSconfig) != 'undefined' && JSconfig.keys['HotCatEditBoxWidth']
| |
| || 40;
| |
| HotCat.suggestions = window.hotcat_suggestions
| |
| || typeof (JSconfig) != 'undefined' && JSconfig.keys['HotCatSuggestions']
| |
| || 'combined';
| |
| if (typeof (HotCat.suggestions) != 'string' || !suggestionConfigs[HotCat.suggestions])
| |
| HotCat.suggestions = 'combined';
| |
| HotCat.fixed_search = window.hotcat_suggestions_fixed
| |
| || typeof (JSconfig) != 'undefined' && JSconfig.keys['HotCatFixedSuggestions']
| |
| || false;
| |
| // Catch both native RTL and "faked" RTL through [[MediaWiki:Rtl.js]]
| |
| is_rtl = hasClass (document.body, 'rtl');
| |
| if (!is_rtl) {
| |
| if (document.defaultView && document.defaultView.getComputedStyle) { // Gecko etc.
| |
| is_rtl = document.defaultView.getComputedStyle (document.body, null).getPropertyValue ('direction');
| |
| } else if (document.body.currentStyle) { // IE, has subtle differences to getComputedStyle
| |
| is_rtl = document.body.currentStyle['direction'];
| |
| } else { // Not exactly right, but best effort
| |
| is_rtl = document.body.style['direction'];
| |
| }
| |
| is_rtl = (is_rtl == 'rtl');
| |
| }
| |
| }
| |
|
| |
| function can_edit () {
| |
| var container = null;
| |
| switch (skin) {
| |
| case 'cologneblue':
| |
| container = document.getElementById ('quickbar');
| |
| // Fall through
| |
| case 'standard':
| |
| case 'nostalgia':
| |
| if (!container) container = document.getElementById ('topbar');
| |
| var lks = container.getElementsByTagName ('a');
| |
| for (var i = 0; i < lks.length; i++) {
| |
| if ( param ('title', lks[i].href) == wgPageName
| |
| && param ('action', lks[i].href) == 'edit')
| |
| return true;
| |
| }
| |
| return false;
| |
| default:
| |
| // all modern skins:
| |
| return document.getElementById ('ca-edit') != null;
| |
| }
| |
| return false;
| |
| }
| |
|
| |
| function setup_upload () {
| |
| onUpload = true;
| |
| // Add an empty category bar above the "watch this" box, and change the onsubmit handler.
| |
| var ip = document.getElementById ('wpWatchthis');
| |
| if (!ip) return;
| |
| var reupload = document.getElementById ('wpForReUpload');
| |
| var destFile = document.getElementById ('wpDestFile');
| |
| if ( (reupload && !!reupload.value)
| |
| || (destFile && (destFile.disabled || destFile.readonly)))
| |
| return; // re-upload form...
| |
| // Insert a table row with two fields (label and empty category bar)
| |
| ip = ip.parentNode.parentNode; // The containing <tr>
| |
| var newRow = make ('tr');
| |
| var labelCell = make ('td');
| |
| var lineCell = make ('td');
| |
| newRow.appendChild (labelCell);
| |
| newRow.appendChild (lineCell);
| |
| // Create the category line
| |
| catLine = make ('div');
| |
| catLine.className = 'catlinks';
| |
| catLine.id = 'catlinks';
| |
| catLine.style.textAlign = 'left';
| |
| lineCell.appendChild (catLine);
| |
| // Create the label
| |
| var label = null;
| |
| if (typeof (UFUI) != 'undefined' &&
| |
| typeof (UFUI.getLabel) == 'function') {
| |
| try {
| |
| label = UFUI.getLabel ('wpCategoriesUploadLbl');
| |
| } catch (ex) {
| |
| label = null;
| |
| }
| |
| }
| |
| if (!label) {
| |
| labelCell.id = 'hotcatLabel';
| |
| labelCell.appendChild (make (HotCat.categories), true);
| |
| } else {
| |
| labelCell.id = 'hotcatLabelTranslated';
| |
| labelCell.appendChild (label);
| |
| }
| |
| labelCell.className = 'mw-label';
| |
| labelCell.style.textAlign = 'right';
| |
| labelCell.style.verticalAlign = 'middle';
| |
| // Change the onsubmit handler
| |
| var form = document.getElementById ('upload') || document.getElementById ('mw-upload-form');
| |
| if (form) {
| |
| var optionsTable = document.getElementById ('mw-htmlform-options');
| |
| if (optionsTable) optionsTable.width = '100%';
| |
| ip.parentNode.insertBefore (newRow, ip);
| |
| textHeight = labelCell.firstChild.offsetHeight;
| |
| form.onsubmit = (function (oldSubmit) {
| |
| return function () {
| |
| var do_submit = true;
| |
| if (oldSubmit) {
| |
| if (typeof (oldSubmit) == 'string')
| |
| do_submit = eval (oldSubmit);
| |
| else if (typeof (oldSubmit) == 'function')
| |
| do_submit = oldSubmit.apply (form, arguments);
| |
| }
| |
| if (!do_submit) return false;
| |
| closeForm ();
| |
| // Copy the categories
| |
| var eb = document.getElementById ('wpUploadDescription')
| |
| || document.getElementById ('wpDesc');
| |
| for (var i = 0; i < editors.length; i++) {
| |
| var t = editors[i].currentCategory;
| |
| if (!t) continue ;
| |
| var key = editors[i].currentKey;
| |
| var new_cat = '[[' + HotCat.category_canonical + ':' + t + (key ? '|' + key : "") + ']]';
| |
| // Only add if not already present
| |
| var cleanedText = eb.value.replace(/<\!--(\s|\S)*?--\>/g, "")
| |
| .replace(/<nowiki\>(\s|\S)*?<\/nowiki>/g, "");
| |
| if (!find_category (cleanedText, t, true)) {
| |
| eb.value += '\n' + new_cat;
| |
| }
| |
| }
| |
| return true;
| |
| };
| |
| }) (form.onsubmit);
| |
| }
| |
| }
| |
|
| |
| var cleanedText = null;
| |
|
| |
| function isOnPage (span) {
| |
| var catTitle = title (span.firstChild.getAttribute ('href', 2));
| |
| if (!catTitle) return null;
| |
| catTitle = catTitle.substr (catTitle.indexOf (':') + 1).replace (/_/g, ' ');
| |
| var result = { title : catTitle, match : ["", "", ""] };
| |
| if (pageText === null) return result;
| |
| if (cleanedText === null) {
| |
| cleanedText = pageText.replace(/<\!--(\s|\S)*?--\>/g, "")
| |
| .replace(/<nowiki\>(\s|\S)*?<\/nowiki>/g, "");
| |
| }
| |
| result.match = find_category (cleanedText, catTitle, true);
| |
| return result;
| |
| }
| |
|
| |
| var initialized = false;
| |
|
| |
| function setup () {
| |
| if (initialized) return;
| |
| initialized = true;
| |
| // Find the category bar, or create an empty one if there isn't one. Then add -/+- links after
| |
| // each category, and add the + link.
| |
| catLine = catLine // Special:Upload
| |
| || document.getElementById ('mw-normal-catlinks') // MW >= 1.13alpha
| |
| || getElementsByClassName (document , 'p' , 'catlinks')[0]; // MW < 1.13
| |
| var hiddenCats = document.getElementById ('mw-hidden-catlinks');
| |
| if (!catLine) {
| |
| var footer = null;
| |
| if (!hiddenCats) {
| |
| footer = getElementsByClassName (document , 'div' , 'printfooter')[0];
| |
| if (!footer) return; // Don't know where to insert the category line
| |
| }
| |
| catLine = make ('div');
| |
| catLine.id = 'mw-normal-catlinks';
| |
| catLine.style.textAlign = 'left';
| |
| // Add a label
| |
| var label = make ('a');
| |
| label.href = wgArticlePath.replace ('$1', 'Special:Categories');
| |
| label.title = HotCat.categories;
| |
| label.appendChild (make (HotCat.categories, true));
| |
| catLine.appendChild (label);
| |
| catLine.appendChild (make (':', true));
| |
| // Insert the new category line
| |
| var container = (hiddenCats ? hiddenCats.parentNode : document.getElementById ('catlinks'));
| |
| if (!container) {
| |
| container = make ('div');
| |
| container.id = 'catlinks';
| |
| footer.parentNode.insertBefore (container, footer.nextSibling);
| |
| }
| |
| container.className = 'catlinks noprint';
| |
| container.style.display = "";
| |
| if (!hiddenCats) {
| |
| container.appendChild (catLine);
| |
| } else {
| |
| container.insertBefore (catLine, hiddenCats);
| |
| }
| |
| textHeight = label.firstChild.offsetHeight;
| |
| } // end if catLine exists
| |
| catLine.style.position = 'relative';
| |
| if (is_rtl) catLine.dir = 'rtl';
| |
|
| |
| // Create editors for all existing categories
| |
|
| |
| function createEditors (line) {
| |
| var cats = line.getElementsByTagName ('span');
| |
| // Copy cats, otherwise it'll also magically contain our added spans as it is a live collection!
| |
| var copyCats = new Array (cats.length);
| |
| for (var i = 0; i < cats.length; i++) copyCats[i] = cats[i];
| |
| var editor = null;
| |
| for (var i = 0; i < copyCats.length; i++) {
| |
| var test = isOnPage (copyCats[i]);
| |
| if (test !== null && test.match !== null) {
| |
| editor = new CategoryEditor (line, copyCats[i], test.title, test.match[2]);
| |
| }
| |
| }
| |
| return copyCats.length > 0 ? copyCats[copyCats.length-1] : null;
| |
| }
| |
|
| |
| var lastSpan = createEditors (catLine);
| |
| // Create one to add a new category
| |
| var editor = new CategoryEditor(catLine, null, null, lastSpan != null);
| |
| if (!onUpload) {
| |
| if (pageText !== null && hiddenCats) {
| |
| hiddenCats.style.position = 'relative';
| |
| if (is_rtl) hiddenCats.dir = 'rtl';
| |
| createEditors (hiddenCats);
| |
| }
| |
| // And finally add the "multi-mode" span. (Do this at the end, otherwise it ends up in the list above.)
| |
| var enableMulti = make ('span');
| |
| enableMulti.className = 'noprint';
| |
| if (is_rtl) enableMulti.dir = 'rtl';
| |
| catLine.insertBefore (enableMulti, catLine.firstChild.nextSibling);
| |
| enableMulti.appendChild (make ('\xa0', true)); // nbsp
| |
| multiSpan = make ('span');
| |
| enableMulti.appendChild (multiSpan);
| |
| multiSpan.innerHTML = '(<a>' + HotCat.addmulti + '</a>)';
| |
| var lk = multiSpan.getElementsByTagName ('a')[0];
| |
| lk.onclick = function (evt) {setMultiInput (); return evtKill (evt);};
| |
| lk.style.cursor = 'pointer';
| |
| }
| |
| cleanedText = null;
| |
| }
| |
|
| |
| function setPage (json) {
| |
| if (!json || !json.query || !json.query.pages) return;
| |
| for (var p in json.query.pages) {
| |
| var page = json.query.pages[p];
| |
| if (!page.revisions || page.revisions.length == 0) break;
| |
| pageText = page.revisions[0]['*'];
| |
| pageTime = page.revisions[0].timestamp.replace (/\D/g, "");
| |
| break;
| |
| }
| |
| }
| |
|
| |
| function getPage () {
| |
| // We know we have an article here.
| |
| if (wgArticleId == 0) {
| |
| // Doesn't exist yet.
| |
| pageText = "";
| |
| pageTime = null;
| |
| setup ();
| |
| } else {
| |
| var url = wgServer + wgScriptPath + '/api.php?format=json&callback=HotCat.start&action=query&titles='
| |
| + encodeURIComponent (wgPageName) + '&prop=info%7Crevisions&rvprop=content%7Ctimestamp';
| |
| var s = make ('script');
| |
| s.src = url;
| |
| s.type = 'text/javascript';
| |
| HotCat.start = function (json) { setPage (json); setup (); };
| |
| document.getElementsByTagName ('head')[0].appendChild (s);
| |
| window.setTimeout (setup, 4000); // 4 seconds. Just in case getting the wikitext takes longer.
| |
| }
| |
| }
| |
|
| |
| function run () {
| |
| initialize ();
| |
|
| |
| if (is_rtl && window.ie6_bugs) return; // Disabled! IE6 with RTL is just too broken...
| |
|
| |
| if (wgNamespaceNumber == -1 && wgCanonicalSpecialPageName == 'Upload' && wgUserName) {
| |
| setup_upload ();
| |
| setup ();
| |
| // Check for state restoration
| |
| if ( typeof (UploadForm) != 'undefined'
| |
| && typeof (UploadForm.previous_hotcat_state) != 'undefined'
| |
| && UploadForm.previous_hotcat_state != null)
| |
| UploadForm.previous_hotcat_state = setState (UploadForm.previous_hotcat_state);
| |
| } else {
| |
| if (!wgIsArticle || wgAction != 'view' || !can_edit() || HotCat.disable()) return;
| |
| getPage ();
| |
| }
| |
| }
| |
|
| |
| // Legacy stuff
| |
|
| |
| function closeForm () {
| |
| // Close all open editors without redirect resolution and other asynchronous stuff.
| |
| for (var i = 0; i < editors.length; i++) {
| |
| if (editors[i].state == CategoryEditor.OPEN) {
| |
| editors[i].cancel();
| |
| } else if (editors[i].state == CategoryEditor.CHANGE_PENDING) {
| |
| editors[i].sanitizeInput ();
| |
| var value = editors[i].text.value.split('|');
| |
| var key = null;
| |
| if (value.length > 1) key = value[1];
| |
| var v = value[0].replace(/_/g, ' ').replace(/^\s+|\s+$/g, "");
| |
| if (v.length == 0) {
| |
| editors[i].cancel ();
| |
| } else {
| |
| editors[i].currentCategory = v;
| |
| editors[i].currentKey = key;
| |
| editors[i].currentExists = this.inputExists;
| |
| editors[i].close ();
| |
| }
| |
| }
| |
| }
| |
| }
| |
|
| |
| function getState () {
| |
| var result = null;
| |
| for (var i = 0; i < editors.length; i++) {
| |
| var text = editors[i].currentCategory;
| |
| var key = editors[i].currentKey;
| |
| if (text && text.length > 0) {
| |
| if (key && key.length > 0) text += '|' + key;
| |
| if (result == null)
| |
| result = text;
| |
| else
| |
| result = result + '\n' + text;
| |
| }
| |
| }
| |
| return result;
| |
| }
| |
|
| |
| function setState (state) {
| |
| var cats = state.split ('\n');
| |
| if (cats.length == 0) return null;
| |
| if (initialized && editors.length == 1 && editors[0].isAddCategory) {
| |
| // Insert new spans and create new editors for them.
| |
| var newSpans = [];
| |
| var before = editors.length == 1 ? editors[0].span : null;
| |
| for (var i = 0; i < cats.length; i++) {
| |
| if (cats[i].length == 0) continue;
| |
| var cat = cats[i].split ('|');
| |
| var key = cat.length > 1 ? cat[1] : null;
| |
| cat = cat[0];
| |
| var lk = make ('a'); lk.href = wgArticlePath.split ('$1').join ('Category:' + encodeURI (cat));
| |
| lk.appendChild (make (cat, true));
| |
| lk.title = cat;
| |
| var span = make ('span');
| |
| span.appendChild (lk);
| |
| if (i == 0) catLine.insertBefore (make (' ', true), before);
| |
| catLine.insertBefore (span, before);
| |
| if (before && i+1 < cats.length) parent.insertBefore (make (' | ', true), before);
| |
| newSpans.push ({element: span, title: cat, 'key': key});
| |
| }
| |
| // And change the last one...
| |
| if (before) {
| |
| before.parentNode.insertBefore (make (' | ', true), before);
| |
| }
| |
| var editor = null;
| |
| for (var i = 0; i < newSpans.length; i++) {
| |
| editor = new CategoryEditor (catLine, newSpans[i].element, newSpans[i].title, newSpans[i].key);
| |
| }
| |
| }
| |
| return null;
| |
| }
| |
|
| |
| // Now export these legacy functions
| |
| window.hotcat_get_state = function () { return getState(); };
| |
| window.hotcat_set_state = function (state) { return setState (state); };
| |
| window.hotcat_close_form = function () { closeForm (); };
| |
|
| |
| addOnloadHook (run);
| |
| })();
| |
|
| |
| } // end if (guard)
| |
| //</source>
| |